- 最后登录
- 2018-1-5
- 注册时间
- 2012-10-11
- 阅读权限
- 30
- 积分
- 651
- 纳金币
- 0
- 精华
- 3
|
很长一段时间没有写帖子了,今天想起来,该写点东西了,写什么呢?就写点实际一点的东西吧!这次的内容主要是关于我之前碰撞检测系列的一个应用。是关于当炮弹集中目标体时进行伤害计算的一种方式。好吧,我们开始吧!
当我们遇到角色发射子弹或者诸如坦克发射炮弹后怎样让对方减血的问题时,我们第一个想法是让子弹在运动的过程中进行基本的碰撞检测,包括成为刚体碰撞器或者成为触发器,然后再那两组函数(OnCollisionEnter等)中调用减血的方法。这种方法理论上虽然可以实现这些功能,但是实施起来并不容易,有时甚至会出现无效的情况(如果子弹运动的速度非常之快,有可能会发生检测不到碰撞的尴尬情况)。这个时候,我们就需要转换一下思维,依靠我们之前讲过的射线的碰撞检测来实现这一功能。这种方法可以说是巧夺天工。下面我来以一个例子说明这一切吧!
首先我们新建一个工程,取名为:BulletProject,保存一下场景,将其命名为Bullet1。我们得搭建一下场景,为了更好地表现子弹碰撞到碰撞器产生碰撞剔除效果,我设计了两个挡板,并且让子弹至于后一个挡板产生碰撞检测。那么我得新建了两个碰撞层:wall1,wall2(关于碰撞层的用法请见我的以前关于碰撞检测方面的帖子),分别设定这两个挡板的层级为以上两个层,其中后面的挡板的layer的名字为:wall1。如下:
为了更形象的表现子弹出膛时的位置,我用到了OnDrawGizmos()函数与Gizmos这个类,这是能够在Scene视图下绘制一些简单几何体的辅助类,用法比较简单,用兴趣的朋友可以在文档上研究一下这个类。新建一个空的GameObject,重命名(F2)为SpawnPoint,适当调整一下位置,使其Z轴正对着那两块挡板。我们写一个简单的脚本:SpawnBulletPoint.cs,如下:
using UnityEngine;
using System.Collections;
public class SpawnBulletPoint : MonoBehaviour {
public float radius = 2f;
void OnDrawGizmos()
{
Gizmos.color = Color.green;//此线框球状物的颜色是红色的
Gizmos.DrawSphere(transform.position, radius);//创建一个线框球形
}
}
然后我们将此脚本保存一下,拖到刚才新建的SpawnPoint上。我们会发现Scene视图下SpawnPoint位置上出现了一个绿色的球状物,但是Game视图下却没有,这正是Gizmos这个类的用处所在,有时候你需要标记很多个特定的点,而这些个点通常都是空的GameObject,你在一般情况下不好找到他们的位置,因此可以适当运用一下这个类来在Scene视图下显式的表现这个GameObject的位置。
我们接下来就是在这个绿球这个位置发射子弹。为此我简单的做了一个子弹,使用圆柱体来表示的,将其拖到Project做成子弹预设,需要写一个脚本:BulletController。
using UnityEngine;
using System.Collections;
public class BulletController : MonoBehaviour {
public float speed = 10f;
public float distance = 0.1f;
private LayerMask mask;
// Use this for initialization
void Start () {
mask = 1 << LayerMask.NameToLayer("wall1");//设定此碰撞检测只与层级名为wall1的GameObject发生
}
// Update is called once per frame
void Update () {
transform.Translate(transform.forward * speed * Time.deltaTime );//子弹不断向前运动
RaycastHit hit;
if(Physics.Raycast(transform.position,transform.forward,out hit,distance,mask))
{//当子弹前方distance距离处击中目标,且此目标属于wall1层,那么执行下列语句
Debug.Log("The bullet hit the target");
Destroy(this.gameObject);
}
}
}
此脚本比较简单,一般人理解起来应该没什么问题。就是在子弹运动的途中,不断在子弹前方distance范围内检测是否发现目标,然后执行相应的行为。我想思路此时再清晰不过了。
最后我们得编写一下子弹出膛的脚本了:SpawnBullet.cs
using UnityEngine;
using System.Collections;
public class SpawnBullet : MonoBehaviour {
public GameObject bullet;
public Transform Spawnpoint;
void OnGUI()
{
if(GUILayout.Button("SpawnBullet"))
{
Instantiate(bullet,Spawnpoint.position,Quaternion.identity);
}
}
}
不必说你也知道,只是克隆出子弹预设。下面我们将这个脚本绑定在摄像机上面,该拖拽的全都拖拽上去。我们的例子大概完成了。运行一下看看:
子弹出膛
子弹穿过第一个挡板,此时控制台上没有打印语句
子弹击中后面的挡板,瞬间消失,且打印出语句。
上面三个情形的结果是意料之中的,重要的是实现手法,我们现在仔细看看,挡子弹击中目标时,我们可以做一些事情,比如让目标减血,并且播放减血的动画。此例子中 只是简单的打印一行语句并销毁子弹。虽然简单,但却很重要,这预示着我们可以再次部分代码调用一些函数,我们看这一部分:
由于此函数我们可以获得hit实例,所以可以根据hit来获得被击中的GameObject,具体做法是
GameObject go = hit.transform.gameObject;
然后就可以调用go身上的某个脚本中的某个函数来执行相应的行为,比如掉血的函数等。而且此部分还运用了LayerMask类,这对于选择性的碰撞检测非常重要,比如我们规定子弹碰到了敌人之后让敌人掉血或死掉,碰到了墙壁之后就播放一个音频,且销毁子弹,碰到了布料后就直接穿过。仔细思考一下,我们可以在此处扩展一下,写一个工具类,然后里面写一个通用的方法,但是要有一个GameObject类型的参数,只要调用这个方法,我们就根据具体传过来的参数来执行相应的代码,我想一个Switch语句就可以吧!我们只需在上面这块代码中调用这个方法,且将hit.transform.gameObject传递到进去就不用再在里面多做些些什么了,只需在那个工具类中书写相应的行为就行了,维护起来特别方便,不是吗?
工具类中我们可以取得传过来的GameObject的tag的具体值来作为switch的判断条件,实现我们可以自定义一些tag,比如Enemy,Player,Wall等tag,然后根据传进来的GameObject并获取其tag来进行相应的case匹配,然后执行相应的动作。好了,这次就先介绍到这了,我将这个例子打个包,一并奉上。
|
|