4-物理检测

内置的碰撞检测

Unity中开启碰撞检测至少需要一方拥有RigidBody,通过OnCollisionEnter方法检测到碰撞

内置碰撞检测的不足

如果我们需要对物理检测进行更精准的定位,可以修改它的检测模式和线性检测方法。将将body的检测模式更改为连续检测模式或者动态连续检测模式。将线性改为线性模式。

1
2
3
Rigidbody rigidbody;  
rigidbody.collisionDetectionMode = CollisionDetectionMode.Continuous;
rigidbody.interpolation = RigidbodyInterpolation.Interpolate;

经过以上调整,我们可以发现并没有得到很好的效果,子弹仍然会在穿墙之后爆炸或者不爆炸。

使用射线检测辅助

经过研究发现仍然问题仍然出现在子弹的速度过快。因为unity的物理是在fix update中进行。

如果当前的update和下一个update的间隔过大。我们仍然不能检测到中间的物体。

但是,我们使用射线在curTime->nextTimepreTime->curTime都进行射线检测,这样确保两个fixupdate之间检测到了物体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private void FixedUpdate()  
{
if (startTime < 0) startTime = Time.time;

//射线检测
var curTime = Time.time - startTime;
var preTime = curTime - Time.fixedDeltaTime;
var nextTime = curTime + Time.fixedDeltaTime;

var curPoint = GetBulletPos(curTime);

RaycastHit tempHit;
if (preTime > 0)
{
var prePoint = GetBulletPos(preTime);
//物理检测
if (Physics.Linecast(prePoint, curPoint, out tempHit))
{
OnHit(tempHit);
}
}

var nextPoint = GetBulletPos(nextTime);
//物理检测
if (Physics.Linecast(curPoint, nextPoint, out tempHit))
{
OnHit(tempHit);
}

//Debug.DrawRay(curPoint, nextPoint - curPoint, Color.red, 5);
}

private void OnHit(RaycastHit hit)
{
Debug.Log(hit.collider.name);
BaseTank hitTank = hit.collider.gameObject.GetComponent<BaseTank>();
if (hitTank == tank)
{
return;
}

if (hitTank != null)
{
// hitTank.Attacked(35);
SendMsgHit(tank, hitTank);
}

//显示爆炸效果
//TODO:对象池
...
}

可以得到非常好的效果了,当时觉得hit.point的位置任然不是很完美,稍微跳转下即可

1
2
3
4
5
6
7
8
9
//显示爆炸效果  
//TODO:对象池
GameObject explode = ResMgr.Instance.AutoLoad<GameObject>("BigExplosion");

Vector3 tempHitPoint = new Vector3(hit.point.x, hit.point.y, hit.point.z - 0.5f);

explode.transform.SetPositionAndRotation(tempHitPoint, transform.rotation);
Destroy(explode, 1);
Destroy(gameObject);