Hello, this is an update to my pre-existing topic about bunny hopping in Roblox. You can find that code here: Quake Engine Movement [Open Source]
Now I am choosing to update my old code as it could be improved quite the amount.
One of the major improvements with my new code is that it uses the humanoid:Move() function instead of a body velocity. This allows the player to be able to climb and be less buggy.
I also am using a part for checking if the player is grounded instead of a raycast so there is no glitch where the player is standing on an edge of a part but the code still thinks the player is in the air.
Enough talk, here is the code!
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
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 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
Thats just the code, but it uses a pre-existing part, so here is a model that you can use, just put it in the StarterPlayerScripts.
https://www.roblox.com/library/6803581351/Bunny-Hopping-Movement
If you would like to see an this in action, check out my game World War Bean!: World War Bean - Roblox