Trying to clamp rotation of character

  1. What do you want to achieve? Keep it simple and clear!
    i need to limit turning control when the player is charging by setting a limit
  2. What is the issue? Include screenshots / videos if possible!
    my rotations simply are not applying, the clamping is working.
  3. What solutions have you tried so far? Did you look for solutions on the Creator Hub?
    i have already looked on the creator hub but this is a more specific use case

im not the best at vector maths so it could be something really stupid

Relevant client code

local previousY = rootPart.Rotation.Y
game:GetService("RunService").RenderStepped:Connect(function()
	if not chargingValue.Value then return end
	local newY = rootPart.Rotation.Y
	local diff = newY - previousY
	diff = math.clamp(diff, -maxTurnRate, maxTurnRate)
	local newRotation = CFrame.new(player.Character:GetPivot().Position) * CFrame.Angles(0, previousY + diff, 0)
	player.Character:PivotTo(newRotation)
	previousY = newY
end)

I am not sure exactly what you are trying to do, but this works

local Players = game:GetService("Players")
local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local rootPart = character.HumanoidRootPart
local previousY = rootPart.Rotation.Y
local maxTurnRate = 50


local function rotateFun()	
	local newY = rootPart.Rotation.Y
	local diff = newY - previousY
	diff = math.clamp(diff, -maxTurnRate, maxTurnRate)
	local newRotation = CFrame.new(player.Character:GetPivot().Position) * CFrame.Angles(0, previousY + diff, 0)
	player.Character:PivotTo(newRotation)
	previousY = newY		
end

while true do
	rotateFun()
	task.wait(1)
end

Also, render stepped is so fast man, you are updating too fast and at the wrong time in the frame cycle.

I think of Presimulation (or Prerender) use

thanks ill try this, im trying to achieve something similar to how the shield charge works in tf2

Again same issue, absolutely nothing is happening, if you would like me to add certain debugs let me know

Wouldn’t this make it unresponsive? i need every mouse movement to be limited so it has to be every frame

Are you using another script, what script makes the player charge ? Its kinda hard to give a complete answer to your scenario/setup without seeing the full picture. How is the chargingValue.Value incrementing?

I just showed you a function being used inside a while true loop and how it works b/c I slowed it down. Something else may need to change, are you using other scripts ?

aws went down i cant get onto studio but ill send you the whole script when finished

-- modules and services
local Players = game:GetService("Players")
local ContextActionService = game:GetService("ContextActionService")
local MovementFunctions = require(script.Parent:WaitForChild("MovementFunctions"))
local FREEZE_ACTION = "freezeMovement"

-- values
local chargingValue = script.Parent.Charging
local canChargeValue = script.Parent.CanCharge
local chargeDurationValue = script.Parent.ChargeDuration

-- constants
local baseMoveSpeed = 16
local maxTurnRate = script.Parent.MaxTurnRate.Value

-- player data
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local humanoid: Humanoid = Players.LocalPlayer.Character:FindFirstChild("Humanoid")
local camera = workspace.CurrentCamera
local rootPart: BasePart = Players.LocalPlayer.Character:WaitForChild("HumanoidRootPart")


local previousY = rootPart.Rotation.Y
local function rotateFun()	
	local newY = rootPart.Rotation.Y
	local diff = newY - previousY
	diff = math.clamp(diff, -maxTurnRate, maxTurnRate)
	local newRotation = CFrame.new(player.Character:GetPivot().Position) * CFrame.Angles(0, previousY + diff, 0)
	player.Character:PivotTo(newRotation)
	previousY = newY
end


-- the following function initiates charging
mouse.Button2Down:Connect(function()
	if chargingValue.Value or not canChargeValue.Value then
		print("charge ended early due to missing requirements")
		return
	end
	print("charging")
	script.Parent.ChargeLeft.Value = chargeDurationValue.Value
	chargingValue.Value = true
	MovementFunctions.DisableMovement()
	humanoid.WalkSpeed = script.Parent.ChargeSpeed.Value
	for i=1, chargeDurationValue.Value / 0.05 do
		MovementFunctions.ForceMovement()
		print("chargingLoop")
		rotateFun()
		task.wait(0.05)
	end
	humanoid.WalkSpeed = baseMoveSpeed
	MovementFunctions.EnableMovement()
	chargingValue.Value = false
	print("chargeEnded")
end)


Like raell said, this code does rotate the character. Try testing either version in a blank studio project.
I noticed that if the previousY was initialized as rootPart.Rotation.Y, the rotation wouldn’t start until I moved my character. You can try setting the previousY as a specific value to test if that changes the issue.

local character = game.Workspace:WaitForChild("youbigown")
local rootPart = character:WaitForChild("HumanoidRootPart")
local maxTurnRate = 10
local previousY = 20

local function rotateFun()	
	local newY = rootPart.Rotation.Y
	local diff = newY - previousY
	diff = math.clamp(diff,-maxTurnRate,maxTurnRate)
	local newRotation = CFrame.new(character:GetPivot().Position) * CFrame.Angles(0, previousY + diff, 0)
	character:PivotTo(newRotation)
	previousY = newY
end

while true do
	rotateFun()
	task.wait(1)
end
1 Like

with this code running with nothing else, the player spins randomly every second

It spins like the code says it does.
What “spin” do you need?

i dont need a spin, i need to limit the turning control as said in the post, if you need an example try to find clips of the shield charge in tf2, charges forward, you cant turn over a certain speed.

What you’re currently trying to do is overriding the rotation each frame?

The overall problem is that from what I know, Roblox doesn’t allow you to edit the sensitivity of default rigs movement. Below are 2 methods that does not rely on frame overwriting.

tf2 is a mouse locked game from what I remember. You can have a mouse locked script that rotates the HumanoidRootPart based on mouse movement (X,Z), and in that script you can have a sensitivity variable in it. The sensitivity variable would be reduced when the charging is ongoing.
Below is a mockup snippet of the camera code from my game. It is a bit of a headache to prevent the camera going through walls though. I’m currently using raycasting, although it causes some camera snapping I haven’t been able to fix fully. If your game is 1st person, you can just set the CameraOffset to (0,0,0) and not have to worry about walls raycasting.

     ---top of script variables
     local camera = workspace.CurrentCamera
     camera.CameraType = Enum.CameraType.Scriptable

    local cameraSensitivity = 0.3
    local cameraOffset = Vector3.new(1.3,2,10)
    local cameraRotationX = 0
    local cameraRotationY = 0

    local targetPart = character.HumanoidRootPart

        --THIS SHOULD BE IN A RUNSERVICE LOOP
	--Get the player mouse values
	local delta = UserInputService:GetMouseDelta()
	cameraRotationX = cameraRotationX - delta.Y * cameraSensitivity
	cameraRotationY = cameraRotationY - delta.X * cameraSensitivity

	--Clamp the vertical rotation to prevent flipping
	cameraRotationX = math.clamp(cameraRotationX,-90,90)

        --Rotate hrp
        targetPart.CFrame = CFrame.new(targetPart.Position) * CFrame.Angles(0,math.rad(cameraRotationY),0)

       --Position camera
       camera.CFrame = CFrame.new(targetPart.Position) * CFrame.Angles(0,math.rad(cameraRotationY),0) * CFrame.Angles(math.rad(cameraRotationX),0,0) * CFrame.new(cameraOffset) 

If you want a game where the mouse is not locked in the center, then you’d have create a script that detects player inputs (WASD) and swiftly tweens the HumanoidRootPart’s rotation in the direction corresponding to the key. You can then have a variable named sensitivityMultiplier that multiplies the rotation amount. When you have the rotation coded, you can then set the MoveDirection of the humanoid depending on the rotation. That is how I imagine Roblox movement is coded (I recently did this sort of simulation coding for my tornado). Since you have now coded it yourself, you can edit the sensitivity when needed.

In both scenarios you need to disable humanoid.Autorotate Humanoid.AutoRotate = false and then script custom rotation script.

One problem with this, the camera is always rotated towards -Z relative to the world every time it starts

you can choose where it starts by setting this in first loop iteration
cameraRotationY = yourRotation

What is more important is that I haven’t added the sensitivityMultiplier (the difficult part). I’m gonna play around in studio to see if I can make it.

alright so here is the version with cameraRotationSpeed. You can set it to math.huge when you want to let the player rotate at any speed, whereas you can set it to for example 100 while the charging is happening. Though I will once again warn that if this game is 3rd person, this kind of script needs raycasting to prevent the camera from going through walls.

You can also just use this script as inspiration for your current system. The point is that this script limits the rotation by looking at how much time has passed and what the rotationSpeed is set as.

local camera = workspace.CurrentCamera
camera.CameraType = Enum.CameraType.Scriptable

local cameraSensitivity = 0.3
local cameraOffset = Vector3.new(1.5,2,10)
local cameraRotationX = 0
local cameraRotationY = 0
local cameraRotationSpeed = 100

local character = script.Parent
local targetPart = character:WaitForChild("HumanoidRootPart")

local UIS = game:GetService("UserInputService")
UIS.MouseBehavior = Enum.MouseBehavior.LockCenter

local runService = game:GetService("RunService")
local latestTime = tick()

runService.RenderStepped:Connect(function()

	local currentTime = tick()
	local passedTime = currentTime - latestTime

	local delta = UIS:GetMouseDelta()
	local targetChangeX = -delta.Y * cameraSensitivity
	local targetChangeY = -delta.X * cameraSensitivity
	local maxChange = cameraRotationSpeed * passedTime
	targetChangeX = math.clamp(targetChangeX,-maxChange,maxChange)
	targetChangeY = math.clamp(targetChangeY,-maxChange,maxChange)

	cameraRotationX = cameraRotationX + targetChangeX
	cameraRotationX = math.clamp(cameraRotationX,-90,90)
	cameraRotationY = cameraRotationY + targetChangeY

	targetPart.CFrame = CFrame.new(targetPart.Position) * CFrame.Angles(0,math.rad(cameraRotationY),0) 
	camera.CFrame = CFrame.new(targetPart.Position) * CFrame.Angles(0,math.rad(cameraRotationY),0) * CFrame.Angles(math.rad(cameraRotationX),0,0) * CFrame.new(cameraOffset)
	
	latestTime = currentTime
	
end)
1 Like

i think you need all of the elements to properly test this as it works fine in a separate place but i need to change stuff to put it into mine
Charge spaghetti.rbxl (70.2 KB)
thats the place file im using, you will find everything inside of starterplayerscripts

Here it is. I centered the camera on the head so it’s 1st person. The charge now limits the rotation of the camera (you can limit it even more by reducing cameraRotationSpeed below 100).
Charge spaghetti (youbigown).rbxl (69.9 KB)