Enemy NPC delay when walking

So I’m making a Block Tales inspired turn-based rpg, but I encountered an issue.

When it’s the enemies’ turn, they have a noticeable delay when they start walking (shown in video).
I’m currently using parts with an AlignPosition inside the HumanoidRootParts of the characters to walk them to their exact position, and I’ve already set all of their NetworkOwnerships to nil.

How can i make them walk smoothly on the server side? Or is it better to set their NetworkOwnerships to the client and handle everything there?

Code:

local function GetWalkForce(char)
	if char:FindFirstChild("WalkForce") then
		return char.WalkForce
	end
	local hrp = char.HumanoidRootPart
	local walkforce = objects.WalkForce:Clone()
	walkforce.Parent = char
	walkforce.Name = "WalkForce"
	walkforce.CFrame = hrp.CFrame
	walkforce.Weld.Part1 = hrp
	walkforce:SetNetworkOwner(nil)
	return walkforce
end

function WalkTo(char, moveGoal, waitUntilFinished)
	local player = game.Players:GetPlayerFromCharacter(char)
	local hrp : Part, hum : Humanoid = char.HumanoidRootPart, char.Humanoid
	hrp:SetNetworkOwner(nil) -- setting all parts seems to have the same effect
	hum:ChangeState(Enum.HumanoidStateType.Running)
	hum.AutoRotate = false
	moveGoal = Vector3.new(moveGoal.x, hrp.Position.y, moveGoal.z)
	local walkforce : Part = GetWalkForce(char)
	local alignPos = walkforce.AlignPosition
	alignPos.Enabled = true
	alignPos.Position = moveGoal
	
	if waitUntilFinished then
		local dist, speed = (hrp.Position-(moveGoal)).Magnitude, hum.WalkSpeed
		local walkDelay = player and -0.4 or 0.1
		task.wait(dist/speed + walkDelay)
		hrp.CFrame = hrp.CFrame.Rotation + Vector3.new(moveGoal.x, hrp.Position.y, moveGoal.z)
		alignPos.Enabled = false
		hum.AutoRotate = true
	end
end

WalkForce:
image

16 Likes

“its a feature not a bug”

wordfill

11 Likes

i saw you used network ownership to nil, but instead try to put the ownership ONLY to the enemy who the player clicked, else do it at the start of the fight

9 Likes

When I set the enemy’s network ownership to the player, their animations become delayed. I also would rather use server ownership for dodging and evading attacks.

9 Likes

I don’t want the enemies to lag before walking, so it’s not going to be a feature in my game. However, maybe I can add that later if I can fix the lag and have the server control the characters smoother.

6 Likes

Here is the correct code you could try. Probably.

local function GetWalkForce(char)
    if char:FindFirstChild("WalkForce") then
        return char.WalkForce
    end
    
    -- Ensure 'WalkForce' exists in ServerStorage or a valid location
    local walkforce = game.ServerStorage.WalkForce:Clone()  -- Replace with actual location of WalkForce
    local hrp = char.HumanoidRootPart

    walkforce.Parent = char
    walkforce.Name = "WalkForce"
    walkforce.CFrame = hrp.CFrame
    
    -- Ensure the AlignPosition constraint is set up properly
    local alignPos = walkforce:FindFirstChildOfClass("AlignPosition") or Instance.new("AlignPosition")
    alignPos.Parent = walkforce
    alignPos.MaxForce = 100000  -- Adjust force as needed
    alignPos.Attachment0 = walkforce:FindFirstChild("Attachment")  -- Ensure this is correct in your setup

    walkforce.Weld.Part1 = hrp
    walkforce:SetNetworkOwner(nil)
    
    return walkforce
end

function WalkTo(char, moveGoal, waitUntilFinished)
    local player = game.Players:GetPlayerFromCharacter(char)
    local hrp, hum = char.HumanoidRootPart, char.Humanoid
    hrp:SetNetworkOwner(nil)  -- Setting all parts seems to have the same effect
    hum:ChangeState(Enum.HumanoidStateType.Running)
    hum.AutoRotate = false
    
    -- Adjust y-coordinate of moveGoal to match the character's current height
    moveGoal = Vector3.new(moveGoal.x, hrp.Position.y, moveGoal.z)
    
    local walkforce = GetWalkForce(char)
    local alignPos = walkforce.AlignPosition
    alignPos.Enabled = true
    alignPos.Position = moveGoal
    
    if waitUntilFinished then
        local dist = (hrp.Position - moveGoal).Magnitude
        local speed = hum.WalkSpeed
        local walkDelay = player and -0.4 or 0.1  -- Small adjustment for player movement

        task.wait(dist / speed + walkDelay)
        
        -- Ensure proper rotation after movement
        hrp.CFrame = CFrame.new(moveGoal.x, hrp.Position.y, moveGoal.z) * hrp.CFrame - hrp.Position
        alignPos.Enabled = false
        hum.AutoRotate = true
    end
end

3 Likes

I’m sorry, but this functions the exact same as my code, but with different formatting and with more comments. Did you AI generate this?

4 Likes

Yeah, they’re trying to get solutions, they’ve been posting ai stuff all day

5 Likes

The problem looks to be, that your movement logic is built around AlignPosition constraints processed by the server-side and that can add extra latency from processing on the server and latency itself.

6 Likes

So should I handle everything on the client? I dont think it would be very secure and it would result in delay for other players in the battle. I have to use AlignPosition because humanoid:MoveTo() is a stud away from the target position.

4 Likes

The game seems deteministic, so the server handles the outcome of the battle (damage, dodge, ecc.) The client plays characters’ animations. By the way it seems that the character has a little delay because the Network Ownership changes from the server to the client (when it gets near the player). But i might be wrong

5 Likes

I think the network ownership stays the same the whole time (i tested through printing). I’m thinking about switching to client side, but I’m still worried it might not be secure and that it could introduce delays for other clients. There could also be walking actions that the enemies could do without a target player, so I have to set network ownership to the server for that.

2 Likes

Just make a remote event that gets fired by the server to all clients when the attack/move function is called. The server still manages the damage, health, ecc… And in the client you play the animation/movement. You just give to the client which character is going to move and the Animation/movemente info.
The client can’t change the outcome of the attack because it is managed in the server.

4 Likes

Okay, I’ll try this out and tell you if it works. Still not very sure about movement on the client as there might be issues with lagging clients.

1 Like

Each change in the part’s position, during the movement, gets replicated to the client. It’s for this reason that the movement often it’s not smooth. So it doesn’t really matter if the client has bad ping/fps.

2 Likes

That makes sense. But what i’m wondering is what could i do if a client is lagging in framerate and an enemy is shown punching on their screen, and on the server the targeted player just landed from their jump (the enemy’s fist phases through the player)?
When the jump is replicated does it account for the other client’s framerates? Or does it skip a few frames to be accurate as possible?
Btw, putting animation/movement on the client seems to be much more smoother. Just not sure about the potential issue i have above.

2 Likes

Sorry for bothering you again. I have another issue with handling everything on the client side. What do i do when a new player joins the battle during a turn? They will just see the characters doing nothing.

1 Like

Using a PlayerAdded event, you can check if a battle is happening and have the same RemoteEvent fire for them so they can see what is happening.

1 Like

I meant to ask about what to do when a player joins in the middle of a turn. Since the remoteEvents for that turn have already been fired to the players that joined before, what do i do for the new player? If i fire the same remote to them, it will be delayed.

2 Likes

You could find workarounds for this such as making a loadingscreen which waits until the turn is over

1 Like