ShrimpleRagdoll.Physics.cs
namespace ShrimpleRagdolls;
public partial class ShrimpleRagdoll
{
[Property, Group( "Physics" )]
public bool MotionEnabled
{
get;
set
{
field = value;
if ( ModelPhysics.IsValid() )
ModelPhysics.MotionEnabled = value;
}
}
/// <summary>
/// Enable/Disable gravity to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public bool Gravity
{
get;
set
{
if ( field == value )
return;
field = value;
SetGravity( value );
}
} = true;
/// <summary>
/// Set the gravity scale to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public float GravityScale
{
get;
set
{
if ( field == value )
return;
field = value;
SetGravityScale( value );
}
} = 1f;
/// <summary>
/// Set the linear damping to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public float LinearDamping
{
get;
set
{
if ( field == value )
return;
field = value;
SetLinearDamping( value );
}
} = 0f;
/// <summary>
/// Set the angular damping to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public float AngularDamping
{
get;
set
{
if ( field == value )
return;
field = value;
SetAngularDamping( value );
}
} = 0f;
/// <summary>
/// Rigidbody flags applied to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public RigidbodyFlags RigidbodyFlags
{
get;
set
{
field = value;
if ( ModelPhysics.IsValid() )
ModelPhysics.RigidbodyFlags = value;
}
}
/// <summary>
/// Rigidbody locking applied to all bodies.
/// </summary>
[Property, Group( "Physics" )]
public PhysicsLock Locking
{
get;
set
{
field = value;
if ( ModelPhysics.IsValid() )
ModelPhysics.Locking = value;
}
}
/// <summary>
/// Sets the mass override of this ragdoll<br />
/// Each body part will have their proportionally changed so they combine to the desired total mass<br />
/// Set to 0 for the default value
/// </summary>
[Property, Group( "Physics" )]
public float MassOverride
{
get;
set
{
field = value;
SetMassOverride( value );
}
} = 0f;
/// <summary>
/// All bodies will be put to sleep on start.
/// </summary>
[Property, Group( "Physics" )]
public bool StartAsleep
{
get;
set
{
field = value;
if ( ModelPhysics.IsValid() )
ModelPhysics.StartAsleep = value;
}
}
/// <summary>
/// All bodies will have impact damage enabled
/// </summary>
[Property, Group( "Physics" )]
public bool EnableImpactDamage
{
get;
set
{
field = value;
SetEnableImpactDamage( value );
}
} = false;
/// <summary>
/// Set the minimum impact speed on all bodies
/// </summary>
[Property, Group( "Physics" )]
public float MinimumImpactSpeed
{
get;
set
{
field = value;
SetImpactMinimumSpeed( value );
}
} = 500f;
/// <summary>
/// Surface to apply to all colliders<br />
/// Set to null for the surfaces defined in the ragdoll
/// </summary>
[Property, Group( "Physics" )]
public Surface Surface
{
get;
set
{
field = value;
SetSurface( value );
}
} = null;
[Property, Group( "Physics" )]
public ColliderFlags ColliderFlags
{
get;
set
{
field = value;
SetColliderFlags( value );
}
}
public float Mass => (ModelPhysics?.Mass ?? 0f) == 0f ? GetModelMass() : ModelPhysics?.Mass ?? 0f;
/// <summary>
/// Calculate the center of mass of the ragdoll in world space based on its bodies' masses and masscenters
/// </summary>
public Vector3 MassCenter => GetMassCenter();
/// <summary>
/// Calculate the center of mass of the ragdoll in world space based on its bodies' masses and masscenters
/// </summary>
/// <returns>World position of the combined center of mass</returns>
public Vector3 GetMassCenter()
{
if ( Renderer.Components.TryGet<Rigidbody>( out var rigidbody ) && rigidbody.IsValid() && rigidbody.Active )
return rigidbody.WorldTransform.PointToWorld( rigidbody.MassCenter );
var totalMass = 0f;
var weightedCenter = Vector3.Zero;
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() || !body.Component.Active )
continue;
var mass = body.Component.PhysicsBody.Mass;
var worldMassCenter = body.Component.WorldTransform.PointToWorld( body.Component.MassCenter );
weightedCenter += worldMassCenter * mass;
totalMass += mass;
}
if ( totalMass <= 0f )
return Renderer.IsValid() ? Renderer.WorldPosition : Vector3.Zero;
return weightedCenter / totalMass;
}
/// <summary>
/// Calculate the mass of the ragdoll using the model data
/// </summary>
/// <returns></returns>
public float GetModelMass() => Renderer?.Model?.Physics?.Parts?.Sum( x => x.Mass ) ?? 0f;
/// <summary>
/// Makes sure to wake up all bodies
/// </summary>
public void WakePhysics()
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.Sleeping = false;
}
/// <summary>
/// Sets all rigidbodies to sleep
/// </summary>
public void SleepPhysics()
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.Sleeping = true;
}
/// <summary>
/// Sets gravity to all rigid bodies
/// </summary>
/// <param name="gravity"></param>
protected void SetGravity( bool gravity )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.Gravity = gravity;
}
/// <summary>
/// Sets gravity scale to all rigid bodies
/// </summary>
/// <param name="gravityScale"></param>
protected void SetGravityScale( float gravityScale )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.GravityScale = gravityScale;
}
/// <summary>
/// Sets the linear damping to all rigid bodies
/// </summary>
/// <param name="damping"></param>
protected void SetLinearDamping( float damping )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.LinearDamping = damping;
}
/// <summary>
/// Sets the angular damping to all rigid bodies
/// </summary>
/// <param name="damping"></param>
protected void SetAngularDamping( float damping )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.AngularDamping = damping;
}
/// <summary>
/// Apply a surface to every collider
/// </summary>
/// <param name="surface"></param>
protected void SetSurface( Surface surface )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() || !body.Component.GameObject.IsValid() )
continue;
foreach ( var collider in body.Component.GameObject.GetComponents<Collider>() )
{
if ( collider.IsValid() )
collider.Surface = surface;
}
}
}
/// <summary>
/// Apply collider flags to every collider
/// </summary>
/// <param name="flags"></param>
protected void SetColliderFlags( ColliderFlags flags )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() || !body.Component.GameObject.IsValid() )
continue;
foreach ( var collider in body.Component.GameObject.GetComponents<Collider>() )
{
if ( collider.IsValid() )
collider.ColliderFlags = flags;
}
}
}
/// <summary>
/// Sets the mass override, each body piece will have their mass proportional so that the total combines to the desired value
/// </summary>
/// <param name="massOverride"></param>
protected void SetMassOverride( float massOverride )
{
if ( !PhysicsWereCreated )
return;
if ( Renderer.IsValid() && Renderer.Components.TryGet<Rigidbody>( out var rigidbody ) && rigidbody.IsValid() && rigidbody.Active )
rigidbody.MassOverride = massOverride;
float totalDefaultMass = 0f;
foreach ( var body in Bodies )
{
if ( body.Component.IsValid() && body.Component.PhysicsBody.IsValid() )
totalDefaultMass += body.Component.PhysicsBody.Mass;
}
if ( totalDefaultMass <= 0f )
return;
// Set proportional masses so they sum to massOverride
foreach ( var body in Bodies )
{
if ( !body.Component.IsValid() || !body.Component.PhysicsBody.IsValid() )
continue;
float proportion = body.Component.PhysicsBody.Mass / totalDefaultMass;
body.Component.MassOverride = massOverride * proportion;
}
}
/// <summary>
/// Sets <see cref="Rigidbody.EnableImpactDamage"/> on all bodies
/// </summary>
/// <param name="enabled"></param>
protected void SetEnableImpactDamage( bool enabled )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.EnableImpactDamage = enabled;
}
/// <summary>
/// Sets <see cref="Rigidbody.MinImpactDamageSpeed"/> on all bodies
/// </summary>
/// <param name="speed"></param>
protected void SetImpactMinimumSpeed( float speed )
{
if ( !PhysicsWereCreated )
return;
foreach ( var body in Bodies )
if ( body.Component.IsValid() )
body.Component.MinImpactDamageSpeed = speed;
}
/// <summary>
/// Sets up all physics related settings for colliders and rigidbodies
/// </summary>
public void SetupPhysics()
{
SetGravity( Gravity );
SetGravityScale( GravityScale );
SetLinearDamping( LinearDamping );
SetAngularDamping( AngularDamping );
SetSurface( Surface );
SetColliderFlags( ColliderFlags );
SetMassOverride( MassOverride );
SetEnableImpactDamage( EnableImpactDamage );
SetImpactMinimumSpeed( MinimumImpactSpeed );
}
}