Reputation: 1040
I am pretty new in Unity and I am building small games to learn.
I am currently building a shooting-game and I have a small problem (miscalculation).
Every time a player presses space key, I am creating new bullet (with RigidBody
) and changes its velocity.
I am trying to calculate where the bullet would land, but something is wrong in my calculation.
I am using the physics formula: dx = x0 + V0*t + 0.5*a*t^2
to calculate when the bullet would land and where.
This is what I have wrote so far:
float g = Physics.gravity.y;
print(transform.position.y); // it starts on 0.5
//Yt = Y0 + 0.5 * g * t^2
float time = ((0.15f - transform.position.y) * 2) / g; // The bullet would land on y equals to 0.15 because its height
print("TIME: " + Mathf.Sqrt(time));
print("dX = " + 100 * Mathf.Sqrt(time));
and to apply the velocity:
if (Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = new Vector3(0, 0, 100);
}
In that case the time is 2.67125 and dX is 26.7125, but in unity inspector I see a bullet traveled 27.91713.
Does anything seems wrong to you?
Below is the bullet in the scene
Upvotes: 1
Views: 2339
Reputation: 21
Not a direct answer to the Op's question, but here it goes for anyone that comes across this thread but is working with Rigidbody2D.
Physics2D.simulationMode = SimulationMode2D.Script;
instead of Physics.autoSimulation = false;
when you want to switch off the Physics auto simulationPhysics2D.simulationMode = SimulationMode2D.FixedUpdate;
instead of Physics.autoSimulation = true;
when you want to turn auto simulation on again.Physics2D.Simulate(Time.fixedDeltaTime);
Official documentation for reference: https://docs.unity3d.com/ScriptReference/Physics2D.Simulate.html
Upvotes: 2
Reputation: 125245
Don't do this manually. The only time you should manually do the calculation is when you have access to Unity's source code but an average Joe don't. Even if you get it working with your calculations the code can break anytime.
Unity 2017.1 introduced the Physics.Simulate
function and Physics.autoSimulation
property. Physics.autoSimulation
is used to disable physics then Physics.Simulate
is then called to manually simulate physics and return the position the Rigidbody object would be in the future.
Your landing point is at 0.15. First, disable physics with Physics.autoSimulation = false;
, Add force to your Rigidbody with velocity
or the AddForce
function. Put Physics.Simulate(Time.fixedDeltaTime);
in a loop and make it run continuously until you reach your landing spot or until pos.y < 0.15
becomes true
. After the while
loop exits, you should obtain the new position and store it in a temporary variable. You can now re-enable physics with Physics.autoSimulation = true;
and reset the transform.
It would also be helpful to implement a timeout so that when the projectile do not reach the landing-spot within the time provided then break out of the loop. This prevents possible infinite loop in your game.
Here is a struct
which holds the landing position, rotation and time result:
public struct PredictResult
{
public Vector3 position;
public Quaternion rotation;
public float landingTime;
}
Here is the function that performs the landing check. It returns true
when successful and false
if it didn't reach the landing point within the time provided in timeOutTime
then you probably have to increase the timeOutTime
variable.
bool PredictRigidBodyLandPos(Rigidbody sourceRigidbody, Vector3 velocity, out PredictResult result)
{
//const float landingYPoint = 0.15f;
const float landingYPoint = -1.651335f;
//Disable Physics AutoSimulation
Physics.autoSimulation = false;
//Shoot the Bullet
sourceRigidbody.velocity = velocity;
//Get current Position and rotation
Vector3 defaultPos = sourceRigidbody.position;
Quaternion defaultRot = sourceRigidbody.rotation;
Debug.Log("Predicting Future Pos from::: x " + defaultPos.x + " y:"
+ defaultPos.y + " z:" + defaultPos.z);
//Exit after x seconds(In physics time) if Object does not land
float timeOutTime = 15f;
//The landing time that will be returned
float landingTime = 0;
//Determines if we landed successfully or not
bool landedSuccess = false;
//Simulate where it will be in x seconds
while (timeOutTime >= Time.fixedDeltaTime)
{
timeOutTime -= Time.fixedDeltaTime;
landingTime += Time.fixedDeltaTime;
Physics.Simulate(Time.fixedDeltaTime);
Vector3 pos = sourceRigidbody.position;
Debug.Log("Pos: " + pos.x + " " + pos.y + " " + pos.z);
//Check if we have landed then break out of the loop
if (pos.y < landingYPoint || Mathf.Approximately(pos.y, landingYPoint))
{
landedSuccess = true;
Debug.LogWarning("Landed");
break;
}
}
//Get future position and rotation and save them to output
Vector3 futurePos = sourceRigidbody.position;
Quaternion futureRot = sourceRigidbody.rotation;
result = new PredictResult();
result.position = futurePos;
result.rotation = futureRot;
result.landingTime = landingTime;
//Re-enable Physics AutoSimulation and Reset position and rotation
Physics.autoSimulation = true;
sourceRigidbody.velocity = Vector3.zero;
//sourceRigidbody.useGravity = false;
sourceRigidbody.transform.position = defaultPos;
sourceRigidbody.transform.rotation = defaultRot;
return landedSuccess;
}
Usage:
Transform cameraTransform;
public float shootSpeed = 300;
public Rigidbody rbdy;
void Start()
{
cameraTransform = Camera.main.transform;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
Vector3 velocity = cameraTransform.forward * shootSpeed;
PredictResult result;
if (PredictRigidBodyLandPos(rbdy, velocity, out result))
{
Debug.Log("DONE Predicting Landing Pos: x " + result.position.x + " y:"
+ result.position.y + " z:" + result.position.z);
Debug.Log("Landing Time: " + result.landingTime);
}
else
{
Debug.Log("Failed to predict landing pos before timeout");
}
}
}
Press the Space
key to shoot a Rigidbody then it returns the landing distance.
Note that you said it should land when pos.y <= 0.15
. If you don't know where the y
landing point is at, make a simple edit to the code and instead, use OnCollisionEnter
to determine when the Object collides with the ground then toggle a boolean variable which is used to exit out of the while
loop instead of pos.y < 0.15
.
Upvotes: 3