I’m making a first person PC game with purposefully clunky controls. I want the player to only be able to move in one direction at a time, effectively limiting them to cardinal directions (relative to their rotation). How could I achieve this in the simplest way possible?
The easiest way I know of to do this is to hijack the controls system under the PlayerModule. If you add this code to a LocalScript under StarterPlayerScripts, it should accomplish what you’re looking for:
local playerScripts = script.Parent
local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
local controls = playerModule:GetControls()
--Override the default Roblox move function
controls.moveFunction = function(player : Player, direction : Vector3, relative : boolean)
local absoluteDirection = direction:Abs()
--Test to see what axis there is greater movement in
if (absoluteDirection.X > absoluteDirection.Z) then
--Prioritize X movement by zeroing out the Z component
player.Move(player, direction * Vector3.xAxis, relative)
else
--Prioritize Z movement by zeroing out the X component
player.Move(player, direction * Vector3.zAxis, relative)
end
end
Tried your script. Movement wasn’t relative to player orientation at first but was easily fixed by replacing “relative” on lines 12 and 15 with “true”.
Now there’s a new issue. While movement is relative and restricted to cardinal directions, your orientation changes which key is considered ‘forward’. For instance, if I were to rotate 180 degrees, now I would have to press S to move forward instead of W, and D to move left instead of A, etc.
Oops! Stepped away for a second and realized that I had forgotten to make it relative. This should work:
local playerScripts = script.Parent
local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
local controls = playerModule:GetControls()
local camera = workspace.CurrentCamera
--Override the default Roblox move function
controls.moveFunction = function(player : Player, direction : Vector3, relative : boolean)
--Convert our direction from world space to camera space (thus making it relative)
local trueDirection = camera.CFrame:VectorToObjectSpace(direction)
local absoluteTrueDirection = trueDirection:Abs()
--Test to see what axis there is greater movement in
if (absoluteTrueDirection.X > absoluteTrueDirection.Z) then
--Prioritize X movement by zeroing out the Z component
player.Move(player, trueDirection * Vector3.xAxis, true)
else
--Prioritize Z movement by zeroing out the X component
player.Move(player, trueDirection * Vector3.zAxis, true)
end
end
It’s near-perfect but I was still able to strafe diagonally at some super specific angles. Fixed it by rounding on line 14. Thanks for all of your help!
local playerScripts = script.Parent
local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
local controls = playerModule:GetControls()
local camera = workspace.CurrentCamera
--Override the default Roblox move function
controls.moveFunction = function(player : Player, direction : Vector3, relative : boolean)
--Convert our direction from world space to camera space (thus making it relative)
local trueDirection = camera.CFrame:VectorToObjectSpace(direction)
local absoluteTrueDirection = trueDirection:Abs()
--Test to see what axis there is greater movement in
if (math.round(absoluteTrueDirection.X) > math.round(absoluteTrueDirection.Z)) then
--Prioritize X movement by zeroing out the Z component
player.Move(player, trueDirection * Vector3.xAxis, true)
else
--Prioritize Z movement by zeroing out the X component
player.Move(player, trueDirection * Vector3.zAxis, true)
end
end
Update: turns out there’s another problem I didn’t notice.
When the camera is panned up or down, the player will move slower. Oddly enough this only happens on the Z axis. I fixed it by replacing trueDirection * Vector3.zAxis with Vector3.new(0,0,math.sign(trueDirection.Z). Functions the same exept the Z axis can now only be 1, -1, or 0. It’s pretty scuffed but hey it works
local playerScripts = script.Parent
local playerModule = require(playerScripts:WaitForChild("PlayerModule"))
local controls = playerModule:GetControls()
local camera = workspace.CurrentCamera
controls.moveFunction = function(player : Player, direction : Vector3, relative : boolean)
local trueDirection = camera.CFrame:VectorToObjectSpace(direction)
local absoluteTrueDirection = trueDirection:Abs()
if (math.round(absoluteTrueDirection.X) > math.round(absoluteTrueDirection.Z)) then
player.Move(player, trueDirection * Vector3.xAxis, true)
else
player.Move(player, Vector3.new(0,0,math.sign(trueDirection.Z)), true)
end
end
There’s probably a way to fix that by creating a new CFrame that only accounts for the camera’s yaw and using that instead of the camera CFrame when declaring trueDirection. I would definitely advise to NOT use math.sign there since:
- It’ll interfere with movement sensitivity on controllers and mobile virtual joystick.
- Controls moveFunction is framerate dependent, so I believe players with framerates above 60 FPS will move significantly faster than others.
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.