Custom movement script works 50% of the time

Hi, I am creating a custom movement script for my game. For some reason when I play in-game, half the time my speed is twice as fast as it should be. In studio, this works perfectly 100% of the time. At the moment I have no attempted solution because I don’t know why this is occurring. This bug has been a major road block in the game’s development, so I am somewhat desperate to fix it.

Issue Visual
Regular:

Double speed:

Relevant Context for Movement Script
Movement is tied to RunService:BindRenderStepped(). The priority is under the Camera’s render priority. V3_dir contains the status of keys pressed. If the X property is 1, then D is pressed, and if it’s -1, then A is pressed. If the Z property is 1, then S is pressed, and if its -1, then W is pressed. If the X property is 0, neither D nor A is pressed. If the Z property is 0, neither W nor S is pressed. V3_dir always has a magnitude of 1.

Discoveries

  1. I attempted to see if the function terminates before the next frame is rendered, and it does regardless if I’m going at the normal speed or the doubled speed. This means the function isn’t running on two separate threads, which I believe is good.
  2. The change in position between sequential HRP positions (in the code it is CF_new.p - i) shows a doubled value when the speed is doubled, and a normal value when the speed is normal. This is expected.
  3. The magnitude of the change in CFrame (in the code is is CF_deltaCF) for the HRP is at it’s expected value when I’m going fast or at the regular speed, which is unexpected. With a doubled speed, one would expect the change in CFrame to also be doubled.

Code:

game:GetService("RunService"):BindToRenderStep("Movement",Enum.RenderPriority.Camera.Value - 1,function(Num_dt)
	
    ...

	print("Begin: "..tostring(n).." - "..tostring(tick()))		

	local CF_curHrp = Inst_player.Character.HumanoidRootPart.CFrame
	local V3_camLv = ControlManager.Camera.Camera.CFrame.lookVector
	
	
	local CF_rot = CFrame.new(Vector3.new(),Vector3.new(V3_camLv.X,0,V3_camLv.Z))
	local CF_hrpPos = CFrame.new(CF_curHrp.p)
	local CF_deltaCF = CFrame.new(V3_dir*ControlManager.SpeedMultiplier*Num_dt)
	
	local CF_new = CF_hrpPos*CF_rot*CF_deltaCF
	print((CF_new.p - i).magnitude,"\t \t \t",CF_rot.p.magnitude,"\t \t \t",CF_deltaCF.p.magnitude)
	i = CF_new.p			

	Inst_player.Character:SetPrimaryPartCFrame(CF_new)
	
	print("End: "..tostring(n).." - "..tostring(tick()))
	
	n = n + 1


	...

end)

I apologize for the long post. If anyone has any idea as to what may cause this, I would appreciate the insight. Thank you.

I copied your code and implemented some simple input-checking using ContextActionService, to try myself to see whats happening.

At first it looked fine, but then I added input-checking for also changing the SpeedMultiplier when in-game, tested it by pressing movement & speed keys at random, and then “something happened”, where the character would not stop moving.

I quickly figured out, that it was due to my simple, and flawed, input-checking implementation, that looks like the below code. - So I wonder if the same is happening to your V3_dir values, not being strictly within the -1…1 range?

When I tested my attempt, it happened, that two (or possibly more) UserInputState.Begin actions for the same key occurred, thereby doubling the V3_dir’s value for a direction.

So perhaps you should re-check that your V3_dir is within the bounds that you specify, as that could explain the “speed is twice as fast”, due to V3_dir probably could contain -2…2 values?

local V3_dir = Vector3.new()
local SpeedMultiplier = 10

local key2dir = {
	[Enum.KeyCode.W] = Vector3.new(0,0,-1),
	[Enum.KeyCode.S] = Vector3.new(0,0, 1),
	[Enum.KeyCode.A] = Vector3.new(-1,0,0),
	[Enum.KeyCode.D] = Vector3.new( 1,0,0),
}

local function handleMovement(actionName, inputState, inputObject)
	if inputObject.UserInputState == Enum.UserInputState.Begin then
		V3_dir = V3_dir + key2dir[inputObject.KeyCode] 
	else
		V3_dir = V3_dir - key2dir[inputObject.KeyCode] 
	end
end

local function handleSpeed(actionName, inputState, inputObject)
	if inputState == Enum.UserInputState.Begin then
		SpeedMultiplier = 50
	else
		SpeedMultiplier = 10
	end
end

local CAS = game:GetService("ContextActionService")
CAS:BindAction("Movement", handleMovement, false, Enum.KeyCode.W, Enum.KeyCode.S, Enum.KeyCode.A, Enum.KeyCode.D)
CAS:BindAction("SpeedMove", handleSpeed, false, Enum.KeyCode.LeftShift)

Thanks for the response. Earlier in the code, I normalize V3_dir, so it always has a magnitude of 1. Good point, however. I’ll make en edit to my original post.

Here’s an exported place for those interested: MovementPlace.rbxl (23.3 KB)