Movement Issues After Morphing Into Custom Character Models

Hey, I have problem with movement system of custom characters in my game.

prop = custom character that the player changed to (always a model)

Problems
Problem 1) ground clipping - some props get stuck in the ground
Problem 2) some of the props are rotated the wrong way - for example pumpkin
Problem 3) as you can see in the video the lantern is a assymetrical object and it has “weird rotations”
Problem 4) if the HumanoidRootPart doesnt touch ground the whole prop just begins sliding wherever it want
Problem 5) some of the props play some lying down animation when i change to them (even when they dont have Animate script inside them) [not seen in the video]
Problem 6) some props bounce after jumping or slow down when autojumping

:warning: Not all props have these problems, just some, and not all props have the same problems :warning:

How the change of player to the custom character works:

  1. you click an item → the prop gets found in the ReplicatedStorage

This is how a model can look like - one of the more complex ones

  1. the prop gets cloned and it sets the important data to the prop (Value, Owner, Prop tag etc.)
  2. HumanoidRootPart gets set as primaryPart in it gets moved to be a child of the prop (prolly not needed)
HRP.Parent = newProp
newProp.PrimaryPart = HRP
  1. the scripts get cloned
for _, child in pairs(oldChar:GetChildren()) do
	if child:IsA("Script") or child:IsA("LocalScript") then
		--if child.Name == "Animate" or child.Name == "Health" then continue end
		child:Clone().Parent = newProp
	end
end
  1. the props gets tped to the ground unless in air (to prevent being stuck in the ground)
local newCf = propUtils.groundFinder(newProp)
	if newCf then
		newProp:PivotTo(newCf)
	end

propUtils.groundFinder script:

function propUtils.groundFinder(prop : Instance) : CFrame
	local primaryPart = prop.PrimaryPart
	if not primaryPart then
		primaryPart = prop:FindFirstChild("HumanoidRootPart")
		if not primaryPart then
			return nil -- no primary part to work with
		end
	end

	local humanoid = prop:FindFirstChildOfClass("Humanoid")
	if humanoid then
		if humanoid.FloorMaterial == Enum.Material.Air then
			print("In the air!")
			return nil -- prop is in the air, skip repositioning
		end
	end

	local rayOrigin = primaryPart.Position
	local rayDirection = Vector3.new(0, -50, 0)

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {prop}
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude

	local raycastResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)

	if raycastResult then
		local cf, size = prop:GetBoundingBox()
		local bottomY = cf.Position.Y - (size.Y / 2)
		local groundY = raycastResult.Position.Y

		local yOffset = groundY - bottomY
		local newCf = primaryPart.CFrame * CFrame.new(0, yOffset, 0)
		return newCf
	end

	return nil
end

The print statement - In the Air! always gets fired and the prop always spawns a little bit over the ground for some reason

  1. newProp.Parent = workspace the props get parented to the workspace
  2. addGroundColliderToModel(newProp)
    - addGroundColliderToModel adds “feet” (non colidable part that touches the ground) to the prop, but it doesnt work with complex models

addGroundColliderToModel script:

local function addGroundColliderToModel(model : Model)
	local cf, size = model:GetBoundingBox()

	local bottomY = cf.Position.Y - (size.Y / 2)

	local groundCollider = Instance.new("Part")
	groundCollider.Name = "GroundCollider"
	groundCollider.Anchored = true
	groundCollider.CanCollide = false
	groundCollider.Transparency = 1
	groundCollider.Size = Vector3.new(size.X, 10, size.Z)
	groundCollider.CFrame = CFrame.new(cf.Position.X, bottomY - 0.15, cf.Position.Z)
	groundCollider.Parent = model
end
  1. weldAndUnanchor(newProp) it gets all welded together to the HumanoidRootPart and unanchored
  2. setNetworkOwnership(player, newProp) player gets set NetworkOwnership over the prop
  3. sizeChanger.scaleProp(player) the prop gets scaled
  4. cameraRotation.ApplyCameraCFrameEvent:FireClient(player, direction) the camera gets rotated in the direction of the player
  5. bounceHandling(prop, hum)just setting some customPhysicalProperties for the prop. you can see in tried solutions

Tried solutions
Bouncing:

  • the issue is just with some props for some reason
for _,v in prop:GetDescendants() do
	if v:IsA("BasePart") then
		v.CustomPhysicalProperties = PhysicalProperties.new(
			100,   -- Density
			0,     -- Friction
			0,     -- Elasticity
			0,     -- FrictionWeight
			100    -- ElasticityWeight
		)
	end
end
  • doesnt fully fix the problem - its just a little better
local downwardForce = -15 -- the lower the number, the more the player will be pushed down

while task.wait() do
	repeat task.wait() until rootPart.Position.Y > oldHeight
	repeat 
		local moveDirection = humanoid.MoveDirection.Unit

		local newForce = Vector3.new(moveDirection.X * moveMultiplier,
			rootPart.AssemblyLinearVelocity.Y,
			moveDirection.Z * moveMultiplier)

		rootPart.AssemblyLinearVelocity = newForce

		task.wait() 
	until rootPart.Position.Y <= oldHeight

	local oldHeight = rootPart.Position.Y
	
	rootPart.AssemblyLinearVelocity = Vector3.new(0, downwardForce, 0)
end
  • fixes the issue with models slowing down, but the bouncing after jumping is a little worse :frowning:

props look different on the server and on the client. i had to handle the scaling on the client, cuz its too laggy on the server