2024-07-23 Unity AI行为树2 —— 项目介绍


项目借鉴 B 站唐老狮 2023年直播内容。 点击前往唐老狮 B 站主页

1 项目介绍

​ 本项目使用 Unity 2022.3.32f1c1,实现基本的 AI 框架。其中,用 Cube(红色)代替怪物模型,Cube(蓝色)代替玩家,即 AI 目标。

image-20240723173801748

​ 项目地址:https://github.com/zheliku/BehaviourTree_AI

2 AI 代码介绍

2.1 BTBaseNode / BTControlNode

​ 所有结点都需要执行各自的任务,因此提取到基类节点中:

/// <summary>
/// 行为树结点基类
/// </summary>
public abstract class BTBaseNode
{
    /// <summary>
    /// 执行节点逻辑的抽象方法
    /// </summary>
    public abstract ENodeState Execute();
}

​ 而控制节点需要知道其控制哪些节点,因此相关内容需要提取到控制节点的基类中:

using System.Collections.Generic;

public abstract class BTControlNode : BTBaseNode
{
    // 存储子节点的 List
    protected List<BTBaseNode> _childList = new List<BTBaseNode>();

    protected int _currentIndex = 0; // 当前执行到的子节点索引

    /// <summary>
    /// 添加子节点
    /// </summary>
    public virtual void AddChild(params BTBaseNode[] node) {
        _childList.AddRange(node);
    }
}

2.2 动作/条件节点

​ 动作节点执行具体的行为,没有子节点。

​ 执行完行为后,可以返回是否执行成功,因此需要外部添加执行的方法。

using System;

/// <summary>
/// 动作节点,执行具体行为,没有子节点
/// </summary>
public class BTActionNode : BTBaseNode
{
    private Func<bool> _action; // 返回值表示执行是否成功

    public BTActionNode(Func<bool> action) { _action = action; }

    public override ENodeState Execute() {
        if (_action == null) return ENodeState.Failure;
        
        // 执行行为
        return _action.Invoke() ? ENodeState.Success : ENodeState.Failure; 
    }
}

​ 条件节点用于评估条件,根据条件结果返回成功 / 失败。

​ 和动作节点类似,也需要外部提供判断条件的方法。

using System;

/// <summary>
/// 条件节点,评估一个条件,并返回成功 / 失败
/// </summary>
public class BTConditionNode : BTBaseNode
{
    private Func<bool> _action; // 返回值表示执行是否成功

    public BTConditionNode(Func<bool> action) { _action = action; }

    public override ENodeState Execute() {
        if (_action == null) return ENodeState.Failure;
        
        // 执行行为
        return _action.Invoke() ? ENodeState.Success : ENodeState.Failure; 
    }
}

2.3 选择 / 顺序节点

​ 选择 / 顺序节点都仅依据子节点的状态,返回自己的状态。因此只需要实现 Execute() 方法即可,区别在于实现的逻辑不同。

using System;

/// <summary>
/// 选择节点<br/>
/// 特点:<br/>
/// 1. 按顺序执行子节点<br/>
/// 2. 如果某子节点返回成功,则返回成功,不执行后续结点<br/>
/// 3. 如果某子节点返回失败,则继续执行下一个子节点
/// </summary>
public class BTSelectNode : BTControlNode
{
    public override ENodeState Execute() {
        var childNode = _childList[_currentIndex];
        var result    = childNode.Execute();
        switch (result) {
            case ENodeState.Success: { // 成功,则重置索引,直接返回
                _currentIndex = 0;
                return ENodeState.Success;
            }
            case ENodeState.Failure: { // 失败,则继续下一个节点
                ++_currentIndex;
                if (_currentIndex == _childList.Count) { // 执行到最后,重置索引
                    _currentIndex = 0;
                    return ENodeState.Failure;
                }
                break;
            }
            case ENodeState.Running: {
                return ENodeState.Running;
            }
            default: throw new ArgumentOutOfRangeException();
        }

        // 没有执行完,或者节点失败,才执行该逻辑
        // 此时仍希望下一帧继续往后执行,因此返回成功
        return ENodeState.Success;
    }
}

/// <summary>
/// 序列节点<br/>
/// 特点:<br/>
/// 1. 按顺序执行子节点<br/>
/// 2. 只要有一个子节点返回失败,则整个节点返回失败<br/>
/// 3. 所有子节点都返回成功,则整个节点返回成功
/// </summary>
public class BTSequenceNode : BTControlNode
{
    public override ENodeState Execute() {
        var childNode = _childList[_currentIndex];
        var result    = childNode.Execute();
        switch (result) {
            case ENodeState.Success: { // 成功,则继续下一个节点
                ++_currentIndex;
                if (_currentIndex == _childList.Count) { // 执行到最后,重置索引
                    _currentIndex = 0;
                    return ENodeState.Success;
                }
                break;
            }
            case ENodeState.Failure: { // 失败,则重置索引,直接返回
                _currentIndex = 0;
                return ENodeState.Failure;
            }
            case ENodeState.Running: {
                return ENodeState.Running;
            }
            default: throw new ArgumentOutOfRangeException();
        }

        return ENodeState.Success;
    }
}

3 怪物实现

​ 类似 2024-07-12 Unity AI状态机1 —— 框架介绍_有限状态机编程框架-CSDN博客 中的怪物实现,但将怪物数据写在各个行为的控制类中,因此具有以下 4 个控制类:

  • PatrolControl(巡逻)
  • ChaseControl(追逐)
  • AttackControl(攻击)
  • BackControl(返回)

​ 其余实现基本一致。

4 其他功能

​ 为了辅助绘图,在 Monster 类中的 Update 方法里判断当前执行的行为 / 状态。用二进制位表示每个状态,异或运算来计算当前的状态:

private void Update() {
    _btAIRoot.Execute(); // 执行行为树

    switch (CurrentState) { // 依据当前行为绘制辅助线
        case 1:
            AttackCtrl.DrawGizmos();
            break;
        case 2:
            BackCtrl.DrawGizmos();
            break;
        case 4:
            ChaseCtrl.DrawGizmos();
            break;
        case 8:
            PatrolCtrl.DrawGizmos();
            break;
    }
}

5 UML 类图

BehaviourTree_AI
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔗理苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值