- Home /
Tracking Force Added to Kinematic Rigidbody
Hello! =)
Short form: Is there anywhere I can see how much force has been added to a kinematic rigidbody over the course of the current frame?
Alternatively: Is there a way to catch or hook into AddForce() method invocations, so that whenever [some script] calls AddForce() on a particular rigidbody, I can execute some code that does something with the parameters of the call?
Long version: Ok, so what I'm attempting to achieve here is to 'ragdoll' a character when the force applied to it exceeds a threshold.
The (Non-humanoid) character consists of a regular Unity character controller and a collection of Kinematic rigidbodies, that are used as hitboxes for it's body and limbs.
I ragdoll the character using a script which deactivates the character controller and animator and sets the rigidbodies to non-kinematic. This works well enough, physics takes over and the character falls over as per the joints configured for the rigidbodies.
My problem lies in triggering the ragdoll state. I want the character to ragdoll upon losing it's balance. My approach is to add together the forces being applied to the character into an 'imbalance' vector, and if this vector's magnitude exceeds a threshold, the character is ragdolled and it falls over.
This is fine for collisions with physics controlled rigidbodies; I can simply calculate the force of the impact in OnCollisionEnter and add it to the running total, but I can't find any similar solution to forces caused by non-collisions.
This means that wind effects, explosions, or any force applied by a script don't contribute to triggering the ragdoll.
The only workable solution I can see at the moment is to modify every script I use that applies forces and add a check for the ragdoll'able character and apply the imbalance directly. This isn't ideal, it'd clutter up those scripts and introduce difficult to detect bugs where I forget a particular call, or don't account for the Forcemode by accident.
Where should I go from here? Is the ugly way my only option? Is there a better, smarter design that doesn't have this issue?
I am also looking into this. Working on an answer and will come back to this once I find it.
Didn't read the whole thing, but couldn't you just get the 'velocity' variable on the rigidbody? I'm not sure if this will work, but I hope it helps.
wow I'm an idiot man. Yes you can because that is all the things I'm needing.
I'm not sure if it has changed in Unity 5, but in previous versions this did not work. Adding force does not immediately change the velocity variable, velocity only changes when the physics step occurs.
$$anonymous$$y rigidbody is kinematic, so its velocity is not affected by add force anyhow.
Actually adding force does immediately change the target if you use forcemode.impulse.
For example: GetComponent().AddForce(0, 0, 10, Force$$anonymous$$ode.Impulse); using impulse will immediately apply 10 force on the z axis.
Example 2: GetComponent().AddForce(0, 0, 10, Force$$anonymous$$ode.Acceleration); using this method will apply force using drag and mass to ramp up to a force of 10.
Here is a link to unity manual on it: http://docs.unity3d.com/ScriptReference/Force$$anonymous$$ode.html
Also, what I needed to do was not applying a force but directly setting the velocity of my ridigbody to match that of the one impacting said object. This is not the best solution but it is a solution that works. Unity manual has this to say about setting velocities:
"In most cases you should not modify the velocity directly, as this can result in unrealistic behavior. Don't set the velocity of an object every physics step, this will lead to unrealistic physics simulation. A typical example where you would change the velocity is when jumping in a first person shooter, because you want an immediate change in velocity."
What I've done is very similar. It is an impulse force I need not an acceleration. Here is how I implemented it.
private Vector3 impactVelocity;
private bool doImpact;
private void OnCollisionEnter(Collision col)
{
if (this.GetComponent<Rigidbody>().is$$anonymous$$inematic)
{
if (col.relativeVelocity.sqr$$anonymous$$agnitude > 6)
{
impactVelocity = col.transform.GetComponent<Rigidbody>().velocity;
doImpact = true;
}
}
}
private void FixedUpdate()
{
if (doImpact)
{
this.GetComponent<Rigidbody>().is$$anonymous$$inematic = false;
this.GetComponent<Rigidbody>().velocity = impactVelocity;
doImpact = false;
}
}
You will likely need to adapt this to your specific needs but this is the basic principal of what I am doing to solve this issue and it is working perfectly for me.
Unfortunately, my question operates on a lower level to what you describe. I mean that velocity does not change within a single frame
ie:
void FixedUpdate () {
step++;
Rigidbody rb = GetComponent<Rigidbody>();
print("Step" + step + ": " + rb.velocity );
rb.AddForce(Vector3.up*10,Force$$anonymous$$ode.Impulse);
print("Step" + step + ": " + rb.velocity );
}
Both of the above print statements produce the same output. rb.AddForce does not affect the value of the velocity attribute.
Answer by SpaceManDan · Apr 15, 2016 at 07:04 PM
Thought I'd throw this in the answer section in-case you decide it works for you too.
private Vector3 impactVelocity;
private bool doImpact;
private void OnCollisionEnter(Collision col)
{
if (this.GetComponent<Rigidbody>().isKinematic)
{
if (col.relativeVelocity.sqrMagnitude > 6)
{
impactVelocity = col.transform.GetComponent<Rigidbody>().velocity;
doImpact = true;
}
}
}
private void FixedUpdate()
{
if (doImpact)
{
this.GetComponent<Rigidbody>().isKinematic = false;
this.GetComponent<Rigidbody>().velocity = impactVelocity;
doImpact = false;
}
}
In my case, I'm using a ragdoll that has several rigidbodies that are set to isKinematic all attached to a parent. I need them all to switch at once so I suggest you use something like this to do that.
public void SetKinematic(bool newBool)
{
Rigidbody[] rigidBodies = GetComponentsInChildren<Rigidbody>();
foreach (Rigidbody rb in rigidBodies)
{
rb.isKinematic = newBool;
}
}
Call this method like this:
SetKinematic(false);
or
SetKinematic(true);
Unfortunately, this isn't really relevant. $$anonymous$$y question calls out collisions with other rigidbodies as the easy part:
This is fine for collisions with physics controlled rigidbodies; I can simply calculate the force of the impact in OnCollisionEnter and add it to the running total, but I can't find any similar solution to forces caused by non-collisions.
$$anonymous$$y question relates to factoring in other forces, such as explosions, wind or magnetism
This means that wind effects, explosions, or any force applied by a script don't contribute to triggering the ragdoll
This part seems to be significantly harder, as these forces are generally applied by other scripts. I can't figure out a way to react to those forces, without modifying those other scripts.
AH ok, I'm on the same page as you. I think you can do this in a simple way. Gimmie a few $$anonymous$$utes.
Any reason you can't just create your own float to watch for this.
private float ragTripThresh;
private void Update()
{
if(ragTripThresh >= 100f)
{
doRagdollTrip();
}
}
Now anytime you apply a force to the object you add a little to "ragTripThresh" until it is over 100?
Your answer
Follow this Question
Related Questions
Disable ragdoll without changing Kinematic boolean? 2 Answers
How can I make character become a ragdoll with isKinematic? 1 Answer
Problems with ragdolls and no gravity 0 Answers
Projectile collides, freezes, but flips to weird angle. Help! 0 Answers
Switch off Kinematic for a Rigidbody in response to an explosion or impact? 1 Answer