Is there a better way to make this movement system?

I’m writing a movement system and how it works right now is that it checks every frame if a certain value is true and if it is true and it acts accordingly. This is the code:

P.S. I tried to add comments to whatever I think may be vague but most of it is pretty self explanatory to me

local MovementController = {}

local StateControllerModule = require(script.Parent.StateController)
local AnimationControllerModule = require(script.Parent.AnimationController)

local UIS = game:GetService("UserInputService")

MovementController.__index = MovementController

function MovementController.new(player : Player)
	local self = setmetatable({}, MovementController)
	
	self.Character = player.Character or player.CharacterAdded:Wait()
	self.Humanoid = self.Character:WaitForChild("Humanoid")
	self.Player = player
	self.Animations = AnimationControllerModule.new(self.Humanoid.Animator)

	self.StateController = StateControllerModule.new(player)
	self.sprintToggled = false
	self.shiftLocked = false
	self.facingCamera = false
	self.lastDirection = nil
	self.currentDirection = "Idle"
	self.lastState = nil
	
	self.WalkSpeed = 16
	self.SprintSpeed = 30
	
	self.isJumping = true --none of this does anything atm
	self.canJump = false
	self.jumpDelay = 1
	
	self.movementKeys = {
		[Enum.KeyCode.LeftAlt] = function() self.sprintToggled = not self.sprintToggled end,
		[Enum.KeyCode.LeftShift] = function() self:ToggleShiftLock() end,
		[Enum.KeyCode.Q] = function() print("call dash function") end,
		[Enum.KeyCode.Space] = function() print("jump") end
	}
	return self
end

function MovementController:Init()--self explanatory
	UIS.InputBegan:Connect(function(input, gpe)
		if gpe then return end
		
		if self.movementKeys[input.KeyCode] then
			self.movementKeys[input.KeyCode]()
		end
	end)
	
	game:GetService("RunService").RenderStepped:Connect(function()
		self:Update()
	end)
	
end

function MovementController:Update()
	local moving = self.Humanoid.MoveDirection.Magnitude > 0.1
	local camera = workspace.CurrentCamera
	local HRP = self.Character:WaitForChild("HumanoidRootPart")
	local rootPart = self.Character:WaitForChild("HumanoidRootPart")
	local moveDirection = self.Humanoid.MoveDirection
	local dot = rootPart.CFrame.LookVector:Dot(moveDirection)
	local sideDot = rootPart.CFrame.RightVector:Dot(moveDirection)
	local currentState =  self.StateController:GetState()
	
	if self.shiftLocked and self.sprintToggled then --self explanatory
		self.facingCamera = false
	elseif self.shiftLocked and not self.sprintToggled then
		self.facingCamera = true
	elseif not self.shiftLocked and self.sprintToggled then
		self.facingCamera = false
	else
		self.facingCamera = false
	end
	
	if moving then --player is moving
		if self.sprintToggled then
			self.lastState = currentState
			self.StateController:SetState("Sprinting")
			self.Humanoid.WalkSpeed = self.SprintSpeed
		else
			self.lastState = currentState
			self.StateController:SetState("Walking")
			self.Humanoid.WalkSpeed = self.WalkSpeed
		end
		
		if self.shiftLocked and currentState == "Walking" then --changes direction based on player movement if they are in shift lock
			if dot > 0.5 then
				self.lastDirection = self.currentDirection
				self.currentDirection = "Forward"
			elseif dot < -0.5 then
				self.lastDirection = self.currentDirection
				self.currentDirection = "Backward"
			end

			if sideDot > 0.5 then
				self.lastDirection = self.currentDirection
				self.currentDirection = "Right"
			elseif sideDot < -0.5 then
				self.lastDirection = self.currentDirection
				self.currentDirection = "Left"
			end
		else
			self.lastDirection = self.currentDirection
			self.currentDirection = "Forward"
		end
	else --not moving
		self.lastState = currentState
		self.lastDirection = self.currentDirection
		self.StateController:SetState("Idle")
		self.currentDirection = "Idle"
	end
	
	if self.facingCamera then --this is so that the player looks at the camera when they are in shift lock but if they start sprinting then they stop facing the camera and turn to wherever they wan
		HRP.CFrame = CFrame.lookAt(HRP.Position, HRP.Position + Vector3.new(camera.CFrame.LookVector.X, 0, camera.CFrame.LookVector.Z))
	end
	
	--print("state: " .. self.StateController:GetState() .. ", direction: " .. self.lastDirection)
	
	--self.Animations:UpdateAnimationsFromStates(self.lastDirection, self.StateController:GetState())
	
	if self.shiftLocked then
		if self.lastDirection ~= self.currentDirection or self.lastState ~= self.currentState then
			self.Animations:UpdateAnimations(self.currentDirection, self.StateController:GetState())
			self.lastDirection = self.currentDirection
			self.lastState = self.currentState
		end
	else --not in shift lock
		if self.lastState ~= self.currentState then
			self.Animations:UpdateAnimations(self.currentDirection, self.StateController:GetState())
			self.lastState = self.currentState
		end
	end
	--print(" ")
	--print("state: " .. self.StateController:GetState())
	--print("direction: " .. self.lastDirection)
	--print(" ")
end

function MovementController:ToggleShiftLock()--self explanatory just tweens the camera when they go in shift lock
	self.shiftLocked = not self.shiftLocked
	
	if self.shiftLocked then
		workspace.CurrentCamera.CameraType = Enum.CameraType.Custom
		local t = game:GetService("TweenService"):Create(self.Humanoid, TweenInfo.new(0.1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CameraOffset  = Vector3.new(0, 3, 0)}):Play()
		game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter
	else
		workspace.CurrentCamera.CameraType = Enum.CameraType.Custom
		local t = game:GetService("TweenService"):Create(self.Humanoid, TweenInfo.new(0.1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out), {CameraOffset  = Vector3.new(0, 0, 0)}):Play()
		game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.Default
	end
end

return MovementController 

The problem is, you can probably see that I’m trying to implement jumping but I don’t know how I would detect when the player jumps, when they land, etc since I’m calling update every frame. Is there a better way to do this where I’m not checking the state every frame? Am I overcomplicating this? I don’t think I’ll be able to add all of the movement that I want to add (jumping, dashing, wall running) using this current system.

Side note: You can still jump because I didn’t disable normal player jumping, I just have no way of detecting when the player jumps and lands
Another side note: I was thinking about making a custom jump function instead of using the Humanoid states to tell when the player jumps, but surely there is a less complicated way right?

2 Likes

You could place this into your MovementController.new() function

self.Humanoid.StateChanged:Connect(function(State: Enum.HumanoidStateType)
	if State == Enum.HumanoidStateType.Jumping then
		print("Jumpped")
	elseif State == Enum.HumanoidStateType.Landed then
		print("Landed")
	end
end)