mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 08:27:12 +08:00
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/60
383 lines
16 KiB
C#
383 lines
16 KiB
C#
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; // 重置内部状态
|
||
}
|
||
}
|
||
} |