Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Docs/VS_Scratch_Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

## イベント
Expand Down
192 changes: 192 additions & 0 deletions Runtime/Integrations/VisualScripting/FUnityScriptThreadManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Unity.VisualScripting;

namespace FUnity.Runtime.Integrations.VisualScripting
{
/// <summary>
/// Visual Scripting の ScriptGraph をスレッド(コルーチン)単位で追跡し、停止制御を提供するマネージャです。
/// </summary>
[AddComponentMenu("")]
public sealed class FUnityScriptThreadManager : MonoBehaviour
{
/// <summary>スレッドに付随する情報を保持するテーブルです。</summary>
private readonly Dictionary<Guid, ScriptThreadInfo> m_Threads = new Dictionary<Guid, ScriptThreadInfo>();

/// <summary>シングルトンインスタンスをキャッシュします。</summary>
private static FUnityScriptThreadManager s_Instance;

/// <summary>
/// Visual Scripting のスレッド情報を表すデータクラスです。
/// </summary>
[Serializable]
public sealed class ScriptThreadInfo
{
/// <summary>属している俳優(Runner)の識別子です。</summary>
public string ActorId;

/// <summary>スレッド固有の ID です。</summary>
public Guid ThreadId;

/// <summary>実行中のコルーチン参照です。</summary>
public Coroutine Coroutine;

/// <summary>実行している ScriptGraph アセットです。</summary>
public ScriptGraphAsset Graph;
}

/// <summary>
/// スレッドマネージャのインスタンスを返します。存在しない場合はシーンに生成します。
/// </summary>
public static FUnityScriptThreadManager Instance
{
get
{
if (s_Instance != null)
{
return s_Instance;
}

s_Instance = FindObjectOfType<FUnityScriptThreadManager>();
if (s_Instance != null)
{
return s_Instance;
}

var go = new GameObject("FUnityScriptThreadManager");
s_Instance = go.AddComponent<FUnityScriptThreadManager>();
DontDestroyOnLoad(go);
return s_Instance;
}
}

/// <summary>
/// 指定したスレッドを登録し、停止管理の対象にします。
/// </summary>
/// <param name="actorId">スレッドが属する俳優の識別子。</param>
/// <param name="coroutine">実行中のコルーチン。</param>
/// <param name="graph">対応する ScriptGraph アセット。</param>
/// <returns>登録したスレッド情報。</returns>
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;
}

/// <summary>
/// 指定した ID のスレッド登録を解除します。
/// </summary>
/// <param name="threadId">解除するスレッド ID。</param>
public void UnregisterThread(Guid threadId)
{
m_Threads.Remove(threadId);
}

/// <summary>
/// 登録済みのすべてのスレッドを停止し、テーブルを初期化します。
/// </summary>
public void StopAllThreads()
{
foreach (var entry in m_Threads.ToList())
{
var info = entry.Value;
if (info?.Coroutine != null)
{
StopCoroutine(info.Coroutine);
}
}

m_Threads.Clear();
}

/// <summary>
/// 指定した ID のスレッドを停止します。
/// </summary>
/// <param name="threadId">停止対象のスレッド ID。</param>
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);
}

/// <summary>
/// 既存スレッドにコルーチン参照を紐付けます。RegisterThread 後に StartCoroutine で取得した参照を設定する際に使用します。
/// </summary>
/// <param name="threadId">更新するスレッド ID。</param>
/// <param name="coroutine">紐付けるコルーチン。</param>
public void UpdateCoroutine(Guid threadId, Coroutine coroutine)
{
if (!m_Threads.TryGetValue(threadId, out var info))
{
return;
}

info.Coroutine = coroutine;
}

/// <summary>
/// 同一俳優に属するスレッドのうち、指定した ID 以外を停止します。
/// </summary>
/// <param name="actorId">対象俳優の識別子。</param>
/// <param name="exceptThreadId">停止から除外するスレッド ID。</param>
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);
}
}

/// <summary>
/// 指定したスレッド ID の情報を取得します。
/// </summary>
/// <param name="threadId">検索するスレッド ID。</param>
/// <param name="info">見つかったスレッド情報。</param>
/// <returns>見つかった場合は true。</returns>
public bool TryGetThreadInfo(Guid threadId, out ScriptThreadInfo info)
{
return m_Threads.TryGetValue(threadId, out info);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ internal static class ScratchUnitUtil
/// <summary>Scratch の 1 歩をピクセルへ換算する倍率です。</summary>
private const float StepToPixels = 1f;

/// <summary>フロー変数に保存するスレッド ID のキーです。</summary>
private const string ThreadIdKey = "FUnity_ThreadId";

/// <summary>フロー変数に保存する俳優 ID のキーです。</summary>
private const string ActorIdKey = "FUnity_ActorId";

/// <summary>
/// 現在のフローがホストしている Runner(Self)に紐づく Object 変数を取得します。
/// </summary>
Expand Down Expand Up @@ -95,6 +101,67 @@ public static object GetUI(Flow flow)
return objectVariables.IsDefined("ui") ? objectVariables.Get("ui") : null;
}

/// <summary>
/// Flow に保存されたスレッドコンテキストを取得し、俳優 ID とスレッド ID を返します。
/// </summary>
/// <param name="flow">現在のフロー情報。</param>
/// <param name="actorId">取得した俳優 ID。</param>
/// <param name="threadId">取得したスレッド ID。</param>
/// <returns>必要な情報が揃っている場合は true。</returns>
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<string>(ActorIdKey);
var threadIdString = variables.Get<string>(ThreadIdKey);
if (!Guid.TryParse(threadIdString, out threadId))
{
return false;
}

return true;
}

/// <summary>
/// Flow に俳優 ID とスレッド ID を保存し、停止系ユニットから参照できるようにします。
/// </summary>
/// <param name="flow">現在のフロー情報。</param>
/// <param name="actorId">保存する俳優 ID。</param>
/// <param name="threadId">保存するスレッド ID。</param>
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());
}

/// <summary>
/// Flow 情報から <see cref="ActorPresenterAdapter"/> を自動的に解決します。
/// 優先度: ScriptGraphAsset Variables → Graph Variables → Object Variables → 自身の GameObject → 静的キャッシュ → シーン検索。
Expand Down
Loading