Ragdoll system: joint spesific ragdolling and more

Merry christmas, everyone :slight_smile:! Here’s my gift to you.

I know there are many ragdoll modules already published on the devforum. However, they seem to have limitations. Many are designed specifically for R15 or R6. Some only support ragdolling on death. Also, I don’t remember seeing any of them mention being able to put only specific joints into ragdoll state. Additionally, I think that the possibility to detach limbs and have the detached limb in ragdoll state is a nice thing to have and I don’t think I’ve seen any ragdoll system post mention this kind of feature.

I decided that I want to make a ragdoll system that gives a lot of freedom while still being easy to use. If all you want is an ability to unragdoll and ragdoll an entire R15 or R6 character, with the built-in settings, doing so is as simple as calling a method in a module script. However, there’s more that this system can do.

Here’s a link to the model.
RagdollSystem - Creator Marketplace

Video

The chat commands in the video are not included in the ragdoll system.

General information

Character structure requirements
Firstly, I wanted this to work with many kinds of characters. Here are the requirements:

  • The bodypart connection structure must not contain cyclic connections. This requirements allows it to be represented as a tree which means that every bodypart will have clearly defined descendant bodyparts and ancestor bodyparts.
  • Each bodypart can be either one BasePart, a Model or a Folder containing BaseParts.
  • Different bodyparts must be connected to each other using Motor6Ds. For each Motor6D connecting two bodyparts, there must also be a rig attachment (name must be equal to Motor6D.Name .. "RigAttachment") in each of the two connected bodyparts. The CFrame of the RigAttachment must be (approximately) equal to either C0 or C1 of the Motor6D depending on whether Part0 or Part1 of the Motor6D is in the same Bodypart as the rig attachment.
  • It’s recommended and may even be a requirement in the future that the Baseparts in a bodypart form a single assembly. I recommend using WeldConstraints for attaching BaseParts in the same Bodypart to each other. If there are Motor6Ds connecting parts in the same bodypart, you must tell the ragdoll system that these Motor6Ds should be ignored (more about this in the documentation of RagdollBodypartNode and system shared settings).

Here are some things that are not required:

  • Humanoid
  • HumanoidRootPart

Objects and data structures
The main class in this RagdollSystem is RagdollStructure. It contains data for a single mechanism of connected bodyparts (a character for example). Such a mechanism must have a top instance that is a Model. For each bodypart, there’s a RagdollBodypartNode object. For each RigAttachment, there’s a RagdollHalfJointNode object.

There are two hierarchies for both RagdollBodypartNodes and RagdollHalfJointNodes. These are paralyze hierarchy and break hierarchy. Each BodypartNode and HalfJointNode contains two hierarchy spesific “component” nodes, one for each hierarchy. In this system, ragdolling a whole RagdollStructure is called stunning and ragdolling spesific joints is called paralyzing. Paralyze hierarchy is used for deciding which other joints to paralyze when the user paralyzes or breaks a joint. Breakhierarchy is used for deciding which Bodyparts are moved to a new RagdollStructure when a joint is broken. There can only be one root node for bodypart hierarchies. However, because the root node of a bodypart hierarchy may be attached to multiple other bodyparts, the half joint hierarchy can have multiple root nodes and is thus represented with a forest data structure.

For each joint, a RagdollConstraint lua object is created. It is created for the HalfJointNode that is in the bodypart that contains the Motor6D. There are a few built-in RagdollConstraint classes that all inherit the base class RagdollConstraint. You can also define your own classes that inherit RagdollConstraint. For every intact joint, there’s also a RagdollIntactJoint object.

Settings
The ragdoll system uses four types of settings: character settings, system server settings, system shared settings and system client settings. It has built in R15 and R6 character settings, and all three system settings. If the characters in your game are R15 or R6 and you are fine with all the behavior and joint rotation limits chosen in the built-in settings, you don’t have to do any settings changes. However, if you want to change something, you can apply your own settings for whatever settings type needs non-default settings. Applying your own settings is meant to be done by calling functions made for this purpose in settings related module scripts instead of editing the built-in settings modules.

In the settings, you can toggle some features and define functions for many things like finding bodypart top instances in a character, instances (like Motor6Ds) that should be ignored in a bodypart or collision BaseParts of a Bodypart. Many of these functions are optional to define. There’s default code for finding bodypart top instances and collision parts. The possibility to define your own functions for these is meant for situations where the default code doesn’t work. In character settings, you can define which ragdoll constraints are used and the rotation limits.

RagdollStructure creation
With the default settings, a RagdollStructure is created for every player character when the character is considered to be fully loaded. The code for detecting when an R15 or R6 character is fully loaded is in a different Package that the RagdollSystem uses (and that package is also made by me). The function that user code should use for creating a new RagdollStructure is the createStructure function in the RagdollStructure module.

Ragdolling and breaking joints
You can ragdoll a whole character or only some joints. As mentioned before in the post, ragdolling a whole RagdollStructure is called stunning and ragdolling spesific joints is called paralyzing. By default, when paralyzing or breaking a joint, its paralyze hierarchy descendants are paralyzed as well. You can opt out of this, though. If you paralyze some joints, stun a character and unstun it, the paralyzed joints stay paralyzed. This allows using stun for temporary ragdolling that could be caused by a hit in the head, for example, and paralyze for permanent ragdolling of spesific parts as a result of weapon damage, for example.

This system never destroys Motor6Ds. Instead, it sets the Enabled property to false. By default, if the character has a humanoid, the RagdollSystem changes the humanoid state in some situations such as when the character is stunned. There is, however, a character setting for disabling this automatic humanoid state changing.

I tried to make Layered clothing look reasonable good after breaking a joint. However, the look is far from perfect and how good it looks depends on the clothing. Also, I did this using a hacky, inefficient way which involves creating as many extra parts as there are bodyparts in an R15 character.

User-defined data
Each RagdollStructure, RagdollBodypartNode, RagdollHalfJointNode and RagdollIntactJoint has a UserDefinedData property. You are free to choose what data, if any, you store in this property. It could contain some kind of a character type, for example.

Server and client
A RagdollStructure can be created either on the server or on a client. Client-only RagdollStructures should be used in cases where you create a character on the client (in which case it only exists on the client).

It is possible to replicate RagdollStructures created on the server to clients. If you opt out of some automatic bahavior, you also get to decide which clients the RagdollStructures are replicated to. It is also possible to replicate UserDefined data but that isn’t entirely automatically handled. The user of the system needs to do some things related to this. The reason why I didn’t even try to make this entirely automatic is that someone may have a table with multiple things as user-defined data and the data may include lua objects with metatables. Thus, a generalized approach wouldn’t work in all cases, only in simple ones.

With the default settings, in addition to RagdollStructures being created for every player, these RagdollStructures are replicated to every player.

The way replication works is that the client creates a copy of the RagdollStructure object and updates this copy when the server informs the client about changes. Instances are used by the RagdollSystem for getting the correct copy on the other side of the network. Instance → lua object dictionaries are used for this. I could alternatively have an id number for each lua object that can be replicated but I found using instances to be sufficient in this case.

By default, the RagdollSystem uses my RemoteEventSystem for creating, firing and listening to RemoteEvents. If you are fine with this, all you need to do is call a function that tells the system to initialize the communication using this system. However, because different people may prefer different ways of handling remote event traffic and may also want to have controll over or monitor the remote event traffic of this RagdollSystem, the Ragdoll system also allows using some other system. You just need to define some middle-man code for communicating between the RagdollSystem and the chosen Remote event code. Also, my RemoteEvent system is kind of a mess with some features that I’ve later considered bad ideas which is also a reason why I don’t want the RagdollSystem to be tightly tied to it.

The RagdollSystem has no client → server communication other than a little in initializing the communication when using the default RemoteEventSystem. All other communication is server → client. For a RagdollStructure that exists on the server, any functions that modify it (like stunning and paralyzing) should always be called on the server. Also, I’m not planning to make a feature that would bind a spesific key to telling the server to ragdoll the player’s character or any other similar feature where the client requests a change. I want to leave user input handling and client request validation to the user of the ragdoll system. These can be very game-spesific and not all games with ragdolls allow the player to decide when they want to ragdoll or unragdoll.

Other
There are methods for anchoring or unanchoring a RagdollStructure, and a method for both anchoring and disabling collisions with other parts which practically makes the ragdoll purely visual. The way anchoring the structure works is that one BasePart is anchored and the others are rigidly attached to it using WeldConstraints.

This RagdollSystem uses two other packages that I’ve made. These are my GeneralUtility package and my RemoteEventSystem package. Using RemoteEventSystem package is optional, though. You can alternatively have the RagdollSystem use some other code for handling remote events. Copies of both of these are included in the RagdollSystem package but if you want to use one or both for other things in your game, I recommend putting a copy of it as a sibling of the RagdollSystem package. That way, both the RagdollSystem and your own code use the same copy of the package instead of using separate copies. While you could also require the nested packages, I don’t recommend doing so.
Edit: I removed the nested packages. Now having GeneralUtility package and RemoteEventSystem package as siblings of RagdollSystem package is required. The purpose of this change is that only one copy of each package is used in a game.

Modules

Modules, functions/methods, properties or fields that are not documented here are not meant to be used by the user of this RagdollSystem unless I’ve just forgotten to include them. Static methods are called with dot syntax.

Field and property explanation

With “field” I mean a regular key value pair with a string key. With “property” I mean something that is read and set with the same kind of syntax as a regular key value pair but actually has a getter and a setter that are called automatically. I use camelCase for fields and PascalCase for properties (and some of my CustomEnumItems also use PascalCase while most use camelCase which is inconsistent).

Modules that contain references to other modules:

RagdollSystem

This is the main module of the RagdollSystem. This is the only module that the user of the system is supposed to directly require. You can get references to the return values (classes, CustomEnums or tables with utility functions) of other modules that are meant to be accessed by the user by indexing the table returned by this module, or the table returned by one of the modules that this module’s table gives a reference to. The references to other modules’ return values:

RagdollStructure
RagdollStructureCollections
RagdollBodypartNodeCollection
RagdollBuiltInConstraintClasses
RagdollSystemServerSettingsHandler -- server only
RagdollSystemClientSettingsHandler -- client only
RagdollSystemSharedSettingsHandler
RagdollCharacterSettingsHandler
RagdollEaseOfUse
RagdollDefaultRemoteEventSystemServerUtility -- server only
RagdollDefaultRemoteEventSystemClientUtility -- client only
RagdollEnums

RagdollEnums

This module contains references to different CustomEnums of the RagdollSystem.

RagdollBodypartCanCollideState
RagdollBodypartHumanoidRootPartConnectionState

JointParalyzeState
JointBreakState

RagdollRigType
RagdollHierarchyType

RagdollBuiltInConstraintClasses

This module contains references to built in joint constraint classes.

Base classes

ragdollBuiltInConstraintClasses.baseClasses

This table contains constraint classes that aren’t functional by themselves. They contain only some of the functionality needed. These can (and RagdollConstraint should) be inherited by other constraint classes. Base classes:

ragdollBuiltInConstraintClasses.baseClasses.RagdollConstaint
ragdollBuiltInConstraintClasses.baseClasses.RagdollOrientedConstraint -- this inherits RagdollConstraint

Derived classes

ragdollBuiltInConstraintClasses.derivedClasses

This table contains constraint classes that are fully functional. They inherit RagdollConstraint either directly or by inheriting RagdollOrientedConstraint which inherits RagdollConstraint. Derived classes:

ragdollBuiltInConstraintClasses.derivedClasses.RagdollNeckConstraint
ragdollBuiltInConstraintClasses.derivedClasses.RagdollNeckConstraintV2
ragdollBuiltInConstraintClasses.derivedClasses.RagdollBallSocketConstraint
ragdollBuiltInConstraintClasses.derivedClasses.RagdollRootConstraint

Classes:

RagdollStructure

This contains the RagdollStructure class which represents a mechanism consisting of one or more bodyparts that are attached to each other.

Constructor

createStructure(characterOrOtherTopInstance: Model | Folder, ragdollCharacterSettings, dataNeededToGetRagdollCharacterSettingsTableOnClient)
  • characterOrOtherTopInstance is an instance that contains all instances that belong to the mechanism for which the RagdollStructure is created (for example Player.Character).
  • ragdollCharacterSettings must be either a valid character settings table, or in case of R6 or R15, it can be nil in which case default character settings are used.
  • dataNeededToGetRagdollCharacterSettingsTableOnClient can be any data that the client can use to get the client copy of the character settings table. You must define a client function that gives the settings table given this data. The table itself can’t be passed directly to the client via a RemoteEvent because it contains functions. If you aren’t replicating the structure to clients, this doesn’t need to be given. Also, if you don’t give ragdollCharacterSettings, don’t give this either.

Object methods
Life cycle

informThatUserInitializationIsDone()

There are events (a static one and an object spesific one) that are fired when this is called. Your other code can listen to either of these events. This can be useful if you need to do some additional initialization to the structure after calling RagdollStructure:createStructure and your other code shouldn’t mess with this structure before the initialization is done. This additional initialization can be setting UserDefinedData, for example.

Replication

replicateToClient()

This sends the client data containing information about the bodyparts in the RagdollStructure and the state of the structure. The client uses this data to create its own copy of the RagdollStructure object.

replicateToAllClients()

This replicated the structure to clients currently on the server and clients that join later.

informClientsAboutUserDefinedDataChange(dataNeededToUpdateUserDefinedDataOnClient)

When you change the userDefinedData on the server and want to replicate the change, call this. A few different classes have this same method but they all actually have a userDefinedData component object whose method they call so the code of the method isn’t duplicated.

  • dataNeededToUpdateUserDefinedDataOnClient must be something that can be sent via a RemoteEvent

Checks

hasExactlyOneJointWithThisName(jointName: string)
containsBodypartHighestInHierarchyOfWholeCharacter(hierarchyType)
  • hierarchyType must be a RagdollHierarchyType CustomEnumItem.
areChangesInThisStructureControlledByThisEnvironment

Tells whether this network environment (server or a spesific client) is the one that has authority over changes in the structure. For a structure that exists on the server, this is true on the server and false on the clients. For a client-only structure, this is true on the client.

Getters

getBrokenJointHalfJoints()
getHalfJointFromRigAttachment(rigAttachment: Attachment)
getHalfJointFromMotor6D(motor6D: Motor6D)

returns the HalfJointNode of the RigAttachment that is a descendant of the bodypart instances whose descendant the Motor6D is.

getHalfJointPairBetweenTwoBodyparts(bodypartNode0, bodypartNode1, hierarchyType)
  • hierarchyType must be a RagdollHierarchyType CustomEnumItem
getAllHalfJointsFromJointName(jointName: string)
getUpperHalfJointFromName(jointName: string, hierarchyType)
getHalfJointPairFromJointName(jointName: string, hierarchyType)
getAllHalfJointPairsFromJointName(jointName: string, hierarchyType)
getBodypartNodeFromName(bodypartName: string)
getAllBodypartNodesFromName(bodypartName: string)
getBodypartNodeFromInstanceInBodypart(instanceInBodypart: Instance)
getAllHalfJointPairs(hierarchyType)
getIntactJointFromMotor6D(motor6D: Motor6D)
getRootBodypartNodeFromHierarchyType(hierarchyType)
getHalfJointHierarchyForestFromHierarchyType(hierarchyType)

Stunning and unstunning

stunIfNotStunned()
unstunIfStunned()
toggleStun()

Paralyzing and unparalyzing

paralyzeHalfJoint(halfJointNode, paralyzeParams)
  • paralyzeParams must contain a boolean implicitlyParalyzeParalyzeHierarchyDescendants
unparalyzeHalfJoint(halfJointNode, unparalyzeParams)
  • unparalyzeParams must contain booleans implicitlyUnparalyzeParalyzeHierarchyDescendants, removeImplicitParalyzeCausedByJointBreakRegardlessOfAncestors and ignoreWhetherParentOfExplicitlyUnparalyzedNodeIsParalyzedFromExplicitParalyze
paralyzeJointBasedOnHalfJointPair(halfJointPair, paralyzeParams)
paralyzeJointBasedOnJointName(jointName: string, paralyzeParams)
unparalyzeJointBasedOnHalfJointPair(halfJointPair, unparalyzeParams)
unparalyzeJointBasedOnJointName(jointName: string, unparalyzeParams)

Breaking and merging

breakJoint(intactJoint, paralyzeParams)
breakJointCorrespondingToHalfJointPair(halfJointPair, paralyzeParams)
breakJointCorrespondingToMotor6D(motor6D: Motor6D, paralyzeParams)
breakJointBetweenTwoBodyparts(bodypartNode0, bodypartNode1, paralyzeParams)
breakJointBasedOnName(nameOfJointToBreak: string, paralyzeParams)
disableCollisionsBetweenBodypartsThatShouldNotCollideInMergedStructure(halfJointInSelf, structureToMergeToSelf)
mergeStructureThatIsLowerInHierarchyToSelf(halfJointInSelf, structureToMergeToSelf, unparalyzeParams)

Anchoring, unanchoring and completely disabling physics

getAnchoringRootBasePart()

This returns the BasePart that is anchored when the structure has been anchored using the anchor() method. The other BasePart are attached to the anchored one using WeldConstraints.

anchor()
unanchor
disablePhysicsInteraction()

This anchors the structure and makes the bodyparts non-collidable.

Physics networking

getAssemblyRootParts()
setNetworkOwner(newNetworkOwner)

Sets the network owner for every assembly that has parts as descendants of the InstanceHierarchyTopInstance of the RagdollStructure object.

  • newNetworkOwner can be either a Player Instance or nil (which means server).
setNetworkOwnershipAuto()

Sets the network ownership changing behavior to the automatic engine behavior for every assembly that has parts as descendants of the InstanceHierarchyTopInstance of the RagdollStructure object.

Clean up

destroy()

This is a clean up method.

Object properties

ParalyzeBodypartHierarchyRootNode
ParalyzeHalfJointHierarchyForest
ParalyzeHalfJointHierarchyRootNodes
BreakBodypartHierarchyRootNode
BreakHalfJointHierarchyForest
BreakHalfJointHierarchyRootNodes
HumanoidRootPartNode
Humanoid
InstanceHierarchyTopInstance
IsStunned
HumanoidPhysicsEnabled
IsOutdated -- true if the structure has been destroyed or merged to a structure higher in break hierarchy
IsClientOnly
IsBeingReplicatedToAllClients
PlayersThatThisHasBeenReplicatedTo
IsBeingReplicatedToNewClients
RagdollCharacterSettings
AssemblyNetworkOwner
Name
UpdateBodypartCanCollideStatesAutomatically
MarkedAsOutdated -- an event
StunStateChanged -- an event
UserInitializationDone -- an event
BodypartsAddedOrRemoved -- an event
IsAnchored
IsUserInitializationDone
UserDefinedData

RagdollStructureNode

This is the base class of RagdollBodypartNode and RagdollHalfJointNode.

Object methods

getHierarchySpesificComponentFromHierarchyType(hierarchyType)
  • hierarchyType should be a RagdollHierarchyType CustomEnumItem
informClientsAboutUserDefinedDataChange(dataNeededToUpdateUserDefinedDataOnClient)
  • see documentation for the same method in RagdollStructure

Object properties

ParalyzeHierarchyComponent
BreakHierarchyComponent
UserDefinedData
IsDestroyed

RagdollBodypartNode

This is a class whose objects contain bodypart related data. This class inherits RagdollStructureNode. Check the documentation for that for the documentation of inherited methods and properties.

Static methods

areThereNoCollisionConstraintsBetweenTheseBodyparts(bodypartNode0, bodypartNode1)
createNoCollisionConstraintsBetweenBodyparts(bodypartNode0, bodypartNode1)

Each CollisionBasePart in bodypartNode0 gets a NoCollisionConstraint with each CollisionBasePart in bodypartNode1.

destroyNoCollisionConstraintsBetweenBodyparts(bodypartNode0, bodypartNode1)

Object methods

setCanCollideState(newCanCollideState)
  • newCanCollideState must be a RagdollBodypartCanCollideState CustomEnumItem
updateCanCollideStateToCurrentlyCorrectOne()
getInstancesInBodypart()
getBaseParts()
addInstanceToIgnoreList(instance: Instance)
  • instance should be a descendant of the top instance of the bodypart.
removeInstanceFromIgnoreList(instance: Instance)

Object properties

BodypartInstance
RagdollStructure
BodypartName
CollisionBaseParts
InstancesIgnoredByRagdollSystem
CanCollideState

RagdollHalfJointNode

This is a class whose Objects contain joint related data. These objects are not destroyed when a joint is broken. This class inherits RagdollStructureNode. Check the documentation for that for the documentation of inherited methods and properties.

Object properties

JointName
BodypartNode
IntactJoint
RigAttachment
Motor6D
RagdollConstraint
RagdollStructure
ParalyzeState
BreakState

RagdollStructureNodeHierarchySpesificComponent

This is the base class of RagdollBodypartNodeHierarchySpesificComponent and RagdollHalfJointNodeHierarchySpesificComponent. This inherits GeneralTreeDataStructureNode which is in the GeneralUtility package for which I haven’t written documentation and thus there isn’t documentation for the methods and properties inherited from that class. Some of these include object methods getRootNodeOfTree(), getAllNodesInTree(), getDescendants and getArrayOfSelfAndDescendants(), and object property Parent.

Object properties

MainNode -- a RagdollBodypartNode or a RagdollHalfJointNode
HierarchyType -- A RagdollHierarchyType CustomEnumItem

RagdollBodypartNodeHierarchySpesificComponent

This class contains hierarchy spesific data for RagdollBodypartNodes.

Object properties

HalfJointNodeHighestInHierarchy -- HalfJointNode connecting to a bodypart higher in the hierarchy (parent bodypart)
OtherHalfJoints -- HalfJointNodes connecting to bodyparts lower in the hierarchy (child bodyparts).

RagdollHalfJointNodeHierarchySpesificComponent

This class contains hierarchy spesific data for RagdollHalfJointNodes.

Object properties
HalfJointPair
IsUpperHalfJointComponent – true if this is the higher (in hierarchy) half joint in the HalfJointPair in which this object is.

RagdollIntactJoint

This is a class whose objects contain data related to an intact joint. Intact joint means that the bodyparts between which the joint is are attached. A joint that is in ragdoll state is still an intact joint because the ragdoll constraint attaches the bodyparts to each other.

Object methods

isJointParalyzed()
informClientsAboutUserDefinedDataChange(dataNeededToUpdateUserDefinedDataOnClient)
  • see documentation for the same method in RagdollStructure

Object properties

ParalyzeHierarchyHalfJointPair
BreakHierarchyHalfJointPair
RagdollStructure
HalfJointWithMotor6D
HalfJointWithoutMotor6D
Motor6D
RagdollConstraint
JointName
UserDefinedData
IsDestroyed

RagdollHalfJointPair

This class contains joint related data. Some of it is hierarchy spesific. There are HalfJointPair objects for both hierarchies.

Object methods

isJointParalyzed()
isJointExplicitlyParalyzed()

This returns whether at least one of the HalfJoints has been explicitly paralyzed.

isJointBroken()
isJointControlAble()

This returns true if the joint isn’t broken or paralyzed.

isJointInNormalState()

Object properties

HierarchyType
UpperHalfJointNodeHierarchySpesificComponent
LowerHalfJointNodeHierarchySpesificComponent
UpperHalfJointNode
LowerHalfJointNode
HalfJointWithMotor6D
HalfJointWithoutMotor6D
Motor6D
RagdollConstraint
IntactJoint
RagdollStructure
JointName

RagdollBallSocketConstraint

Constructor

new

This isn’t meant to be called by the user of the RagdollSystem. Instead, it should be included in the joint settings in character settings.

RagdollNeckConstraint

Constructor

new

This isn’t meant to be called by the user of the RagdollSystem. Instead, it should be included in the joint settings in character settings.

RagdollNeckConstraintV2

Constructor

new

This isn’t meant to be called by the user of the RagdollSystem. Instead, it should be included in the joint settings in character settings.

RagdollRootConstraint

Constructor

new

This isn’t meant to be called by the user of the RagdollSystem. Instead, it should be included in the joint settings in character settings.

Object methods

recreateChoppinessFixClone()

This is related to the client-side visual fix for a problem that happens when ragdolling. The problem is that the character freezes for a bit and then teleports to where it should be. I used a client-only clone of the character for fixing this. When called on the client, that client’s clone is recreated. When called on the server, the server tells every client to whom the RagdollStructure is replicated to recreate their clone.

Settings fetching and updating

RagdollCharacterSettingsHandler

This is an utility module used for setting and fetching default character settings for R15 and R6 characters. These default settings are initially the built-in ones but you can also set your own default settings. Default settings are used when you don’t give a ragdollCharacterSettings table to RagdollStructure.createStructure(). You can’t have default settings for characters other than R15 or R6.

Functions

applyDefaultR15Settings(newDefaultR15Settings, dataNeededToGetSettingsTableOnClient)
  • dataNeededToGetSettingsTableOnClient needs to be something that can be sent via a RemoteEvent. You must define a client function for getting settings given the data.
applyDefaultR6Settings(newDefaultR6Settings, dataNeededToGetSettingsTableOnClient)
getDefaultR15Settings
getDefaultR6Settings

RagdollSystemServerSettingsHandler

This is an utility module used for setting and fetching system server settings. These settings are initially the built-in ones but you can also set your own settings.

Functions

applySystemServerSettings(newServerSettings)
getSystemServerSettings()

Fields

newRagdollSystemServerSettingsApplied

RagdollSystemClientSettingsHandler

This is an utility module used for setting and fetching system client settings. These settings are initially the built-in ones but you can also set your own settings.

Functions

applySystemClientSettings(newClientSettings)
getSystemClientSettings()

Fields

newRagdollSystemClientSettingsApplied

RagdollSystemSharedSettingsHandler

This is an utility module used for setting and fetching system shared settings. These settings are initially the built-in ones but you can also set your own settings. You are meant to onlys set shared settings on the server. When they are set, the server informs the client about them changing and sends the client the data that you gave to applySystemClientSettings() so that the client can get the settings table using the function defined in system client settings.

Functions

applySystemSharedSettings(newSharedSettings, dataNeededToGetSettingsTableOnClient)
* dataNeededToGetSettingsTableOnClient must be something that can be sent via a RemoteEvent and that the function you define in system client settings can use to fetch the settings table.
getSystemSharedSettings()

Fields

newRagdollSystemSharedSettingsApplied -- an event

Default RemoteEventSystem middleman modules

RagdollDefaultRemoteEventSystemServerUtility

This is a server middle man between the RagdollSystem and my RemoteEventSystem. As mentioned earlier in the post, you can also use some other remote event code. You just have to define a function (functionToFireRemoteEvent) in the system server settings and call RagdollNewPlayerCommunicationOrderHandler.informThatClientIsReadyForReplication when the client is ready to listen for RagdollSystem remote event firings.

Functions

setUpServerClientCommunicationOnServer()

Fields

fireClient
  • This is a function but I put it in the fields category because it’s not meant to be called directly from this module. It’s meant to be set as a setting in system server settings if you want the RagdollSystem to use my RemoteEventSystem.

RagdollDefaultRemoteEventSystemClientUtility

This is a client middle man between the RagdollSystem and my RemoteEventSystem. As mentioned earlier in the post, you can also use some other remote event code.

Functions

setUpServerClientCommunicationOnClient()

RemoteEvent modules not spesific to the default RemoteEventSystem

RagdollNewPlayerCommunicationOrderHandler

This is a server-only module used for starting communication with clients and specifying the order in which the client is informed about different things.

Functions

hasClientConnectedRadollSystemCodeToRagdollSystemRemoteEvents(player: Player)
getClientsWhoHaveConnectedRagdollSystemCodeToRagdollSystemRemoteEvents()
informThatClientIsReadyForReplication(player: Player)

You should only call this if you are not using my RemoteEventSystem.

Fields

playerReadyForReplication -- an event

This gives the following to connected functions:

  • the Player

RagdollInternalRemoteEventsClientHandler

This is a client-only module.

Functions

handleRagdollSystemClientEvent(remoteEventPurposeValue: number, ...)

This should be called by whatever remote event system is being used. remoteEventPurposeValue should be the number that the RagdollSystem gave to functionToFireRemoteEvent. functionToFireRemoteEvent is defined in system server settings.

Modules for ease of use of the system

RagdollStructureCollections

This is an utility module for getting already created RagdollStructures from instances and for listening to the creation of new structures or the initialization of them being completed.

Functions

getRagdollStructureFromInstance(instance: Instance)
  • instance should be the InstanceHierarchyTopInstance of the RagdollStructure object or a descendant of this top instance.
getPlayerCharacterRagdollStructure(player: Player)
setPlayerCharacterRagdollStructure(player: Player, playerCharacterRagdollStructure)

If the character for which a RagdollStructure is created is Player.Character of a player, then this function is called automatically when the structure is created. But if you have a custom player character that is not Player.Character, you can call this function if you want to be able to later access the RagdollStructure using the Player instance.

getPlayerSpesificRagdollStructureCreatedEvent(player: Player)
getPlayerSpesificRagdollStructureUserInitializationDoneEvent(player: Player)

Fields

ragdollStructureCreated -- an event

This gives the following to connected functions:

  • the RagdollStructure object
  • the InstanceHierarchyTopInstance of the RagdollStructure object
ragdollStructureUserInitializationDone -- an event

This gives the following to connected functions:

  • the RagdollStructureObject

RagdollBodypartNodeCollection

This is an utility module for getting a RagdollBodypartNode from an instance.

Functions

getBodypartNodeFromInstanceInBodypart(instanceInBodypart: Instance)
  • instanceInBodypart should be either the top instance of a bodypart or a descendant of that top instance.

RagdollEaseOfUse

This is an utility module that is meant to make it easy to use this RagdollSystem for simple things.

Functions

getRagdollStructureFromCharacterOrPlayer(characterOrPlayer: Model | Player)
getLocalPlayerCharacterRagdollStructure()
stunCharacter(characterOrPlayer: Model | Player)

If the RagdollStructure doesn’t exist yet, a ragdoll structure with default character settings will be created when the character exists (if characterOrPlayer is a player) and is ready (has bodyparts that a normal R15 or R6 character should have etc.), and it will then be stunned. If the given characterOrPlayer is an NPC, it is expected to be ready when this function is called.

unstunCharacter(characterOrPlayer: Model | Player)

Similar to RagdollEaseOfUse.stunCharacter but unstuns.

toggleRagdoll(characterOrPlayer: Model | Player)

Similar to RagdollEaseOfUse.stunCharacter but toggles stun state (either stuns or unstuns).

Fields

ragdollStructureAddedForPlayerCharacter -- an event

This gives the following to connected functions:

  • the RagdollStructure object
  • the character Model
  • the Player
ragdollStructureAddedForLocalPlayerCharacter -- a client-only event

This gives the following to connected functions:

  • the RagdollStructure object
  • the character Model

CustomEnums of the RagdollSystem

RagdollRigType

CustomEnumItems

R6
R15
Custom

RagdollHierarchyType

CustomEnumItems

paralyzeHierarchy
breakHierarchy

JointParalyzeState

CustomEnumItems

controlable
explicitlyParalyzed
implicitlyParalyzedFromExplicitParalyze
implicitlyParalyzedFromJointBreak
implicitlyParalyzedFromBothExplicitParalyzeAndJointBreak
explicitlyParalyzedAndImplicitlyParalyzedFromJointBreak

JointBreakState

CustomEnumItems

Intact
Broken

RagdollBodypartCanCollideState

CustomEnumItems

AllBasePartsCanCollideTrue
AllBasePartsCanCollideFalse
UpdateBasedOnHumanoidState
NotControlledByRagdollSystem
Undefined

Settings

If you aren’t happy with the built-in settings, you can define your own settings.

Info about settings that can be defined in either system or character settings

Some settings can be defined in either character or system settings (or neither if its an optional setting). If the setting is defined in both system and character settings, the value in character settings is used. The purpose of this is that, if a setting is the same for most character types in the game, you can put that setting value in system settings so you don’t need to set it to multiple character settings. If some characters need a diffrent setting value than most, you can define the setting separately for them in character settings.

Here’s what the settings tables must or can contain.

Character settings

rigType

a RagdollRigType CustomEnumItem

defaultFadeFadeTimeFromRagdollOffsetToAnimationOffset

This can alternatively be defined in system shared settings. See the documentation for system shared settings for the documentation of this.

mirrorLimitsWhenNameContainsRight

This is used when creating RagdollBallSocketConstraints. This name may be somewhat misleading. The limit properties of the BallSocketConstraint instance are not changed depending on this. Instead, the mirroring is done by changing the rotation of the Attachments (the rig attachments’ rotation isn’t altered; instead the BallSocketConstraint uses Attachments that are parented to the rig attachments).

revertRigAttachmentRotationOnRagdollConstraintAttachments

When this is true, the rotation of the Attachments of a RagdollConstraint is not affected by the rotation of the RigAttachment. (their rotation in this case is RigAttachment.CFrame.Rotation:Inverse() * definedRotationCFrame).

chooseRigAttachmentOfBodypartWithMotor6DAsRigAttachment1

This affects how a BallSocketConstraint limits the rotation.

shouldRagdollSystemChangeHumanoidState

If this is false, the RagdollSystem won’t automatically change the state of the Humanoid in the character even if the Humanoid exists.

nameOfParalyzeHierarchyRootBodypart
nameOfJointConnectingToStructureHigherInParalyzeHierarchy

This shouldn’t be defined for a whole character. It should only be defined when creating a structure that is meant to be merged to another structure, like a metal limb that can be used to replace a lost limb.

nameOfBreakHierarchyRootBodypart
nameOfJointConnectingToStructureHigherInBreakHierarchy

Same explanation as for nameOfJointConnectingToStructureHigherInParalyzeHierarchy.

jointSettings

For each joint type name (for example, the joint type name of LeftShoulder and RightShoulder is Shoulder in the default R15 settings), data for creating RagdollConstraints is defined in this table.
This should contain jointTypeName → {ragdollConstraintSettings = a table of settings} key value pairs. The table of settings should have ragdollConstraintConstructor as one of its keys and other needed key value pairs (what is needed depends on the constraint class) as the other key value pairs.

The constructor is given the following:

  • joint name
  • joint type RagdollConstraint settings (constructor and constraint type dependent settings)
  • ragdoll character settings
  • replicated instances of the RagdollConstraint if the lua object being created is a client copy.

The constraint type spesific settings are the following:
RagdollBallSocketConstraint:

  • rotationAxis: a string (“X”, “-X”, “Y”, “-Y”, “Z” or “-Z”) or a vector
  • secondaryAxis: a string (one of the above) or a vector
  • upperAngle: an angle in degrees
  • twistLowerAngle: an angle in degrees
  • twistUpperAngle: an angle in degrees
  • maxFrictionTorque: a number

RagdollRootConstraint:

  • primaryAxis: a string (one of the above) or a vector
  • secondaryAxis: a string (one of the above) or a vector
  • setServerAsNetworkOwnerOnEnable: a boolean
  • doChoppinessFix: a boolean
  • choppinessFixInterpolationDuration: a number

RagdollNeckConstraint:

  • torsoYAxisSecondary: a string (one of the above) or a vector
  • headXAxisSecondary: a string (one of the above) or a vector
  • headZAxisSecondary: a string (one of the above) or a vector
  • torsoYAxis: an angle in degrees
  • headXAxis: an angle in degrees
  • headZAxis: an angle in degrees
  • maxFrictionTorque: a number

RagdollNeckConstraintV2:

  • maxBallSocketConstraintTwistAngle: an angle in degrees
  • maxBallSocketConstraintUpperAngle: an angle in degrees
  • maxFrictionTorque: a number
constraintDefaultValues

This can be used to set some default values for RagdollConstraints so that you don’t need to store everything separately for every joint. Currently, setting anything other than maxFrictionTorque here is not supported for the built-in constraint classes.

functionToGetJointTypeName
extraNoCollisionPairs

This should contain bodypart name pair arrays ({name, anotherName}) defining which bodyparts other than ones directly connected to each other should have NoCollisionConstraints between them.

functionToGetCollisionPartsFromBodypartInstance

This can be either nil or a function that returns an array of BaseParts given the top instance of a bodypart. If this is nil, any BasePart that is either the top instance of the bodypart or its descendant is considered a collision BasePart.

shouldUserDefinedDataBeReplicatedToClients

This can alternatively be defined in system server settings. See the documentation for system server settings for the documentation of this.

functionToConvertRagdollStructureUserDefinedDataToFormThatCanBeSentToClient
functionToConvertBodypartNodeUserDefinedDataToFormThatCanBeSentToClient
functionToConvertHalfJointNodeUserDefinedDataToFormThatCanBeSentToClient
functionToConvertIntactJointUserDefinedDataToFormThatCanBeSentToClient

These can alternatively be defined in system server settings. See the documentation for system server settings for the documentation of these.

functionToConstructRagdollStructureUserDefinedDataOnClient
functionToConstructBodypartNodeUserDefinedDataOnClient
functionToConstructHalfJointNodeUserDefinedDataOnClient
functionToConstructIntactJointUserDefinedDataOnClient

These can alternatively be defined in system client settings. See the documentation for system client settings for the documentation of these.

functionToUpdateRagdollStructureUserDefinedDataOnClient
functionToUpdateBodypartNodeUserDefinedDataOnClient
functionToUpdateHalfJointNodeUserDefinedDataOnClient
functionToUpdateIntactJointUserDefinedDataOnClient

These can alternatively be defined in system client settings. See the documentation for system client settings for the documentation of these.

functionToGetBodypartInstances
functionToGetInitialInstancesToIgnoreForBodypartInstance
functionToGetNewParentOfBodypartInstance

These can alternatively be defined in system shared settings. See the documentation for system shared settings for the documentation of these.

System server settings

automaticallyHandleRagdollStructureCreationReplicationAndDestructionForPlayerCharacters

When this is true, a RagdollStructure is created for a player character when GeneralUtility.Characters.CharacterState.CharacterHasEverything fires on the server. The RagdollStructure is also replicated for all players and destroyed on Player.CharacterRemoving.

shouldUserDefinedDataBeReplicatedToClients

If this is true but the functions for replicating aren’t defined, an error will be thrown.

functionToFireRemoteEvent

This function needs to take a number, a player and a variable number of other arguments. So the function definition should be something like this: function(fireTypeNum: number, player: Player, ...). The remote event system that is used must give this number and the … arguments to RagdollInternalRemoteEventsClientHandler.handleRagdollSystemClientEvent on the client.

functionToConvertRagdollStructureUserDefinedDataToFormThatCanBeSentToClient
functionToConvertBodypartNodeUserDefinedDataToFormThatCanBeSentToClient
functionToConvertHalfJointNodeUserDefinedDataToFormThatCanBeSentToClient
functionToConvertIntactJointUserDefinedDataToFormThatCanBeSentToClient

These only need to be defined when the user-defined data should be replicated. These should convert it to something that can be sent via a RemoteEvent. The client needs to be able to construct and set the data using the construction functions defined in system client settings or character settings given the Object whose user-defined data it is and the return value of these functions.

System client settings

choppinessFixCloneParent

This is an optional setting. It’s value must be either an Instance or nil. If it’s nil, the clone is parented to the parent of the original.

functionToGetSettingsTableUsingDataReceivedFromServer

The data is the data given to this is the data you gave to RagdollStructure.createStructure, RagdollCharacterSettingsHandler.applyDefaultR15/R6Settings or RagdollSystemSharedSettingsHandler.applySystemSharedSettings for getting the settings on the client.

functionToConstructRagdollStructureUserDefinedDataOnClient
functionToConstructBodypartNodeUserDefinedDataOnClient
functionToConstructHalfJointNodeUserDefinedDataOnClient
functionToConstructIntactJointUserDefinedDataOnClient

These functions are given the object whose UserDefinedData should be created and set, and the data that the corresponding conversion function defined in system server settings returns.

functionToUpdateRagdollStructureUserDefinedDataOnClient
functionToUpdateBodypartNodeUserDefinedDataOnClient
functionToUpdateHalfJointNodeUserDefinedDataOnClient
functionToUpdateIntactJointUserDefinedDataOnClient

These functions are given the object whose UserDefinedData should be updated, and the data that you gave to the informClientsAboutUserDefinedDataChange method of the object whose user-defined data was updated.

System shared settings

maxDistanceToBeConsideredToBeRelatedToTheSameJoint
minCorrespondingDirectionVectorDotProductToBeConsideredToBeRelatedToTheSameJoint

These are used for checking whether Motor6D C0 or C1 and rig attachment CFrame are close enough to each other.

automaticallyRagdollOnHumanoidDeath
defaultFadeFadeTimeFromRagdollOffsetToAnimationOffset

When a joint goes from ragdoll state to normal state (Motor6D is enabled), the Transform property is gradually interpolated between the ragdoll state rotation and animation rotation. This defines the time it takes for the interpolation to reach the Animation Transform. The reason I put “default” in the name was probably that I was planning to allow the user to define their own way of transitioning from ragdoll to animations. However, at least currently, such a possibility is not implemented.

functionToGetBodypartInstances

This is optional to define. If it is defined, it should return an array of bodypart top instances given the top instance of the RagdollStructure being created.

functionToGetInitialInstancesToIgnoreForBodypartInstance

This is optional to define. If it is defined, it should take a bodypart top instance and return an array of instances (descendants of the bodypart top instance) that should be ignored by the RagdollSystem.

functionToGetNewParentOfBodypartInstance

This is optional to define. If it is defined, it should return the new parent of the top instance of a bodypart given the bodypart node and the top instance of the RagdollStructure to which the bodypart was moved as a result of breaking or merging a joint.

17 Likes

Usage examples

Placing the package in your game

I recommend parenting the RagdollSystemPackage folder to a folder in ReplicatedStorage.

RagdollSystem initialization with built-in settings and default RemoteEventSystem

server:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RagdollSystem = require(ReplicatedStorage.--[[path to RagdollSystem package]].RagdollSystem)
local RagdollDefaultRemoteEventSystemServerUtility = RagdollSystem .RagdollDefaultRemoteEventSystemServerUtility

RagdollSystem.initialize()
RagdollDefaultRemoteEventSystemServerUtility.setUpServerClientCommunicationOnServer()

client:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RagdollSystem = require(ReplicatedStorage.--[[path to RagdollSystem package]].RagdollSystem)
local RagdollDefaultRemoteEventSystemClientUtility = RagdollSystem .RagdollDefaultRemoteEventSystemClientUtility

RagdollSystem.initialize()
RagdollDefaultRemoteEventSystemClientUtility.setUpServerClientCommunicationOnClient()

Ragdolling and unragdolling an entire character when using the default settings

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RagdollSystem = require(ReplicatedStorage.--[[path to RagdollSystem package]].RagdollSystem)
local RagdollEaseOfUse = RagdollSystem.RagdollEaseOfUse

-- when you want to ragdoll a character (character here is a character model)
RagdollEaseOfUse.stunCharacter(character)

-- when you want to unragdoll a character
RagdollEaseOfUse.unstunCharacter(character)

Paralyzing the left leg of an R15 character (joints hip, knee and ankle will be paralyzed)

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RagdollSystem = require(ReplicatedStorage.--[[path to RagdollSystem package]].RagdollSystem)
local RagdollStructureCollections = RagdollSystem.RagdollStructureCollections

-- when you want to paralyze the leg (character is a character Model)
local structure = RagdollStructureCollections.getRagdollStructureFromInstance(character)
structure:paralyzeJointBasedOnJointName("LeftHip")

Breaking the right shoulder of an R15 character

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RagdollSystem = require(ReplicatedStorage.--[[path to RagdollSystem package]].RagdollSystem)
local RagdollStructureCollections = RagdollSystem.RagdollStructureCollections

-- when you want to break the shoulder (character is a character Model)
local structure = RagdollStructureCollections.getRagdollStructureFromInstance(character)
structure:breakJointBasedOnName("RightShoulder")

Known issues

  • Chat bubble vertical positioning breaks after breaking and merging joints on an R6 character.
  • Paralyzed bodyparts cause undesired behavior when jumping.
  • (partially solved) In a live game, ragdolling is laggy. I haven’t tested if setting network ownership to the server would solve this.
  • Ragdolls sometimes shake.
  • 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.

Layered clothing

  • Layered clothing doesn’t always look very good when breaking joints.
  • (partially solved) Layered clothing that becomes irrelevant when breaking a joint isn’t removed which sometimes causes weird looking results.

Possible future updates

  • I may fix some naming inconsistencies.
    – The CustomEnumItems of some CustomEnums are uppercase although the CustomEnumItems of most CustomEnums are lowercase.
    – The function for paralyzing a joint based on joint name is called :paralyzeJointBasedOnJointName while the function for breaking a joint based on joint name is called breakJointBasedOnName (difference: JointName vs. Name).
  • I may later change the default way of getting bodypart instances and the code for validating the bodypart instances given by a user of the system.
  • I may later split the paralyze state into two states: one for paralyze from joint break, another for paralyze from explicit paralyze
  • There are probably some other things I’ve considered but don’t remember.

Things that are unlikely to be added soon if ever despite code having something related

  • There are some active ragdoll related things in certain modules. However, I came to the conclusion that proper active ragdolls (ones that try to balance themselves while also obeying animations as much as possible) seem too complicated for me. I most likely won’t actually implement active ragdolls because I don’t know how I would do that.

Change log

December 2023

26.12.2023

  • Fixed a mistake that caused the server to inform all clients to which a structure had been replicated earlier about the structure having initialization finished whenever the structure was replicated to a new client. This caused errors on clients when there were more than one player because the function for informing about initialization being finished is only allowed to be called once.
  • If the NetworkOwner of a character is a player, the network owner will now be requested to enable or disable humanoid physics even if the RagdollStructure is not replicated to the network owner.
  • I had forgotten to add code for updating bodypart CanCollideState when a RagdollStructure is unanchored. This is needed if the ragdoll structure had been not only anchored but also made CanCollide false using RagdollStructure:disablePhysicsInteraction(). I also added code for actually changing the CanCollide property of the BaseParts instead of just the CanCollideState of the BodypartNode when RagdollStructure:disablePhysicsInteraction() or RagdollStructure:unanchor() is called.

27.12.2023

  • Added static methods getPlayerSpesificRagdollStructureCreatedEvent and getPlayerSpesificRagdollStructureUserInitializationDoneEvent to RagdollStructureCollections
  • RagdollEaseOfUse functions stunCharacter, unstunCharacter and toggleRagdoll will now create a RagdollStructure if one doesn’t exist for the character yet. With built-in settings, a RagdollStructure is automatically created for each player character, but this will at least make things easier for NPCs. Also, for player characters, the aforementioned functions can now be called before the character is fully loaded, and if you give a player instead of a character, they can even be called before spawning.
  • I also realised that Marketplace Models don’t actually have to have a Model as their top instance. I assumed that would be a requirement because InsertService:LoadAsset() gives a model. I’ve now updated the RagdollSystem Marketplace model to not have a redundant Model Instance as its top instance.

February 2024

19.2.2024

  • defaultFadeFadeTimeFromRagdollOffsetToAnimationOffset can now be defined in either system or character settings. Previously it could only be defined in character settings.
  • NoCollisionConstraints are now only created between CollisionBaseParts (each collisionBasePart in bodypartNode0 gets a NoCollisionConstraint with each collisionBasePart in bodypartNode1). Previously they were created between other baseparts as well. The purpose of this change is that multiple BaseParts can be used for visual details of a bodypart without it causing a lot of NoCollisionConstraints to be created.
  • Added code for moving layered clothing that becomes irrelevant for the original RagdollStructure to the detached RagdollStructure when breaking a joint. Previously, layered clothing was never removed from the original structure and instead a clone was given to the detached structure if the item was relevant for the detached structure. Also added code for moving layered clothing from structure being merged to the structure it is being merged to if the structure being merged is considered to contain the whole item (has all bodyparts that are considered relevant).
  • Changed definitions for which bodyparts are considered relevant to which types of layered clothing items (AccessoryType Enums).
  • Added choppiness / teleport problem fixing code for RagdollRootConstraint.
  • Added methods RagdollStructure:getAssemblyRootParts(), RagdollStructure:setNetworkOwner() and RagdollStructure:setNetworkOwnershipAuto().
  • Added event property RagdollStructure.BodypartsAddedOrRemoved.
  • Added method RagdollRootConstraint:recreateChoppinessFixClone().
  • Added choppiness / teleport fix related settings for RagdollRootConstraint, and a setting for whether Network ownership should be given to the server when ragdolling.
  • Added an optional ragdoll system client setting for the parent of choppiness fix clones.
  • Fixed a typo (RagdollStructure:getHalfJointFromRigAttachemnt changed to RagdollStructure:getHalfJointFromRigAttachment).

March 2024

9.3.2024

  • I had forgotten to disable the code that automatically initializes the default RemoteEventSystem when initializing the RagdollSystem. Now RemoteEventSystem isn’t automatically initialized anymore.
  • Fixed a typo (Folder name DataForRemoveEventSystem changed to DataForRemoteEventSystem).
  • Because Roblox announced the minimum part density change, I thought I’d have the RagdollRootConstraint set the density of HumanoidRootPart to the future minimum density (0.0001). However, that causes warnings so I’ve now changed it to the current minimum density (0.01).
  • I removed chat related things from GeneralUtility. My TextChatService related module that used to be in GeneralUtility sets TextChatService.OnIncomingMessage. This was required automatically when GeneralUtility was required. The problem with the module is that it may break other code by setting TextChatService.OnIncomingMessage after other code has set it, or it may be broken by other code that sets TextChatService.OnIncomingMessage. Now GeneralUtility shouldn’t automatically modify the state of the game when required.
  • RagdollSystem now requires having GeneralUtilityPackage and RemoteEventSystemPackage as siblings of RagdollSystemPackage. Previously nested copies of these Packages were used if RagdollSystemPackage didn’t have them as siblings. The purpose of this change is that there won’t be multiple copies of the same package/free model in the game.
  • Removed the RagdollCharacterSettingsTemplate module that was outdated and wasn’t used for anything.
  • RagdollSystemClientSettingsHandler and RagdollSystemServerSettingsHandler used to fire a settings changed event even when they automatically set the initial built-in settings. Now they don’t do so anymore. Now all settings handler modules only fire their events when the user sets new settings. Now both old settings and new settings given to the connected functions will always be non-nil.
5 Likes

Woah! really nice ragdoll man!

But In my opinion you should make it a bit harder to use because needing a title as an
Operations-Research Analyst isn’t enough.

3 Likes

Thank you for the compliment :slightly_smiling_face:.

If I interpreted correctly, you are saying that it’s difficult to use. What exactly feels difficult? The basic things (ragdolling and unragdolling entire characters with built-in settings) should be pretty easy. There’s an example in the “Usage examples section”. Also, today I made these basic things slightly easier than before :wink:. You can read more about this in the change log (27.12.2023).

I’m not sure, It’s just the fact that I did exactly what you showed in your example and I got 8 errors in the console just makes it look very complicated, on top of that having to like initialize it on the server and then on the client and this and that just adds to it.

(Keep in mind these errors are most likely not your fault, just me messing up the setup, but that just shows that I had trouble setting it up, and *in my opinion* It's a little complicated)

Of course, I’m not saying It’s bad, like this is legit one of the best radgolls I’ve seen.
But I’m not sure I would use it in a game over other ragdolls just because of the setup difficulty, specially if the ragdoll feature isn’t that important for the game.

Since you mentioned you changed, I will give it another try and I’ll let you know my results :+1:

Edit: Just tried it again, I did get it to work and the ragdoll was really smooth, had some issues with hitboxes since It creates parts in the player’s head, also struggled to apply force to the ragdoll, but overall really nice!

3 Likes

If you are talking about the parts related to RagdollNeckConstraint (to which there’s an alternative RagdollNeckConstraintV2 with no extra parts btw.), they are supposed to have CanCollide set to false but I may have a mistake in the code that causes them to have CanCollide true.

The purpose of initializing is that, if you need to debug something and ensure that the problem isn’t related to the RagdollSystem, you can just comment out the initialization lines and disable your own ragdoll related code instead of having to comment out every line where you require the RagdollSystem module or get a reference to one of its submodules (because such lines may exist in multiple scripts, local scripts or module scripts while the initialization only exists in place on the server and one place on the client).

Initializing the server → client communication has its own function instead of it being part of the main initialization function because I don’t want the default RemoteEventSystem to be initialized if the user of the RagdollSystem wants to use some other system for the server-client communication. I could alternatively add a boolean parameter for this in the main initialization function but it felt like a better idea to explicitly write a function call for initializing the server client communication as that more clearly shows what is being done.

Also, it’s been a while since I last used the automatic player character ragdoll structure creation when testing the system so the errors may have been (and most likely were because I noticed some outdated code in the automatic player character ragdoll structure creation yesterday) my fault.

4 Likes

This looks very very nice, it would be nice if u made an example place that utilizes rocket launchers and what not to play around with the ragdoll and act as a learning example to utilize the module and to showcase what u can do with it.

2 Likes

Wow this is extremely detailed and impressive, the amount of features and effort is exceptional. This is one of the best ragdoll system to exist in my opinion. Quick question though, is it possible to sort of play animations and be ragdolled, i saw a RagdollFadeToAnimation module but not sure what it does. What im trying to do is sort of have an euphoria ragdoll imitation by lets say the player gets shot by a gun, and i play an animation that sort of has the player touch the limb to see if its okay, aka inspecting the hurt limb. I want to ragdoll blended with the animation. Is there anyway to do this? Or is there anything in this ragdoll system that allows me to do this?

2 Likes

The RagdollFadeToAnimation module is used to make the transition non-instant when unragdolling (unstun or unparalyze). It won’t work for the purpose you are describing and doesn’t make a realistic transition.

If I understood correctly, you want the character to be in an active ragdoll state where it has no humanoid physics or rigid joints and needs to keep its balance by adjusting joint rotations as necessary while also following animations as much as possible.

This is something that I don’t know how to do. I think AlignOrientations together with BallSocketConstraints could be used for rotating the joints in a way that respects physics. However, the problem is that I don’t know how I should calculate the target rotations.

1 Like

Nice Work man, and thanks for sharing it!. :pray:

2 Likes

Any effort put into fixing the known errors?

1 Like

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

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.

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.

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.

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.

2 Likes