Reputation: 2385
I am working on a game that will fling a projectile that reacts to physics forces. I am using the built in Unity Physics in the game, but I am drawing the trajectory of the projectile before it is launched. To do so, I'm using a LineRenderer and a simple Physics class I rolled to compute the positions:
public class SlingPhysics
{
private Vector3 HalfGravity;
private float ForceMultiplier;
public void Init(Vector3 gravity, float slingForce, float mass, float drag)
{
HalfGravity = gravity / 2.0f;
ForceMultiplier = slingForce / mass / (1 + drag);
}
public Vector3 GetPosition(Vector3 start, Vector3 direction, float timeDelta)
{
Vector3 pos = direction * (timeDelta * ForceMultiplier);
pos.y += (HalfGravity.y * timeDelta * timeDelta);
return start + pos;
}
/// <summary>
/// Computes an array of Trajectory object positions by time.
/// </summary>
/// <returns>Number of positions filled into the buffer</returns>
/// <param name="startPos">Starting Position</param>
/// <param name="direction">Direction (and magnitude) vector</param>
/// <param name="timeIncrement">Time increment of the samples</param>
/// <param name="yFloor">Minimum height, below which is clipped</param>
/// <param name="positions">Buffer to fill with positions</param>
public int GetPositions(Vector3 startPos, Vector3 direction, float timeIncrement, float yFloor, Vector3[] positions)
{
int maxItems = positions.Length;
int i = 0;
for (; i < maxItems; i++)
{
Vector3 pos = GetPosition(startPos, direction, timeIncrement * i);
if (pos.y < yFloor)
break;
positions[i] = pos;
}
return i;
}
}
This works great and (somewhat surprisingly) exactly matches the actual trajectory that the projectile ends up traveling when launched and is governed by the Unity Physics engine.
But now, we decided to introduce drag on to the Rigidbody and the trajectory is no longer perfect. I could continue to try to change my physics model to match what Unity is doing, but this seems like a losing battle.
Is there any way to precompute and draw the trajectory using the Unity Physics engine?
If not, how are drag and angular drag implemented mathematically in Unity Physics?
Is there a documentation source for Unity's (5.3.4) Physics implementation?
Upvotes: 0
Views: 7309
Reputation: 2385
The way to calculate drag in to match the Unity engine is to compute and keep track of velocity in FixedUpdate and then update velocity for Drag.
The Unity formula for Drag was provided here: http://forum.unity3d.com/threads/drag-factor-what-is-it.85504/
It is indeed simple; the key part is this:
velocity *= Mathf.Clamp01(1f - Drag * Time.fixedDeltaTime);
The custom Physics class had to be reworked to require that the time increment be Unity's Time.fixedDeltaTime (the time between FixedUpdate calls)
public class SlingPhysics
{
private Vector3 Gravity;
private float Force, Mass, Drag;
public void Init(Vector3 gravity, float force, float mass, float drag)
{
Gravity = gravity;
Force = force;
Mass = mass;
Drag = drag;
}
/// <summary>
/// Computes an array of Trajectory object positions by time.
/// </summary>
/// <returns>Number of positions filled into the buffer</returns>
/// <param name="startPos">Starting Position</param>
/// <param name="direction">Direction (and magnitude) vector</param>
/// <param name="yFloor">Minimum height, below which is clipped</param>
/// <param name="positions">Buffer to fill with positions</param>
public int GetPositions(Vector3 startPos, Vector3 direction, float yFloor, Vector3[] positions)
{
float timeIncrement = Time.fixedDeltaTime;
int maxItems = positions.Length;
int i = 0;
Vector3 velocity = direction * Force / Mass;
Vector3 pos = startPos;
for (; i < maxItems; i++)
{
velocity += Gravity * timeIncrement;
velocity *= Mathf.Clamp01(1f - Drag * timeIncrement);
pos += velocity * timeIncrement;
if (pos.y < yFloor)
break;
positions[i] = pos;
}
return i;
}
}
Upvotes: 4
Reputation: 125245
I could continue to try to change my physics model to match what Unity is doing
No, don't do it. Whenever Unity is updated, physics stuff like that break. For example, Unity 4 and 5 physics are not the-same. Had to heavily modify my Unity 4 physics code to make it work properly.
The easiest solution to your problem is to not calculate anything at all. The way I do it is to create two projectiles. Projectile1 is the main projectile that can be seen and shot. Projectile2 is a hidden projectile that can't be seen by the player.
On drag mode, shoot Projectile2(hidden) then store the its path or position(Vector3) it has traveled through in a List
. Use that stored path to draw your Projectile Trajectory
.
You only need to shoot Projectile2(hidden) once and store the path. If during drag mode, the finger, mouse or drag moves to another position, then you have to clear the List
, re-shoot Projectile2(hidden) once again, store the position to the List
and then update your Projectile Trajectory
again.
When the finger is released, you can now shot the Projectile1(main projectile) which is visible to the player.
If you are using lineRenderer
, the number of SetVertexCount
is the-same as the number of your List.Count
. Drawing the lines can be easily done by looping over the positions in the List
.
The result is always the-same no matter how many times Unity is updated.
Upvotes: 5