New Bunny Hoppy Movement Code

In the AirMove() function change this block of code from this:

local wishSpeed = wishDir.Magnitude

wishSpeed *= moveSpeed

if wishDir ~= Vector3.new(0, 0, 0) then
	wishDir = wishDir.Unit
end

To this:

if wishDir ~= Vector3.new(0, 0, 0) then
	wishDir = wishDir.Unit
end

local wishSpeed = moveSpeed

We can just set wishSpeed to moveSpeed now because wishDir.Magnitude will always be 1 when the wishDir is a unit vector.

1 Like

Hi,

Can the model be updated with the latest code?

Thanks

One last question,

Is the default values accurate to CS:GO? Just wondering, if not Iā€™ll play with the values. I asked this since in order to gain a slight bit more of speed you just have to hold either A or D and look the direction and you gain around 1-2 units of speed.

1 Like

No I just messed with the values until it was to my liking. Iā€™ll be honest Iā€™m not very good with vector math so I donā€™t even really know what changing each value does lol.

However I would not set sideStrafeSpeed greater than 1 otherwise you can easily gain speed super fast.

1 Like

The animation is surprisingly great, reminds me of CS GO, good jobšŸ‘

I noticed something while playing with this. It bugged me quite a bit, even though it was such a small thing that it took me a while to realize it was happening.

hereā€™s the problem:

hereā€™s a fixed version:

and hereā€™s a side-by-side of the two:

for anyone wondering how to fix this here is the solution I used in the video above:

	local camY, camX, camZ = camera.CFrame:ToOrientation()
	local cameraCFrame = CFrame.new(camera.CFrame.p) * CFrame.Angles(0, camX, 0)
	local inputVel = Vector3.new(sideMove, 0, forwardMove)

	local wishDir = cameraCFrame:VectorToWorldSpace(inputVel)
	wishDir = Vector3.new(wishDir.X, 0, wishDir.Z)


you just apply this in the groundMove function and the airMove function and it should fix that problem.

Hereā€™s the full default code but with the fix:

local ContextActionService = game:GetService("ContextActionService")
local RunService = game:GetService("RunService")

local player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera

local character
local humanoidRootPart
local humanoid
local groundCheck

player.CharacterAdded:Connect(function(newCharacter)

	character = newCharacter

	humanoidRootPart = character:WaitForChild("HumanoidRootPart")
	humanoid = character:WaitForChild("Humanoid")

	groundCheck = script.GroundCheck:Clone()
	groundCheck.Weld.Part0 = humanoidRootPart
	groundCheck.Parent = character
	
end)

local Cmd = {

	upMove = false;
	downMove = false;

	leftMove = false;
	rightMove = false;

	lastUp = false;
	lastLeft = false;

	jump = false;
	justPressed = false;

	crouch = false;
	cjustPressed = false;

}

local function onUp(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.upMove = true
		Cmd.lastUp = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.upMove = false

	end

end

local function onLeft(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.leftMove = true
		Cmd.lastLeft = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.leftMove = false

	end

end

local function onDown(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.downMove = true
		Cmd.lastUp = false

	elseif inputState == Enum.UserInputState.End then

		Cmd.downMove = false

	end

end

local function onRight(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.rightMove = true
		Cmd.lastLeft = false

	elseif inputState == Enum.UserInputState.End then

		Cmd.rightMove = false

	end

end

local function onJump(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.jump = true
		Cmd.justPressed = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.jump = false

	end

end

local function onCrouch(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.crouch = true
		Cmd.cjustPressed = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.crouch = false

	end

end

ContextActionService:BindAction("Up", onUp, false, "w")
ContextActionService:BindAction("Left", onLeft, false, "a")
ContextActionService:BindAction("Down", onDown, false, "s")
ContextActionService:BindAction("Right", onRight, false, "d")
ContextActionService:BindAction("Jump", onJump, false, Enum.KeyCode.Space)
ContextActionService:BindAction("Crouch", onCrouch, false, "c")


local moveSpeed = 30
local runAccel = 5
local runDeaccel = 5
local airAccel = 2.5
local airDeaccel = 2.5
local sideStrafeAccel = 100
local sideStrafeSpeed = 1
local friction = 8
local airFriction = 3
local maxSpeed = 30

local playerVel = Vector3.new(0, 0, 0)
local forwardMove = 0
local sideMove = 0

local grounded = false

local wishJump = false
local canJump = true
local jumpBuffer = 0.1
local holdJumpToBhop = true


local function Update(deltaTime)	

	if not humanoidRootPart then return end
	
	if Cmd.leftMove and Cmd.rightMove then

		if Cmd.lastLeft then sideMove = -1 else sideMove = 1 end

	else

		if Cmd.leftMove then sideMove = -1 elseif Cmd.rightMove then sideMove = 1 else sideMove = 0 end

	end

	if Cmd.upMove and Cmd.downMove then

		if Cmd.lastUp then forwardMove = -1 else forwardMove = 1 end

	else

		if Cmd.upMove then forwardMove = -1 elseif Cmd.downMove then forwardMove = 1 else forwardMove = 0 end

	end

	QueueJump()
	grounded = CheckGround()

	if grounded then

		GroundMove()

	else

		AirMove()

	end

	local newDir = Vector3.new()
	if playerVel ~= Vector3.new(0, 0, 0) then newDir = playerVel.Unit end

	local newSpeed = playerVel.Magnitude
	newSpeed = math.clamp(newSpeed, 0, maxSpeed)

	humanoid.WalkSpeed = newSpeed
	humanoid:Move(newDir, false)
end

RunService.RenderStepped:Connect(Update)


function QueueJump()

	if holdJumpToBhop then

		wishJump = Cmd.jump
		return

	end

	if Cmd.jump and not wishJump and Cmd.justPressed then

		wishJump = true
		Cmd.justPressed = false

	end

	if not Cmd.jump then

		wishJump = false

	end

end


function CheckGround()

	local connection = groundCheck.Touched:Connect(function()end)

	for index, value in ipairs(groundCheck:GetTouchingParts()) do

		if value:IsA("BasePart") and value.Parent ~= character and value.CanCollide then

			connection:Disconnect()
			return true

		end

	end

	if humanoid:GetState() == Enum.HumanoidStateType.Climbing then

		return true

	end

	connection:Disconnect()
	return false

end


function AirMove()

	if forwardMove ~= 0 then

		ApplyFriction(1, true)

	end

	local accel
	
	local camY, camX, camZ = camera.CFrame:ToOrientation()
	local cameraCFrame = CFrame.new(camera.CFrame.p) * CFrame.Angles(0, camX, 0)
	local inputVel = Vector3.new(sideMove, 0, forwardMove)

	local wishDir = cameraCFrame:VectorToWorldSpace(inputVel)
	wishDir = Vector3.new(wishDir.X, 0, wishDir.Z)
	local wishSpeed = wishDir.Magnitude

	wishSpeed *= moveSpeed

	if wishDir ~= Vector3.new(0, 0, 0) then

		wishDir = wishDir.Unit

	end

	if playerVel:Dot(wishDir) < 0 then

		accel = airDeaccel

	else

		accel = airAccel

	end

	if forwardMove == 0 and sideMove ~= 0 then

		if wishSpeed > sideStrafeSpeed then

			wishSpeed = sideStrafeSpeed

		end

		accel = sideStrafeAccel

	end

	Accelerate(wishDir, wishSpeed, accel)

end


function GroundMove()

	if not wishJump and canJump then

		ApplyFriction(1, false)

	else

		ApplyFriction(0, false)

	end

	local camY, camX, camZ = camera.CFrame:ToOrientation()
	local cameraCFrame = CFrame.new(camera.CFrame.p) * CFrame.Angles(0, camX, 0)
	local inputVel = Vector3.new(sideMove, 0, forwardMove)

	local wishDir = cameraCFrame:VectorToWorldSpace(inputVel)
	wishDir = Vector3.new(wishDir.X, 0, wishDir.Z)

	if wishDir ~= Vector3.new(0, 0, 0) then

		wishDir = wishDir.Unit

	end

	local wishSpeed = wishDir.Magnitude

	wishSpeed *= moveSpeed

	Accelerate(wishDir, wishSpeed, runAccel)

	playerVel = Vector3.new(playerVel.X, 0, playerVel.Z)

	if wishJump and canJump then

		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		wishJump = false
		canJump = false

		local co = coroutine.create(function()

			local begin = tick()

			while tick() - begin < jumpBuffer do

				RunService.Heartbeat:Wait()

			end

			canJump = true

		end)

		coroutine.resume(co)

	end

end


function ApplyFriction(t, inAir)

	local vec = Vector3.new(playerVel.X, 0, playerVel.Z)
	local speed = vec.Magnitude
	local drop = 0
	local control = 0
	local newFriction = inAir and airFriction or friction

	control = speed < runDeaccel and runDeaccel or speed
	drop = control * newFriction * RunService.Heartbeat:Wait() * t


	local newspeed = speed - drop

	if newspeed < 0 then

		newspeed = 0

	end

	if speed > 0 then

		newspeed /= speed

	end

	local x = playerVel.X * newspeed
	local z = playerVel.Z * newspeed
	playerVel = Vector3.new(x, 0, z)

end


function Accelerate(wishDir, wishSpeed, accel)

	local currentspeed = playerVel:Dot(wishDir)
	local addSpeed = wishSpeed - currentspeed

	if addSpeed <= 0 then return end

	local accelSpeed = accel * RunService.Heartbeat:Wait() * wishSpeed

	if accelSpeed > addSpeed then

		accelSpeed = addSpeed

	end

	local x = playerVel.X + accelSpeed * wishDir.X
	local z = playerVel.Z + accelSpeed * wishDir.Z
	playerVel = Vector3.new(x, 0, z)

end

It was just a small problem but it really urked me. Hope that helped anyone else who was struggling to fix this problem :smile:

5 Likes

Is there any way to make the movement code work with external forces?
Like, for example: If youā€™re rocket jumping then you donā€™t keep any of the acceleration from the blast and slow down mid-air, is there a way to be able to boost off the rocket jump?

I added this for game controller support. i could not figure out mobile support though. I just use default humanoid movement for mobile in my game

local function onMoveThumbstick(_, _, inputObject)
	local x = math.round(inputObject.Position.X)
	local y = math.round(inputObject.Position.Y)
	
	-- no left or right
	if x == 0 then
		onLeft("actionname", Enum.UserInputState.End)
		onRight("actionname", Enum.UserInputState.End)
	else
		-- right
		if x == 1 then
			onRight("actionname", Enum.UserInputState.Begin)
			onLeft("actionname", Enum.UserInputState.End)
		end
		--left
		if x == -1 then
			onLeft("actionname", Enum.UserInputState.Begin)
			onRight("actionname", Enum.UserInputState.End)
		end
	end

	-- no up or down
	if y == 0 then
		onUp("actionname", Enum.UserInputState.End)
		onDown("actionname", Enum.UserInputState.End)
	else
		--up
		if y == 1 then
			onUp("actionname", Enum.UserInputState.Begin)
			onDown("actionname", Enum.UserInputState.End)
		end
		--down
		if y == -1 then
			onDown("actionname", Enum.UserInputState.Begin)
			onUp("actionname", Enum.UserInputState.End)
		end
	end
end
ContextActionService:BindAction("MoveThumbstick", onMoveThumbstick, false, Enum.KeyCode.Thumbstick1)

2 Likes

@SnarlyZoo Thank you for posting this movement script, i love it in my game

1 Like

Yo thats sick, if you wanted to use the default PlayerModule controls you probably could so that way you could have all the different default Roblox input stuff. I always like making my own input code instead of using Robloxā€™s but I am pretty sure there is some sort of method called GetMoveVector() or something either in PlayerModule or PlayerModule:GetControls().

I donā€™t like how you can easily be fast by simply holding D and W, how do I fix that?

In the function AirMove() always apply friction. So change it from this:

if forwardMove ~= 0 then
	ApplyFriction(1, true)
end

to this:

ApplyFriction(1, true)

This keeps the bhopping functionality right?

Cause, when I played around with this, I couldnā€™t find any solutions to even fix this.

Btw I found a really helpful video on this on youtube

2 Likes

Yeah I would honestly follow his tutorial than use my code.

I made this script a while ago, and thereā€™s a lot of things I would change, so I might remake it sometime.

1 Like

the crouch and sprint options are missing, are they supposed to be? iā€™m very confused (i donā€™t script)

In the video its just bhoping without crouch jumps

iā€™m trying to add sprinting and crouching to this script but crouching is very bugged? help would be appreciated

local ContextActionService = game:GetService("ContextActionService")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local player = game:GetService("Players").LocalPlayer
local camera = workspace.CurrentCamera

local character
local humanoidRootPart
local humanoid
local groundCheck

player.CharacterAdded:Connect(function(newCharacter)

	character = newCharacter

	humanoidRootPart = character:WaitForChild("HumanoidRootPart")
	humanoid = character:WaitForChild("Humanoid")

	groundCheck = script.GroundCheck:Clone()
	groundCheck.Weld.Part0 = humanoidRootPart
	groundCheck.Parent = character

end)

local Cmd = {

	upMove = false;
	downMove = false;

	leftMove = false;
	rightMove = false;

	lastUp = false;
	lastLeft = false;

	jump = false;
	justPressed = false;

	crouch = false;
	cjustPressed = false;

}

local function onUp(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.upMove = true
		Cmd.lastUp = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.upMove = false

	end

end

local function onLeft(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.leftMove = true
		Cmd.lastLeft = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.leftMove = false

	end

end

local function onDown(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.downMove = true
		Cmd.lastUp = false

	elseif inputState == Enum.UserInputState.End then

		Cmd.downMove = false

	end

end

local function onRight(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.rightMove = true
		Cmd.lastLeft = false

	elseif inputState == Enum.UserInputState.End then

		Cmd.rightMove = false

	end

end

local function onJump(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.jump = true
		Cmd.justPressed = true

	elseif inputState == Enum.UserInputState.End then

		Cmd.jump = false

	end

end

local moveSpeed = 16
local runAccel = 8
local runDeaccel = 8
local airAccel = 2.5
local airDeaccel = 2.5
local sideStrafeAccel = 100
local sideStrafeSpeed = 1
local friction = 8
local airFriction = 3
local maxSpeed = 30

local playerVel = Vector3.new(0, 0, 0)
local forwardMove = 0
local sideMove = 0

local grounded = false

local wishJump = false
local canJump = true
local jumpBuffer = 0.1
local holdJumpToBhop = false

local function onCrouch(actionName, inputState)

	if inputState == Enum.UserInputState.Begin then

		Cmd.crouch = true
		Cmd.cjustPressed = true
	elseif inputState == Enum.UserInputState.End then
		Cmd.crouch = false
	end

end

ContextActionService:BindAction("Up", onUp, false, "w")
ContextActionService:BindAction("Left", onLeft, false, "a")
ContextActionService:BindAction("Down", onDown, false, "s")
ContextActionService:BindAction("Right", onRight, false, "d")
ContextActionService:BindAction("Jump", onJump, false, Enum.KeyCode.Space)
ContextActionService:BindAction("Crouch", onCrouch, false, "c")

local Crouch = false

local function onInputBeganCrouch(input, gameProcessed)
	if not gameProcessed then
		if input.KeyCode == Enum.KeyCode.LeftControl then
			if moveSpeed == 16 then
				moveSpeed = 8
				maxSpeed = 10

				player.Character:FindFirstChild("Humanoid").HipHeight = -1
				Crouch = true
			end
		end
	end
end

local function onInputEndedCrouch(input, gameProcessed)
	if not gameProcessed then
		if input.KeyCode == Enum.KeyCode.LeftControl then
			if moveSpeed == 8 then
				moveSpeed = 16
				maxSpeed = 30
				
				player.Character:FindFirstChild("Humanoid").HipHeight = 0
				Crouch = false
			end
		end
	end
end

local function onInputBeganSprint(input, gameProcessed)
	if not gameProcessed then
		if input.KeyCode == Enum.KeyCode.LeftShift then
			if moveSpeed == 16 then
				if Crouch == false then
					moveSpeed = 25
					maxSpeed = 50
				end
			end
		end
	end
end

local function onInputEndedSprint(input, gameProcessed)
	if not gameProcessed then
		if input.KeyCode == Enum.KeyCode.LeftShift then
			if moveSpeed == 25 then
				if Crouch == false then
					moveSpeed = 16
					maxSpeed = 50
				end
			end
		end
	end
end

local function Update(deltaTime)
	
	if not humanoidRootPart then return end
	
	if Cmd.leftMove and Cmd.rightMove then

		if Cmd.lastLeft then sideMove = -1 else sideMove = 1 end

	else

		if Cmd.leftMove then sideMove = -1 elseif Cmd.rightMove then sideMove = 1 else sideMove = 0 end

	end

	if Cmd.upMove and Cmd.downMove then

		if Cmd.lastUp then forwardMove = -1 else forwardMove = 1 end

	else

		if Cmd.upMove then forwardMove = -1 elseif Cmd.downMove then forwardMove = 1 else forwardMove = 0 end

	end

	QueueJump()
	grounded = CheckGround()

	if grounded then

		GroundMove()

	else

		AirMove()

	end

	local newDir = Vector3.new()
	if playerVel ~= Vector3.new(0, 0, 0) then newDir = playerVel.Unit end

	local newSpeed = playerVel.Magnitude
	newSpeed = math.clamp(newSpeed, 0, maxSpeed)

	humanoid.WalkSpeed = newSpeed
	humanoid:Move(newDir, false)
	
	UserInputService.InputBegan:Connect(onInputBeganCrouch)
	UserInputService.InputEnded:Connect(onInputEndedCrouch)
	
	UserInputService.InputBegan:Connect(onInputBeganSprint)
	UserInputService.InputEnded:Connect(onInputEndedSprint)
	
	script.Speed.Value = moveSpeed
end

RunService.RenderStepped:Connect(Update)

function QueueJump()

	if holdJumpToBhop then

		wishJump = Cmd.jump
		return

	end

	if Cmd.jump and not wishJump and Cmd.justPressed then

		wishJump = true
		Cmd.justPressed = false

	end

	if not Cmd.jump then

		wishJump = false

	end

end


function CheckGround()

	local connection = groundCheck.Touched:Connect(function()end)

	for index, value in ipairs(groundCheck:GetTouchingParts()) do

		if value:IsA("BasePart") and value.Parent ~= character and value.CanCollide then

			connection:Disconnect()
			return true

		end

	end

	if humanoid:GetState() == Enum.HumanoidStateType.Climbing then

		return true

	end

	connection:Disconnect()
	return false

end


function AirMove()

	if forwardMove ~= 0 then

		ApplyFriction(1, true)

	end

	local accel
	local cameraCFrame = camera.CFrame
	local inputVel = Vector3.new(sideMove, 0, forwardMove)

	local wishDir = cameraCFrame:VectorToWorldSpace(inputVel)
	wishDir = Vector3.new(wishDir.X, 0, wishDir.Z)
	local wishSpeed = wishDir.Magnitude

	wishSpeed *= moveSpeed

	if wishDir ~= Vector3.new(0, 0, 0) then

		wishDir = wishDir.Unit

	end

	if playerVel:Dot(wishDir) < 0 then

		accel = airDeaccel

	else

		accel = airAccel

	end

	if forwardMove == 0 and sideMove ~= 0 then

		if wishSpeed > sideStrafeSpeed then

			wishSpeed = sideStrafeSpeed

		end

		accel = sideStrafeAccel

	end

	Accelerate(wishDir, wishSpeed, accel)

end


function GroundMove()

	if not wishJump and canJump then

		ApplyFriction(1, false)

	else

		ApplyFriction(0, false)

	end

	local cameraCFrame = camera.CFrame
	local inputVel = Vector3.new(sideMove, 0, forwardMove)

	local wishDir = cameraCFrame:VectorToWorldSpace(inputVel)
	wishDir = Vector3.new(wishDir.X, 0, wishDir.Z)

	if wishDir ~= Vector3.new(0, 0, 0) then

		wishDir = wishDir.Unit

	end

	local wishSpeed = wishDir.Magnitude

	wishSpeed *= moveSpeed

	Accelerate(wishDir, wishSpeed, runAccel)

	playerVel = Vector3.new(playerVel.X, 0, playerVel.Z)

	if wishJump and canJump then

		humanoid:ChangeState(Enum.HumanoidStateType.Jumping)
		wishJump = false
		canJump = false

		local co = coroutine.create(function()

			local begin = tick()

			while tick() - begin < jumpBuffer do

				RunService.Heartbeat:Wait()

			end

			canJump = true

		end)

		coroutine.resume(co)

	end

end


function ApplyFriction(t, inAir)

	local vec = Vector3.new(playerVel.X, 0, playerVel.Z)
	local speed = vec.Magnitude
	local drop = 0
	local control = 0
	local newFriction = inAir and airFriction or friction

	control = speed < runDeaccel and runDeaccel or speed
	drop = control * newFriction * RunService.Heartbeat:Wait() * t


	local newspeed = speed - drop

	if newspeed < 0 then

		newspeed = 0

	end

	if speed > 0 then

		newspeed /= speed

	end

	local x = playerVel.X * newspeed
	local z = playerVel.Z * newspeed
	playerVel = Vector3.new(x, 0, z)

end


function Accelerate(wishDir, wishSpeed, accel)

	local currentspeed = playerVel:Dot(wishDir)
	local addSpeed = wishSpeed - currentspeed

	if addSpeed <= 0 then return end

	local accelSpeed = accel * RunService.Heartbeat:Wait() * wishSpeed

	if accelSpeed > addSpeed then

		accelSpeed = addSpeed

	end

	local x = playerVel.X + accelSpeed * wishDir.X
	local z = playerVel.Z + accelSpeed * wishDir.Z
	playerVel = Vector3.new(x, 0, z)

end

ik this is a pretty old post but its the only one that looks promisingā€¦ the camera isnt moving and in its ā€œdefaultā€ position???