各位看官老爺們,這裏是RuaiRuai工做室,一個作單機遊戲的興趣做坊。git
本文對該2D項目中戰鬥底層組件的開發及設計思路作一個總結,但願各路同行多多交流,各路大佬多多指點。github
實例特徵分析數組
首先對於各個可以參加戰鬥的實例來講,好比能主動攻擊的主角、可以和主角戰鬥的怪物,都須要一種手段(component in unity)來施加"攻擊"這個語義的動做,以及受到來自攻擊者的"被攻擊"的動做。故筆者初步設計一個組件來表達這兩種語義。ide
接着,一個顯然的問題出現了,單一職責原則,戰鬥組件承擔了攻擊函數的調用和受傷函數的調用兩種邏輯語義,那有沒有一種需求,只須要攻擊需求而不須要受擊需求呢,顯然,好比只能捱打的寶箱、只能攻擊的機關等等。因而,設計變成了這樣:函數
攻擊組件提供對單體傷害、對範圍內傷害、擊退、施加異常效果、施加元素攻擊等基本攻擊接口。spa
受擊組件擁有一個記錄當前幀全部攻擊信息的棧,以供高層組件調用,同時提供受擊、以及棧操做等接口。設計
類圖以下code
小做坊,意思到了就行2333component
這樣,在敵人的AI腳本中,或者是主角的技能腳本中,就能夠調用相應的函數來執行不一樣的技能邏輯傷害效果。orm
最後給出代碼:
using UnityEngine; public class CanFight : MonoBehaviour { /// <summary> /// CanFight類中範圍攻擊最多一次能夠攻擊多少個目標 /// </summary> public const int ENMEIES_MAX_NUM_ONEATTACK = 32; /// <summary> /// 檢測敵人碰撞體的篩選器,做爲Collider2D.OverlapLayer()的第一個參數。具體做用見Unity API。 /// </summary> private ContactFilter2D filter = new ContactFilter2D(); private bool isInitialized = false; /// <summary> /// 構造函數,初始化篩選器的配置 /// </summary> public void Initiailize(string[] layerNames) { filter.useNormalAngle = false; filter.useDepth = false; filter.useOutsideDepth = false; filter.useOutsideNormalAngle = false; filter.useTriggers = false; filter.useLayerMask = true; LayerMask targetLayer = 0; foreach(string layername in layerNames) { targetLayer ^= 1 << LayerMask.NameToLayer(layername); } //Debug.Log("在" + gameObject.name + "中,可攻擊到的層爲" + System.Convert.ToString(targetLayer,2)); filter.layerMask = targetLayer; //32個bit表示32個層,左移表示篩選須要哪一個層 isInitialized = true; } /// <summary> /// 對一個可以戰鬥的目標形成傷害,做爲底層私有函數被調用 /// </summary> /// <param name="target">形成傷害的目標</param> /// <param name="damage">形成輸入數值的傷害</param> /// <param name="interruptType">攻擊打斷類型,默認爲無打斷</param> /// <returns>返回形成了多少傷害,具體用法有待進一步討論</returns> public int Attack(CanBeFighted target, int damage, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL) { if(!isInitialized) { Debug.LogError("在" + gameObject.name + "物體中,CanFight組件未初始化!"); } return target.BeAttacked(gameObject, damage, interruptType, element); } /// <summary> /// 範圍性攻擊,實現方法爲輸入表示範圍的Collier2D,檢測範圍內的每一個擁有CanBeFighted的敵人,調用BeAttacked /// </summary> /// <param name="area">表示攻擊範圍的collier2d, 應該爲trigger態</param> /// <param name="damage">該次範圍攻擊形成了多少傷害</param> /// <param name="interruptType">該次攻擊爲什麼種打斷類型</param> /// <returns>返回攻擊到的敵人對象的CanBeFighted組件的數組</returns> public CanBeFighted[] AttackArea(Collider2D area, int damage, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL) { //輸入範圍須要Trigger才行 if(!area.isTrigger) { Debug.LogError("在" + gameObject.name + "釋放範圍攻擊時,輸入的collider2d並非trigger態"); return null; } Collider2D[] enemies = new Collider2D[ENMEIES_MAX_NUM_ONEATTACK]; int enemiesNumber = area.OverlapCollider(filter, enemies); if(enemiesNumber != 0) { Debug.Log("攻擊碰到敵人"); CanBeFighted[] enemiesAttacked = new CanBeFighted[enemiesNumber]; CanBeFighted enemyBody; //對碰到的敵人進行如下操做,若是敵人有CanBeFighted組件,則施加攻擊,不然報錯 for(int i = 0; i < enemiesNumber; i++) { if (enemies[i].TryGetComponent<CanBeFighted>(out enemyBody)) { Attack(enemyBody, damage, AttackInterruptType.NONE, element); enemiesAttacked[i] = enemyBody; } else { Debug.LogError("在" + gameObject.name + "釋放範圍攻擊時,這些物體被檢測爲敵人,可是沒有CanBeFighted組件" + enemies[i].gameObject.name); } } return enemiesAttacked; } return null; } public CanBeFighted[] AttackArea(Collider2D area, int damage, CanBeFighted[] hasAttacked, AttackInterruptType interruptType = AttackInterruptType.NONE, ElementAbilityManager.Element element = ElementAbilityManager.Element.NULL) { //輸入範圍須要Trigger才行 if (!area.isTrigger) { Debug.LogError("在" + gameObject.name + "釋放範圍攻擊時,輸入的collider2d並非trigger態"); return null; } Collider2D[] enemies = new Collider2D[ENMEIES_MAX_NUM_ONEATTACK]; int enemiesNumber = area.OverlapCollider(filter, enemies); if (enemiesNumber != 0) { Debug.Log("攻擊碰到敵人"); CanBeFighted[] enemiesAttacked = new CanBeFighted[enemiesNumber]; CanBeFighted enemyBody; //對碰到的敵人進行如下操做,若是敵人有CanBeFighted組件,則施加攻擊,不然報錯 for (int i = 0; i < enemiesNumber; i++) { if (enemies[i].TryGetComponent<CanBeFighted>(out enemyBody)) { Attack(enemyBody, damage, AttackInterruptType.NONE, element); enemiesAttacked[i] = enemyBody; } else { Debug.LogError("在" + gameObject.name + "釋放範圍攻擊時,這些物體被檢測爲敵人,可是沒有CanBeFighted組件" + enemies[i].gameObject.name); } } return enemiesAttacked; } return null; }
整個項目原型github地址:
www.gitHub.com/yunshiyue/elementgame
看官有何看法,有何指點,歡迎留言,也歡迎私聊~