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: