From cbb5820612b5d067506dc7536ae4a8ab3c10cb36 Mon Sep 17 00:00:00 2001 From: papacoder Date: Sat, 15 Nov 2025 23:27:28 +0900 Subject: [PATCH] feat(vs): add stop control units and thread manager --- Docs/VS_Scratch_Mapping.md | 3 + .../FUnityScriptThreadManager.cs | 192 ++++++++++++++++++ .../FUnityScriptThreadManager.cs.meta | 11 + .../Units/ScratchUnits/ScratchUnitUtil.cs | 67 ++++++ .../Units/ScratchUnits/StopControlUnits.cs | 118 +++++++++++ .../ScratchUnits/StopControlUnits.cs.meta | 11 + 6 files changed, 402 insertions(+) create mode 100644 Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs create mode 100644 Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs.meta create mode 100644 Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs create mode 100644 Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs.meta diff --git a/Docs/VS_Scratch_Mapping.md b/Docs/VS_Scratch_Mapping.md index e994e17..9e471e2 100644 --- a/Docs/VS_Scratch_Mapping.md +++ b/Docs/VS_Scratch_Mapping.md @@ -62,6 +62,9 @@ Scratch ブロックと FUnity 独自 Visual Scripting Unit の対応関係で | ○のクローンを作る | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.CreateCloneOfDisplayNameUnit | ○のクローンを作る | FUnity/Scratch/制御 | 指定俳優を複製。定義: Runtime/.../CloneUnits.cs | | クローンされたとき | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.WhenIStartAsCloneUnit | クローンされたとき | Events/FUnity/Scratch/制御 | クローン生成時イベント。定義: Runtime/.../CloneUnits.cs | | このクローンを削除する | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.DeleteThisCloneUnit | このクローンを削除する | FUnity/Scratch/制御 | クローンを破棄。定義: Runtime/.../CloneUnits.cs | +| すべてを止める | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.StopAllUnit | Scratch/すべてを止める | FUnity/Scratch/制御 | ScriptThreadManager で全スレッド停止。定義: Runtime/.../StopControlUnits.cs | +| このスクリプトを止める | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.StopThisScriptUnit | Scratch/このスクリプトを止める | FUnity/Scratch/制御 | 現在スレッドのみ停止。定義: Runtime/.../StopControlUnits.cs | +| スプライトの他のスクリプトを止める | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.StopOtherScriptsInSpriteUnit | Scratch/スプライトの他のスクリプトを止める | FUnity/Scratch/制御 | 同俳優の他スレッド停止。定義: Runtime/.../StopControlUnits.cs | | もし○なら | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.IfThenUnit | もし○なら | FUnity/Scratch/制御 | 条件成立時のみ本体を実行。定義: Runtime/.../ConditionUnits.cs | ## イベント diff --git a/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs b/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs new file mode 100644 index 0000000..3643bc6 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Unity.VisualScripting; + +namespace FUnity.Runtime.Integrations.VisualScripting +{ + /// + /// Visual Scripting の ScriptGraph をスレッド(コルーチン)単位で追跡し、停止制御を提供するマネージャです。 + /// + [AddComponentMenu("")] + public sealed class FUnityScriptThreadManager : MonoBehaviour + { + /// スレッドに付随する情報を保持するテーブルです。 + private readonly Dictionary m_Threads = new Dictionary(); + + /// シングルトンインスタンスをキャッシュします。 + private static FUnityScriptThreadManager s_Instance; + + /// + /// Visual Scripting のスレッド情報を表すデータクラスです。 + /// + [Serializable] + public sealed class ScriptThreadInfo + { + /// 属している俳優(Runner)の識別子です。 + public string ActorId; + + /// スレッド固有の ID です。 + public Guid ThreadId; + + /// 実行中のコルーチン参照です。 + public Coroutine Coroutine; + + /// 実行している ScriptGraph アセットです。 + public ScriptGraphAsset Graph; + } + + /// + /// スレッドマネージャのインスタンスを返します。存在しない場合はシーンに生成します。 + /// + public static FUnityScriptThreadManager Instance + { + get + { + if (s_Instance != null) + { + return s_Instance; + } + + s_Instance = FindObjectOfType(); + if (s_Instance != null) + { + return s_Instance; + } + + var go = new GameObject("FUnityScriptThreadManager"); + s_Instance = go.AddComponent(); + DontDestroyOnLoad(go); + return s_Instance; + } + } + + /// + /// 指定したスレッドを登録し、停止管理の対象にします。 + /// + /// スレッドが属する俳優の識別子。 + /// 実行中のコルーチン。 + /// 対応する ScriptGraph アセット。 + /// 登録したスレッド情報。 + public ScriptThreadInfo RegisterThread(string actorId, Coroutine coroutine, ScriptGraphAsset graph) + { + var info = new ScriptThreadInfo + { + ActorId = string.IsNullOrEmpty(actorId) ? "(Unknown Actor)" : actorId, + ThreadId = Guid.NewGuid(), + Coroutine = coroutine, + Graph = graph + }; + + m_Threads[info.ThreadId] = info; + return info; + } + + /// + /// 指定した ID のスレッド登録を解除します。 + /// + /// 解除するスレッド ID。 + public void UnregisterThread(Guid threadId) + { + m_Threads.Remove(threadId); + } + + /// + /// 登録済みのすべてのスレッドを停止し、テーブルを初期化します。 + /// + public void StopAllThreads() + { + foreach (var entry in m_Threads.ToList()) + { + var info = entry.Value; + if (info?.Coroutine != null) + { + StopCoroutine(info.Coroutine); + } + } + + m_Threads.Clear(); + } + + /// + /// 指定した ID のスレッドを停止します。 + /// + /// 停止対象のスレッド ID。 + public void StopThread(Guid threadId) + { + if (!m_Threads.TryGetValue(threadId, out var info)) + { + return; + } + + if (info.Coroutine != null) + { + StopCoroutine(info.Coroutine); + } + + m_Threads.Remove(threadId); + } + + /// + /// 既存スレッドにコルーチン参照を紐付けます。RegisterThread 後に StartCoroutine で取得した参照を設定する際に使用します。 + /// + /// 更新するスレッド ID。 + /// 紐付けるコルーチン。 + public void UpdateCoroutine(Guid threadId, Coroutine coroutine) + { + if (!m_Threads.TryGetValue(threadId, out var info)) + { + return; + } + + info.Coroutine = coroutine; + } + + /// + /// 同一俳優に属するスレッドのうち、指定した ID 以外を停止します。 + /// + /// 対象俳優の識別子。 + /// 停止から除外するスレッド ID。 + public void StopThreadsOfActorExcept(string actorId, Guid exceptThreadId) + { + var normalized = string.IsNullOrEmpty(actorId) ? string.Empty : actorId; + foreach (var entry in m_Threads.ToList()) + { + var info = entry.Value; + if (info == null) + { + continue; + } + + if (!string.Equals(info.ActorId, normalized, StringComparison.Ordinal)) + { + continue; + } + + if (info.ThreadId == exceptThreadId) + { + continue; + } + + if (info.Coroutine != null) + { + StopCoroutine(info.Coroutine); + } + + m_Threads.Remove(info.ThreadId); + } + } + + /// + /// 指定したスレッド ID の情報を取得します。 + /// + /// 検索するスレッド ID。 + /// 見つかったスレッド情報。 + /// 見つかった場合は true。 + public bool TryGetThreadInfo(Guid threadId, out ScriptThreadInfo info) + { + return m_Threads.TryGetValue(threadId, out info); + } + } +} diff --git a/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs.meta b/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs.meta new file mode 100644 index 0000000..4d77545 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 20550019b1da461697ef97f23c98053d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/ScratchUnitUtil.cs b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/ScratchUnitUtil.cs index a54cf77..fefd9bf 100644 --- a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/ScratchUnitUtil.cs +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/ScratchUnitUtil.cs @@ -26,6 +26,12 @@ internal static class ScratchUnitUtil /// Scratch の 1 歩をピクセルへ換算する倍率です。 private const float StepToPixels = 1f; + /// フロー変数に保存するスレッド ID のキーです。 + private const string ThreadIdKey = "FUnity_ThreadId"; + + /// フロー変数に保存する俳優 ID のキーです。 + private const string ActorIdKey = "FUnity_ActorId"; + /// /// 現在のフローがホストしている Runner(Self)に紐づく Object 変数を取得します。 /// @@ -95,6 +101,67 @@ public static object GetUI(Flow flow) return objectVariables.IsDefined("ui") ? objectVariables.Get("ui") : null; } + /// + /// Flow に保存されたスレッドコンテキストを取得し、俳優 ID とスレッド ID を返します。 + /// + /// 現在のフロー情報。 + /// 取得した俳優 ID。 + /// 取得したスレッド ID。 + /// 必要な情報が揃っている場合は true。 + public static bool TryGetThreadContext(Flow flow, out string actorId, out Guid threadId) + { + actorId = null; + threadId = Guid.Empty; + + if (flow == null) + { + return false; + } + + var variables = flow.Variables; + if (variables == null) + { + return false; + } + + if (!variables.IsDefined(ActorIdKey) || !variables.IsDefined(ThreadIdKey)) + { + return false; + } + + actorId = variables.Get(ActorIdKey); + var threadIdString = variables.Get(ThreadIdKey); + if (!Guid.TryParse(threadIdString, out threadId)) + { + return false; + } + + return true; + } + + /// + /// Flow に俳優 ID とスレッド ID を保存し、停止系ユニットから参照できるようにします。 + /// + /// 現在のフロー情報。 + /// 保存する俳優 ID。 + /// 保存するスレッド ID。 + public static void SetThreadContext(Flow flow, string actorId, Guid threadId) + { + if (flow == null) + { + return; + } + + var variables = flow.Variables; + if (variables == null) + { + return; + } + + variables.Set(ActorIdKey, actorId ?? string.Empty); + variables.Set(ThreadIdKey, threadId.ToString()); + } + /// /// Flow 情報から を自動的に解決します。 /// 優先度: ScriptGraphAsset Variables → Graph Variables → Object Variables → 自身の GameObject → 静的キャッシュ → シーン検索。 diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs new file mode 100644 index 0000000..7e621a5 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs @@ -0,0 +1,118 @@ +using Unity.VisualScripting; +using FUnity.Runtime.Integrations.VisualScripting; + +namespace FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits +{ + /// + /// Scratch の「すべてを止める」に対応し、全俳優の全スクリプトを停止するユニットです。 + /// + [UnitTitle("Scratch/すべてを止める")] + [UnitCategory("FUnity/Scratch/制御")] + [UnitSubtitle("funity scratch 制御 stop all すべてを止める")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class StopAllUnit : Unit + { + /// 入力フローを受け取る ControlInput です。 + [DoNotSerialize] + private ControlInput m_Input; + + /// + /// フロー入力のみを受け付け、出力は作成しません。 + /// + protected override void Definition() + { + m_Input = ControlInput("in", OnEnter); + } + + /// + /// すべてのスレッドを停止し、後続フローには進めません。 + /// + /// 現在のフロー情報。 + /// 常に null を返し、フローを終端させます。 + private ControlOutput OnEnter(Flow flow) + { + FUnityScriptThreadManager.Instance.StopAllThreads(); + return null; + } + } + + /// + /// Scratch の「このスクリプトを止める」に対応し、実行中のスレッドのみ停止するユニットです。 + /// + [UnitTitle("Scratch/このスクリプトを止める")] + [UnitCategory("FUnity/Scratch/制御")] + [UnitSubtitle("funity scratch 制御 stop this script このスクリプトを止める")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class StopThisScriptUnit : Unit + { + /// 入力フローを受け取る ControlInput です。 + [DoNotSerialize] + private ControlInput m_Input; + + /// + /// フロー入力のみを定義し、出力は作成しません。 + /// + protected override void Definition() + { + m_Input = ControlInput("in", OnEnter); + } + + /// + /// 現在のフローが紐づくスレッドを停止します。 + /// + /// 現在のフロー情報。 + /// 常に null を返し、フローを終端させます。 + private ControlOutput OnEnter(Flow flow) + { + if (ScratchUnitUtil.TryGetThreadContext(flow, out _, out var threadId)) + { + FUnityScriptThreadManager.Instance.StopThread(threadId); + } + + return null; + } + } + + /// + /// Scratch の「スプライトの他のスクリプトを止める」に対応し、同一俳優の他スレッドを停止します。 + /// + [UnitTitle("Scratch/スプライトの他のスクリプトを止める")] + [UnitCategory("FUnity/Scratch/制御")] + [UnitSubtitle("funity scratch 制御 stop other scripts 他のスクリプトを止める")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class StopOtherScriptsInSpriteUnit : Unit + { + /// 入力フローを受け取る ControlInput です。 + [DoNotSerialize] + private ControlInput m_Input; + + /// 停止後もフローを継続する ControlOutput です。 + [DoNotSerialize] + private ControlOutput m_Output; + + /// + /// 入出力ポートを定義します。 + /// + protected override void Definition() + { + m_Input = ControlInput("in", OnEnter); + m_Output = ControlOutput("out"); + Succession(m_Input, m_Output); + } + + /// + /// 同一俳優に属するスレッドのうち、自身以外を停止します。 + /// + /// 現在のフロー情報。 + /// 常に後続へ継続する ControlOutput を返します。 + private ControlOutput OnEnter(Flow flow) + { + if (ScratchUnitUtil.TryGetThreadContext(flow, out var actorId, out var threadId)) + { + FUnityScriptThreadManager.Instance.StopThreadsOfActorExcept(actorId, threadId); + } + + return m_Output; + } + } +} diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs.meta b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs.meta new file mode 100644 index 0000000..22736e6 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/StopControlUnits.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d900636829c447ee9b2356b68d4dc804 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 0} + userData: + assetBundleName: + assetBundleVariant: