這篇文章主要介紹了C#/基于Unity 行為樹(shù)的實(shí)現(xiàn)步驟,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)建站是一家集網(wǎng)站建設(shè),鐘山企業(yè)網(wǎng)站建設(shè),鐘山品牌網(wǎng)站建設(shè),網(wǎng)站定制,鐘山網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷,網(wǎng)絡(luò)優(yōu)化,鐘山網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
using BTAI; using UnityEngine; public class TestBT : MonoBehaviour, BTAI.IBTDebugable { Root aiRoot = BT.Root(); private void OnEnable() { aiRoot.OpenBranch( BT.If(TestVisibleTarget).OpenBranch( BT.Call(Aim), BT.Call(Shoot) ), BT.Sequence().OpenBranch( BT.Call(Walk), BT.Wait(5.0f), BT.Call(Turn), BT.Wait(1.0f), BT.Call(Turn) ) ); } private void Turn() { Debug.Log("執(zhí)行了 Turn"); } private void Walk() { Debug.Log("執(zhí)行了 Walk"); } private void Shoot() { Debug.Log("執(zhí)行了 Shoot"); } private void Aim() { Debug.Log("執(zhí)行了 Aim"); } private bool TestVisibleTarget() { var isSuccess = UnityEngine.Random.Range(0, 2) == 1; Debug.Log("執(zhí)行了 TestVisibleTarget Result:" + isSuccess); return isSuccess; } private void Update() { aiRoot.Tick(); } public Root GetAIRoot() { return aiRoot; } }
using System.Collections.Generic; using UnityEngine; ////// 這只是腳本系統(tǒng) /// 行為樹(shù)會(huì)從Root節(jié)點(diǎn)開(kāi)始遍歷子節(jié)點(diǎn)。Update中執(zhí)行 /// 每個(gè)節(jié)點(diǎn)都有相關(guān)的操作,但是基本上就是返回三種狀態(tài) /// ● Success: 節(jié)點(diǎn)成功完成任務(wù) /// ● Failure: 節(jié)點(diǎn)未通過(guò)任務(wù) /// ● Continue:節(jié)點(diǎn)尚未完成任務(wù)。 /// 但是每個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)對(duì)子節(jié)點(diǎn)的結(jié)果處理方式還不同。 例如 /// ● Test 節(jié)點(diǎn): 測(cè)試節(jié)點(diǎn)將調(diào)用其子節(jié)點(diǎn)并在測(cè)試為真時(shí)返回子節(jié)點(diǎn)狀態(tài),如果測(cè)試為假,則返回Failure而不調(diào)用其子節(jié)點(diǎn)。 /// 行為樹(shù)的一種構(gòu)造方式如下: /// Root aiRoot = BT.Root(); /// aiRoot.Do( /// BT.If(TestVisibleTarget).Do( /// BT.Call(Aim), /// BT.Call(Shoot) /// ), /// BT.Sequence().Do( /// BT.Call(Walk), /// BT.Wait(5.0f), /// BT.Call(Turn), /// BT.Wait(1.0f), /// BT.Call(Turn) /// ) /// ); ///然后在Update中 調(diào)用 aiRoot.Tick() 。 剛剛構(gòu)造的行為樹(shù)是怎么樣的檢查過(guò)程呢? ///1、首先檢查TestVisibleTarget是否返回Ture,如果是繼續(xù)執(zhí)行子節(jié)點(diǎn)執(zhí)行Aim函數(shù)和Shoot函數(shù) ///2、TestVisibleTarget是否返回false,if節(jié)點(diǎn)將返回Failure, 然后Root 將轉(zhuǎn)向下一個(gè)子節(jié)點(diǎn)。這是個(gè)Sequence節(jié)點(diǎn),它從執(zhí)行第一個(gè)子節(jié)點(diǎn)開(kāi)始。 /// 1)將調(diào)用Walk函數(shù),直接返回 Success,以便Sequence將下一個(gè)子節(jié)點(diǎn)激活并執(zhí)行它。 /// 2)執(zhí)行Wait 節(jié)點(diǎn),只是要等待5秒,還是第一次調(diào)用,所以肯定返回Running狀態(tài), 當(dāng)Sequence從子節(jié)點(diǎn)上得到Running狀態(tài)時(shí),不會(huì)更改激活的子節(jié)點(diǎn)索引,下次Update的時(shí)候還是從這個(gè)節(jié)點(diǎn)開(kāi)始執(zhí)行 ///3、Update的執(zhí)行,當(dāng)Wait節(jié)點(diǎn)等待的時(shí)間到了的時(shí)候,將會(huì)返回Success, 以便序列將轉(zhuǎn)到下一個(gè)孩子。 ///腳本中的Node列表 /// Sequence: //一個(gè)接一個(gè)地執(zhí)行子節(jié)點(diǎn)。如果子節(jié)點(diǎn)返回: //●Success:Sequence將選擇下一幀的下一個(gè)孩子開(kāi)始。 //●Failure:Sequence將返回到下一幀的第一個(gè)子節(jié)點(diǎn)(從頭開(kāi)始)。 //●Continue:Sequence將在下一幀再次調(diào)用該節(jié)點(diǎn)。 //RandomSequence: // 每次調(diào)用時(shí),從子列表中執(zhí)行一個(gè)隨機(jī)子節(jié)點(diǎn)。您可以在構(gòu)造函數(shù)中指定要應(yīng)用于每個(gè)子項(xiàng)的權(quán)重列表作為int數(shù)組,以使某些子項(xiàng)更有可能被選中。 //Selector : //按順序執(zhí)行所有子項(xiàng),直到一個(gè)返回Success,然后退出而不執(zhí)行其余子節(jié)點(diǎn)。如果沒(méi)有返回Success,則此節(jié)點(diǎn)將返回Failure。 // Condition : // 如果給定函數(shù)返回true,則此節(jié)點(diǎn)返回Success;如果為false,則返回Failure。 // 與其他依賴于子節(jié)點(diǎn)結(jié)果的節(jié)點(diǎn)鏈接時(shí)很有用(例如,Sequence,Selector等) // If : //調(diào)用給定的函數(shù)。 // ●如果返回true,則調(diào)用當(dāng)前活動(dòng)的子級(jí)并返回其狀態(tài)。 // ●否則,它將在不調(diào)用其子項(xiàng)的情況下返回Failure // While: //只要給定函數(shù)返回true,就返回Continue(因此,下一幀將再次從該節(jié)點(diǎn)開(kāi)始,而不會(huì)評(píng)估所有先前的節(jié)點(diǎn))。 //子節(jié)點(diǎn)們將陸續(xù)被執(zhí)行。 //當(dāng)函數(shù)返回false并且循環(huán)中斷時(shí),將返回Failure。 // Call //調(diào)用給定的函數(shù),它將始終返回Success。是動(dòng)作節(jié)點(diǎn)! //Repeat //將連續(xù)執(zhí)行給定次數(shù)的所有子節(jié)點(diǎn)。 //始終返回Continue,直到達(dá)到計(jì)數(shù),并返回Success。 //Wait //將返回Continue,直到達(dá)到給定時(shí)間(首次調(diào)用時(shí)開(kāi)始),然后返回Success。 //Trigger //允許在給定的動(dòng)畫師animator中設(shè)置Trigger參數(shù)(如果最后一個(gè)參數(shù)設(shè)置為false,則取消設(shè)置觸發(fā)器)。始終返回成功。 //SetBool //允許在給定的animator中設(shè)置布爾參數(shù)的值。始終返回成功 //SetActive //設(shè)置給定GameObject的活動(dòng)/非活動(dòng)狀態(tài)。始終返回成功。 /// namespace BTAI { public enum BTState { Failure, Success, Continue, Abort } ////// 節(jié)點(diǎn) 對(duì)象工廠 /// public static class BT { public static Root Root() { return new Root(); } public static Sequence Sequence() { return new Sequence(); } public static Selector Selector(bool shuffle = false) { return new Selector(shuffle); } public static Action RunCoroutine(System.Func> coroutine) { return new Action(coroutine); } public static Action Call(System.Action fn) { return new Action(fn); } public static ConditionalBranch If(System.Func fn) { return new ConditionalBranch(fn); } public static While While(System.Func fn) { return new While(fn); } public static Condition Condition(System.Func fn) { return new Condition(fn); } public static Repeat Repeat(int count) { return new Repeat(count); } public static Wait Wait(float seconds) { return new Wait(seconds); } public static Trigger Trigger(Animator animator, string name, bool set = true) { return new Trigger(animator, name, set); } public static WaitForAnimatorState WaitForAnimatorState(Animator animator, string name, int layer = 0) { return new WaitForAnimatorState(animator, name, layer); } public static SetBool SetBool(Animator animator, string name, bool value) { return new SetBool(animator, name, value); } public static SetActive SetActive(GameObject gameObject, bool active) { return new SetActive(gameObject, active); } public static WaitForAnimatorSignal WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { return new WaitForAnimatorSignal(animator, name, state, layer); } public static Terminate Terminate() { return new Terminate(); } public static Log Log(string msg) { return new Log(msg); } public static RandomSequence RandomSequence(int[] weights = null) { return new BTAI.RandomSequence(weights); } } /// /// 節(jié)點(diǎn)抽象類 /// public abstract class BTNode { public abstract BTState Tick(); } ////// 包含子節(jié)點(diǎn)的組合 節(jié)點(diǎn)基類 /// public abstract class Branch : BTNode { protected int activeChild; protected Listchildren = new List (); public virtual Branch OpenBranch(params BTNode[] children) { for (var i = 0; i < children.Length; i++) this.children.Add(children[i]); return this; } public List Children() { return children; } public int ActiveChild() { return activeChild; } public virtual void ResetChildren() { activeChild = 0; for (var i = 0; i < children.Count; i++) { Branch b = children[i] as Branch; if (b != null) { b.ResetChildren(); } } } } /// /// 裝飾節(jié)點(diǎn) 只包含一個(gè)子節(jié)點(diǎn),用于某種方式改變這個(gè)節(jié)點(diǎn)的行為 /// 比如過(guò)濾器(用于決定是否允許子節(jié)點(diǎn)運(yùn)行的,如:Until Success, Until Fail等),這種節(jié)點(diǎn)的子節(jié)點(diǎn)應(yīng)該是條件節(jié)點(diǎn),條件節(jié)點(diǎn)一直檢測(cè)“視線中是否有敵人”,知道發(fā)現(xiàn)敵人為止。 /// 或者 Limit 節(jié)點(diǎn),用于指定某個(gè)子節(jié)點(diǎn)的最大運(yùn)行次數(shù) /// 或者 Timer節(jié)點(diǎn),設(shè)置了一個(gè)計(jì)時(shí)器,不會(huì)立即執(zhí)行子節(jié)點(diǎn),而是等一段時(shí)間,時(shí)間到了開(kāi)始執(zhí)行子節(jié)點(diǎn) /// 或者 TimerLimit節(jié)點(diǎn),用于指定某個(gè)子節(jié)點(diǎn)的最長(zhǎng)運(yùn)行時(shí)間。 /// 或者 用于產(chǎn)生某個(gè)返回狀態(tài), /// public abstract class Decorator : BTNode { protected BTNode child; public Decorator Do(BTNode child) { this.child = child; return this; } } ////// 順序節(jié)點(diǎn) (從左到右依次執(zhí)行所有子節(jié)點(diǎn),只要子節(jié)點(diǎn)返回Success就繼續(xù)執(zhí)行后續(xù)子節(jié)點(diǎn),直到遇到Failure或者Runing, /// 停止后續(xù)執(zhí)行,并把這個(gè)節(jié)點(diǎn)返回給父節(jié)點(diǎn),只有它的所有子節(jié)點(diǎn)都是Success他才會(huì)向父節(jié)點(diǎn)返回Success) /// public class Sequence : Branch { public override BTState Tick() { var childState = children[activeChild].Tick(); switch (childState) { case BTState.Success: activeChild++; if (activeChild == children.Count) { activeChild = 0; return BTState.Success; } else return BTState.Continue; case BTState.Failure: activeChild = 0; return BTState.Failure; case BTState.Continue: return BTState.Continue; case BTState.Abort: activeChild = 0; return BTState.Abort; } throw new System.Exception("This should never happen, but clearly it has."); } } ////// 選擇節(jié)點(diǎn)從左到右依次執(zhí)行所有子節(jié)點(diǎn) ,只要遇到failure就繼續(xù)執(zhí)行后續(xù)子節(jié)點(diǎn),直到遇到一個(gè)節(jié)點(diǎn)返回Success或Running為止。向父節(jié)點(diǎn)返回Success或Running /// 所有子節(jié)點(diǎn)都是Fail, 那么向父節(jié)點(diǎn)凡湖Fail /// 選擇節(jié)點(diǎn) 用來(lái)在可能的行為集合中選擇第一個(gè)成功的。 比如一個(gè)試圖躲避槍擊的AI角色, 它可以通過(guò)尋找隱蔽點(diǎn), 或離開(kāi)危險(xiǎn)區(qū)域, 或?qū)ふ以榷喾N方式實(shí)現(xiàn)目標(biāo)。 /// 利用選擇節(jié)點(diǎn),他會(huì)嘗試尋找Cover,失敗后在試圖逃離危險(xiǎn)區(qū)域。 /// public class Selector : Branch { public Selector(bool shuffle) { if (shuffle) { var n = children.Count; while (n > 1) { n--; var k = Mathf.FloorToInt(Random.value * (n + 1)); var value = children[k]; children[k] = children[n]; children[n] = value; } } } public override BTState Tick() { var childState = children[activeChild].Tick(); switch (childState) { case BTState.Success: activeChild = 0; return BTState.Success; case BTState.Failure: activeChild++; if (activeChild == children.Count) { activeChild = 0; return BTState.Failure; } else return BTState.Continue; case BTState.Continue: return BTState.Continue; case BTState.Abort: activeChild = 0; return BTState.Abort; } throw new System.Exception("This should never happen, but clearly it has."); } } ////// 行為節(jié)點(diǎn) 調(diào)用方法,或運(yùn)行協(xié)程。完成實(shí)際工作, 例如播放動(dòng)畫,讓角色移動(dòng)位置,感知敵人,更換武器,播放聲音,增加生命值等。 /// public class Action : BTNode { System.Action fn; System.Func> coroutineFactory; IEnumerator coroutine; public Action(System.Action fn) { this.fn = fn; } public Action(System.Func > coroutineFactory) { this.coroutineFactory = coroutineFactory; } public override BTState Tick() { if (fn != null) { fn(); return BTState.Success; } else { if (coroutine == null) coroutine = coroutineFactory(); if (!coroutine.MoveNext()) { coroutine = null; return BTState.Success; } var result = coroutine.Current; if (result == BTState.Continue) return BTState.Continue; else { coroutine = null; return result; } } } public override string ToString() { return "Action : " + fn.Method.ToString(); } } /// /// 條件節(jié)點(diǎn) 調(diào)用方法,如果方法返回true則返回成功,否則返回失敗。 /// 用來(lái)測(cè)試當(dāng)前是否滿足某些性質(zhì)或條件,例如“玩家是否在20米之內(nèi)?”“是否能看到玩家?”“生命值是否大于50?”“彈藥是否足夠?”等 /// public class Condition : BTNode { public System.Funcfn; public Condition(System.Func fn) { this.fn = fn; } public override BTState Tick() { return fn() ? BTState.Success : BTState.Failure; } public override string ToString() { return "Condition : " + fn.Method.ToString(); } } /// /// 當(dāng)方法為True的時(shí)候 嘗試執(zhí)行當(dāng)前 子節(jié)點(diǎn) /// public class ConditionalBranch : Block { public System.Funcfn; bool tested = false; public ConditionalBranch(System.Func fn) { this.fn = fn; } public override BTState Tick() { if (!tested) { tested = fn(); } if (tested) { // 當(dāng)前子節(jié)點(diǎn)執(zhí)行完就進(jìn)入下一個(gè)節(jié)點(diǎn)(超上限就返回到第一個(gè)) var result = base.Tick(); // 沒(méi)執(zhí)行完 if (result == BTState.Continue) return BTState.Continue; else { tested = false; // 最后一個(gè)子節(jié)點(diǎn)執(zhí)行完,才會(huì)為Ture return result; } } else { return BTState.Failure; } } public override string ToString() { return "ConditionalBranch : " + fn.Method.ToString(); } } /// /// While節(jié)點(diǎn) 只要方法 返回True 就執(zhí)行所有子節(jié)點(diǎn), 否則返回 Failure /// public class While : Block { public System.Funcfn; public While(System.Func fn) { this.fn = fn; } public override BTState Tick() { if (fn()) base.Tick(); else { //if we exit the loop ResetChildren(); return BTState.Failure; } return BTState.Continue; } public override string ToString() { return "While : " + fn.Method.ToString(); } } /// /// 阻塞節(jié)點(diǎn) 如果當(dāng)前子節(jié)點(diǎn)是Continue 說(shuō)明沒(méi)有執(zhí)行完,阻塞著,執(zhí)行完之后在繼續(xù)它后面的兄弟節(jié)點(diǎn) 不管成功失敗。 /// 如果當(dāng)前結(jié)點(diǎn)是最后一個(gè)節(jié)點(diǎn)并執(zhí)行完畢,說(shuō)明成功!否則就是處于Continue狀態(tài)。 /// 幾個(gè)基本上是抽象節(jié)點(diǎn), 像是讓所有子節(jié)點(diǎn)都執(zhí)行一遍, 當(dāng)前子節(jié)點(diǎn)執(zhí)行完就進(jìn)入下一個(gè)節(jié)點(diǎn)(超上限就返回到第一個(gè)) /// public abstract class Block : Branch { public override BTState Tick() { switch (children[activeChild].Tick()) { case BTState.Continue: return BTState.Continue; default: activeChild++; if (activeChild == children.Count) { activeChild = 0; return BTState.Success; } return BTState.Continue; } } } public class Root : Block { public bool isTerminated = false; public override BTState Tick() { if (isTerminated) return BTState.Abort; while (true) { switch (children[activeChild].Tick()) { case BTState.Continue: return BTState.Continue; case BTState.Abort: isTerminated = true; return BTState.Abort; default: activeChild++; if (activeChild == children.Count) { activeChild = 0; return BTState.Success; } continue; } } } } ////// 多次運(yùn)行子節(jié)點(diǎn)(一個(gè)子節(jié)點(diǎn)執(zhí)行一次就算一次) /// public class Repeat : Block { public int count = 1; int currentCount = 0; public Repeat(int count) { this.count = count; } public override BTState Tick() { if (count > 0 && currentCount < count) { var result = base.Tick(); switch (result) { case BTState.Continue: return BTState.Continue; default: currentCount++; if (currentCount == count) { currentCount = 0; return BTState.Success; } return BTState.Continue; } } return BTState.Success; } public override string ToString() { return "Repeat Until : " + currentCount + " / " + count; } } ////// 隨機(jī)的順序 執(zhí)行子節(jié)點(diǎn) /// public class RandomSequence : Block { int[] m_Weight = null; int[] m_AddedWeight = null; ////// 每次再次觸發(fā)時(shí),將選擇一個(gè)隨機(jī)子節(jié)點(diǎn) /// /// 保留null,以便所有子節(jié)點(diǎn)具有相同的權(quán)重。 /// 如果權(quán)重低于子節(jié)點(diǎn), 則后續(xù)子節(jié)點(diǎn)的權(quán)重都為1 public RandomSequence(int[] weight = null) { activeChild = -1; m_Weight = weight; } public override Branch OpenBranch(params BTNode[] children) { m_AddedWeight = new int[children.Length]; for (int i = 0; i < children.Length; ++i) { int weight = 0; int previousWeight = 0; if (m_Weight == null || m_Weight.Length <= i) {//如果沒(méi)有那個(gè)權(quán)重, 就將權(quán)重 設(shè)置為1 weight = 1; } else weight = m_Weight[i]; if (i > 0) previousWeight = m_AddedWeight[i - 1]; m_AddedWeight[i] = weight + previousWeight; } return base.OpenBranch(children); } public override BTState Tick() { if (activeChild == -1) PickNewChild(); var result = children[activeChild].Tick(); switch (result) { case BTState.Continue: return BTState.Continue; default: PickNewChild(); return result; } } void PickNewChild() { int choice = Random.Range(0, m_AddedWeight[m_AddedWeight.Length - 1]); for (int i = 0; i < m_AddedWeight.Length; ++i) { if (choice - m_AddedWeight[i] <= 0) { activeChild = i; break; } } } public override string ToString() { return "Random Sequence : " + activeChild + "/" + children.Count; } } ////// 暫停執(zhí)行幾秒鐘。 /// public class Wait : BTNode { public float seconds = 0; float future = -1; public Wait(float seconds) { this.seconds = seconds; } public override BTState Tick() { if (future < 0) future = Time.time + seconds; if (Time.time >= future) { future = -1; return BTState.Success; } else return BTState.Continue; } public override string ToString() { return "Wait : " + (future - Time.time) + " / " + seconds; } } ////// 設(shè)置動(dòng)畫 trigger 參數(shù) /// public class Trigger : BTNode { Animator animator; int id; string triggerName; bool set = true; //如果 set == false, 則重置trigger而不是設(shè)置它。 public Trigger(Animator animator, string name, bool set = true) { this.id = Animator.StringToHash(name); this.animator = animator; this.triggerName = name; this.set = set; } public override BTState Tick() { if (set) animator.SetTrigger(id); else animator.ResetTrigger(id); return BTState.Success; } public override string ToString() { return "Trigger : " + triggerName; } } ////// 設(shè)置動(dòng)畫 boolean 參數(shù) /// public class SetBool : BTNode { Animator animator; int id; bool value; string triggerName; public SetBool(Animator animator, string name, bool value) { this.id = Animator.StringToHash(name); this.animator = animator; this.value = value; this.triggerName = name; } public override BTState Tick() { animator.SetBool(id, value); return BTState.Success; } public override string ToString() { return "SetBool : " + triggerName + " = " + value.ToString(); } } ////// 等待animator達(dá)到一個(gè)狀態(tài)。 /// public class WaitForAnimatorState : BTNode { Animator animator; int id; int layer; string stateName; public WaitForAnimatorState(Animator animator, string name, int layer = 0) { this.id = Animator.StringToHash(name); if (!animator.HasState(layer, this.id)) { Debug.LogError("The animator does not have state: " + name); } this.animator = animator; this.layer = layer; this.stateName = name; } public override BTState Tick() { var state = animator.GetCurrentAnimatorStateInfo(layer); if (state.fullPathHash == this.id || state.shortNameHash == this.id) return BTState.Success; return BTState.Continue; } public override string ToString() { return "Wait For State : " + stateName; } } ////// 設(shè)置 GameObject 的激活狀態(tài) /// public class SetActive : BTNode { GameObject gameObject; bool active; public SetActive(GameObject gameObject, bool active) { this.gameObject = gameObject; this.active = active; } public override BTState Tick() { gameObject.SetActive(this.active); return BTState.Success; } public override string ToString() { return "Set Active : " + gameObject.name + " = " + active; } } ////// 等待animator從SendSignal狀態(tài)機(jī)行為 接收信號(hào)。 SendSignal : StateMachineBehaviour /// public class WaitForAnimatorSignal : BTNode { // 進(jìn)入或退出動(dòng)畫都為 False, 只有執(zhí)行中為True internal bool isSet = false; string name; int id; public WaitForAnimatorSignal(Animator animator, string name, string state, int layer = 0) { this.name = name; this.id = Animator.StringToHash(name); if (!animator.HasState(layer, this.id)) { Debug.LogError("The animator does not have state: " + name); } else { SendSignal.Register(animator, name, this); } } public override BTState Tick() { if (!isSet) return BTState.Continue; else { isSet = false; return BTState.Success; } } public override string ToString() { return "Wait For Animator Signal : " + name; } } ////// 終止節(jié)點(diǎn) 切換到中止 狀態(tài) /// public class Terminate : BTNode { public override BTState Tick() { return BTState.Abort; } } ////// Log 輸出Log 的節(jié)點(diǎn) /// public class Log : BTNode { string msg; public Log(string msg) { this.msg = msg; } public override BTState Tick() { Debug.Log(msg); return BTState.Success; } } } #if UNITY_EDITOR namespace BTAI { public interface IBTDebugable { Root GetAIRoot(); } } #endif
using BTAI; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace Gamekit2D { ////// 運(yùn)行是查看 行為樹(shù)中所有節(jié)點(diǎn)的狀態(tài) /// public class BTDebug : EditorWindow { protected BTAI.Root _currentRoot = null; [MenuItem("Kit Tools/Behaviour Tree Debug")] static void OpenWindow() { BTDebug btdebug = GetWindow(); btdebug.Show(); } private void OnGUI() { if (!Application.isPlaying) { EditorGUILayout.HelpBox("Only work during play mode.", MessageType.Info); } else { if (_currentRoot == null) FindRoot(); else { RecursiveTreeParsing(_currentRoot, 0, true); } } } void Update() { Repaint(); } void RecursiveTreeParsing(Branch branch, int indent, bool parentIsActive) { List nodes = branch.Children(); for (int i = 0; i < nodes.Count; ++i) { EditorGUI.indentLevel = indent; bool isActiveChild = branch.ActiveChild() == i; GUI.color = (isActiveChild && parentIsActive) ? Color.green : Color.white; EditorGUILayout.LabelField(nodes[i].ToString()); if (nodes[i] is Branch) RecursiveTreeParsing(nodes[i] as Branch, indent + 1, isActiveChild); } } void FindRoot() { if (Selection.activeGameObject == null) { _currentRoot = null; return; } IBTDebugable debugable = Selection.activeGameObject.GetComponentInChildren (); if (debugable != null) { _currentRoot = debugable.GetAIRoot(); } } } }
就是在菜單“Kit Tools/Behaviour Tree Debug" 可以查看TestBT.cs 對(duì)象所在行為樹(shù)
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享C#/基于Unity 行為樹(shù)的實(shí)現(xiàn)步驟對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問(wèn)題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!