行为树

作者:追风剑情 发布于:2016-2-17 15:46 分类:Unity3d

111111.png

=====================行为树(Behavior Tree)框架代码======================

父节点:


using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 父节点
/// </summary>
public class BehaviorTreeNode : MonoBehaviour, IBehaviourTreeNode
{
    public int weight; //加权

    [HideInInspector]
    protected RunStatus runStatus = RunStatus.Success;

    public virtual RunStatus Run()
    {
        return runStatus;
    }

    public void Success()
    {
        runStatus = RunStatus.Success;
    }

    public void Failure()
    {
        runStatus = RunStatus.Failure;
    }

    public void Running()
    {
        runStatus = RunStatus.Running;
    }

    public RunStatus GetRunStatus()
    {
        return runStatus;
    }

    protected static void RandSortList(List<BehaviorTreeNode> list)
    {
        int size = list.Count;
        int seed = 10000;

        System.Random rand = new System.Random();
        for (int i = 0; i < size; i++)
        {
            int randIndex = (int)(rand.Next(seed) % (size - i)) + i;
            BehaviorTreeNode j = list[i];
            list[i] = list[randIndex];
            list[randIndex] = j;
        }
    }
}


根节点:


using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 根结点
/// </summary>
public class BehaviorTreeRoot : MonoBehaviour {

    public List<BehaviorTreeNode> childList;

    // 每隔多少帧驱动一次行为树
    public int updatePerFrame = 0;

    private int frame = 0;

	void Update () {
        if (updatePerFrame <= 0)
        {
            Run();
        }
        else
        {
            frame = frame++ % updatePerFrame;
            if(frame == 0)
                Run();
        }
	}

    void Run()
    {
        for (int i = 0; i < childList.Count; i++)
        {
            childList[i].Run();
        }
    }
}


组合节点:

(1) 顺序节点


using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 决策节点: 顺序结点 (逻辑与)
/// </summary>
public class SequenceNode : BehaviorTreeNode
{
    public enum SequenceType
    {
        NormalSequence,   //按标准顺序迭代子节点
        RandomSequence    //按随机顺序迭代子节点
    }

    public SequenceType sequenceType;

    public List<BehaviorTreeNode> childList;
    
    private int currentIndex = 0;

    public override RunStatus Run()
    {
        if (null == childList)
            return RunStatus.Success;

        if (sequenceType == SequenceType.RandomSequence)
        {
            if (runStatus != RunStatus.Running)
                RandSortList(childList);
        }

        IterateChilds();

        return runStatus;
    }

    private void IterateChilds()
    {
        for (int i = currentIndex; i < childList.Count; i++)
        {
            runStatus = childList[i].Run();

            if (runStatus == RunStatus.Success)
            {
                currentIndex = 0;
            }
            else if (runStatus == RunStatus.Failure)
            {
                currentIndex = 0;
                break;
            }
            else if (runStatus == RunStatus.Running)
            {
                currentIndex = i;
                break;
            }
        }
    }
}


(2) 选择节点


using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 决策节点: 选择结点 (逻辑或)
/// </summary>
public class SelectorNode : BehaviorTreeNode
{
    public enum SelectorType
    {
        NormalSelector,         //标准选择器
        WeightRandomSelector,   //加权随机选择器 (第一个子节点随机选择)
        WeightRandomSequence    //序列随机选择器 (每次按不同的顺序执行子节点)
    }

    public SelectorType selectorType;

    public List<BehaviorTreeNode> childList;

    private int weightSum = -1;
    private int currentIndex = 0;

    public override RunStatus Run()
    {
        if (null == childList)
            return RunStatus.Success;

        switch (selectorType)
        {
            case SelectorType.NormalSelector:
                NormalSelector();
                break;
            case SelectorType.WeightRandomSelector:
                WeightRandomSelector();
                break;
            case SelectorType.WeightRandomSequence:
                WeightRandomSequence();
                break;
        }

        return runStatus;
    }

    private void WeightRandomSelector()
    {
        if (CurrentNodeRunning())
            return;

        if (weightSum == -1)
        {
            for (int i = 0; i < childList.Count; i++)
                weightSum += childList[i].weight;
        }

        // 按加权随机执行第一个子节点
        System.Random rand = new System.Random();
        int randWeight = rand.Next(weightSum);

        int ignoreIndex = -1;
        for (int i = 0; i < childList.Count; i++)
        {
            if (randWeight <= childList[i].weight)
            {
                if (runStatus == RunStatus.Success)
                {
                    currentIndex = 0;
                    return;
                }
                else if (runStatus == RunStatus.Failure)
                {
                    currentIndex = 0;
                    ignoreIndex = i;
                    break;
                }
                else if (runStatus == RunStatus.Running)
                {
                    currentIndex = i;
                    return;
                }
            }
        }

        // 按顺序执行其他子节点
        IterateChilds(ignoreIndex);
    }

    private void WeightRandomSequence()
    {
        if (runStatus != RunStatus.Running)
            RandSortList(childList);

        IterateChilds();
    }

    private void NormalSelector()
    {
        IterateChilds();
    }

    private bool CurrentNodeRunning()
    {
        // 如果处于Running状态,则继续调用当前节点。
        if (runStatus == RunStatus.Running)
        {
            runStatus = childList[currentIndex].Run();
            if (runStatus == RunStatus.Running)
                return true;
        }
        return false;
    }

    private void IterateChilds(int ignoreIndex = -1)
    {
        for (int i = currentIndex; i < childList.Count; i++)
        {
            if (i == ignoreIndex)
                continue;

            runStatus = childList[i].Run();

            if (runStatus == RunStatus.Success)
            {
                currentIndex = 0;
                break;
            }
            else if (runStatus == RunStatus.Failure)
            {
                currentIndex = 0;
            }
            else if (runStatus == RunStatus.Running)
            {
                currentIndex = i;
                break;
            }
        }
    }
}


(3) 并行节点


using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// 决策节点: 并行结点
/// 返回值由返回策略决定
/// </summary>
public class ParallelNode : BehaviorTreeNode
{
    // 返回策略
	public enum Strategy
    {
        Sequence,   //有一个子节点返回False则返回False,否则返回True。
        Selector,   //有一个子节点返回True则返回True,否则返回False。
        FailureAll, //所有子节点False才返回False,否则返回True。
        SuccessAll, //所有子节点True才返回True,否则返回False。
        Hybird      //指定数量的子节点返回True或False后,才决定结果。
    }

    public Strategy strategy;

    //有hybirdCount个子节点返回hybird,则返回True,否则返回False
    public RunStatus hybird = RunStatus.Success;
    public int hybirdCount = 1;

    public List<BehaviorTreeNode> childList;

    public override RunStatus Run()
    {
        switch (strategy)
        {
            case Strategy.Sequence:     runStatus = Sequence();     break;
            case Strategy.Selector:     runStatus = Selector();     break;
            case Strategy.FailureAll:   runStatus = FailureAll();   break;
            case Strategy.SuccessAll:   runStatus = SuccessAll();   break;
            case Strategy.Hybird:       runStatus = Hybird();       break;
        }
        return runStatus;
    }

    private RunStatus Sequence()
    {
        RunStatus status = RunStatus.Success;
        for (int i = 0; i < childList.Count; i++)
        {
            if (childList[i].Run() == RunStatus.Failure)
                status = RunStatus.Failure;
        }
        return status;
    }

    private RunStatus Selector()
    {
        RunStatus status = RunStatus.Success;
        for (int i = 0; i < childList.Count; i++)
        {
            if (childList[i].Run() == RunStatus.Success)
                status = RunStatus.Success;
        }
        return status;
    }

    private RunStatus FailureAll()
    {
        RunStatus status = RunStatus.Success;
        for (int i = 0; i < childList.Count; i++)
        {
            if (childList[i].Run() == RunStatus.Success)
                status = RunStatus.Failure;
        }
        return status;
    }

    private RunStatus SuccessAll()
    {
        RunStatus status = RunStatus.Success;
        for (int i = 0; i < childList.Count; i++)
        {
            if (childList[i].Run() == RunStatus.Failure)
                status = RunStatus.Failure;
        }
        return status;
    }

    private RunStatus Hybird()
    {
        int num = 0;
        for (int i = 0; i < childList.Count; i++)
        {
            if (childList[i].Run() == hybird)
                num++;
        }
        return num >= hybirdCount ? RunStatus.Success : RunStatus.Failure;
    }
}


行为节点:


using UnityEngine;

/// <summary>
/// 行为结点 (叶节点)
/// </summary>
public class ActionNode : BehaviorTreeNode
{
    public GameObject eventReceiver;
    public string functionName = "OnAction";

    public override RunStatus Run()
    {
        eventReceiver.SendMessage(functionName, this, SendMessageOptions.DontRequireReceiver);
        return runStatus;
    }
}


条件节点:

(1)  一般条件节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 行为结点 (叶节点)
/// 返回判断结果
/// </summary>
public class ConditionNode : BehaviorTreeNode
{
    public GameObject eventReceiver;
    public string functionName = "OnCondition";

    public override RunStatus Run()
    {
        eventReceiver.SendMessage(functionName, this, SendMessageOptions.DontRequireReceiver);
        return runStatus;
    }
}


(2) 前置条件节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 前置条件 (只有当前置条件返回Success时,下面的行为结点才会被调用)
/// </summary>
public class WithPrecondition : BehaviorTreeNode
{
    public GameObject eventReceiver;
    public string functionName = "OnWithPrecondition";
    public BehaviorTreeNode actionNode;

    public override RunStatus Run()
    {
        runStatus = RunStatus.Failure;

        eventReceiver.SendMessage(functionName, this, SendMessageOptions.DontRequireReceiver);
        if (runStatus == RunStatus.Success)
            runStatus = actionNode.Run();

        return runStatus;
    }
}


装饰结点: 这类节点有很多,可根据项目需要自行增加。

(1) 等待节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 等待结点
/// </summary>
public class WaitNode : BehaviorTreeNode
{
    public float waitSecond;//等待时间(单位: 秒)
    public BehaviorTreeNode node;

    private float lastTime;

    public override RunStatus Run()
    {
        if (Time.time - lastTime < waitSecond)
            return RunStatus.Failure;

        lastTime = Time.time;
        runStatus = node.Run();

        return runStatus;
    }
}


(2) 逻辑非节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 装饰节点
/// 逻辑非
/// </summary>
public class DecoratorNode : BehaviorTreeNode
{
    public override RunStatus Run()
    {
        //TODO:: 待实现

        return runStatus;
    }
}


(3) 计数节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 装饰节点
/// 计数
/// </summary>
public class DecoratorCounter : BehaviorTreeNode
{
    public override RunStatus Run()
    {
        //TODO:: 待实现

        return runStatus;
    }
}


(4) 直到失败节点


using UnityEngine;
using System.Collections;

/// <summary>
/// 装饰节点
/// 直到子结点返回失败才返回True
/// </summary>
public class DecoratorFailUtil : BehaviorTreeNode
{
    public override RunStatus Run()
    {
        //TODO:: 待实现

        return runStatus;
    }
}



NPC示例脚本:


using UnityEngine;
using System.Collections;

public class NPCAction : MonoBehaviour {

    #region 巡逻
    // 前置条件
    void PreconditionPatrol(IBehaviourTreeNode node)
    {
        if (Input.GetKeyDown(KeyCode.P))
            node.Success();
        else
            node.Failure();
    }

    // 行为
    void Patrol(IBehaviourTreeNode node)
    {
        node.Running();
        Debug.Log("Patrol: RunStatus=" + node.GetRunStatus());
    }
    #endregion

    #region 逃跑
    // 前置条件
    void PreconditionEscape(IBehaviourTreeNode node)
    {
        if (Input.GetKeyDown(KeyCode.E))
            node.Success();
        else
            node.Failure();
    }

    // 行为
    void Escape(IBehaviourTreeNode node)
    {
        node.Running();
        Debug.Log("Escape: RunStatus=" + node.GetRunStatus());
    }
    #endregion

    #region 攻击
    // 前置条件
    void PreconditionAttack(IBehaviourTreeNode node)
    {
        if (Input.GetKeyDown(KeyCode.A))
            node.Success();
        else
            node.Failure();
    }

    // 行为
    void Attack(IBehaviourTreeNode node)
    {
        node.Running();
        Debug.Log("Attack: RunStatus=" + node.GetRunStatus());
    }
    #endregion

    #region 回血
    void AddHP1(IBehaviourTreeNode node)
    {
        node.Success();
        Debug.Log("AddHP1");
    }

    void AddHP2(IBehaviourTreeNode node)
    {
        node.Success();
        Debug.Log("AddHP2");
    }

    void AddHP3(IBehaviourTreeNode node)
    {
        node.Success();
        Debug.Log("AddHP3");
    }
    #endregion

    #region 条件检查
    // 是否满足死忘条件
    void ConditionDie(IBehaviourTreeNode node)
    {
        if (Input.GetKeyDown(KeyCode.D))
        {
            node.Success();
            Debug.Log("ConditionDie: RunStatus=" + node.GetRunStatus());
        }
        
        node.Failure();
    }

    // 是否满足复活条件
    void ConditionRelive(IBehaviourTreeNode node)
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            node.Success();
            Debug.Log("ConditionRelive: RunStatus=" + node.GetRunStatus());
        }
            
        node.Failure();
    }
    #endregion
} 


附上Demo源文件

行为树Demo.rar (受空间权限限制可能无法下载)

360网盘下载

https://yunpan.cn/cxqTN8Ns4syam  访问密码 0079

标签: Unity3d

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号