I am running a lot of raycasts to ensure NPCs don’t clash into each other. This of course causes issues and I think is the reason why some of my rigs stopped moving but walked in place beforehand, but I decided to make a forum post once I couldn’t even run the game properly anymore (1 fps is frequent)
This is my script that is inserted to all rigs – the reason why I check if the humanoid’s ancestor is game or rigs is to seperate players and rigs, the rigs should only wait when i find a player
local anim = Instance.new("Animation")
anim.Parent = script.Parent
anim.AnimationId = "rbxassetid://WalkAnimIdThatIDidntPutInThisForumPost"
local antrac = script.Parent.Humanoid.Animator:LoadAnimation(anim)
while true do
script.Parent.Humanoid.WalkSpeed = 10
script.Parent.Humanoid:MoveTo(Vector3.new(math.random(-19.4, 392), 1.148, math.random(81.4, 363.3)))
if antrac.IsPlaying == false then
antrac:Play()
antrac.Looped = true
end
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {script.Parent}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local ray = workspace:Raycast(script.Parent.HumanoidRootPart.Position, script.Parent.HumanoidRootPart.Position + Vector3.new(0, 0, 3), rayParams)
if not ray then return end
if ray.Instance.Parent:FindFirstChild("Humanoid") then
if ray.Instance.Parent:FindFirstChild("Humanoid").Parent.Parent.Parent.Name == "rigs" then
ray.Instance.Parent:FindFirstChild("Humanoid"):MoveTo(ray.Instance.Parent:FindFirstChild("Humanoid").Parent.HumanoidRootPart.Position)
antrac:Stop()
continue
elseif ray.Instance.Parent:FindFirstChild("Humanoid").Parent.Parent.Parent.Name ~= "rigs" then
if ray.Instance.Parent:FindFirstChild("Humanoid").Parent.Parent.Name == "Game" then
ray.Instance.Parent:FindFirstChild("Humanoid"):MoveTo(ray.Instance.Parent:FindFirstChild("Humanoid").Parent.HumanoidRootPart.Position)
antrac:Stop()
task.wait(math.random(5, 9))
continue
end
end
end
script.Parent.Humanoid.MoveToFinished:Connect(function()
antrac:Stop()
end)
task.wait(math.random(5, 9))
end
I highly recommend having ONE script for all NPC’s. A centralized script essentially. Every time you create a new script, you are creating a new Lua thread (What roblox calls a “context”). Each thread has overhead. It needs its own execution state, stack space, and scheduling by Roblox’s task scheduler.
If you have 100 NPC’s, each with a script, you have 100 separate threads that need to be interpreted.
I also recommend looking into parallel Luau which I think will improve performance a ton. Here’s a video by CrusherFire that explains it perfectly:
Npcs are the perfect place to use OOP, please look into it. It works so good because every npc has the same methods, but can also carry their own data like their origins. You could just raycast from self.model.origin
Exactly what biasxed above said. OOP would work great here. You’re right, each NPC would need their own raycast, but you can do it in the same while true do loop.
It’s really not. NPC logic is literally what ECS and functional programming patterns were built for.
Luau doesn’t have real OOP and never will; it’s just metatable fanfic.
Don’t throw corporate buzzwords at people like they’re universal truths.
OP, go with what actually performs and scales; don’t use cOOPe.
Hmm maybe the video I found when researching about it wasnt truly OOP, but the video was similar enough for me to implement my system, so I marked it as the solution
You can’t have real OOP in Luau.
The language doesn’t have classes, inheritance models, or any of the machinery real OOP relies on.
What people call “OOP” in Luau is just tables + metatables pretending to be something they’re not - cOOPe.
ECS/FP sits way closer to how the VM actually works, so it performs better, scales better, and doesn’t fight the compiler.
The “OOP” patterns people copy-paste are just overhead and usually slow the game down.
Yeah that seems about right… Saw a lot of metatables and tables in the video. What do you recommend? You could have suggested something in that reply in the 4th sentence but I don’t really understand it tbh, seems like theres a lot of acronyms there
See, that’s the issue - metatables aren’t just confusing at first, they’re also slow. There’s basically zero upside to using them for “OOP” stuff (aside from __mode, which is super niche).
ECS in Luau is basically functional programming with extra steps, but in a good way:
E - Entity (your ID, usually the instance) C - Component (data stored in a dictionary keyed by the entity) S - System (a function that processes those dictionaries)
Here is a sample pseudo code:
local Health = {}
Players.PlayerAdded:Connect(function(plr)
Health[plr] = 100
end)
Players.PlayerRemoving:Connect(function(plr)
Health[plr] = nil
end)
That’s it. Super lightweight, super fast.
Just for God’s sake, don’t use JECS; it’s a fake ECS and all OOP underneath. Use this pattern I provided instead.
OP please, don’t listen to them. I know the solution is marked, but I’m trying to recorrect you so you don’t dig yourself a hole.
That is absolutely NOT ECS; the example he gave. That is literally just storing stuff in a dictionary. By their logic I’ve been doing ECS my very first days in Luau.
ECS zealots seem to come from backgrounds where they’ve read about how big studios optimize engines, and they want to apply these patterns everywhere.
Real ECS involves systems that iterate over ENTITIES with specific COMPONENT combinations. What was shown is just Health[Player] = 100. Thats literally just normal key-value storage.
“OOP bad, ECS good” take is architectural philosophy, not reality. Focus on optimizing your actual raycasts instead of rewriting your architecture based on misconceptions spread by incompetent people.
this is your OPINION, and there’s a lot of ways for different people to do the same thing. just because you think that one way is better because you understand it, doesn’t mean other people want to use the same strategy. OOP is perfectly fine, and though I realize it’s not “real OOP,” and is just a workaround, it doesn’t really matter. it is scalable, and tons of people use it. it isn’t throwing out buzz words if it is a legitimate valid solution to the question. and optimization wise, there’s more important things you can focus on.
How many raycasts are you shooting for it to start lagging? Because raycasts are dirt cheap and you shouldn’t have to optimize them from what I’m reading in your code. It’s only of length 3 so it’s super short as well