diff --git a/Docs/VS_Scratch_Mapping.md b/Docs/VS_Scratch_Mapping.md index 7a7bab1..9c558f5 100644 --- a/Docs/VS_Scratch_Mapping.md +++ b/Docs/VS_Scratch_Mapping.md @@ -37,6 +37,9 @@ Scratch ブロックと FUnity 独自 Visual Scripting Unit の対応関係で ## 見た目 | Scratch ブロック (日本語) | FUnity Unit クラス | UnitTitle | UnitCategory | 備考 | | --- | --- | --- | --- | --- | +| コスチュームを○にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SetCostumeNumberUnit | コスチュームを○にする | FUnity/Scratch/見た目 | コスチューム番号を絶対設定。定義: Runtime/.../CostumeUnits.cs | +| 次のコスチュームにする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.NextCostumeUnit | 次のコスチュームにする | FUnity/Scratch/見た目 | コスチュームを循環切替。定義: Runtime/.../CostumeUnits.cs | +| コスチュームの番号 | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.GetCostumeNumberUnit | コスチュームの番号 | FUnity/Scratch/見た目 | 現在の番号を取得。定義: Runtime/.../CostumeUnits.cs | | 大きさを○%にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SetSizePercentUnit | 大きさを○%にする | FUnity/Scratch/見た目 | 拡大率を絶対設定。定義: Runtime/.../SizeUnits.cs | | 大きさを○%ずつ変える | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.ChangeSizeByPercentUnit | 大きさを○%ずつ変える | FUnity/Scratch/見た目 | 拡大率を相対変更。定義: Runtime/.../SizeUnits.cs | | ○と○秒言う | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SayForSecondsUnit | ○と○秒言う | FUnity/Scratch/見た目 | 指定秒数で吹き出し表示。定義: Runtime/.../SpeechUnits.cs | diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs new file mode 100644 index 0000000..331eed9 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs @@ -0,0 +1,235 @@ +// Updated: 2025-05-22 +using System.Collections; +using UnityEngine; +using Unity.VisualScripting; +using FUnity.Runtime.Integrations.VisualScripting; +using FUnity.Runtime.Presenter; + +namespace FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits +{ + /// + /// Scratch の「コスチュームを ◯ にする」ブロックに対応し、Presenter を通じてコスチューム番号を絶対設定する Visual Scripting Unit です。 + /// + [UnitTitle("コスチュームを○にする")] + [UnitCategory("FUnity/Scratch/見た目")] + [UnitSubtitle("funity scratch 見た目 costume set コスチューム")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class SetCostumeNumberUnit : Unit + { + /// ログ出力に利用するユニット名です。 + private const string UnitName = "コスチュームを○にする"; + + /// 制御フローを受け取る ControlInput です。 + [DoNotSerialize] + private ControlInput m_Enter; + + /// 後続へ制御を渡す ControlOutput です。 + [DoNotSerialize] + private ControlOutput m_Exit; + + /// 1 始まりのコスチューム番号を受け取る ValueInput です。 + [DoNotSerialize] + private ValueInput m_CostumeNumber; + + /// enter ポートへの参照です。 + public ControlInput Enter => m_Enter; + + /// exit ポートへの参照です。 + public ControlOutput Exit => m_Exit; + + /// costumeNumber ポートへの参照です。 + public ValueInput CostumeNumber => m_CostumeNumber; + + /// + /// ポート定義を行い、enter→exit の制御線とコスチューム番号入力を登録します。 + /// + protected override void Definition() + { + m_Enter = ControlInputCoroutine("enter", Run); + m_Exit = ControlOutput("exit"); + m_CostumeNumber = ValueInput("costumeNumber", 1); + + Succession(m_Enter, m_Exit); + } + + /// + /// フロー入力時に Presenter を解決し、指定されたコスチューム番号を適用します。 + /// + /// 現在のフロー情報。 + /// exit ポートへ制御を渡す列挙子。 + private IEnumerator Run(Flow flow) + { + if (!CostumeUnitUtil.TryResolvePresenter(flow, UnitName, out var presenter)) + { + yield return m_Exit; + yield break; + } + + var spriteCount = presenter.SpriteCount; + if (spriteCount <= 0) + { + presenter.SetSpriteIndex(0); + yield return m_Exit; + yield break; + } + + var costumeNumber = flow.GetValue(m_CostumeNumber); + var clampedNumber = Mathf.Clamp(costumeNumber, 1, spriteCount); + var index0 = clampedNumber - 1; + + presenter.SetSpriteIndex(index0); + yield return m_Exit; + } + } + + /// + /// Scratch の「次のコスチュームにする」ブロックに対応し、コスチュームを循環的に切り替える Visual Scripting Unit です。 + /// + [UnitTitle("次のコスチュームにする")] + [UnitCategory("FUnity/Scratch/見た目")] + [UnitSubtitle("funity scratch 見た目 costume next 次へ")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class NextCostumeUnit : Unit + { + /// ログ出力に利用するユニット名です。 + private const string UnitName = "次のコスチュームにする"; + + /// 制御フローを受け取る ControlInput です。 + [DoNotSerialize] + private ControlInput m_Enter; + + /// 後続へ制御を渡す ControlOutput です。 + [DoNotSerialize] + private ControlOutput m_Exit; + + /// enter ポートへの参照です。 + public ControlInput Enter => m_Enter; + + /// exit ポートへの参照です。 + public ControlOutput Exit => m_Exit; + + /// + /// ポート定義を行い、enter→exit の制御線のみを登録します。 + /// + protected override void Definition() + { + m_Enter = ControlInputCoroutine("enter", Run); + m_Exit = ControlOutput("exit"); + + Succession(m_Enter, m_Exit); + } + + /// + /// フロー入力時に Presenter を解決し、次のコスチュームへ循環的に切り替えます。 + /// + /// 現在のフロー情報。 + /// exit ポートへ制御を渡す列挙子。 + private IEnumerator Run(Flow flow) + { + if (!CostumeUnitUtil.TryResolvePresenter(flow, UnitName, out var presenter)) + { + yield return m_Exit; + yield break; + } + + var spriteCount = presenter.SpriteCount; + if (spriteCount <= 0) + { + presenter.SetSpriteIndex(0); + yield return m_Exit; + yield break; + } + + var current = presenter.SpriteIndex; + var safeCurrent = Mathf.Clamp(current, 0, spriteCount - 1); + var next = (safeCurrent + 1) % spriteCount; + + presenter.SetSpriteIndex(next); + yield return m_Exit; + } + } + + /// + /// Scratch の「コスチュームの番号」ブロックに対応し、現在のコスチューム番号(1 始まり)を取得する Visual Scripting Unit です。 + /// + [UnitTitle("コスチュームの番号")] + [UnitCategory("FUnity/Scratch/見た目")] + [UnitSubtitle("funity scratch 見た目 costume number 番号")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class GetCostumeNumberUnit : Unit + { + /// ログ出力に利用するユニット名です。 + private const string UnitName = "コスチュームの番号"; + + /// 現在のコスチューム番号を出力する ValueOutput です。 + [DoNotSerialize] + private ValueOutput m_CostumeNumber; + + /// costumeNumber ポートへの参照です。 + public ValueOutput CostumeNumber => m_CostumeNumber; + + /// + /// ポート定義を行い、現在のコスチューム番号を返す ValueOutput を登録します。 + /// + protected override void Definition() + { + m_CostumeNumber = ValueOutput("costumeNumber", GetCostumeNumber); + } + + /// + /// Presenter を解決して現在のコスチューム番号(1 始まり)を返します。 + /// + /// 現在のフロー情報。 + /// 1 始まりで表現したコスチューム番号。利用可能な Sprite が無い場合は 0。 + private int GetCostumeNumber(Flow flow) + { + if (!CostumeUnitUtil.TryResolvePresenter(flow, UnitName, out var presenter)) + { + return 0; + } + + var spriteCount = presenter.SpriteCount; + if (spriteCount <= 0) + { + return 0; + } + + var index0 = Mathf.Clamp(presenter.SpriteIndex, 0, spriteCount - 1); + return index0 + 1; + } + } + + /// + /// コスチューム系ユニットで共通利用する Presenter 解決ロジックをまとめたユーティリティです。 + /// + internal static class CostumeUnitUtil + { + /// + /// ActorPresenterAdapter および ActorPresenter を解決し、失敗時には警告ログを出力します。 + /// + /// 現在のフロー情報。 + /// ログ出力に使用するユニット名。 + /// 解決できた Presenter。失敗時は null。 + /// 解決に成功した場合は true + public static bool TryResolvePresenter(Flow flow, string unitName, out ActorPresenter presenter) + { + presenter = null; + + var adapter = ScratchUnitUtil.ResolveAdapter(flow); + if (adapter == null) + { + Debug.LogWarning($"[FUnity] Scratch/Looks/{unitName}: ActorPresenterAdapter を自動解決できません。ScriptMachine の Variables 設定を確認してください。"); + return false; + } + + presenter = adapter.Presenter; + if (presenter == null) + { + Debug.LogWarning($"[FUnity] Scratch/Looks/{unitName}: ActorPresenter が未接続のためコスチュームを処理できません。Adapter と Presenter の紐付けを確認してください。"); + return false; + } + + return true; + } + } +} diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs.meta b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs.meta new file mode 100644 index 0000000..11b8ecc --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/CostumeUnits.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ffb3ccd41ff7495cb4b0f1fabbf42585