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)
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.
There are two problems I found while debugging the code:
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.
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.
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).
-- 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
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
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