架构大更
This commit is contained in:
139
Assets/Scripts/SLSUtilities/CommandQueue/Cmd.cs
Normal file
139
Assets/Scripts/SLSUtilities/CommandQueue/Cmd.cs
Normal file
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Continentis.MainGame.Character;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 命令静态工厂。
|
||||
/// Mod 制作者通过此类创建常用命令,无需直接实例化具体命令类。
|
||||
/// </summary>
|
||||
public static class Cmd
|
||||
{
|
||||
// ── 基础工厂 ─────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>创建一条等待指定秒数后完成的命令。</summary>
|
||||
public static CommandBase Wait(float seconds)
|
||||
{
|
||||
return new WaitCommand(seconds);
|
||||
}
|
||||
|
||||
/// <summary>创建一条执行同步 Action 的命令。</summary>
|
||||
public static CommandBase Do(Action action)
|
||||
{
|
||||
return new ActionCommand(action);
|
||||
}
|
||||
|
||||
/// <summary>创建一条执行异步 UniTask 的命令。</summary>
|
||||
public static CommandBase Do(Func<UniTask> asyncAction)
|
||||
{
|
||||
return new AsyncActionCommand(asyncAction);
|
||||
}
|
||||
|
||||
/// <summary>创建一条延迟指定秒数后执行同步 Action 的命令。</summary>
|
||||
public static CommandBase After(float seconds, Action action)
|
||||
{
|
||||
return new ActionCommand(action).SetDelay(seconds);
|
||||
}
|
||||
|
||||
// ── 组合工厂 ─────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>创建一个顺序执行的命令组。</summary>
|
||||
public static CommandGroup Sequential(params CommandBase[] cmds)
|
||||
{
|
||||
return new CommandGroup(ExecutionMode.Sequential, cmds);
|
||||
}
|
||||
|
||||
/// <summary>创建一个并行执行的命令组。</summary>
|
||||
public static CommandGroup Parallel(params CommandBase[] cmds)
|
||||
{
|
||||
return new CommandGroup(ExecutionMode.Parallel, cmds);
|
||||
}
|
||||
|
||||
// ── Target 遍历工厂 ───────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 对 <paramref name="targets"/> 列表中的每个目标调用 <paramref name="factory"/>,
|
||||
/// 将结果按顺序组合为一个 Sequential <see cref="CommandGroup"/>。
|
||||
/// </summary>
|
||||
public static CommandGroup ForEachTarget(
|
||||
List<CharacterBase> targets,
|
||||
Func<CharacterBase, CommandBase> factory)
|
||||
{
|
||||
var group = new CommandGroup(ExecutionMode.Sequential);
|
||||
foreach (CharacterBase target in targets)
|
||||
{
|
||||
CharacterBase captured = target;
|
||||
group.AddCommand(factory(captured));
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对 <paramref name="targets"/> 列表中的每个目标调用 <paramref name="factory"/>,
|
||||
/// 将结果按顺序组合为一个 Sequential <see cref="CommandGroup"/>。
|
||||
/// </summary>
|
||||
public static CommandGroup ForEachTarget(
|
||||
List<CharacterBase> targets,
|
||||
Func<CharacterBase, CommandGroup> factory)
|
||||
{
|
||||
var group = new CommandGroup(ExecutionMode.Sequential);
|
||||
foreach (CharacterBase target in targets)
|
||||
{
|
||||
CharacterBase captured = target;
|
||||
group.AddCommand(factory(captured));
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
||||
// ── 内部实现类 ────────────────────────────────────────────────────
|
||||
|
||||
private sealed class WaitCommand : CommandBase
|
||||
{
|
||||
private readonly float seconds;
|
||||
|
||||
public WaitCommand(float seconds)
|
||||
{
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(seconds));
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ActionCommand : CommandBase
|
||||
{
|
||||
private readonly Action action;
|
||||
|
||||
public ActionCommand(Action action)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
protected override UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
action?.Invoke();
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class AsyncActionCommand : CommandBase
|
||||
{
|
||||
private readonly Func<UniTask> asyncAction;
|
||||
|
||||
public AsyncActionCommand(Func<UniTask> asyncAction)
|
||||
{
|
||||
this.asyncAction = asyncAction;
|
||||
}
|
||||
|
||||
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
if (asyncAction != null)
|
||||
await asyncAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/SLSUtilities/CommandQueue/Cmd.cs.meta
Normal file
2
Assets/Scripts/SLSUtilities/CommandQueue/Cmd.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e2394fde2169fba46b02ebba695e5210
|
||||
55
Assets/Scripts/SLSUtilities/CommandQueue/CommandBase.cs
Normal file
55
Assets/Scripts/SLSUtilities/CommandQueue/CommandBase.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 命令基类。子类重写 <see cref="ExecuteAsync"/> 实现具体逻辑。
|
||||
/// 框架通过 <see cref="RunAsync"/> 调用,先处理可选延迟再执行核心逻辑。
|
||||
/// </summary>
|
||||
public abstract class CommandBase
|
||||
{
|
||||
/// <summary>命令自身的私有上下文,可携带局部参数。</summary>
|
||||
public CommandContext selfContext;
|
||||
|
||||
private float delayBeforeExecute;
|
||||
|
||||
public CommandBase(CommandContext selfContext = null)
|
||||
{
|
||||
this.selfContext = selfContext ?? new CommandContext();
|
||||
}
|
||||
|
||||
/// <summary>设置命令执行前的延迟秒数,返回自身支持链式调用。</summary>
|
||||
public CommandBase SetDelay(float seconds)
|
||||
{
|
||||
delayBeforeExecute = seconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 框架调用入口:先执行可选延迟,再调用 <see cref="ExecuteAsync"/>。
|
||||
/// 子类不应重写此方法。
|
||||
/// </summary>
|
||||
public async UniTask RunAsync(CommandContext outerContext)
|
||||
{
|
||||
if (delayBeforeExecute > 0f)
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(delayBeforeExecute));
|
||||
|
||||
await ExecuteAsync(outerContext);
|
||||
}
|
||||
|
||||
/// <summary>子类重写此方法以实现命令的核心逻辑。</summary>
|
||||
protected virtual async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
await UniTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>浅拷贝当前命令,selfContext 也会被拷贝一份新实例。</summary>
|
||||
public virtual CommandBase Clone()
|
||||
{
|
||||
CommandBase clone = (CommandBase)MemberwiseClone();
|
||||
clone.selfContext = selfContext.Clone();
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf83d22138b5b29449dd0334aff4a3f0
|
||||
109
Assets/Scripts/SLSUtilities/CommandQueue/CommandContext.cs
Normal file
109
Assets/Scripts/SLSUtilities/CommandQueue/CommandContext.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 指令上下文 (Command Context)
|
||||
/// 以强类型键值对存储指令执行时传递的游戏状态信息。
|
||||
/// outerContext 由 CommandQueueManager 创建并传递给每条顶层指令;
|
||||
/// selfContext 是每条指令自身私有的上下文,可携带局部参数。
|
||||
/// </summary>
|
||||
public class CommandContext
|
||||
{
|
||||
// 保留公开字段以兼容尚未迁移的调用点,后续步骤中逐一移除直接访问
|
||||
public readonly Dictionary<string, object> context;
|
||||
|
||||
public CommandContext()
|
||||
{
|
||||
context = new Dictionary<string, object>();
|
||||
}
|
||||
|
||||
public CommandContext((string, object)[] pairs)
|
||||
{
|
||||
context = new Dictionary<string, object>();
|
||||
foreach ((string key, object value) in pairs)
|
||||
context[key] = value;
|
||||
}
|
||||
|
||||
public CommandContext(Dictionary<string, object> initialInfo)
|
||||
{
|
||||
context = new Dictionary<string, object>();
|
||||
foreach (var pair in initialInfo)
|
||||
context[pair.Key] = pair.Value;
|
||||
}
|
||||
|
||||
// ── 强类型访问 API ────────────────────────────────────────────────
|
||||
|
||||
/// <summary>以强类型写入一个值。</summary>
|
||||
public void Set<T>(string key, T value)
|
||||
{
|
||||
context[key] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以强类型读取一个值。
|
||||
/// 找不到 key 或类型不匹配时抛出 <see cref="KeyNotFoundException"/>。
|
||||
/// </summary>
|
||||
public T Get<T>(string key)
|
||||
{
|
||||
if (context.TryGetValue(key, out object raw) && raw is T typed)
|
||||
return typed;
|
||||
|
||||
throw new KeyNotFoundException(
|
||||
$"CommandContext 中不存在键 '{key}',或其类型与 {typeof(T).Name} 不匹配。");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 以强类型尝试读取一个值。
|
||||
/// 找不到 key 或类型不匹配时返回 false,result 为默认值。
|
||||
/// </summary>
|
||||
public bool TryGet<T>(string key, out T result)
|
||||
{
|
||||
if (context.TryGetValue(key, out object raw) && raw is T typed)
|
||||
{
|
||||
result = typed;
|
||||
return true;
|
||||
}
|
||||
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── 旧 API(向后兼容,后续步骤迁移完成后移除) ──────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 以强类型读取一个值。找不到或类型不匹配时返回默认值并写日志警告。
|
||||
/// 新代码请改用 <see cref="Get{T}"/> 或 <see cref="TryGet{T}"/>。
|
||||
/// </summary>
|
||||
[Obsolete("请改用 Get<T>(key) 或 TryGet<T>(key, out result)。")]
|
||||
public T GetInfo<T>(string key)
|
||||
{
|
||||
if (TryGet<T>(key, out T result))
|
||||
return result;
|
||||
|
||||
UnityEngine.Debug.LogWarning(
|
||||
$"CommandContext 中不存在键 '{key}',或其类型不匹配。返回默认值。");
|
||||
return default;
|
||||
}
|
||||
|
||||
// ── 工具方法 ──────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>返回一个拥有相同键值对副本的新上下文(浅拷贝)。</summary>
|
||||
public CommandContext Clone()
|
||||
{
|
||||
var copy = new CommandContext();
|
||||
foreach (var pair in context)
|
||||
copy.context[pair.Key] = pair.Value;
|
||||
return copy;
|
||||
}
|
||||
|
||||
/// <summary>将 other 的所有键值合并到当前上下文(同键覆盖),返回自身以支持链式调用。</summary>
|
||||
public CommandContext Merge(CommandContext other)
|
||||
{
|
||||
foreach (var pair in other.context)
|
||||
context[pair.Key] = pair.Value;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 120104451e143f74b82c3d8c9046fcb4
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 框架预定义的 CommandContext 键常量。
|
||||
/// 所有 Command 内部对 context 的读写均应引用此处的常量,消灭魔法字符串。
|
||||
/// </summary>
|
||||
public static class CommandContextKeys
|
||||
{
|
||||
/// <summary>当前命令作用的目标角色(CharacterBase)。</summary>
|
||||
public const string Target = "Target";
|
||||
|
||||
/// <summary>本次抽牌操作实际抽到的牌列表(List<CardLogicBase>)。</summary>
|
||||
public const string DrawnCards = "DrawnCards";
|
||||
|
||||
/// <summary>本次弃牌操作弃掉的牌列表(List<CardLogicBase>)。</summary>
|
||||
public const string DiscardedCards = "DiscardedCards";
|
||||
|
||||
/// <summary>本次消耗操作消耗的牌列表(List<CardLogicBase>)。</summary>
|
||||
public const string ExhaustedCards = "ExhaustedCards";
|
||||
|
||||
/// <summary>本次伤害命令实际造成的伤害量(int)。</summary>
|
||||
public const string DamageDealt = "DamageDealt";
|
||||
|
||||
/// <summary>本次治疗命令实际恢复的血量(int)。</summary>
|
||||
public const string HealingDone = "HealingDone";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ed0836069916dd4abc695e149f30c93
|
||||
140
Assets/Scripts/SLSUtilities/CommandQueue/CommandGroup.cs
Normal file
140
Assets/Scripts/SLSUtilities/CommandQueue/CommandGroup.cs
Normal file
@@ -0,0 +1,140 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cysharp.Threading.Tasks;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>命令组的执行模式。</summary>
|
||||
public enum ExecutionMode
|
||||
{
|
||||
/// <summary>子命令逐条顺序执行。</summary>
|
||||
Sequential,
|
||||
/// <summary>子命令同时并行执行。</summary>
|
||||
Parallel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 命令组,将多条 <see cref="CommandBase"/> 按 Sequential 或 Parallel 模式执行。
|
||||
/// 自身也是 <see cref="CommandBase"/>,可嵌套组合。
|
||||
/// </summary>
|
||||
public class CommandGroup : CommandBase
|
||||
{
|
||||
/// <summary>子命令列表。</summary>
|
||||
public readonly List<CommandBase> commands = new List<CommandBase>();
|
||||
|
||||
/// <summary>本组的执行模式。</summary>
|
||||
public readonly ExecutionMode mode;
|
||||
|
||||
/// <summary>
|
||||
/// 组级别上下文,执行过程中会合并每条子命令的 outerContext 与 selfContext。
|
||||
/// 可在组执行完成后读取聚合数据(如 DrawnCards)。
|
||||
/// </summary>
|
||||
public readonly CommandContext groupContext;
|
||||
|
||||
public CommandGroup(ExecutionMode mode = ExecutionMode.Sequential)
|
||||
{
|
||||
this.mode = mode;
|
||||
groupContext = new CommandContext();
|
||||
}
|
||||
|
||||
public CommandGroup(ExecutionMode mode, params CommandBase[] commands)
|
||||
{
|
||||
this.mode = mode;
|
||||
groupContext = new CommandContext();
|
||||
this.commands.AddRange(commands);
|
||||
}
|
||||
|
||||
public CommandGroup(params CommandBase[] commands)
|
||||
{
|
||||
mode = ExecutionMode.Sequential;
|
||||
groupContext = new CommandContext();
|
||||
this.commands.AddRange(commands);
|
||||
}
|
||||
|
||||
/// <summary>添加一条子命令,返回自身支持链式调用。</summary>
|
||||
public CommandGroup AddCommand(CommandBase command)
|
||||
{
|
||||
commands.Add(command);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有子命令。设 <paramref name="alsoIncludeGroups"/> 为 true 时,
|
||||
/// 返回结果中也包含 <see cref="CommandGroup"/> 自身节点。
|
||||
/// </summary>
|
||||
public List<CommandBase> GetAllCommands(bool alsoIncludeGroups = false)
|
||||
{
|
||||
//CommandBase可能也是CommandGroup,需要递归获取
|
||||
var allCommands = new List<CommandBase>();
|
||||
|
||||
if (alsoIncludeGroups)
|
||||
{
|
||||
allCommands.Add(this);
|
||||
}
|
||||
|
||||
foreach (CommandBase cmd in commands)
|
||||
{
|
||||
if (cmd is CommandGroup group)
|
||||
{
|
||||
if (alsoIncludeGroups)
|
||||
{
|
||||
allCommands.Add(group);
|
||||
}
|
||||
|
||||
allCommands.AddRange(group.GetAllCommands(alsoIncludeGroups));
|
||||
}
|
||||
else
|
||||
{
|
||||
allCommands.Add(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
return allCommands;
|
||||
}
|
||||
|
||||
/// <summary>获取所有指定类型的子命令(递归,包含组节点)。</summary>
|
||||
public List<T> GetAllCommands<T>() where T : CommandBase
|
||||
{
|
||||
return GetAllCommands(true).OfType<T>().ToList();
|
||||
}
|
||||
|
||||
/// <summary>深拷贝整个命令组及其子命令。</summary>
|
||||
public override CommandBase Clone()
|
||||
{
|
||||
var cloned = new CommandGroup(mode);
|
||||
foreach (var cmd in commands)
|
||||
cloned.AddCommand(cmd.Clone());
|
||||
return cloned;
|
||||
}
|
||||
|
||||
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
if (mode == ExecutionMode.Sequential)
|
||||
{
|
||||
foreach (var cmd in commands)
|
||||
{
|
||||
await cmd.RunAsync(outerContext);
|
||||
MergeContexts(cmd, outerContext);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await UniTask.WhenAll(commands.Select(cmd =>
|
||||
ExecuteAndMergeAsync(cmd, outerContext)));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>执行单条子命令并将上下文合并到 groupContext。</summary>
|
||||
private async UniTask ExecuteAndMergeAsync(CommandBase cmd, CommandContext outerContext)
|
||||
{
|
||||
await cmd.RunAsync(outerContext);
|
||||
MergeContexts(cmd, outerContext);
|
||||
}
|
||||
|
||||
private void MergeContexts(CommandBase cmd, CommandContext outerContext)
|
||||
{
|
||||
groupContext.Merge(outerContext);
|
||||
groupContext.Merge(cmd.selfContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f117f21c0bf77fb499c14f6bbedec76c
|
||||
18
Assets/Scripts/SLSUtilities/CommandQueue/CommandLane.cs
Normal file
18
Assets/Scripts/SLSUtilities/CommandQueue/CommandLane.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 命令队列的优先级分桶。数值越小优先级越高。
|
||||
/// Mod 制作者只需使用 <see cref="Main"/>,其余供框架内部使用。
|
||||
/// </summary>
|
||||
public enum CommandLane
|
||||
{
|
||||
/// <summary>最高优先级:死亡判定、强制打断。</summary>
|
||||
Interrupt = 0,
|
||||
/// <summary>Buff 触发链、被动响应。</summary>
|
||||
Reaction = 10,
|
||||
/// <summary>主流程:出牌、抽牌、弃牌(默认)。</summary>
|
||||
Main = 20,
|
||||
/// <summary>最低优先级:UI 刷新、VFX 清理。</summary>
|
||||
Cleanup = 30,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3a4efa5c124233847b2921f8a3bf1f59
|
||||
134
Assets/Scripts/SLSUtilities/CommandQueue/CommandQueueManager.cs
Normal file
134
Assets/Scripts/SLSUtilities/CommandQueue/CommandQueueManager.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using System.Collections.Generic;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局命令队列管理器。
|
||||
/// 内部按 <see cref="CommandLane"/> 优先级分桶,同桶内保持 FIFO 顺序。
|
||||
/// 使用 <see cref="AddCommand"/> 将命令加入队列;队列空闲时自动休眠,有新命令时唤醒。
|
||||
/// </summary>
|
||||
public class CommandQueueManager : Singleton<CommandQueueManager>
|
||||
{
|
||||
// 每个 Lane 独立维护一个 FIFO 队列,key 数值越小优先级越高
|
||||
private readonly SortedDictionary<CommandLane, Queue<CommandBase>> lanes =
|
||||
new SortedDictionary<CommandLane, Queue<CommandBase>>();
|
||||
|
||||
// 全局 outerContext,在整个战斗生命周期内共享
|
||||
private readonly CommandContext globalContext = new CommandContext();
|
||||
|
||||
private bool isLoopRunning = false;
|
||||
|
||||
/// <summary>当前是否有命令正在执行或等待执行。</summary>
|
||||
public bool IsBusy => HasPending || isProcessing;
|
||||
|
||||
private bool isProcessing = false;
|
||||
|
||||
private bool HasPending
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var queue in lanes.Values)
|
||||
if (queue.Count > 0) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
|
||||
// 预建所有 Lane 的队列,避免运行时动态创建
|
||||
foreach (CommandLane lane in System.Enum.GetValues(typeof(CommandLane)))
|
||||
lanes[lane] = new Queue<CommandBase>();
|
||||
|
||||
StartProcessLoop().Forget();
|
||||
}
|
||||
|
||||
// ── 公开 API ─────────────────────────────────────────────────────
|
||||
|
||||
/// <summary>
|
||||
/// 将命令追加到指定 Lane 的队列末尾。
|
||||
/// 默认 Lane 为 <see cref="CommandLane.Main"/>,Mod 制作者通常不需要指定 Lane。
|
||||
/// </summary>
|
||||
public CommandBase AddCommand(CommandBase command, CommandLane lane = CommandLane.Main)
|
||||
{
|
||||
if (command == null) return null;
|
||||
lanes[lane].Enqueue(command);
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将多条命令批量追加到指定 Lane。
|
||||
/// </summary>
|
||||
public void AddCommands(IEnumerable<CommandBase> commands, CommandLane lane = CommandLane.Main)
|
||||
{
|
||||
if (commands == null) return;
|
||||
foreach (var cmd in commands)
|
||||
AddCommand(cmd, lane);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将命令插入到 <see cref="CommandLane.Interrupt"/> Lane 的队首(最高优先级)。
|
||||
/// 仅供框架内部使用(死亡判定、强制打断等)。
|
||||
/// </summary>
|
||||
public CommandBase AddCommandNext(CommandBase command)
|
||||
{
|
||||
if (command == null) return null;
|
||||
|
||||
// Interrupt Lane 的队列本身是 FIFO,但它的整体优先级最高,
|
||||
// 因此直接 Enqueue 到 Interrupt 即可保证在 Main 之前执行。
|
||||
lanes[CommandLane.Interrupt].Enqueue(command);
|
||||
return command;
|
||||
}
|
||||
|
||||
/// <summary>等待直到队列完全空闲(所有 Lane 均无待执行命令)。</summary>
|
||||
public async UniTask WaitUntilIdle()
|
||||
{
|
||||
await UniTask.WaitUntil(() => !IsBusy);
|
||||
}
|
||||
|
||||
// ── 内部处理循环 ─────────────────────────────────────────────────
|
||||
|
||||
private async UniTaskVoid StartProcessLoop()
|
||||
{
|
||||
if (isLoopRunning) return;
|
||||
isLoopRunning = true;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// 队列为空时休眠,避免空转
|
||||
await UniTask.WaitUntil(() => HasPending);
|
||||
|
||||
CommandBase next = DequeueHighestPriority();
|
||||
if (next == null) continue;
|
||||
|
||||
isProcessing = true;
|
||||
try
|
||||
{
|
||||
await next.RunAsync(globalContext);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[CommandQueue] 命令 {next.GetType().Name} 执行出错:{ex}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isProcessing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>从优先级最高的非空 Lane 取出队首命令。</summary>
|
||||
private CommandBase DequeueHighestPriority()
|
||||
{
|
||||
foreach (var queue in lanes.Values)
|
||||
{
|
||||
if (queue.Count > 0)
|
||||
return queue.Dequeue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb973b62a3a5b6f47bf4fcc4bf05a7e6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: -50
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Scripts/SLSUtilities/CommandQueue/Examples.meta
Normal file
8
Assets/Scripts/SLSUtilities/CommandQueue/Examples.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 43903b94b47a0984a8b9645b80ee4d3c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,27 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>从 outerContext 中读取一个变量并输出到控制台。</summary>
|
||||
public class Cmd_GetAndLogVariable : CommandBase
|
||||
{
|
||||
private readonly string variableName;
|
||||
|
||||
public Cmd_GetAndLogVariable(string variableName)
|
||||
{
|
||||
this.variableName = variableName;
|
||||
}
|
||||
|
||||
protected override UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
if (outerContext.context.TryGetValue(variableName, out object value))
|
||||
Debug.Log($"[Cmd_GetAndLogVariable] 获取变量 '{variableName}',值为: '{value?.ToString() ?? "null"}'");
|
||||
else
|
||||
Debug.LogWarning($"[Cmd_GetAndLogVariable] 变量 '{variableName}' 未在上下文中定义。");
|
||||
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f69b73b7e4bbb84bb29e4d5000b1292
|
||||
@@ -0,0 +1,26 @@
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>在 outerContext 中设置一个键值对。</summary>
|
||||
public class Cmd_SetVariable : CommandBase
|
||||
{
|
||||
private readonly string variableName;
|
||||
private readonly object value;
|
||||
|
||||
public Cmd_SetVariable(string variableName, object value)
|
||||
{
|
||||
this.variableName = variableName;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
protected override UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
Debug.Log($"[Cmd_SetVariable] 设置变量 '{variableName}',值为: '{value}'");
|
||||
outerContext.context[variableName] = value;
|
||||
return UniTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16d73f0ca2725b6459957343e231bc32
|
||||
@@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using SLSFramework.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLSFramework.General
|
||||
{
|
||||
/// <summary>等待指定秒数后在控制台输出一条信息。</summary>
|
||||
public class Cmd_WaitAndLog : CommandBase
|
||||
{
|
||||
private readonly float duration;
|
||||
private readonly string message;
|
||||
|
||||
public Cmd_WaitAndLog(float duration, string message)
|
||||
{
|
||||
this.duration = duration;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
protected override async UniTask ExecuteAsync(CommandContext outerContext)
|
||||
{
|
||||
await UniTask.Delay(TimeSpan.FromSeconds(duration));
|
||||
Debug.Log($"[Cmd_WaitAndLog] 等待 {duration} 秒后... 输出: '{message}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25d431de73c653d42a6950339d22aa16
|
||||
Reference in New Issue
Block a user