Character flinging out of control when exiting marble

Hello forum. I’m trying to make a rolling ability similar to that in the game Knockout City, but I am running into an issue that I have been unable to debug for about half an hour now.

Issue

As you can see in the video below, my character gets up perfectly fine when I am moving forward and exit the marble, but if I am traveling sideways or backward, the character glitches out and spins really fast.

robloxapp-20240531-2149577.wmv (4.8 MB)

Code:

Server:

ReplicatedStorage.BallForm.OnServerEvent:Connect(function(Player, Argument)
	if not Player or not Argument then return end
	
	if Argument == 'Add' then
		
		local Ball = makeBall()	
		
		for _, part in Player.Character:GetDescendants() do
			if part:IsA('Part', 'Meshpart') then
				part.CanCollide = false
				print(part.Name)
			end
		end
		
		Ball.Parent = Player.Character
		Ball.CFrame = Player.Character.HumanoidRootPart.CFrame
		
		
		local Weld = Instance.new('Weld', Ball)
		Weld.Part0 = Player.Character.HumanoidRootPart
		Weld.Part1 = Ball
		
		local Velocity = Instance.new('BodyAngularVelocity', Ball)
		
		Player.Character.Humanoid.PlatformStand = true
		
		coroutine.wrap(function()
			while task.wait() do
				pcall(function()
					Velocity.AngularVelocity = Vector3.new(Player.Character.Humanoid.MoveDirection.Z * SPEED, 0, Player.Character.Humanoid.MoveDirection.X * -SPEED)
					Velocity.MaxTorque = Vector3.new(30000, 0, 30000)
					
					if Player.Character.Humanoid.MoveDirection == Vector3.zero then
						Velocity.MaxTorque = Vector3.zero
					end
				end)
			end
		end)()
		
	elseif Argument == 'Remove' then
		
		if Player.Character:FindFirstChild('Ball') then

			Player.Character.Ball:Destroy()
			Player.Character.Humanoid.PlatformStand = false
			
		end
		
	end
end)

Client:

local inBallForm = false

UserInputService.InputBegan:Connect(function(input, processed)
	if input.KeyCode == Enum.KeyCode.R and not processed then
		if inBallForm then
			ReplicatedStorage.BallForm:FireServer('Remove')
			cam.CameraSubject = plr.Character.Humanoid
			ballFormAnim:Stop()
			char:PivotTo(CFrame.lookAlong(char.PrimaryPart.Position, cam.CFrame.LookVector))
			inBallForm = false
		else
			ReplicatedStorage.BallForm:FireServer('Add')
			cam.CameraSubject = plr.Character.HumanoidRootPart
			ballFormAnim:Play()
			inBallForm = true
		end
	end
end)

RunService.RenderStepped:Connect(function()
	if char:FindFirstChild('Torso') then
		if inBallForm then
			char.Torso.CanCollide = false
		end
	end
end)

I set the torso to CanCollide = false in the RenderStepped function because roblox automatically sets it to true each frame
(also I know that BodyAngularVelocity is deprecated but I’m too stubborn to use attachments)

The marble is welded to the player’s HumanoidRootPart, the character’s body parts all have CanCollide == False, and the player’s Humanoid has PlatformStand == true. The mouse is locked to the center of the screen, and the camera’s RotationType is set to CameraRelative.
When the marble is disabled, all that happens is the ball is destroyed, and the Humanoid’s PlatformStand property is set to false.

I believe that this occurs because the character has to snap to the camera rotation after exiting the marble with a drastically different orientation, but I am unsure. I have tried setting the character’s CFrame manually after exiting the marble but it has not resolved my issue.

If anybody has any idea of how to fix this issue, a reply would be greatly appreciated. :slight_smile:

try setting the player’s root part velocity to 0,0,0 when they exit the marble

I added these two lines of code in the disable function on the server script for the marble, yet I am still getting the same result.

Player.Character.HumanoidRootPart.AssemblyLinearVelocity = Vector3.zero
Player.Character.HumanoidRootPart.AssemblyAngularVelocity = Vector3.zero

in addition to those lines, add this

Player.Character.HumanoidRootPart.Velocity = Vector3.zero

I tried this, but I am once again receiving the same result. Also I am pretty sure BasePart.Velocity is deprecated and works almost exactly the same as AssemblyLinearVelocity.

Also I just realized that I added this line to the client side:

char:PivotTo(CFrame.lookAlong(char.PrimaryPart.Position, cam.CFrame.LookVector))

And when I removed it, the error happened regardless of which direction I was moving.

From the looks of your video, I would guess the player is still being affected by the roll when they have already exited; possibly your timing is a bit off. Your scripts are incomplete, so I really have nothing to test with. Here are a few links that may help:

video
forum
forum

That is actually the exact video that I used to start this project. However, even in the video this bug is present, and it does not explain how to fix it.

As for testing, here is an uncopylocked place with all of the related scripts:

When the player is getting out, make the player unable to collide with anything until the animation has ended.

That is how you get a script fixed … :rofl:

UserInputService.InputBegan:Connect(function(input, processed)
	if input.KeyCode == Enum.KeyCode.R and not processed then
		if inBallForm then
			ReplicatedStorage.BallForm:FireServer('Remove')
			cam.CameraSubject = plr.Character.Humanoid
			
			--copied from the sever script to here and also left this there.
			plr.Character.Ball:Destroy()
			plr.Character.Humanoid.PlatformStand = false

			plr.Character.HumanoidRootPart.AssemblyLinearVelocity = Vector3.zero
			plr.Character.HumanoidRootPart.AssemblyAngularVelocity = Vector3.zero
			--
			
			ballFormAnim:Stop() 
			char:PivotTo(CFrame.lookAlong(char.PrimaryPart.Position, cam.CFrame.LookVector))
			inBallForm = false
		else
			ReplicatedStorage.BallForm:FireServer('Add')
			cam.CameraSubject = plr.Character.HumanoidRootPart
			ballFormAnim:Play()
			inBallForm = true
		end
	end
end)

I can’t believe I didnt think of this! :person_facepalming:
Thank you!

EDIT: This solution seems to only work for me in studio, and not in game. Is there a reason why this is happening?

On the client script make it:
plr.Character.Humanoid.PlatformStand = false
plr.Character.Ball:Destroy()

Then take out from both:
Player.Character.HumanoidRootPart.AssemblyLinearVelocity = Vector3.zero
Player.Character.HumanoidRootPart.AssemblyAngularVelocity = Vector3.zero

Server script only has:
plr.Character.Humanoid.PlatformStand = false

Tested in game …

You have to use that on the server script or it will fail next time you try it.
Also putting that on the client script hopefully is avoiding any update speed loss.