Code Feedback Please

Hello, I am in search of feedback on my script. So I am making a movement system and right now I have got 4 scripts: main controller, state controller, input controller and force controller. I would greatly appreciate if someone would look over my code and point out any inefficiencies or any other unlogical stuff. Thank you very much!

BASIC RUNDOWN OF THE SYSTEM:

  • main script gets called in an external local script
  • it calls the three other scripts
  • input controller begins listening to keys and updates values accordingly
  • main script starts updating states based on input
  • main script moves character utilizing force controller

MAIN: (ACTS AS A MIDDLEMAN BETWEEN OTHER SCRIPTS)

local movement_controller_module = {}
movement_controller_module.__index = movement_controller_module

--services--
local runservice = game:GetService("RunService")

--modules--
local input_manager = require(script:WaitForChild("input_manager")).new()
input_manager:begin_listening()

local state_controller = require(script:WaitForChild("state_controller")).new()
local force_controller = require(script:WaitForChild("force_controller")).new()

--constructor
function movement_controller_module.new()
	local self = setmetatable({}, movement_controller_module)

	self:__init()

	return self
end

--methods
function movement_controller_module:__init()
	self.walking_speed = 4000
	self.running_speed = 10000
	self.crouching_speed = 2000
	self.jump_force = 5000
end

function movement_controller_module:begin_updating_state()

	--renderstepped loop to update state every frame
	self.update_state_conn = runservice.PreRender:Connect(function()
		print(state_controller:get_current_state())

		--for standing state (none of movement keys are pressed = standing)
		if not input_manager:is_key_pressed(Enum.KeyCode.W) and not input_manager:is_key_pressed(Enum.KeyCode.A) and not input_manager:is_key_pressed(Enum.KeyCode.S) and not input_manager:is_key_pressed(Enum.KeyCode.D) then
			state_controller:set_state("standing")
			force_controller:nullify_force("forward_force")
		end
		
		--for walking state (movement keys are pressed = walking)
		if input_manager:is_key_pressed(Enum.KeyCode.W) or input_manager:is_key_pressed(Enum.KeyCode.A) or input_manager:is_key_pressed(Enum.KeyCode.S) or input_manager:is_key_pressed(Enum.KeyCode.D) then
			state_controller:set_state("walking")
			force_controller:apply_constant_force("forward_force", self:calculate_direction(), self.walking_speed)
		end	
		
		--for running state (left shift pressed + character is not standing still = running)
		if input_manager:is_key_pressed(Enum.KeyCode.LeftShift) and state_controller:get_current_state() ~= "standing" then
			state_controller:set_state("running")
			force_controller:apply_constant_force("forward_force", self:calculate_direction(), self.running_speed)
		end
		
		if input_manager:is_key_pressed(Enum.KeyCode.C) then
			state_controller:set_state("crouching")
			force_controller:apply_constant_force("forward_force", self:calculate_direction(), self.crouching_speed)
		end
		
		if input_manager:is_key_pressed(Enum.KeyCode.Space) and state_controller:get_current_state() ~= "crouching" then
			state_controller:set_state("jumping")
			force_controller:apply_momental_force("up_force", Vector3.yAxis, self.jump_force)
		end

	end)
end


function movement_controller_module:stop_updating_states()

	--disconnects render stepped
	if self.update_state_conn ~= nil then
		self.update_state_conn:Disconnect()
		self.update_state_conn = nil
	end
end

function movement_controller_module:calculate_direction()

	local direction = Vector3.new()
	local cam = workspace.CurrentCamera
	
	--checks every movement key
	if input_manager:is_key_pressed(Enum.KeyCode.W) then direction += cam.CFrame.LookVector end
	if input_manager:is_key_pressed(Enum.KeyCode.A) then direction += -cam.CFrame.RightVector end
	if input_manager:is_key_pressed(Enum.KeyCode.S) then direction += -cam.CFrame.LookVector end
	if input_manager:is_key_pressed(Enum.KeyCode.D) then direction += cam.CFrame.RightVector end
	
	--if direction is 0 (no keys are being pressed OR keys that are pressed cancel eachother out)
	if direction.Magnitude == 0 then
		return Vector3.new()
	end

	--returns force without the Y axis as a UNIT VECTOR
	return (direction * Vector3.new(1, 0, 1)).Unit
end

return movement_controller_module

STATE MANAGER

local state_controller_module = {}
state_controller_module.__index = state_controller_module

local default_state = "standing"

--constructor
function state_controller_module.new()
	local self = setmetatable({}, state_controller_module)
	
	self:__init()
	
	return self
end

--methods
function state_controller_module:__init()
	self.current_state = default_state
	self.possible_states = {
		"standing",
		"walking",
		"running",
		"jumping",
		"falling",
		"crouching"
	}
end

function state_controller_module:get_current_state(): string
	return self.current_state
end

function state_controller_module:set_state(state: string)
	
	--checks if state we are setting is a possible state
	if table.find(self.possible_states, state) then
		self.current_state = state
	end
end

return state_controller_module

INPUT MANAGER

local input_manager_module = {}
input_manager_module.__index = input_manager_module

--services
local uis = game:GetService("UserInputService")

--constructor
function input_manager_module.new()
	local self = setmetatable({}, input_manager_module)

	self:__init()

	return self
end

--methods
function input_manager_module:__init()
	self.W = false
	self.A = false
	self.S = false
	self.D = false
	
	self.C = false
	
	self.Space = false
	self.LeftShift = false
end

function input_manager_module:begin_listening()

	--prevents creating multiple connections
	if not self.begin_conn then
		
		--inputbegan connection
		self.begin_conn = uis.InputBegan:Connect(function(input, processed)

			--if its one of the keys we are tracking
			if self[input.KeyCode.Name] ~= nil then
				self[input.KeyCode.Name] = true
			end
		end)
	end

	--prevents creating multiple connections
	if not self.end_conn then
		
		--inputended connection
		self.end_conn = uis.InputEnded:Connect(function(input, processed)

			--if its one of the keys we are tracking
			if self[input.KeyCode.Name] ~= nil then
				self[input.KeyCode.Name] = false
			end
		end)
	end
end

function input_manager_module:cut_connections()
	
	--disconnects inputbegan
	if self.begin_conn then 
		self.begin_conn:Disconnect()
		self.begin_conn = nil
	end
	
	--disconnects inputended
	if self.end_conn then 
		self.end_conn:Disconnect()
		self.end_conn = nil
	end
end

function input_manager_module:is_key_pressed(key: Enum.KeyCode): boolean
	
	--checks if the asked is one of which we are checking
	if self[key.Name] ~= nil then
		return self[key.Name]
	end
	
	warn(key.Name.." is not being tracked by the input manager.")
end

return input_manager_module

FORCE CONTROLLER

local force_controller = {}
force_controller.__index = force_controller

force_controller.forces = {}

--private variables--
local player = game.Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local humrootpart = char:WaitForChild("HumanoidRootPart")

--constructor
function force_controller.new()
	local self = setmetatable({}, force_controller)
	
	--create forces
	self.force_attachment = self:create_attachment()
	self:add_force("forward_force")
	self:add_force("up_force")
	
	return self
end

--methods
function force_controller:create_attachment()
	
	--prevents creating more if 1 already exists
	if self.force_attachment then return self.force_attachment end
	
	--cfg attachment
	local attachment = Instance.new("Attachment")
	attachment.Name = "force_attachment"
	attachment.Parent = humrootpart
	
	--saves to controller object
	return attachment
end

function force_controller:get_attachment()
	
	--lazy loads attachment if it doesn't exist
	if self.force_attachment then return self.force_attachment end
	self:create_attachment()
end

function force_controller:add_force(name: string)
	
	--cfg force instance
	local force = Instance.new("VectorForce")
	force.Name = name
	force.ApplyAtCenterOfMass = true
	force.Force = Vector3.new()
	force.Attachment0 = self:get_attachment()
	force.Parent = self:get_attachment()

	--saves to controller object
	force_controller.forces[name] = force
end

function force_controller:get_force(name: string)
	
	--lazy loads attachment if it doesn't exist
	if force_controller.forces[name] then return force_controller.forces[name] end
	self:add_force(name)
end

function force_controller:apply_constant_force(force_name: string, direction: Vector3, amount: number)
	
	local force = self:get_force(force_name)
	force.Force = direction.Unit * amount
	
end

function force_controller:apply_momental_force(force_name: string, direction: Vector3, amount: number)

	local force = self:get_force(force_name)
	force.Force = direction.Unit * amount
	
	wait(0.2)
	
	--removes force after slight delay
	force.Force = Vector3.new(0, 0, 0)
end

function force_controller:update_force(name: string, new_amount: number)
	
	--updates force amount
	local force = self:get_force(name)
	force.Force = force.Force.Unit * new_amount
end

function force_controller:nullify_force(force_name: string)
	local force = self:get_force(force_name)
	force.Force = Vector3.zero
end

return force_controller

#help-and-feedback:code-review

1 Like