Character movement trouble

I’m trying to make character movement similar to the movement in the game starscape on Roblox.

The issue is that due to the camera offset when trying to change direction from being stationary it jumps/jitters as seen in the video. (The offset is for an over the shoulder view)

I’m using BodyGyro and setting its CFrame to the camera’s lookVector, here’s my code:


local RunService = game:GetService('RunService')
local userInputService = game:GetService("UserInputService")

local player = game.Players.LocalPlayer
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local camera = game.Workspace.CurrentCamera
local mouse = player:GetMouse()

local bodyGyro = Instance.new("BodyGyro")
bodyGyro.Parent = humanoidRootPart
bodyGyro.MaxTorque = Vector3.new(400000, 400000, 400000)
bodyGyro.P = 500000

userInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
humanoid.CameraOffset = Vector3.new(2.5, 2, 0)
player.CameraMaxZoomDistance = 10
player.CameraMinZoomDistance = 9


local function updateBodyGyro()
	local lookVector = camera.CFrame.LookVector
	lookVector = Vector3.new(lookVector.X, 0, lookVector.Z)
	bodyGyro.CFrame = CFrame.new(humanoidRootPart.Position, humanoidRootPart.Position + lookVector)
end


local function onRunning(speed)
	if speed > 0 then
		updateBodyGyro()
	end
end

character:WaitForChild("Humanoid").Running:Connect(onRunning)

game:GetService("RunService").Heartbeat:Connect(function()
	if character:FindFirstChild("Humanoid") then
		if character.Humanoid.MoveDirection.Magnitude > 0 then
			updateBodyGyro()
		end
	end
end)

This is the expected result I’m trying to go for:

Any help appreciated!

2 Likes

Are you sure the camera type was just not locked to follow or orbit??

I don’t think so, orbital seems to lock the Y axis but doesn’t make the character face the camera direction, and I don’t see anything happen when I set it to follow.

Anyone know how to go about this?

There are two problems I found while debugging the code:

  1. In the example video of the camera mechanics desired, it seems like the camera revolves around the player depending on where the camera is looking. Therefore, the camera position should be updating as the camera changes orientation in the XZ plane.
  2. It also seems like the camera and player rotate independently of each other. So in this case, we cannot use humanoid.CameraOffset or it might cause some unintended side effects like jittering (I’ve already tested this and it seems to be true).
  • Fundamentally, this is because the player’s orientation depends on the camera’s look vector, and conversely, humanoid.CameraOffset depends on the player’s orientation.
  1. Though unrelated to the question, it seems like the bodyGyro on the character is a bit too high when looking at the example video. I lowered it in my solution, however, feel free to change it to your desire (This won’t cause any issues for the camera).

Here is a solution that addresses these issues! (Click here for a demo of the solution)

-- use render stepped for the camera
RunService.RenderStepped:Connect(function()
	if humanoid then
		updateCamera()
	end
end)

-- new settings to match smoothness of the provided ideal gameplay
bodyGyro.MaxTorque = Vector3.new(10000, 10000, 10000)
bodyGyro.P = 15000

local camUpOffset = 2 -- height
local camRightOffset = 2.5 -- length
local camBackOffset = 10 -- depth

function updateCamera()
	
	local camVector = camera.CFrame.LookVector
	local hrpPos = humanoidRootPart.CFrame.Position
	
	local newCamCFrame = CFrame.new(hrpPos - camVector * camBackOffset, hrpPos)
	
	local camRightVector = newCamCFrame.RightVector * camRightOffset
	local camUpVector = newCamCFrame.UpVector * camUpOffset
	
	newCamCFrame = CFrame.new(newCamCFrame.Position + camRightVector + camUpVector, hrpPos + camRightVector + camUpVector)
	camera.CFrame = newCamCFrame
end
1 Like

I’m using your code now but for some reason I’m not getting the same results? I’m not getting any error’s and it seems the BodyGyro is not reacting at all.

local RunService = game:GetService('RunService')

local player = game.Players.LocalPlayer
local character = script.Parent
local humanoid = character:WaitForChild("Humanoid")
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local camera = game.Workspace.CurrentCamera
local mouse = player:GetMouse()

local bodyGyro = Instance.new("BodyGyro")
bodyGyro.Parent = humanoidRootPart

-- use render stepped for the camera
RunService.RenderStepped:Connect(function()
	if humanoid then
		updateCamera()
	end
end)

-- new settings to match smoothness of the provided ideal gameplay
bodyGyro.MaxTorque = Vector3.new(10000, 10000, 10000)
bodyGyro.P = 15000

local camUpOffset = 2 -- height
local camRightOffset = 2.5 -- length
local camBackOffset = 10 -- depth

function updateCamera()
	
	local camVector = camera.CFrame.LookVector
	local hrpPos = humanoidRootPart.CFrame.Position

	local newCamCFrame = CFrame.new(hrpPos - camVector * camBackOffset, hrpPos)

	local camRightVector = newCamCFrame.RightVector * camRightOffset
	local camUpVector = newCamCFrame.UpVector * camUpOffset

	newCamCFrame = CFrame.new(newCamCFrame.Position + camRightVector + camUpVector, hrpPos + camRightVector + camUpVector)
	camera.CFrame = newCamCFrame
end

You’re just supposed to add my code to yours!

Here’s the entire script:

local RunService = game:GetService('RunService')
local userInputService = game:GetService("UserInputService")

local player = game.Players.LocalPlayer
local character = script.Parent
local humanoid : Humanoid = character:WaitForChild("Humanoid")
local humanoidRootPart = character:WaitForChild("HumanoidRootPart")
local camera = game.Workspace.CurrentCamera
local mouse = player:GetMouse()

local bodyGyro = Instance.new("BodyGyro")
bodyGyro.Parent = humanoidRootPart

userInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
player.CameraMaxZoomDistance = 10
player.CameraMinZoomDistance = 9


local function updateBodyGyro()
	local lookVector = camera.CFrame.LookVector
	lookVector = Vector3.new(lookVector.X, 0, lookVector.Z)
	bodyGyro.CFrame = CFrame.new(humanoidRootPart.Position, humanoidRootPart.Position + lookVector)
end


local function onRunning(speed)
	if speed > 0 then
		updateBodyGyro()
	end
end

character:WaitForChild("Humanoid").Running:Connect(onRunning)

game:GetService("RunService").Heartbeat:Connect(function()
	if character:FindFirstChild("Humanoid") then
		if character.Humanoid.MoveDirection.Magnitude > 0 then
			updateBodyGyro()
		end
	end
end)

RunService.RenderStepped:Connect(function()
	if humanoid then
		updateCamera()
	end
end)

-- new settings to match smoothness of the provided ideal gameplay
bodyGyro.MaxTorque = Vector3.new(10000, 10000, 10000)
bodyGyro.P = 15000

local camUpOffset = 2 -- height
local camRightOffset = 2.5 -- length
local camBackOffset = 10 -- depth

function updateCamera()
	
	local camVector = camera.CFrame.LookVector
	local hrpPos = humanoidRootPart.CFrame.Position
	
	local newCamCFrame = CFrame.new(hrpPos - camVector * camBackOffset, hrpPos)
	
	local camRightVector = newCamCFrame.RightVector * camRightOffset
	local camUpVector = newCamCFrame.UpVector * camUpOffset
	
	newCamCFrame = CFrame.new(newCamCFrame.Position + camRightVector + camUpVector, hrpPos + camRightVector + camUpVector)
	camera.CFrame = newCamCFrame
end
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.