zoukankan      html  css  js  c++  java
  • [Unity3D]自己动手重制坦克舰队ArmadaTank(2)从碰撞说起

    [Unity3D]自己动手重制坦克舰队ArmadaTank(2)从碰撞说起

    上一篇里我给出了重制的坦克舰队效果图和试玩程序。本篇介绍一下玩家坦克和敌方坦克碰撞问题。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    我们需要什么样的碰撞

    原版里,玩家与其它坦克碰撞时,玩家与对方都不能移动;而敌方坦克之间相互碰撞时,是无视碰撞直接穿透的。这些功能的实现需要一些特殊的设计。

    需要注意到,坦克舰队里的坦克移动方式是以格为单位的,每次移动都会移动完整的1个单位。就是说,坦克在下图所示的D字母里面只有上边和下边两个停留位置,而不会在中间某处停留。

    据此,我们给出实现方法。

    障碍物检测法

    在游戏画面中,看到的坦克是这样的:

    但是为了实现原版的碰撞效果,我们给每个坦克都加上前后左右4个cube作为障碍物检测器(Obstacle Detector),让他们分别检测坦克所在位置的前后左右是否存在障碍物。

    为了避免与子弹碰撞,我们把这些cube压扁了,其高度值只有0.1,而长宽仍为1。

    此外,这些cube最终目的是检测坦克的存在,所以要给坦克本身一个Collider。

    注意,这里我们让敌方坦克自身的Collider半径稍微小于0.5,以便后续的实现。

    而玩家坦克的Collider半径保持0.5不变。

    你会看到,这样的微小差异,会使敌方坦克之间的碰撞检测稍微迟钝一点,所以他们就可以相互穿透;而敌方坦克与玩家坦克之间仍然不可穿透。

    Cube的作用

    cube的作用是这样的:每个cube内部维护一个List<GameObject> obstacles列表,这个列表记录此检测器遇到的所有障碍物。每当触发OnTriggerEnter(Collider other)时,就把other指定的物体(障碍物)加入obstacles列表;每当触发OnTriggerExit(Collider other)时,就把other指定的物体(障碍物)移出obstacles列表。这样,就可以通过分析obstacles里的情况判断出此cube代表的方向(前后左右之一)是否有障碍物了。

     1 public abstract class ObstacleDetector : MonoBehaviour {
     2 
     3     public System.Collections.Generic.List<GameObject> obstacles;
     4 
     5     void Awake()
     6     {
     7         if (obstacles == null)
     8         {
     9             obstacles = new System.Collections.Generic.List<GameObject>();
    10         }
    11     }
    12 
    13     protected abstract void OnTriggerEnter(Collider other);
    14 
    15     void OnTriggerExit(Collider other)
    16     {
    17         this.obstacles.Remove(other.gameObject);
    18     }
    19 
    20     public bool IsUnblocked()
    21     {
    22         for (int i = 0; i < this.obstacles.Count; i++)
    23         {
    24             if(this.obstacles[i] != null && this.obstacles[i].collider.enabled)
    25             {
    26                 return false;
    27             }
    28         }
    29         return true;
    30     }
    31 } 

    敌方坦克的障碍物检测器

    所有类型的敌方坦克的碰撞效果都相同,所以统一用下面的脚本:

     1 public class EnemyObstacleDetector : ObstacleDetector {
     2 
     3     protected override void OnTriggerEnter(Collider other)
     4     {
     5         var tag = other.tag;
     6         if (tag != null)
     7         {
     8             if (tag == Tags.EnemyObstacleDetector) { return; }
     9             if (tag == Tags.PlayerObstacleDetector) { return; }
    10             // 对敌方坦克来说,另一个敌方坦克是可以穿透过去的,所以不应加入obstacles列表。
    11             if (tag == Tags.EnemyObstacleDetectorCenter) { return; }
    12         }
    13 
    14         this.obstacles.Add(other.gameObject);
    15     }
    16 }

    对敌方坦克来说,另一个敌方坦克是可以穿透过去的,所以不应加入obstacles列表。

    如果碰到的是别人的cube(障碍物检测器),直接忽略即可。

    玩家坦克的障碍物检测器

    玩家坦克与敌方坦克的碰撞效果不同,所以需要单独处理。

     1 public class PlayerObstacleDetector : ObstacleDetector
     2 {
     3 
     4     protected override void OnTriggerEnter(Collider other)
     5     {
     6         var tag = other.tag;
     7         if (tag != null)
     8         {
     9             if (tag == Tags.EnemyObstacleDetector) { return; }
    10             if (tag == Tags.PlayerObstacleDetector) { return; }
    11         }
    12 
    13         this.obstacles.Add(other.gameObject);
    14     }
    15 
    16 }

    对玩家坦克来说,其它坦克是不能穿透的。

    同样,如果碰到的是别人的cube(障碍物检测器),直接忽略即可。

    继承与GetComponent

    ObstacleDetector是一个抽象基类,敌方坦克和玩家坦克分别使用的是其子类EnemyObstacleDetectorPlayerObstacleDetector。此时,GetComponent<T>()仍然可以正常使用。它返回一个基类对象的引用,此引用实际指向的则是某个子类的对象。

    这是一个通用的知识。

     1 public class TankTranslate : MonoBehaviour
     2 {
     3     private System.Collections.Generic.Dictionary<TankToward, ObstacleDetector> obstacleDetectorDict;
     4 
     5     // Use this for initialization
     6     void Start()
     7     {
     8         if (obstacleDetectorDict == null)
     9         { obstacleDetectorDict = new System.Collections.Generic.Dictionary<TankToward, ObstacleDetector>(); }
    10         var names = new string[] { "forward", "backward", "left", "right" };
    11         var direction = new TankToward[] { TankToward.Z, TankToward.NZ, TankToward.NX, TankToward.X };
    12         for (int i = 0; i < names.Length; i++)
    13         {
    14             var child = this.transform.FindChild(names[i]);
    15             var script = child.GetComponent<ObstacleDetector>();
    16             obstacleDetectorDict.Add(direction[i], script);
    17         }
    18     }
    19 } 

    起火冒烟

    坦克被击中会损失health值,视觉效果上我们用冒烟的浓度显示。

    冒烟效果用particle System组件来做。

    控制冒烟的浓度需要一个脚本。

     1 public class TankSmoke : MonoBehaviour
     2 {
     3     public TankHealth tankHealthScript;
     4     private float lastHealth;
     5 
     6     // Update is called once per frame
     7     void Update()
     8     {
     9         if (lastHealth != tankHealthScript.health)
    10         {
    11             var lostHealth = (TankHealth.maxHealth - tankHealthScript.health);
    12             if (lostHealth <= 0)
    13             {
    14                 this.particleSystem.enableEmission = false;
    15             }
    16             else
    17             {
    18                 this.particleSystem.Play();
    19                 this.particleSystem.enableEmission = true;
    20                 this.particleSystem.emissionRate = 50 * lostHealth / TankHealth.maxHealth;
    21             }
    22             lastHealth = tankHealthScript.health;
    23         }
    24     }
    25 }

    原版坦克舰队里的冒烟素材图片是这样的:

    为了正常显示,需要设置其Shader为Mobile Particles/Alpha Blended,并设置Tiling和Offset如下图所示。

    如果您不了解Tiling和Offset的含义,可以参考这里(图文详解Unity3D中Material的Tiling和Offset是怎么回事)。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    如果您需要项目源码请通过下方二维码捐赠10元并留下您的联系方式。

    ps:感谢上次捐赠的人,虽然只有1人。此文作为回馈,但愿您满意。

    pps:我更换了博客皮肤,自我感觉比较良好。

  • 相关阅读:
    实现粘贴WORD图片的在线编辑器
    js实现大文件分片上传的工具
    Nginx实现大文件分片上传的方法
    百度WebUploader实现大文件分片上传的方法
    ckeditor粘贴上传图片
    umediter实现粘贴word图片
    java.net.Socket
    matlab创建对角型矩阵
    QtList
    matlab矩阵行最简形
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/tank-hero-2-collision.html
Copyright © 2011-2022 走看看