Friday, December 18, 2015

unity - Projectile Not Correctly Inheriting Parent Velocity (Unity3D)


I am trying to fire projectiles from a player who is moving at very high speeds. The issue is that the bullets do not seem to inherit the angular velocity. If the player is moving in a straight line at 500 U/s, then the bullet correctly inherits that velocity and adds its own speed onto that:


GameObject bullet = Instantiate(projectile, gun.transform.position, gun.transform.rotation);
bullet.GetComponent().velocity = r.velocity + bullet.transform.forward *bulletSpeed;


However, if the player is turning really quickly, it does not inherit the rotational velocity, and the bullet looks like it veers off to the side.


How can I implement this? I tried assigning the angular velocity of the player to the bullet, but this just caused the bullet to spin while moving in the same direction.


Some extra details:




  • I am using rigidbody.MoveRotation() to rotate my player, but I manually calculated angularVelocity to assign it to the bullet




  • I tried moving the player with AddTorque(), but could not produce any results with the bullets






Answer



We can slightly improve the perceived course of the projectile by taking the velocity of the muzzle of the gun, rather than the velocity of the body's pivot. This will include any sideways linear velocity due to the gun swinging around at an offset from the body's center of mass as the body rotates:


// I'd recommend defining your prefab as Rigidbody type, so that you
// can get it directly from the Instantiate without a GetComponent call
Rigidbody projectile;

void Fire() {
Rigidbody bullet = Instantiate(

projectile,
gun.transform.position,
gun.transform.rotation
);

// Inherit not just the linear velocity of the parent's center of mass,
// but also the sideways motion due to the parent's angular velocity.
Vector3 inheritedVelocity = r.GetPointVelocity(gun.transform.position);
bullet.velocity = inheritedVelocity + bullet.transform.forward * bulletSpeed;
}


To make this work, you'll need the body to have a valid angular velocity. Assuming you're setting the rotation each frame, you can adapt your existing MoveRotation code to use this instead:


void MoveRotationWithAngularVelocity(Quaternion rotation) {
Quaternion delta = rotation * Quaternion.Inverse(r.rotation);

float angle; Vector3 axis;
delta.ToAngleAxis(out angle, out axis);

// I don't recall the range returned by ToAngleAxis, so this is just to be safe.
if(Mathf.Abs(angle) > 180f) {

axis = -axis;
angle = 360f - Mathf.Abs(angle);
}

// Compute an angular velocity that accomplishes this rotation in 1 time step.
r.angularVelocity = angle * Mathf.Deg2Rad * axis / Time.deltaTime;
}

The difference between the normal MoveRotation and this one is that the object will keep spinning with inertia on subsequent updates, but if you're setting its rotation each FixedUpdate anyway then the angular velocity calculated will naturally go to zero once it arrives at the target rotation.


Note that this will not ensure that the bullet stays in line with the barrel as the barrel continues to rotate through a wide angle. That doesn't happen in real physics: think of a rock released from a spinning sling - the rock inherits the linear velocity of the end of the sling at the moment of release, and zips off in a straight line, not an expanding spiral revolving about the sling's pivot.



If you want to trace such a spiral instead of the normal straight-line trajectory that physics suggests, then you'll need a script on the bullet to apply additional compensating accelerations each frame. If that's what you need, comment and I'll add formulas for calculating those accelerations.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...