我在Unity方面还很陌生,我正在开发一些小游戏来学习。
我目前正在制作射击游戏,但有一个小问题(计算错误)。
每当玩家按下空格键时,我都会创建新的项目符号(使用RigidBody
)并更改其速度。
我正在尝试计算子弹将落在何处,但是我的计算有误。
我正在使用物理公式:dx = x0 + V0*t + 0.5*a*t^2
来计算子弹何时着陆以及在何处着陆。
这是我到目前为止写的:
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));
并应用速度:
if (Input.GetKeyDown(KeyCode.Space))
{
rb.velocity = new Vector3(0, 0, 100);
}
在这种情况下,时间是2.67125,dX是26.7125,但是在统一检查员中,我看到一颗子弹经过了27.91713。
您觉得有什么不对吗?
下面是场景中的子弹
答案 0 :(得分:2)
请勿手动执行此操作。唯一应该手动进行计算的时间是可以访问Unity的源代码,而普通的Joe则没有。即使您将其用于计算,代码也可能随时中断。
Unity 2017.1 引入了Physics.Simulate
函数和Physics.autoSimulation
属性。 Physics.autoSimulation
用于禁用物理,然后调用Physics.Simulate
来手动模拟物理并返回Rigidbody对象将来的位置。
您的着陆点位于 0.15 。首先,使用Physics.autoSimulation = false;
禁用物理学,使用velocity
或AddForce
函数向刚体添加力。将Physics.Simulate(Time.fixedDeltaTime);
置于一个循环中,使其连续运行,直到到达着陆点或pos.y < 0.15
变成true
。在while
循环退出之后,您应该获取新位置并将其存储在一个临时变量中。现在,您可以使用Physics.autoSimulation = true;
重新启用物理学,并重置变换。
实现超时也很有帮助,这样当弹丸在规定的时间内没有到达着陆点时,就退出循环。这样可以防止游戏中出现无限循环。
这里是struct
,其中包含着陆位置,旋转和时间结果:
public struct PredictResult
{
public Vector3 position;
public Quaternion rotation;
public float landingTime;
}
这是执行着陆检查的功能。成功时它将返回true
,如果在false
所提供的时间内未到达降落点,则返回timeOutTime
,则可能必须增加timeOutTime
变量。>
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;
}
用法:
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");
}
}
}
按【{1】】键拍摄一个刚体,然后它返回着陆距离。
请注意,您说它应该在Space
时降落。如果您不知道pos.y <= 0.15
着陆点在哪里,请对代码进行简单的编辑,而使用y
确定对象何时与地面碰撞,然后切换一个布尔变量,即退出OnCollisionEnter
而不是while
循环。
答案 1 :(得分:1)
不是对 Op 问题的直接回答,但适用于遇到此线程但正在使用 Rigidbody2D 的任何人。
Physics2D.simulationMode = SimulationMode2D.Script;
而不是 Physics.autoSimulation = false;
Physics2D.simulationMode = SimulationMode2D.FixedUpdate;
而不是 Physics.autoSimulation = true;
。Physics2D.Simulate(Time.fixedDeltaTime);
官方文档供参考: https://docs.unity3d.com/ScriptReference/Physics2D.Simulate.html