[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:我更換了博客皮膚,自我感覺比較良好。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM