How do I make the Player.Character remain upright while welded/constrained to a rolling part?

One of the easier ways is to create 2 collision groups - for the ball and for the body parts. Set these 2 collision groups to be non-collidable and instead of using a Weld, use AlignPosition to align the HumanoidRootPart to the ball.

local PhysicsService = game:GetService("PhysicsService")

PhysicsService:RegisterCollisionGroup("PlayerBodyPart")
PhysicsService:RegisterCollisionGroup("PlayerBall")
PhysicsService:CollisionGroupSetCollidable("PlayerBodyPart", "PlayerBall", false)

game:GetService('Players').PlayerAdded:Connect(function(Player)
	local GiveBall = function()
		local Character = Player.Character or Player.CharacterAdded:Wait();
		local Humanoid = Character:WaitForChild('Humanoid');
		local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");
		local RootAttachment = HumanoidRootPart:WaitForChild("RootAttachment")
		
		local Ball = Instance.new("Part");
		Ball.Name = "Ball"
		Ball.Anchored = false;
		Ball.Shape = Enum.PartType.Ball;
		Ball.Size = Vector3.new(7, 7, 7.375);
		Ball.Material = Enum.Material.Plastic;
		Ball.CFrame = Player.Character.HumanoidRootPart.CFrame;
		Ball.Transparency = 0.85;
		Ball.Color = Color3.new(1, 0.32549, 0.337255);
		Ball.CustomPhysicalProperties = PhysicalProperties.new(1, 2, 0.2, 1, 1);
		Ball.CollisionGroup = "PlayerBall";
		
		local BallAttachment = Instance.new("Attachment")
		BallAttachment.Name = "BallAttachment"
		BallAttachment.Parent = Ball
		
		local RootAlignPosition = Instance.new("AlignPosition")
		RootAlignPosition.Name = "RootAlignPosition"
		RootAlignPosition.Attachment0 = RootAttachment
		RootAlignPosition.Attachment1 = BallAttachment
		RootAlignPosition.MaxForce = math.huge
		RootAlignPosition.MaxVelocity = math.huge
		RootAlignPosition.Parent = HumanoidRootPart
		
		-- always parent after setting properties/children so there is
		-- only one replication to the client as opposed to
		-- x amount of property manipulation/children
		Ball.Parent = Character;
		Ball:SetNetworkOwner(Player);
		
		local function onChildAdded(child)
			if child:IsA("BasePart") == true and child ~= Ball then
				child.CollisionGroup = "PlayerBodyPart"
			end
		end

		for _, child in Character:GetChildren() do
			task.spawn(onChildAdded, child)
		end
		
		Character.ChildAdded:Connect(onChildAdded)
	end

	GiveBall();

	Player.CharacterAdded:Connect(function()
		GiveBall();
	end)
end)

The collision group implementation isn’t working on my end, the character ends up getting stuck to the front of the ball aggressively trying to align.

It seems the onChildAdded function isn’t declaring the body parts as non-collidable with the ball. That, and the ball won’t respond to inputs; I believe it may need to be parented to the player.

Can you check if your body parts’ CollisionGroup to PlayerBodyPart and the ball to PlayerBall? If they are, it might be accessories that are still collidable, in which case you can switch to GetDescendants and DescendantAdded instead. Also ensure you are actually registering your collision groups and collidable behavior properly.

I tested the script before sending and works just fine on my end.

Figured it out! Your implementation DOES work, it just needed a single line declaring the ball’s parent as the character, then it works exactly as intended! Just inserted this at line 25 and it worked like a charm.

Ball.Parent = Character

Pretty sure this is because the ball is identified elsewhere as a child of the character, so it only acts accordingly when they’re properly parented. Thank you for the help!!!

Spoke too soon, new problem: it’s inconsistent, my line change did nothing

I don’t know quite why but my hunch is that the ball is spawning in after the character and the function is run late; it always breaks after respawning once.

I’ve tried a couple of different ways to implement this, but nothing seems to be getting every descendant part of the player model. I don’t know what’s not colliding correctly, since as I said before, results are inconsistent.

What does your new code look like?

local function onDescendantAdded(descendant)
			if descendant:IsA("BasePart") and descendant ~= Ball then
				descendant.CollisionGroup = "PlayerBodyPart"
			end
		end

		for _, descendant in Character:GetDescendants() do
			task.spawn(onDescendantAdded, descendant)
		end

		Character.DescendantAdded:Connect(onDescendantAdded)
	end

I’ve swapped out everything and even checked for changes to the parent at some point. Nada.

I meant your entire code. I’m running near similar code from what I posted before and I have no issues with it, even when respawning. Client script never changed from your original.

local PhysicsService = game:GetService("PhysicsService")

PhysicsService:RegisterCollisionGroup("PlayerBodyPart")
PhysicsService:RegisterCollisionGroup("PlayerBall")
PhysicsService:CollisionGroupSetCollidable("PlayerBodyPart", "PlayerBall", false)

game:GetService('Players').PlayerAdded:Connect(function(Player)
	local function onCharacterAdded(Character)
		local Humanoid = Character:WaitForChild('Humanoid');
		local HumanoidRootPart = Character:WaitForChild("HumanoidRootPart");
		local RootAttachment = HumanoidRootPart:WaitForChild("RootAttachment")
		
		-- setup ball and collision group
		local Ball = Instance.new("Part");
		Ball.Name = "Ball"
		Ball.Anchored = false;
		Ball.Shape = Enum.PartType.Ball;
		Ball.Size = Vector3.new(7, 7, 7.375);
		Ball.Material = Enum.Material.Plastic;
		Ball.CFrame = Character.HumanoidRootPart.CFrame;
		Ball.Transparency = 0.85;
		Ball.Color = Color3.new(1, 0.32549, 0.337255);
		Ball.CustomPhysicalProperties = PhysicalProperties.new(1, 2, 0.2, 1, 1);
		Ball.CollisionGroup = "PlayerBall";
		
		-- setup ball attachment, parent, and network ownership
		local BallAttachment = Instance.new("Attachment")
		BallAttachment.Name = "BallAttachment"
		BallAttachment.Parent = Ball
		Ball.Parent = Character;
		Ball:SetNetworkOwner(Player);
		
		-- setup root AlignPosition
		local RootAlignPosition = Instance.new("AlignPosition")
		RootAlignPosition.Name = "RootAlignPosition"
		RootAlignPosition.Attachment0 = RootAttachment
		RootAlignPosition.Attachment1 = BallAttachment
		RootAlignPosition.MaxForce = math.huge
		RootAlignPosition.MaxVelocity = math.huge
		RootAlignPosition.Responsiveness = math.huge
		RootAlignPosition.Parent = HumanoidRootPart
		
		-- set limbs/accessories to player collision group
		local function onDescendantAdded(descendant)
			if descendant:IsA("BasePart") == true and descendant:FindFirstAncestorOfClass("Tool") == nil and descendant ~= Ball then
				descendant.CollisionGroup = "PlayerBodyPart"
			end
		end

		for _, descendant in Character:GetDescendants() do
			task.spawn(onDescendantAdded, descendant)
		end

		Character.DescendantAdded:Connect(onDescendantAdded)
	end

	Player.CharacterAdded:Connect(onCharacterAdded)
	
	if Player.Character ~= nil then
		task.spawn(onCharacterAdded, Player.Character)
	end
end)

Pretty much only the child to descendant stuff was changed. aside from that, I can’t really say what will make a difference. I tried the script you posted here and was met with the same inconsistency.

Plus, when I respawn after it does work, the character no longer spawns in the ball.

In your explorer while you’re in game, can you double check these following things for me:

  1. The ball’s CollisionGroup is set to PlayerBall.
  2. All the parts’ CollisionGroup (excluding the ball), including accessories, are set to PlayerBodyPart.
  3. The ball has a BallAttachment in the center (you can view it by clicking the Visible property or clicking the move tool).
  4. The HumanoidRootPart has a RootAttachment and a RootAlignPosition.
  5. The RootAlignPosition’s Attachment0 is set to the RootAttachment and the Attachment1 is set to BallAttachment (this is very important it is in this order).
  6. The RootAlignPosition is Enabled with the MaxForce, MaxVelocity, and Responsiveness set to inf.

Let me know the results from the list above and send me your current server/client script.

1 Like

ball.rbxl (54.7 KB)
Here is my place file that is working completely fine.

Ok, your place file works perfectly on my end, so something’s up with mine.

All the body parts above the legs don’t have their collisions changed, and the rootpart is missing its alignposition. Don’t know why it’s not getting assigned. Does this place being r6 have anything to do with it?

It shouldn’t, since both R6 and R15 rigs have a Humanoid, HumanoidRootPart, and a RootAttachment inside the HumanoidRootPart (double check that for me since I don’t want to publish just to switch rig types), and all limbs are BaseParts. Double check if there are other scripts somehow interfering as well. Also, you still have not sent your version of the script, so I cannot help you at this point in time.

Right, sorry. I swapped out the old one with your implement but there are a couple here, one of which may be interfering. There’s a custom respawn behavior I put in that when disabled makes the place unplayable, but here it is.

local Players = game:GetService("Players")

Players.PlayerAdded:Connect(function(Player)

	Player.CharacterAdded:Connect(function(Character)

		Character.Humanoid.Died:Connect(function()
			wait(3)
			Player:LoadCharacter()
		end)
	end)

	Player:LoadCharacter()

end)

I suspect this may be causing problems, but I can’t disable it wholesale. I’m currently trying other scripts in your place, along with r6, and it all works.

Check the output, are there any errors? In your current place, is your workspace.SignalBehavior set to Default or Immediate? If so, set it to Deferred and try again.

1 Like

Setting it to deferred did the trick!! Output was giving absolutely nothing which is why I was having such a hard time pinpointing the problem. That should do it for now.

I don’t understand why the default SignalBehavior would cause the problem, was the workspace loading stuff in prematurely?

The Default behavior is Immediate, which is what old games before the change used. After the change, confusing as it is, the default property is actually Deferred, not Default. If you would like to read more on deferred behavior, read this post and any information linked: Deferred Engine Events

This affects when your connections gets called. In this case, Immediate mode will trigger your CharacterAdded code without your character even parented in the workspace yet, and internal code to function your character is not done. With Deferred mode, it will trigger at the next cycle and your code will basically run when your character is already in the workspace and the internal code is (almost always) done, avoiding race conditions.

1 Like

Makes sense, I used the classic obby template for the world but didn’t realize how that would screw with things.

Thank you so much for your help! This implementation has been driving me up the wall, I’m so glad it’s working!

1 Like

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