Choppy Humanoids: Target Position updating too quick for the server?

Alright, it’s about time I take it to my own post, as I’ve searched the deepest depths of the forums for what feels like weeks now and found nothing that is helping and/or getting an explanation to anything that is apparently supposed to solve my problem. So do not bother linking me to other posts that have a similar problem or topic.

The Problem:
Been working on a project that has to do with many enemies, by this you can probably guess, it involves pathfinding. The pathfinding works great, in fact I even have a form of AI separation in place. For whatever reason, whenever the enemy finally catches up to the target/player they begin to get choppy. Even if they’re extremely faster than the target they still do not catch up ALLEGEDLY. Why do I say allegedly? Because apparently on the server’s end of things the enemies are at least somtimes keeping up with the target. Still, on the client they appear to be a fair distance behind.

What I believe the problem is:
Considering the client has network ownership of their own character, there is no delay in movement, however the server has a pause before processing their new position. Essentially, the client’s position is updating too quick for the server.

Things I’ve Tried:

  • Binding a function on the client to update the server with the client’s true position and then fat checking it. (Barely makes a difference).
  • Applying Network Ownership to all parts of an enemy’s rig, to the client and vise versa. Giving the client network ownership however seems to make things worse. (I’m extremely sick of hearing people talk about this as a solution as it isn’t solving my problem).
  • Predicting the player’s position by LookVectors, Target & Enemy Velocity, etc. (Makes a huge difference but not a good difference. The enemies just overshot where they’re supposed to end up at).
  • Applying an Impulse to the root part of an enemy. (Sort of like the one above).

There is likely more advanced options, but I’m either incapable of doing them or need better explaining from before putting in the effort. I may have done some of these methods incorrectly as well, so you may mention them, but do explain.

I know many people suffer from this problem, and it’s about time I get an answer that I can understand. If you read this far, thank you, you’re more than welcome to generate a response with all this in mind. Ask questions if I wasn’t clear enough or you need a better idea of what I’m doing or what I mean.

P.S. Took me a while to type this because I don’t know how to put this into a post. I’ll provide screenshots/videos and some code IF it’s truly necessary.

2 Likes

First time bumping this. Want to make sure it’s seen.

1 Like

Second time bumping this. Want to make sure it’s seen.

1 Like

During the NPC movement, are you waiting for the MoveToFinished event in some fashion in a server script? (i.e. Humanoid.MoveToFinished:Wait())

This is very important as I fully believe this to be the cause of the problem you’re experiencing. Let’s assume you are for a moment.

You aren’t going to want to hear this, but if you are then network ownership being on the client IS very likely the problem. Picture this: while the NPC is far away and NetworkOwnership is set to Auto, the NPC will appear to move smoothly across the waypoints. Why? Because you’re listening to the MoveToFinished event on the Server. The server is doing the work in simulating the humanoid’s movement and other various duties that the humanoid needs to perform while players are not nearby and NetworkOwnership is set to Auto. However, default state for unanchored assembly is Auto network ownership. I’ll explain why this is a problem, and why your past attempt actually made the problem worse below.

In your post you mentioned this:

  • Applying Network Ownership to all parts of an enemy’s rig, to the client and vise versa. Giving the client network ownership however seems to make things worse. (I’m extremely sick of hearing people talk about this as a solution as it isn’t solving my problem).

You’ve made one very important mistake here, you’ve given the Client ownership of the physics, and thus, the simulation for the humanoid of the NPCs as well. while you’re still awaiting that MoveToFinished event on the server. Why is this a problem? Simple: network latency. Your path waypoints are roughly 4 studs apart by default, the MoveTo looks nice and smooth between them, but once it finishes (i.e. reaches its destination), the client has to tell the server and then the server has to actually start the next MoveTo, but since the client owns the physics of the humanoid simulation then the server is really just telling the client to perform that MoveTo, that’s two round trips.

Assuming still that all of the above is true. A solution to your problem would be to explicitly set the HumanoidRootPart NetworkOwner to nil via HumanoidRootPart:SetNetworkOwner(nil) on the server. This will prevent that automatic ownership change as the NPC approaches a player and should resolve the choppiness issue you’re seeing.

tl;dr: Set the NetworkOwnership on the HumanoidRootPart to nil via Humanoid:SetNetworkOwner(nil). This will prevent network ownership from being taken from the server which will likely solve your choppiness problem.

1 Like

I have already done this prior to the creation of this post. I said I’ve set it to the client to give it a try, as it seemed to be a solution for some people?? I was implying I’ve tried giving the server and the client both ownership for certain but it does not help. Setting it to the server of course keeps them nice and smooth, but it doesn’t appear to keep up when they get closer.

function Enemy:SetNetworkOwner(owner)
	
	local Humanoid = self.Rig:FindFirstChild("Humanoid")
	
	for index, instance in pairs(self.Rig:GetDescendants()) do
		if (instance:IsA("Part") or instance:IsA("MeshPart") or instance:IsA("UnionOperation")) then
			instance.CollisionGroup = "Enemies"
			instance:SetNetworkOwner(owner)
		end
	end
	
end

That’s my code for applying network ownership.

So you’re describing two different issues, I missed that second one which was a result of testing with Network Owner nil

So I’m assuming you mean that the NPC is “lagging” behind the player as they chase them. That’s another network latency issue which is a result of setting the Network Owner to the server. Hopefully I’m following correctly.

So there’s a couple of things to consider here.

Case 1: Network Owner is server.

Pros:

  • NPC moves smoothly along the waypoints.

Cons:

  • Network latency makes it lag behind its actual position on the server from the client’s perspective.

Case 2: Network Owner is client.

Pros:

  • NPC has no network latency.

Cons:

  • NPC appears to stop at each waypoint for a moment. (fixable by moving all movement logic to the client).
  • NPC control is exploitable and they can be manipulated by the client (exploiter).

This is the unfortunate bane of online games. There’s not really a perfect solution here for network latency since it requires that you sacrifice control of the NPC over to the client which is easily exploited, perhaps that isn’t a concern for you. If it is not a concern then you could easily hand over all of the movement logic to the client and simulate it entirely there, with the exception of things like damage being handled on the server. If that network latency is more of an issue than an exploiter teleporting the NPCs around and making them chase other players, etc, then you could definitely go this route instead.

One alternative is to attempt to create your own extrapolation logic for the NPCs… this would likely cause a lot of strange teleporting in your NPCs from the client’s perspective as the server update trickles in.

In the case that you do go with fully simulating movement on the client you would also need to consider how you make that consistent across clients. Presumably that would be something like a RemoteEvent that instructs all clients to move the NPC to some point or to some part, etc.

One thing I also should mention is that your server might be lagging depending on how much stress the NPCs cause on it. It’s tough to say without seeing a video of the issue to help visualize the problem.

When they’re much slower to the target. I already made this prediction in my initial post.

I will not do the logic on the client as it does make them exploitable and it’d expose my code. I would’ve already resorted to this if I didn’t have a problem. Considering 50% of the gameplay of this type of game is based around the functionality and smoothness of the Enemies, I simply cannot be leaving it in the client’s hands.

I personally feel it’s Roblox ultimately more than just games in general. I see many games off of Roblox pull off flawless AI in this regard but of course I need to know that it’s off of Roblox, it isn’t LUA, and it is likely much more sophisticated and secure on the client in comparison to Roblox.

Overall, I expected to hear exactly everything you said. If you have any more suggestions or ideas, do share or you know people who might have ideas or concepts, feel free to suggest this post to them. I will be leaving this post up and will continue to bump it a bit less often, but often nevertheless, as I’m down bad to find a solution to this. Many people experience a problem with this, there must surely be a way to let the server handle everything and still be at least 90% accurate all of the time.

Feel free to post a video of the issue. Would be helpful to see what you mean by “they’re much slower to the target.” Do they slow down when they get near a player? It’s kind of tough to understand what you mean by that. I’m assuming the walkspeed between the player and the NPC are the same.

Sure give me a few, to setup a video.

Not always the case. The AI is supposed to be flexible, and start very slow and even reach absurdly high speeds in comparison to the player depending on the factor in the game which is what will be the “wave”.

Not always the case. The AI is supposed to be flexible, and start very slow and even reach absurdly high speeds in comparison to the player depending on the factor in the game which is what will be the “wave”.

For my sanity, can you confirm that when they’re “slower to the target” that the WalkSpeed on the NPC is what you expect it to be?

Sure give me a few, to setup a video.

Awesome, thanks.

Mind if I sent it over dm, or do you want it sent here?

Others might benefit from this discussion too but you can send it over dms if you want.

It wouldn’t send here, so I dmed it to you. I moved it to a different test place for demonstration and also disabled AI seperation as it has no effect and weight on the problem. Please refer here, though still.

1 Like

Sorry, was busy.

It is calculating while they’re moving. But it’s a bit unnoticeable when they get closer, likely because the paths are shorter.

Yeah, so I’m guessing they reach the end of their path before you generate the next one.

Assuming that there isn’t some like 2-3 second delay in actually asking for a new path, if so you might just shorten that time for shorter paths? If you’re constantly recalculating could you perhaps have them go straight at the target if they run out of path waypoints? Once the next path is finished it could override that behavior and continue following a path. I get that doing something like that can lead to them getting caught on walls or falling into holes but there’s not really a good way to make the pathfinder faster unless you are able to do some kind of aggregate pathfinding that a group of the NPCs is able to use.

Apologies for the late response, I had to go for the evening.

Well, a few things. The path obviously cant be updated every single milisecond, especially when you have up to 50-100+ humanoids all doing that at once. The current frequency a new path is calculated is half a second. Now, I do realize they get just a tiny bit closer when I make the update speed every returned task.wait() rather than half second. But, it becomes a huge problem once there is over 100 humanoids, and it doesn’t completely solve the problem.

No worries!

I’ve also tried having them MoveTo() directly to the target while a path is updating or wtv, but they seem to still bug.

maybe I’m not doing it right though? idk

Do you think you’d need to see some code?