Custom Character Movement

Hello everyone,
I am making a custom character and I would like it to follow the mouse smoothly.

Here is my current script:

wait(3)
local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:wait()
local mouse = player:GetMouse()

local Shift = Enum.KeyCode.LeftShift
local hrp = character.HumanoidRootPart
local speed = 0.5

mouse.Move:Connect(function()
	wait()
	local x, y, z = mouse.Hit.X,mouse.Hit.Y,mouse.Hit.Z
	hrp.CFrame = CFrame.new(x * speed,y * speed,z * speed)
end)

Ask if I need to add more info.
Thanks for the Help!

you can use RunService.RenderStepped:wait() inside the mouse.Move function it is the fastest

  mouse.Move:Connect(function()
	game:GetService("RunService").RenderStepped:wait()
	local x, y, z = mouse.Hit.X,mouse.Hit.Y,mouse.Hit.Z
	hrp.CFrame = CFrame.new(x * speed,y * speed,z * speed)
end)

I think I wasn’t clear enough; I wanted my custom character to follow the mouse smoothly, like a spaceship floating or hovering. And when the custom character moves, I want it to move using velocity following the mouse(or how a jetpack moves). Please ask if I am not clear or need to add more information.

tl;dr the bottom of this reply has a working code snippet

You would probably want the general direction you want the character to go:

local delta = mouse.Hit.Position - hrp.Position

This stores two variables, the direction you want the character to go, delta.Unit, and how far the character needs to go to reach the goal, delta.Magnitude.

First of all mouse.Move only fires when the mouse moves, so unless you move the mouse around frantically the entire time you’re flying, we should connect this to something else. A good place would be RunService.Heartbeat:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local player = Players.LocalPlayer

local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
player.CharacterAdded:Connect(function(char)
    hrp = char:WaitForChild("HumanoidRootPart")
end)

local mouse = player:GetMouse()

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position
    -- ...
end)

Since we want the character to move at a constant speed per unit of time, we should multiply speed by dt to give the intended distance we want the character to go:

-- ...

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position
    local deltaSpeed = speed * dt
    -- ...
end)

You can then take your speed variable and multiply it by delta.Unit to get the displacement of your character.

-- ...

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position
    local deltaSpeed = speed * dt

    hrp.CFrame += delta.Unit * deltaSpeed
end)

The only problem you would run into here is the fact that direction.Unit * speed can overshoot the goal once it gets close enough, and it would make your character all jittery, so you would want to get the minimum of the distance delta.Magnitude and the speed:

-- ...

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position
    local deltaSpeed = speed * dt
    local newSpeed = math.min(deltaSpeed, delta.Magnitude)

    hrp.CFrame += delta.Unit * newSpeed
end)

Finally, if the character is right on top of the target, delta becomes <0, 0, 0>, which makes delta.Unit NaN, which would make your character glitch out and disappear if you try to set hrp.Position again. We can fix it with a simple conditional:

-- ...

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position

    if delta ~= Vector3.zero then
        local deltaSpeed = speed * dt
        local newSpeed = math.min(deltaSpeed, delta.Magnitude)

        hrp.CFrame += delta.Unit * newSpeed
    end
end)

The final product should look like this:

local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local player = Players.LocalPlayer

local character = player.Character or player.CharacterAdded:Wait()
local hrp = character:WaitForChild("HumanoidRootPart")
player.CharacterAdded:Connect(function(char)
    hrp = char:WaitForChild("HumanoidRootPart")
end)

local mouse = player:GetMouse()

local speed = 30 -- how far the character goes in a second
RunService.Heartbeat:Connect(function(dt)
    local delta = mouse.Hit.Position - hrp.Position

    if delta ~= Vector3.zero then
        local deltaSpeed = speed * dt
        local newSpeed = math.min(deltaSpeed, delta.Magnitude)

        hrp.CFrame += delta.Unit * newSpeed
    end
end)

I didn’t and can’t test this, so if it doesn’t work, please tell me :grinning:

The only other problem I can think of is the character clipping into the floor, since mouse.Hit would presumably be describing the surface of a part. You would have to cast a ray using mouse.UnitRay, but by then, you would forgo the Mouse API altogether and use more updated APIs:

local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")

local player = Players.LocalPlayer
local camera = Workspace.CurrentCamera

local rayParams = RaycastParams.new()

local hrp
local function onCharacterAdded(char)
    rayParams.IgnoreDescendantsInstances = { char }
    hrp = char:WaitForChild("HumanoidRootPart")
end

if player.Character then
    onCharacterAdded(player.Character)
end
player.CharacterAdded:Connect(onCharacterAdded)

local function raycastFromMouse()
    local mousePos = UserInputService:GetMouseLocation()

    local ray = camera:ViewportPointToRay(mousePos.X, mousePos.Y)
    return Workspace:Raycast(ray.Origin, ray.Direction * 1000, rayParams)
end

local speed = 30
RunService.Heartbeat:Connect(function(dt)
    local result = raycastFromMouse()
    local delta = result.Position + result.Normal * 7 - hrp.Position

    if delta ~= Vector3.zero then
        local deltaSpeed = speed * dt
        local newSpeed = math.min(delta.Magnitude, deltaSpeed)
        hrp.CFrame += delta.Unit * newSpeed
    end
end)

Since this method does not use physics, it would probably be best to anchor the hrp.

Sorry for the late reply, but why does the character stay at one y-pos, is it the clipping problem you were talking about? Also thanks for the explanation!