查看: 66624|回复: 185

【飞机大战】开发手记(四)碰撞检测

[复制链接]

1

主题

342

帖子

7万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
75866
发表于 2014-9-4 05:05:31 | 显示全部楼层 |阅读模式
虽然有了子弹和敌我双方飞机,但它们好像存在平行世界里一样没有交集。这个时候就需要做碰撞检测了,在《飞机大战》游戏中一共有三种碰撞关系需要检测:
  • 敌方机体和我方机体碰撞,我方机体受到伤害。
  • 导弹和敌方机体碰撞,敌方机体受到伤害。
  • 我方机体和道具碰撞,触发相应的道具效果。
本篇暂时只做前两条的碰撞检测,剩下的原理是一样的,一并放到后面的道具篇里记录。
在开始之前需要做些准备工作,因为在unity中,父对象是无法获知子对象的碰撞事件的,所以需要用一个碰撞事件分发器来发布碰撞事件消息。在游戏开始时,父对象上的脚本遍历并订阅所有子对象上的碰撞事件。当子对象发生碰撞时,父对象就可以针对相应的事件进行处理。



namespace Frederick.ProjectAircraft
{
    using UnityEngine;

    /// <summary>
    /// 碰撞事件分发器。
    /// </summary>
    public class CollisionEventDispatcher : MonoBehaviour
    {
        /// <summary>
        /// 碰撞事件处理器。
        /// </summary>
        ///碰撞信息
        public delegate void CollisionEventHandler(Collision collision);

        /// <summary>
        /// 触发器碰撞事件处理器。
        /// </summary>
        ///发送事件的游戏对象
        ///触发事件的另外一个碰撞器
        public delegate void TriggerEventHandler(GameObject sender, Collider other);

        /// <summary>
        /// 当有触发器进入时候触发此事件。
        /// </summary>
        public event TriggerEventHandler TriggerEntered;

        protected void OnTriggerEnter(Collider other)
        {
            if (TriggerEntered != null)
                TriggerEntered(gameObject, other);
        }
    }
}


飞机的子对象只需要保持对父对象的引用就可以了,由于需要做碰撞检测,所以还需要加上对刚体组件的要求(Rigidbody)。


namespace Frederick.ProjectAircraft
{
    using UnityEngine;

    /// <summary>
    /// 飞机的一部分。
    /// </summary>
    [RequireComponent(typeof (CollisionEventDispatcher))]
    [RequireComponent(typeof (Rigidbody))]
    public class AircraftPart : MonoBehaviour
    {
        /// <summary>
        /// 获取或设置所属的飞机对象。
        /// </summary>
        public Aircraft Aircraft { get; set; }
    }
}

飞机的父对象在启动时获取所有子部件,并订阅其碰撞事件,统一到OnPartTriggerEnter这个虚方法里处理。这样就可以在子类中重写碰撞逻辑了。


namespace Frederick.ProjectAircraft
{
    using System.Collections.Generic;
    using UnityEngine;

    /// <summary>
    /// 飞机。
    /// </summary>
    public class Aircraft : MonoBehaviour
    {
        /// <summary>
        /// 获取飞机所有的部分。
        /// </summary>
        public IEnumerable<AircraftPart> Parts
        {
            get { return mParts.AsReadOnly(); }
        }

        protected virtual void Awake()
        {
            var self = GetComponent<AircraftPart>();
            var children = GetComponentsInChildren<AircraftPart>();
            if (self != null)
                mParts.Add(self);
            mParts.AddRange(children);
            foreach (var part in mParts)
            {
                part.Aircraft = this;
                var dispatcher = part.GetComponent<CollisionEventDispatcher>();
                dispatcher.TriggerEntered += OnPartTriggerEnter;
            }
        }

        protected virtual void OnDestroy()
        {
            foreach (var part in mParts)
            {
                part.Aircraft = null;
                var dispatcher = part.GetComponent<CollisionEventDispatcher>();
                dispatcher.TriggerEntered -= OnPartTriggerEnter;
            }
        }

        /// <summary>
        /// 当飞机的一部分触发碰撞时调用此方法。
        /// </summary>
        /// <param name="part碰撞的部分</param>
        /// <param name="other碰撞的碰撞器</param>
        protected virtual void OnPartTriggerEnter(GameObject part, Collider other)
    {
        Debug.Log(string.Format("OnPartTriggerEnter:part={0} other={1}", part.transform.parent.name, other.transform.parent.name));
    }

        private readonly List<AircraftPart> mParts = new List<AircraftPart>();
    }
}


将Aircraft脚本分别附加到我方飞机和敌方飞机的Body上,点击运行游戏,此时敌方飞机还没有移动的逻辑,所以在场景视图中手动拖动到我方飞机上。在控制台中会立即打印出碰撞调试信息。
【飞机大战】开发手记(四)碰撞检测
以上是从飞机机体的角度进行碰撞检测,下面为导弹和机体之间增加碰撞检测。 选中敌方机体的Body,并在检视面板中设置标记(Tag)为Enemy。由于这个标记是自定义的,所以首先需要点击“增加标记(Add Tag...)”在标记管理器面板(Tag Manager)中新增。设置成功后别忘了还要把预设Apply一下,将标记应用到其他敌方机体上。
【飞机大战】开发手记(四)碰撞检测

接下来就可以修改Missile脚本,在导弹中进行碰撞检测了。




namespace Frederick.ProjectAircraft
{
    using UnityEngine;

    /// <summary>
    /// 导弹。
    /// </summary>
    [RequireComponent(typeof (BoxCollider))]
    public class Missile : MonoBehaviour
    {
        public float StantardSpeed = 800;

        protected void Awake()
        {
            mTransform = transform;
        }

        protected void OnTriggerEnter(Collider other)
        {
            if (other.tag != "Enemy")
                return;
            var part = other.GetComponent<AircraftPart>();
            Debug.Log(string.Format("{0} hit!", part.Aircraft.name));
        }

        protected void Update()
        {
            var position = mTransform.localPosition + mDirection * StantardSpeed * Time.deltaTime;
            if (position.y > 960)
            {
                Destroy(gameObject);
                return;
            }
            mTransform.localPosition = position;
        }

        private readonly Vector3 mDirection = Vector3.up;
        private Transform mTransform;
    }
}

点击运行游戏,此时当导弹与敌机碰撞后,导弹就会销毁,并打印出命中的调试信息了。那么下一篇就准备将之前的调试信息删除,然后换上生命和伤害计算的逻辑吧。
【飞机大战】开发手记(四)碰撞检测







回复

使用道具 举报

fgutrhdp2y 该用户已被删除
发表于 2014-12-28 03:05:48 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

1199

帖子

3852

积分

vip会员

Rank: 1

积分
3852
发表于 2015-4-6 17:04:56 来自手机 | 显示全部楼层
锄禾日当午,发帖真辛苦。谁知坛中餐,帖帖皆辛苦!
回复 支持 反对

使用道具 举报

追忆似水年华 该用户已被删除
发表于 2015-4-10 13:33:17 来自手机 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

天亦有情 该用户已被删除
发表于 2015-4-11 00:03:26 来自手机 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

0

主题

1289

帖子

4023

积分

vip会员

Rank: 1

积分
4023
发表于 2015-4-11 10:06:18 来自手机 | 显示全部楼层
确实不错,顶先
回复 支持 反对

使用道具 举报

0

主题

1240

帖子

3897

积分

vip会员

Rank: 1

积分
3897
发表于 2015-4-12 00:35:05 来自手机 | 显示全部楼层
支持,这个很稀有的说
回复 支持 反对

使用道具 举报

0

主题

1273

帖子

4057

积分

vip会员

Rank: 1

积分
4057
发表于 2015-4-12 13:27:19 来自手机 | 显示全部楼层
有空一起交流一下
回复 支持 反对

使用道具 举报

海豚的心事 该用户已被删除
发表于 2015-4-14 14:45:55 来自手机 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

神话 该用户已被删除
发表于 2015-4-16 16:02:48 来自手机 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 支持 反对

使用道具 举报

*滑块验证:
您需要登录后才可以回帖 登录 | enginedx注册

本版积分规则

 
 



邮件留言:


 
返回顶部