[Unity3D]自己動手重制坦克艦隊ArmadaTank(2)從碰撞說起
在上一篇里我給出了重制的坦克艦隊效果圖和試玩程序。本篇介紹一下玩家坦克和敵方坦克碰撞問題。
我們需要什么樣的碰撞
原版里,玩家與其它坦克碰撞時,玩家與對方都不能移動;而敵方坦克之間相互碰撞時,是無視碰撞直接穿透的。這些功能的實現需要一些特殊的設計。
需要注意到,坦克艦隊里的坦克移動方式是以格為單位的,每次移動都會移動完整的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是一個抽象基類,敵方坦克和玩家坦克分別使用的是其子類EnemyObstacleDetector和PlayerObstacleDetector。此時,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是怎么回事)。
如果您需要項目源碼請通過下方二維碼捐贈10元並留下您的聯系方式。
ps:感謝上次捐贈的人,雖然只有1人。此文作為回饋,但願您滿意。
pps:我更換了博客皮膚,自我感覺比較良好。