Is the character welded to the sphere? A humanoid object usually attempts to keep the character upright. If the sphere is welded to the character, this will cause the humanoid to resist the sphere’s rotation. You can fix this by changing the humanoid’s state to Enum.HumanoidStateType.Physics
.
Also, is 50 supposed to be the maximum linear speed? Why are you setting MaxTorque equal to max speed? Torque causes angular acceleration, it’s not angular speed or linear speed.
If I understood correctly what you want, I’d recommend setting the AngularVelocity only around one axis but updating this axis every frame when humanoid.MoveDirection is not a zero vector. In order to update the rotation axis every frame, you’ll also need Attachment1.
The AngularVelocity property only needs to be set once. You’ll have to calculate the desired angular speed, negate it so that the rotation is clockwise, and then set it as the AngularVelocity around the x-axis of Attachment1. Angular speed of a rolling sphere in radians per second is its linear speed divided by its radius (assuming that there’s enough friction to keep it from sliding). Here’s the code for setting AngularVelocity properties that only need to be set once.
-- I put math.min just in case the sphere doesn't have the same size on all axes for whatever reason.
local sphereRadius: number = .5 * math.min(sphere.Size.X, sphere.Size.Y, sphere.Size.Z)
local angularSpeed: number = maxLinearVelocity / sphereRadius
angularVelocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment1
angularVelocity.AngularVelocity = Vector3.new(-angularSpeed, 0, 0)
You can parent Attachment1 to terrain. Set its CFrame every frame such that its RightVector is parallel to the desired axis of rotation (perpendicular to the desired plane of rotation). CFrame.lookAt(Vector3.zero, Humanoid.MoveDirection) is such a CFrame. In order to prevent the AngularVelocity from accelerating the ball when the player isn’t holding a movement key, multiply the max torque with the magnitude of humanoid.MoveDirection (because torque causes angular acceleration). Here’s the code that should be run every frame.
angularVelocity.MaxTorque = humanoid.MoveDirection.Magnitude * maxTorque
if humanoid.MoveDirection.Magnitude > 1e-4 --[[just an arbitrary small number]] then
attachment1.CFrame = CFrame.lookAt(Vector3.zero, humanoid.MoveDirection)
end
Here's some testing code I wrote. It's client-only, though. The ball created by it won't exist on the server and for some reason the character position doesn't replicate either.
--!strict
--https://devforum.roblox.com/t/how-can-i-achieve-this/3035272
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local sphereTransparency: number = .5
local sphereColor: Color3 = Color3.new(1, 0, 0)
local sphereMaterial: Enum.Material = Enum.Material.Marble
local sphereDiameter: number = 10
local maxLinearVelocity: number = 50
local maxTorque: number = 50_000
local humanoid: Humanoid
local sphere: Part
local attachment1: Attachment
local angularVelocity: AngularVelocity
local runServiceConnection: RBXScriptConnection?
local function createInstances(humanoidRootPart: Part): ()
sphere = Instance.new("Part")
sphere.Transparency = sphereTransparency
sphere.Color = sphereColor
sphere.Material = sphereMaterial
sphere.Shape = Enum.PartType.Ball
sphere.BottomSurface, sphere.TopSurface = Enum.SurfaceType.Smooth, Enum.SurfaceType.Smooth
sphere.Size = sphereDiameter * Vector3.one
sphere.CFrame = humanoidRootPart.CFrame
local weldConstraint: WeldConstraint = Instance.new("WeldConstraint")
weldConstraint.Part0, weldConstraint.Part1 = humanoidRootPart, sphere
weldConstraint.Parent = sphere
local attachment0: Attachment = Instance.new("Attachment")
attachment1 = Instance.new("Attachment")
attachment0.Name, attachment1.Name = "Attachment0", "Attachment1"
attachment0.Parent, attachment1.Parent = sphere, workspace.Terrain
angularVelocity = Instance.new("AngularVelocity")
angularVelocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment1
angularVelocity.AngularVelocity = Vector3.new(-2 * maxLinearVelocity / sphereDiameter, 0, 0)
angularVelocity.Attachment0, angularVelocity.Attachment1 = attachment0, attachment1
angularVelocity.Parent = sphere
sphere.Parent = workspace
end
local function onRunServiceEvent(): ()
angularVelocity.MaxTorque = humanoid.MoveDirection.Magnitude * maxTorque
if humanoid.MoveDirection.Magnitude > 1e-4 then
attachment1.CFrame = CFrame.lookAt(Vector3.zero, humanoid.MoveDirection)
end
--print(humanoid.MoveDirection)
print(sphere.AssemblyLinearVelocity.Magnitude)
end
local function onCharacterAdded(char: Model)
local humanoidRootPart: Part = char:WaitForChild("HumanoidRootPart") :: Part
humanoid = char:WaitForChild("Humanoid") :: Humanoid
humanoid:ChangeState(Enum.HumanoidStateType.Physics)
createInstances(humanoidRootPart)
if runServiceConnection ~= nil then
runServiceConnection:Disconnect()
end
runServiceConnection = RunService.Heartbeat:Connect(onRunServiceEvent)
end
Players.LocalPlayer.CharacterAdded:Connect(onCharacterAdded)
Additionally, in the code you posted, you’ve accidentally written AngularVelocity.Torque
instead of AngularVelocity.AngularVelocity
.