Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/AI/CompositeNodeBase.cs

383 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Text;
using Data;
using Parsing;
using UnityEngine;
using Utils;
namespace AI
{
public abstract class CompositeNodeBase : BehaviorTreeBase
{
// 组合节点特有的成员:子行为树列表
protected List<BehaviorTreeBase> children = new();
/// <summary>
/// 初始化组合行为树节点及其子节点。
/// </summary>
/// <param name="def">行为树定义。</param>
/// <param name="selfEntity">关联的实体。</param>
public override void Init(BehaviorTreeDef def, Entity.Entity selfEntity)
{
base.Init(def, selfEntity); // 调用基类Init处理 SelfEntity 和 blackboard
if (def.childTree == null) return;
foreach (var childDef in def.childTree)
{
var childNode = BehaviorTreeUtils.ConvertToAIBase(childDef);
if (childNode == null) continue;
children.Add(childNode);
childNode.Init(childDef, selfEntity); // 递归初始化子节点
}
}
/// <summary>
/// 重置组合行为树节点的状态及其所有子节点的状态。
/// </summary>
public override void Reset()
{
base.Reset(); // 调用基类Reset重置自身状态
foreach (var child in children) child?.Reset(); // 确保子节点不为 null 时才调用 Reset
}
/// <summary>
/// 返回表示当前行为树节点及其子节点层次结构的字符串。
/// </summary>
/// <returns>行为树的层次结构字符串。</returns>
public override string ToString()
{
return ToString(0); // 从根节点开始初始缩进为0
}
/// <summary>
/// 返回表示当前行为树节点及其子节点层次结构的字符串,带有指定缩进。
/// </summary>
/// <param name="indent">当前节点的缩进级别。</param>
/// <returns>行为树的层次结构字符串。</returns>
protected virtual string ToString(int indent)
{
var sb = new StringBuilder();
var indentStr = new string(' ', indent * 2); // 每个级别2个空格
sb.AppendLine($"{indentStr}[{GetType().Name}] ");
foreach (var child in children)
// 确保子节点不为 null 且支持 ToString(int) 方法
if (child is CompositeNodeBase compositeChild)
{
sb.Append(compositeChild.ToString(indent + 1));
}
else if (child != null) // 对于叶子节点或非CompositeNodeBase的BehaviorTreeBase
{
// 如果子节点不是复合节点但有自己的ToString实现直接调用其ToString
// 否则打印其类型和ID/名称
var childIndentStr = new string(' ', (indent + 1) * 2);
sb.AppendLine($"{childIndentStr}[{child.GetType().Name}] ");
}
return sb.ToString();
}
}
public class ThinkNode_Selector : CompositeNodeBase
{
// 用于跟踪正在运行的子节点索引
protected int currentChildIndex;
/// <summary>
/// 执行选择器节点的逻辑。它会按顺序执行子节点,直到一个子节点成功或正在运行。
/// </summary>
/// <returns>节点的执行状态。</returns>
public override Status Tick()
{
// 如果是从 Running 状态恢复,则从上次停止的子节点开始
// 否则,从第一个子节点开始
for (var i = currentChildIndex; i < children.Count; i++)
{
var child = children[i];
if (child == null) // 处理 null 子节点,避免 NullReferenceException
{
Debug.LogWarning($"选择器节点在索引 {i} 处有空子节点。跳过。");
continue; // 跳过此 null 子节点
}
CurrentStatus = child.Tick(); // 执行当前子节点的 Tick
switch (CurrentStatus)
{
case Status.Success:
currentChildIndex = 0;
return Status.Success;
case Status.Running:
// 如果子节点正在运行,选择器也正在运行
// 记录当前子节点的索引,以便下次 Tick 时继续
currentChildIndex = i;
return Status.Running;
}
}
currentChildIndex = 0;
return Status.Failure;
}
/// <summary>
/// 重置选择器节点的状态,包括子节点索引。
/// </summary>
public override void Reset()
{
base.Reset(); // 调用基类的 Reset 方法
currentChildIndex = 0; // 重置子节点索引
}
}
public class ThinkNode_Conditional : ThinkNode_Selector
{
// 条件函数,返回 true 表示满足条件
private Func<Entity.Entity, bool> condition;
/// <summary>
/// 初始化条件节点,创建条件委托。
/// </summary>
/// <param name="def">行为树定义。</param>
/// <param name="selfEntity">关联的实体。</param>
public override void Init(BehaviorTreeDef def, Entity.Entity selfEntity)
{
base.Init(def, selfEntity); // 调用基类的 Init 方法,初始化子节点和 SelfEntity
if (!string.IsNullOrEmpty(def.value))
try
{
condition = ConditionDelegateFactory.CreateConditionDelegate(
def.value,
typeof(Entity.Entity),
typeof(ConditionFunctions) // 指定查找条件函数的类
);
}
catch (Exception ex)
{
// 记录错误,并使条件始终不满足
Debug.LogError($"无法为 '{def.value}' 创建条件委托: {ex.Message}");
condition = e => false;
}
else
condition = e => false; // 如果没有指定条件,则条件始终不满足
}
/// <summary>
/// 执行条件节点的逻辑。首先检查条件,如果条件满足则执行其子节点(作为选择器)。
/// </summary>
/// <returns>节点的执行状态。</returns>
public override Status Tick()
{
// 在执行任何子节点之前,先检查条件
if (condition == null || !condition(SelfEntity)) // 使用 SelfEntity 来执行条件检查
{
// 如果条件不满足,则直接返回 Failure
currentChildIndex = 0; // 条件不满足时,重置子节点索引
return Status.Failure;
}
// 如果条件满足,则像父类 Selector 一样执行子节点
return base.Tick();
}
}
public class ThinkNode_Sequence : CompositeNodeBase
{
// 用于跟踪正在运行的子节点索引
private int currentChildIndex;
/// <summary>
/// 执行序列器节点的逻辑。它会按顺序执行子节点,直到一个子节点失败或正在运行。
/// </summary>
/// <returns>节点的执行状态。</returns>
public override Status Tick()
{
// 从上次停止的子节点或者从第一个子节点开始
for (var i = currentChildIndex; i < children.Count; i++)
{
var child = children[i];
if (child == null) // 处理 null 子节点,避免 NullReferenceException
{
Debug.LogWarning($"序列器节点在索引 {i} 处有空子节点。跳过。");
// Null 子节点对于 Sequence 来说是失败的表现
currentChildIndex = 0;
return Status.Failure;
}
CurrentStatus = child.Tick(); // 执行当前子节点的 Tick
switch (CurrentStatus)
{
case Status.Failure:
// 如果子节点失败,序列器也失败
// 重置索引,以便下次从头开始
currentChildIndex = 0;
return Status.Failure;
case Status.Running:
// 如果子节点正在运行,序列器也正在运行
// 记录当前子节点的索引,以便下次 Tick 时继续
currentChildIndex = i;
return Status.Running;
}
// 如果子节点返回 Success则继续尝试下一个子节点 (这是 Sequence 的核心)
}
// 如果所有子节点都返回 Success (即循环结束且没有返回 Failure 或 Running)
// 则 Sequence 也返回 Success。
// 重置索引
currentChildIndex = 0;
return Status.Success;
}
/// <summary>
/// 重置序列器节点的状态,包括子节点索引。
/// </summary>
public override void Reset()
{
base.Reset(); // 调用基类的 Reset 方法
currentChildIndex = 0; // 重置子节点索引
}
}
public enum BranchExecutionPhase
{
None, // 初始或重置状态
EvaluatingCondition, // 正在执行条件节点
ExecutingTrueBranch, // 正在执行真分支节点
ExecutingFalseBranch // 正在执行假分支节点
}
public class ThinkNode_Branch : CompositeNodeBase
{
// 内部状态,用于跟踪分支节点当前正在执行哪个子节点
private BranchExecutionPhase _currentPhase = BranchExecutionPhase.None;
// 用于防止日志刷屏
private bool _hasLoggedMisconfigurationWarning;
/// <summary>
/// 初始化分支节点。
/// </summary>
/// <param name="def">行为树定义。</param>
/// <param name="selfEntity">关联的实体。</param>
public override void Init(BehaviorTreeDef def, Entity.Entity selfEntity)
{
base.Init(def, selfEntity); // 调用基类的 Init 方法,初始化子节点和 SelfEntity
_currentPhase = BranchExecutionPhase.None; // 确保初始化时状态为 None
_hasLoggedMisconfigurationWarning = false; // 确保 Init 时也重置警告标志
}
/// <summary>
/// 执行分支节点的逻辑。它会首先评估条件树,然后根据条件结果执行真分支或假分支。
/// </summary>
/// <returns>节点的执行状态。</returns>
public override Status Tick()
{
// 如果之前有警告但现在配置正确了,就重置警告标志
if (_hasLoggedMisconfigurationWarning && children.Count >= 2) _hasLoggedMisconfigurationWarning = false;
// 确保至少有两个子节点 (条件树和真树)
if (children.Count < 2)
{
if (!_hasLoggedMisconfigurationWarning) // 只有在未记录过警告时才记录
{
Debug.LogWarning(
"分支节点至少需要2个子节点(条件和真分支)。当前实例配置错误。 " +
"此实例的进一步警告将在重置或更正后才显示。 " +
$"实体: {SelfEntity?.name ?? "N/A"}。"); // 增加更多上下文信息
_hasLoggedMisconfigurationWarning = true; // 标记已记录
}
Reset(); // 重置内部状态 (包括_currentPhase 和 _hasLoggedMisconfigurationWarning)
return Status.Failure;
}
// 获取条件树和真树
var conditionTree = children[0];
var trueTree = children[1];
// 假树是可选的
var falseTree = children.Count > 2 ? children[2] : null;
// 如果是首次 Tick 或者 Reset 后,将阶段设置为评估条件
if (_currentPhase == BranchExecutionPhase.None) _currentPhase = BranchExecutionPhase.EvaluatingCondition;
Status result;
switch (_currentPhase)
{
case BranchExecutionPhase.EvaluatingCondition:
// 执行条件树
if (conditionTree == null)
{
Debug.LogError("分支节点: 条件树为空。返回失败。");
Reset();
return Status.Failure;
}
result = conditionTree.Tick();
if (result == Status.Running)
// 条件正在运行,则整个分支也运行
return Status.Running;
if (result == Status.Success)
{
// 条件成功,转移到执行真树阶段
_currentPhase = BranchExecutionPhase.ExecutingTrueBranch;
// 继续执行真树(因为条件成功,我们可以立即尝试执行真树)
goto case BranchExecutionPhase.ExecutingTrueBranch;
}
// result == Status.Failure
// 条件失败,转移到执行假树阶段
_currentPhase = BranchExecutionPhase.ExecutingFalseBranch;
// 继续执行假树(如果存在)
goto case BranchExecutionPhase.ExecutingFalseBranch;
case BranchExecutionPhase.ExecutingTrueBranch:
// 执行真树
if (trueTree == null)
{
Debug.LogError("分支节点: 真分支树为空。返回失败。");
Reset();
return Status.Failure;
}
result = trueTree.Tick();
if (result == Status.Running)
// 真树正在运行,则整个分支也运行
return Status.Running;
// 真树完成 (成功或失败),分支节点完成,重置状态
Reset();
return result;
case BranchExecutionPhase.ExecutingFalseBranch:
// 执行假树 (如果存在)
if (falseTree != null)
{
result = falseTree.Tick();
if (result == Status.Running)
// 假树正在运行,则整个分支也运行
return Status.Running;
// 假树完成 (成功或失败),分支节点完成,重置状态
Reset();
return result;
}
// 没有假树,且条件失败,则分支节点失败
Reset();
return Status.Failure;
case BranchExecutionPhase.None:
default:
// 不应该到达这里,如果到达表示有逻辑错误
Debug.LogError($"分支节点处于意外阶段: {_currentPhase}。返回失败。");
Reset();
return Status.Failure;
}
}
/// <summary>
/// 重置分支节点的状态,包括内部执行阶段。
/// </summary>
public override void Reset()
{
base.Reset(); // 调用基类的 Reset 方法来确保子节点也重置
_currentPhase = BranchExecutionPhase.None; // 重置内部状态
}
}
}