Currently, I am planning out an enemy/npc movement system that is performant (should be able to handle lots of npcs around the player), and ensures that the movement looks realistic and smooth. In my game, I am using lots of uneven and complex terrain, and this has puzzled me when deciding how to approach this sort of system. Its also important to mention that I would like the movement to be all calculated on the server, and the client simply renders the movement and animations as I do not want exploiters messing around with enemy positions.
What I am considering
The first thing I considered was to take a physics-based approach using align orientation and body velocity, as I have read that this system would work well with dynamic terrain and obstacles. In addition, a game called Vesteria uses body gyros and body velocities, and the final product of their npcs movement system works well with how many enemies they normally have spawned during gameplay. However, I have also read that there are heavy performance costs associated with using this sort of system with lots of npcs, and so I have been hesitant to immediately go with this approach. I also haven’t seen many recent posts talking about using this approach, instead they are more focused on using humanoid move to and lerping/tweening.
The second thing I am considering is to take a linear-interpolation based approach using lerping or tweening based on updates sent from the server to the client. I have read that this approach comes with good performance and allows for more predictable movement, but I am discouraged from using this approach as I fear there would be lots of extra adjustments to be made in order to smoothly navigate complex terrain – this might be too much of a headache for me to figure out.
I would also love to know if there are any other approaches that I haven’t considered that could work better, and also if any of my considered approaches are missing features that would make them better. Thank you so much for reading this post and I hope to hear your input
Edit: I have also used chat gpt to come up with more approaches that I could use. It suggested using a custom physics-based character controller instead of Humanoid – I am unsure, however, how to implement a system like this, and whether it would better suit my game.
Raycast is a line, Spherecast is a sphere, Blockcast is a cframe & size, and Shapecast takes a part. Perhaps you can use these to calculate the positional height for the NPC.
I was also thinking, you can figure out which 4 voxels the NPC is trying to walk on using the position of the HRP. Using ReadVoxels you can see what the character’s height should be.
Thank you for your reply. Yes, from what I have looked at I was going to raycast from above the npc downwards and adjust the position accordingly. I have also considered using two separate raycasts to find the slope angle in order to detect whether the npc can walk up it or not. I have not implemented this yet though with the linear-interpolation based approach since I would like to know whether using body gyros would be a better alternative . I have never heard of using read voxels before – I will have a look at that.
Place1.rbxl (84.0 KB)
Here I did some testing to see if I could find the ground of Terrain and position a part above the ground.
I was able to find the ground decently well with this system.
– one thing I didn’t do here is try to use the XZ position of the part to weigh each occupancy value. (likely more accurate)
Yeah I have looked at some posts and you can indeed get the position of terrain using FindPartOnRay:() with the last arguments set to false. Reading terrain voxels seems quite involved and complicated, so i’d rather attempt to just use the raycasting if it does work. But what I would like to know is whether the physics based approach is a better alternative to the interpolation based approach in terms of performance and realism.
Note that FindPartOnRay is depreciated and not recommended for future use.
Personally I’ve never seen Roblox physics look real or outperform raycasts – that being said, I’ve not tested it.
In my current tests, I was not able to outperform the Humanoid using ReadVoxelChannels. However I believe this is likely because the Humanoids are doing some tasks in parallel. Thus if you do wish to outperform Humanoid, you will likely need to make your code parallel.
update – after parallelizing my code, it performs identical to Humanoid.
Okay it’s pretty simple, use Lerping to move the enemies and script your own movement system using CFrame.
You will need to do movement calculations on the server and do any movement lerping (visual movement) on the client. For example calculating an enemies new position on the server then sending it to the client to lerp.
Roblox’s pathfinding service is a big performance eater so it will lag if you have over 10 npcs with it.
I wouldn’t recommend using Lerping to move the HRP. This is due to the difference between add-style animation and set-style animation. When moving an NPC into a wall, setting the CFrame will eventually go through the wall. But if you add to the current position, the physics calculation will move it back out of the wall each time.
Depending on how you use the PathfindingService / Path object, it can be a good option even for large quantities of NPCs. To give an example, if a mob of 30 zombies are targeting 1 player, theoretically you only need 1 path for all the zombies to follow. That said, actually implementing this is another story.
– a note on animations:
You likely need to not play animations on the server. I’m not entirely sure how to turn them off properly, but I do know all Animator-controlled (Motor6D & Bones) are local even for the server.
Animations from the animator will do 3 cframe multiplications per Motor6D per frame in a “parallel batch job” (see Transform). An R15 has 15 joints and R6 has 6 joints (which are Motor6D’s).
I created & cloned a lot of Humanoid rigs. Then I made a script to tell them to move around randomly on some terrain. Then I disabled that script and created a “custom” script. The custom script deletes the Humanoids and calculates the character height using ReadVoxelChannels. Then it sets the cframe of the HRP’s in PostSimulation. Then I created a simple movement function and told them to move around randomly as well.
Finally, I compared frame rates using ctrl+shift+F5.
I am not sure what. you mean by the following and how it is related to lerping:
This is due to the difference between add-style animation and set-style animation
If an npc was moving into a wall, surely the server would just keep on sending the same position, and not some position beyond the wall? In my game, I will have checks to make sure sight between the enemy and the target position is not blocked in order to start moving in the first place, and so i dont think this type of issue would be possible.
To handle animations, I was going to just handle them on the client. In my final system, an enemy represented on the server would be no more than a dictionary object with some key values (e.g position, health, buffs…), and an enemy on the client would be the visible model of the enemy with all the code to render movement and animations based on server events.
What’s the exact reason you want a custom movement system? Because 98% of the time, you barely get anything for your effort when making a custom movement system.
I know because I tried to make my own which required many fixes over the course of months, before I got fed up and switched to humanoids. The difference between the two systems besides one being much buggier was effectively zero.
Time cost is a factor that exists. Yes, they are easy, and yes, in theory a custom system could far surpass it in performance. But you need to consider the amount of time it takes vs the reward for your effort.
The reason I need to make a custom system is simply because using humanoid move to is just not viable for performance in my game, and also many sources urge to not use this feature if there will be lots of npcs. I know implementing a custom system is not as easy, but it is definitely within the realms of possibility for me once I figure out the best approach for me to use. I think the time trade-off is definitely worth it, having a custom system would really benefit development for me as I could fine tune the system to my specific needs