How to do custom character movement?

I’m trying to re-create custom movement that feels like the default one, but for the purpose that all the controls can be customised and changed.

I have two variables that change with inputs:

movez = 0
movex = 0

Where if you press W for example it will be set to 1 and if you release it will be 0, if you press S it will go to -1, and same for movex where A is -1.

I’m wondering about how I would go about moving the character (note: This will be first person so no third person stuff to worry about), so that it moves in the direction of the camera and desired inputs and feels like the default movement system?

All I know is I might need to use this:

local direction = camera.CFrame.lookVector

I’m not sure whether it should be updated on the client or on the server, because the client could hack it and do a speed hack but on the server the client will see input lag if there connection is slow (>5ms which is kinda crazy)

note: if anyone’s wondering how I decided the input:

local movez = 0
local movex = 0

local movements = {
    ["forw"] = Enum.KeyCode.W,
    ["left"] = Enum.KeyCode.A,
    ["back"] = Enum.KeyCode.S,
    ["righ"] = Enum.KeyCode.D,
	["jump"] = Enum.KeyCode.Space
	
}

local inputs = setmetatable({}, {
    __index = function(t, k)
        return uis:IsKeyDown(movements[k]) and true or false
    end
})
10 Likes

I believe the default Roblox control system is client-based, and makes updates to the character’s movement client-sided, and each player has their own network ownership to their characters, so it updates to the server regardless.

I would probably do it client-sided, after learning about how if the client has network ownership to something, changes made to that object or model will update server-sided as well. (atleast I think that principle is true towards anything else as well, not just the character model)

8 Likes

I would keep it on the server. Unless you change the character’s network ownership to the server, you aren’t going to be able to stop speed hackers.

LookVector gives you a vector which is offset from the cameras cframe by 1 stud forwards.
cf.LookVector is functionally the same as (cf*CFrame.new(0,0,-1)).p-cf.p

There is also RightVector which is offset from the cameras cframe by 1 stud to the right.
cf.RightVector is functionally the same as (cf*CFrame.new(1,0,0)).p-cf.p

To get the direction vector that you want, you would times these two vectors by X and Z respectively, and add them together, like so:
local direction = cf.LookVector * X + cf.RightVector * Z

You can then call the Move function of humanoid with this value.
humanoid:Move(direction)

EDIT:

I should also mention that using LookVector & RightVector will almost always be better than creating the CFrames and removing the offset like I did above. While cf.RightVector is functionally the same as (cf*CFrame.new(1,0,0)).p-cf.p, the C++ roblox api is much faster and will get you more accurate results.

6 Likes

I believe this should work:

Player:Move(Camera.lookVector * movez + Camera.rightVector * movex, true)
7 Likes

If you are using Player:Move() the solutions above will probably work. If you aren’t using Humanoids and are using body movers by chance, you are going to be feeding it a look vector that is likely facing downwards. You can do a bit of CFrame math to properly eliminate the downwards facing part of that look vector.

To start with, you need to understand what the cross product of 2 vectors is. If you have 2 vectors (assuming they aren’t [0,0,0] or parallel ) there exists 2 additional vectors which are perfectly right to both vectors. WHICH of those 2 possible vectors that is returned by Vector3:Cross() is determined by the the right hand rule.

So with that in mind, imagine you have a camera CFrame like so:

With the yellow face representing its forward facing direction. Notice how its looking downward. If you just take the look vector of that face it is going to also be looking partially downward, which you want to avoid if you are not using humanoids.

So what you want to find is a vector that is looking forwards and not partially downwards. So what you can do is take the rightVector of the camera (the red line) and :Cross() it with a vector looking upwards in world space (the blue line, Vector3.new(0, 1, 0)). Following the right hand rule, this is actually going to result in a vector that is looking BACKWARDS, so you have to inverse it. This will result in the green vector.

From there you can do what the other suggestions said, free from a look vector looking downwards. So in code form:

local currentCamCF = game.Workspace.CurrentCamera.CFrame
	
local right = currentCamCF.RightVector
local top = Vector3.new(0, 1, 0) 
local frontVector = -(right:Cross(top))
local movementVector = (frontVector*movez) + (right * movex)

Also in terms of who should own the rig, do movement client sided. Server side will be unplayable for many people due to lag. No one wants to play a game where it takes 200ms for their “move forward” key to do anything.

20 Likes

Thank you so much, I literally never though it would be this easy, I really appreciate this, I probably couldn’t have done this otherwise

1 Like

I know this is a bump but the client should always have the network ownership of the character to prevent visible delay read more here again sorry for the bump

4 Likes