Skip to content

Instantly share code, notes, and snippets.

@ImXirvin
Created May 15, 2024 00:41
Show Gist options
  • Save ImXirvin/142439248f677f86997c9e1f3286d49f to your computer and use it in GitHub Desktop.
Save ImXirvin/142439248f677f86997c9e1f3286d49f to your computer and use it in GitHub Desktop.
Movement Controller for Roblox
local RunService = game:GetService("RunService")
local ContextActionService = game:GetService("ContextActionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Controllers = ReplicatedStorage:WaitForChild("Controllers")
local HUDController = require(Controllers.Character.HUDController)
-- Constants
local HEAD_HORIZONTAL_FACTOR = 1
local HEAD_VERTICAL_FACTOR = 0.6
local BODY_HORIZONTAL_FACTOR = 0.5
local BODY_VERTICAL_FACTOR = 0.4
local UPDATE_SPEED = 0.25
local playerConfig = require(ReplicatedStorage.Configs.Character.player).movement
local camera = workspace.CurrentCamera
local PlayerController = require(Controllers.PlayerController)
local character = PlayerController.Character
local humanoid = PlayerController.Humanoid
local humanoidRootPart = humanoid.RootPart
local isR6 = humanoid.RigType == Enum.HumanoidRigType.R6
local head = character.Head
local torso = isR6 and character.Torso or character.UpperTorso
local neck = isR6 and torso.Neck or head.Neck
local waist = not isR6 and torso.Waist
local neckOriginalCFrame = neck.C0
local waistOriginalCFrame = not isR6 and waist.C0
local _ = require(script.Parent:WaitForChild("Movement").footsteps)
local MovementController = {}
neck.MaxVelocity = 1 / 3
RunService.RenderStepped:Connect(function()
-- Check if every required body part exists and whether the CurrentCamera's CameraSubject is the Humanoid
if torso and head and ((isR6 and neck) or (neck and waist)) and camera.CameraSubject == humanoid then
local cameraCFrame = camera.CFrame
local headCFrame = head.CFrame
local torsoLookVector = torso.CFrame.lookVector
local distance = (headCFrame.p - cameraCFrame.p).magnitude
local heightDifference = headCFrame.Y - cameraCFrame.Y
local asinDifferenceDistance = math.asin(heightDifference / distance)
local perpendicular = ((headCFrame.p - cameraCFrame.p).Unit:Cross(torsoLookVector)).Y
local angleHeadVerticalFactor = asinDifferenceDistance * HEAD_VERTICAL_FACTOR
local angleHorizontalFactor = -perpendicular * HEAD_HORIZONTAL_FACTOR
if isR6 then
local neckRotation = CFrame.Angles(-angleHeadVerticalFactor, 0, angleHorizontalFactor)
neck.C0 = neck.C0:lerp(neckOriginalCFrame * neckRotation, UPDATE_SPEED)
else
local headRotation = CFrame.Angles(angleHeadVerticalFactor, angleHorizontalFactor, 0)
neck.C0 = neck.C0:lerp(neckOriginalCFrame * headRotation, UPDATE_SPEED)
local bodyRotation = CFrame.Angles(
angleHeadVerticalFactor * BODY_VERTICAL_FACTOR,
angleHorizontalFactor * BODY_HORIZONTAL_FACTOR,
0
)
waist.C0 = waist.C0:lerp(waistOriginalCFrame * bodyRotation, UPDATE_SPEED)
end
end
end)
local function handleSprint(shouldSprint)
if shouldSprint == true then
if not MovementController:IsEventAllowed("sprint") then
return
end
local currentStamina = HUDController.Status:GetStatus("stamina")
if currentStamina <= 0 then
return
end
humanoid:SetAttribute("sprinting", true)
task.spawn(function()
local previousMultiplier = character:GetAttribute("stamina_multiplier")
character:SetAttribute("stamina_multiplier", 0)
local drain = playerConfig.staminaSprintDrainPerSecond
while humanoid:GetAttribute("sprinting") do
HUDController.Status:UpdateStatus("stamina", drain)
local currentStamina = HUDController.Status:GetStatus("stamina")
if currentStamina <= 0 then
humanoid:SetAttribute("sprinting", false)
break
end
task.wait(1)
end
--Maybe an exploit here
character:SetAttribute("stamina_multiplier", previousMultiplier)
end)
else
humanoid:SetAttribute("sprinting", false)
end
end
PlayerController:OnHumanoidAttributeChanged("sprinting", function()
local sprinting = humanoid:GetAttribute("sprinting")
if sprinting then
humanoid.WalkSpeed = playerConfig.sprintSpeed
else
humanoid.WalkSpeed = playerConfig.walkSpeed
end
end)
local function handleInput(actionName, inputState, inputObject)
if actionName == "Sprint" then
local shouldSprint = false
if inputState == Enum.UserInputState.Begin then
shouldSprint = true
end
handleSprint(shouldSprint)
end
end
function MovementController:Init()
character = PlayerController.Character
humanoid = PlayerController.Humanoid
humanoidRootPart = humanoid.RootPart
ContextActionService:BindAction("Sprint", handleInput, true, Enum.KeyCode.LeftShift, Enum.KeyCode.ButtonX)
ContextActionService:SetTitle("Sprint", "Sprint")
end
function MovementController:IsEventAllowed(event: string)
if not self.events or not self.events[event] then
return true
end
for _, callback in pairs(self.events[event]) do
if callback then
if not callback() then
return false
end
end
end
return true
end
function MovementController:RegisterHook(event: string, callback: () -> boolean)
self.events = self.events or {}
local length = #self.events + 1
if not self.events[event] then
self.events[event] = {}
end
self.events[event][length] = callback
return length
end
function MovementController:UnregisterHook(event: string, id: number)
self.events = self.events or {}
self.events[event][id] = nil
end
HUDController.Status:OnChange("stamina", function(value)
local jumpDrain = playerConfig.staminaJumpDrain
local allowedJump = humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping)
if value < -jumpDrain and allowedJump then
humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, false)
elseif value > -jumpDrain and not allowedJump then
humanoid:SetStateEnabled(Enum.HumanoidStateType.Jumping, true)
end
end)
PlayerController:OnHumanoid("Jumping", function()
local jumpDrain = playerConfig.staminaJumpDrain
HUDController.Status:UpdateStatus("stamina", jumpDrain)
end)
PlayerController:OnHumanoid("FreeFalling", function()
local PlrVelocity = humanoidRootPart.Velocity.Y
PlrVelocity *= -1
if PlrVelocity > playerConfig.maxFallingVelocity then
humanoid.Health = 0
elseif PlrVelocity > playerConfig.minFallingVelocity then
print("Falling damage")
humanoid.Health -= PlrVelocity / 4
end
end)
return MovementController
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment