Ragdoll system: joint spesific ragdolling and more

Some of the problems are ones that I don’t know how to fix. However, since you asked this, I decided to do something about two problems and give more information about the unsolved problems.

Partially solved problems

In a live game, ragdolling is laggy
[details = Causes of the problem and different things I tried:]
Apparently, for the network owner client, the problem was caused by detaching HumanoidRootPart from the rest of the character. I noticed that this caused the engine to give the network ownership of the rest of the character to the server while the HumanoidRootPart’s network owner was still the client. The reason for the HumanoidRootPart being detached was that my “RagdollRootConstraint” object used an AlignPosition with ReactionForceEnabled false in order to have the HumanoidRootPart follow the torso without affecting the physics of the ragdoll. It also uses an AlignOrientation to have the HumanoidRootPart face the direction that I considered the front direction of the ragdoll. This determines the look direction of the character after unragdolling.

I thought I might be able to solve this by setting the network owner of the LowerTorso to be the same as the network owner of the HumanoidRootPart. This was set every frame on Hearbeat while the RagdollRootConstraint was enabled. This fixed the problem for the network owner but not for other players. For other players, the HumanoidRootPart moved as it should but the rest of the character teleported with a delay.

I think the cause of the problem for other clients is that when the character is split from one assembly into multiple assemblies, the original assembly’s root part, which is HumanoidRootPart, will continue having its CFrame replicated but the replication for the new assemblies begins later for some reason.

I also tried if using a BallSocketConstraint instead of an AlignPosition would solve the problem, but it didn’t. The reason why I didn’t originally use a BallSocketConstraint is that then the HumanoidRootPart affects the ragdoll physics which I consider undesirable. However, I mitigated this by using an anti-gravity force on the HumanoidRootPart and giving it a low density during ragdolling. Another thing I tried was keeping the Root Motor6D enabled but that just made the LowerTorso move with the HumanoidRootPart while the rest of the character still had the teleport problem.

Finally, I decided to move the rest of the character on each client other than the network owner every frame such that it follows the HumanoidRootPart.
[details = This local updating wasn’t as easy as I thought.]
Apparently updating the CFrames with lua code caused replicated CFrame updates that arrived to the client soon after that to be discarded which meant that the offsets of the bodyparts from each other never updated. This meant that the visible bodyparts did move with the HumanoidRootPart but they didn’t rotate relative to each other so the character remained in a fixed pose on each client other than the network owner until unragdolling. I solved this by having the server see when the first CFrame update from the network owner comes and then tell the other clients to stop the lua code updates so that future CFrame updates would work.
[/details]
The end result for player characters was that, when the player kept the characters network ownership during ragdolling, ragdolling was smooth for the network owner client and I managed to mitigate the teleportation problem for other clients. However, the character still stayed in a fixed pose on the other clients until they began getting CFrame updates from the network owner. If the server was set as the Network owner of the player character at the moment of ragdolling, then every client had a delay but it wasn’t as big as the delay when a player was the network owner. So, for player characters, there were two options:

  1. no delay for network owner and somewhat big delay for others
  2. a delay for everyone, but it is smaller than the delay of others in option 1.

I added a setting setServerAsNetworkOwnerOnEnable for RagdollRootConstraint so that users could easily choose between these options. However, I noticed that for characters that are constantly owned by the server, there was still a teleport issue.

My last attempt to fix the teleport issue is by temporarily using a client-only clone of the character. This clone is recreated when the RigAttachment properties of the RagdollRootConstraint are changed, when bodyparts are added to or removed from the RagdollStructure, or when the user calls the :recreateChoppinessFixClone() method of a RagdollRootConstraint object. The clone is invisible when it’s not needed.

When RagdollRootConstraint is enabled on a client other than the network owner, the clone is made visible and the original character is made invisible. The clone’s HumanoidRootPart’s CFrame is updated every frame on RunService.PreRender to be equal to the CFrame of the original character’s HumanoidRootPart. When the client starts getting CFrame updates for the other parts, it begins to interpolate the rotation offsets between the clone’s parts towards the offset of the corresponding parts in the original character. There’s a RagdollRootConstraint setting for the duration of this interpolation. After this interpolation ends, the clone is made invisible and the original character is made visible.
[/details]

Here are some problems with the current clone solution (some also apply to the earlier solutions):

  • The character stays in a fixed pose on the clients other than the network owner until they begin getting CFrame updates from the server.
  • The fix only works for clients to which the RagdollStructure object is replicated.
  • If the network owner is changed at the moment of ragdolling, there’s a short back and forth movement for the clients. With the earlier solution, I think I only saw such a movement happen for the original network owner, but I’m not sure.
  • Cloning is obviously more expensive for a character with more instances (but I did add a RagdollRootConstraint setting for not using this choppiness fix / teleportation fix).

Neither the earlier solution involving a remote event nor the new one involving a local clone feels elegant but I’m out of ideas. I don’t know of any solution that would work well in all cases. Of course, there could be an easy solution that I haven’t thought of or found by googling.

Layered clothing that becomes irrelevant when breaking a joint isn’t removed which sometimes causes weird looking results.
I have now added code for removing layered clothing items for which none of the remaining bodyparts are considered to be relevant. The removing doesn’t mean destroying but instead just moving it to the detached RagdollStructure. When it shouldn’t be removed, it’s kept in the original structure and a clone is created for the detached structure. Previously I gave the detached structure a clone instead of the original item regardless of whether the item is relevant to the original structure so the clothing item in original structure remained there even if it became irrelevant.

I also added code for moving layered clothing from a structure that is being merged to the structure it is being merged to if

  • the structure being merged contains all bodyparts that are considered relevant and
  • the structure it is being merged to doesn’t already contain an item of that type.

However, the code that determines which items a RagdollStructure should or shouldn’t have doesn’t give correct results in all cases. The problem is that I’m pretty sure there’s no way to find out which bodyparts are relevant to a spesific layered clothing item. This means that I need to make some assumptions based on the AccessoryType (shoe/jacket/shirt etc.). These assumptions are not necessarily correct. There is variation between items of the same category. For example, some pants only cover legs while some also cover torso and arms. Some shoes only cover the feet while boots cover more. There are also shoes that were put in the pants category and a fluffy pen held in hand which is in the jackets category.

Before the new functionality mentioned in this post, I already used this kind of assumptions for determining which layered clothing items should be cloned to a detached structure. However, now I also changed my definitions of which bodyparts each type of layered clothing is considered to be relevant to. For example, jackets were previously considered to be relevant to head but aren’t anymore. I believe these new bodypart lists will work better in most situations although there are some situations in which the old ones would work better (such as aura jackets). The problem with considering jacket relevant to head was that, in the case of jackets that don’t have anything around the head, if the head was detached, the jacket was just a weird rectangle in the bottom of the detached head.

Unsolved problems

Chat bubble vertical positioning breaks after breaking and merging joints on an R6 character.
I don’t know why this happens or how to fix it.

Paralyzed bodyparts cause undesired behavior when jumping.
In the case of character rotating slightly around its z-axis when a limb on one side is paralyzed, I believe the reason is that the paralyzed parts pull the rest of the character down when jumping as they are in separate assemblies (the mass of the assemblies of these parts resists the velocity change caused by the jump). I’m not sure about this, though. I also don’t know how to fix this. Sometimes paralyzed bodyparts also cause the character to quickly move a long distance in some direction. I don’t have a fix for this either. Replacing BallSocketConstraint with AlignPosition would prevent the paralyzed parts from pulling the other parts but this approach would have problems. There could be situations where the detached bodyparts are unable to get to the correct position, and not using a BallSocketConstraint would also remove rotation limits.

Ragdolls sometimes shake.
I believe this might have something to do with rotations exceeding BallSocketConstraint limits for whatever reason, but I’m not sure. Changing the rotation limits might help.

When resetting (Reset character button or the keybind), the character either doesn’t die on the server, dies on the server many seconds late or dies when something collides with the character.
Setting both BreakJointsOnDeath and RequiresNeck to true seems to fix this (and that’s the default value of these properties). However, RequiresNeck being true prevents making living ragdolls so I’m not going to do this.

I assume resetting could be fixed by firing a RemoteEvent when the player resets and having the server do the server side killing when it’s fired. The API mentioned in the announcement below could be used for this. However, I consider that out of scope for this ragdoll system, and having the ragdoll system use the API might interfere with other code using the same API. I also haven’t tested if this works.

Layered clothing doesn’t always look very good when breaking joints.
I don’t think I can make all layered clothing look good when breaking joints. Simple shirts and pants should look reasonably good while something more unusual may not.

4 Likes