diff --git a/Docs/VS_Scratch_Mapping.md b/Docs/VS_Scratch_Mapping.md index 2eac337..e4dca19 100644 --- a/Docs/VS_Scratch_Mapping.md +++ b/Docs/VS_Scratch_Mapping.md @@ -49,6 +49,11 @@ Scratch ブロックと FUnity 独自 Visual Scripting Unit の対応関係で ## 見た目 | Scratch ブロック (日本語) | FUnity Unit クラス | UnitTitle | UnitCategory | 備考 | | --- | --- | --- | --- | --- | +| 背景を (背景の番号) にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SetBackgroundByIndexUnit | 背景を〇にする | FUnity/Blocks/見た目 | StageBackgroundInfo のリストを 1 始まり番号で参照し、FUnityManager 経由で StageBackgroundService を更新。定義: Runtime/.../BackgroundUnits.cs | +| 背景を (背景の名前) にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SetBackgroundByNameUnit | 背景を (名前) にする | FUnity/Blocks/見た目 | DisplayName 一致で背景を切替。未一致時は警告のみで維持。定義: Runtime/.../BackgroundUnits.cs | +| 次の背景にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.NextBackgroundUnit | 次の背景にする | FUnity/Blocks/見た目 | 背景一覧を循環させ、末尾で先頭へ戻る。定義: Runtime/.../BackgroundUnits.cs | +| 背景の番号 | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.GetBackgroundIndexUnit | 背景の番号 | FUnity/Blocks/見た目 | 現在の背景を 1 始まりの番号で返却。背景未設定時は 0。定義: Runtime/.../BackgroundUnits.cs | +| 背景の名前 | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.GetBackgroundNameUnit | 背景の名前 | FUnity/Blocks/見た目 | 現在の背景 DisplayName を返却。定義: Runtime/.../BackgroundUnits.cs | | コスチュームを ( ) にする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.SetCostumeNumberUnit | コスチュームを〇にする | FUnity/Scratch/見た目 | ActorState.CostumeIndex と FUnityActorData.Sprites を利用。定義: Runtime/.../CostumeUnits.cs | | 次のコスチュームにする | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.NextCostumeUnit | 次のコスチュームにする | FUnity/Scratch/見た目 | コスチュームを 1 → 2 → … → N → 1 と循環。定義: Runtime/.../CostumeUnits.cs | | コスチュームの番号 | FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits.CostumeNumberUnit | コスチュームの番号 | FUnity/Scratch/見た目 | 1 始まりの Scratch コスチューム番号を返す。定義: Runtime/.../CostumeUnits.cs | diff --git a/Runtime/Core/FUnityManager.cs b/Runtime/Core/FUnityManager.cs index 0405421..6b4468c 100644 --- a/Runtime/Core/FUnityManager.cs +++ b/Runtime/Core/FUnityManager.cs @@ -1,4 +1,5 @@ // Updated: 2025-02-14 +using System; using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -134,6 +135,15 @@ private readonly Dictionary> m_Sp /// マウス座標を監視し、Scratch 座標系へ変換するサービス。 private MousePositionService m_MousePositionService; + /// ステージに登録されている背景一覧。空コレクションは背景未登録を示す。 + private IReadOnlyList m_StageBackgrounds = Array.Empty(); + + /// 現在適用中の背景インデックス(0 始まり)。背景未設定時は -1。 + private int m_CurrentBackgroundIndex = -1; + + /// 背景スケールのキーワード。StageData から取得し、背景差し替え時に再利用する。 + private string m_CurrentBackgroundScale = FUnityStageData.BackgroundScaleContain; + /// 変数モニターの UI 表示を担当する Presenter。 private VariableUiPresenter m_VariableUiPresenter; @@ -1476,20 +1486,198 @@ private void ApplyStage(StageElement stageElement, FUnityStageData stage) stageElement?.Configure(stage); ApplyScratchStageSizing(stageElement); + UpdateStageBackgroundState(stage); + } + + /// + /// ステージ背景の状態を更新し、現在の背景色・画像を StageBackgroundService へ適用する。 + /// + /// 適用対象のステージ設定。null の場合は背景一覧を初期化する。 + private void UpdateStageBackgroundState(FUnityStageData stage) + { if (stage == null) { + m_StageBackgrounds = Array.Empty(); + m_CurrentBackgroundIndex = -1; + m_CurrentBackgroundScale = FUnityStageData.BackgroundScaleContain; return; } + m_StageBackgrounds = stage.Backgrounds ?? Array.Empty(); + m_CurrentBackgroundScale = stage.BackgroundScale; + m_StageBackgroundService.SetBackgroundColor(stage.BackgroundColor); - if (stage.BackgroundImage != null) + if (m_StageBackgrounds.Count == 0) + { + m_CurrentBackgroundIndex = -1; + m_StageBackgroundService.SetBackgroundFromResources(DefaultStageBackgroundName, m_CurrentBackgroundScale); + return; + } + + var defaultIndex = Mathf.Clamp(stage.DefaultBackgroundIndex, 0, m_StageBackgrounds.Count - 1); + ApplyBackgroundByIndexInternal(defaultIndex, false); + } + + /// + /// 指定したインデックスの背景を適用し、現在の背景インデックスを更新する。 + /// + /// 0 始まりの背景インデックス。 + /// 範囲外の値を循環させる場合は true。 + private void ApplyBackgroundByIndexInternal(int zeroBasedIndex, bool allowWrap) + { + if (m_StageBackgrounds == null || m_StageBackgrounds.Count == 0) + { + Debug.LogWarning("[FUnity] Stage 背景が未登録のため背景を切り替えられません。"); + return; + } + + var count = m_StageBackgrounds.Count; + var normalized = zeroBasedIndex; + + if (allowWrap) + { + normalized = ((zeroBasedIndex % count) + count) % count; + } + + normalized = Mathf.Clamp(normalized, 0, count - 1); + + var info = m_StageBackgrounds[normalized]; + if (info == null) { - m_StageBackgroundService.SetBackground(stage.BackgroundImage, stage.BackgroundScale); + Debug.LogWarning($"[FUnity] インデックス {normalized} の StageBackgroundInfo が null です。"); return; } - m_StageBackgroundService.SetBackgroundFromResources(DefaultStageBackgroundName, stage.BackgroundScale); + m_CurrentBackgroundIndex = normalized; + ApplyBackgroundFromInfo(info); + } + + /// + /// 背景情報から StageBackgroundService へテクスチャを適用する。 + /// + /// 適用対象の背景情報。 + private void ApplyBackgroundFromInfo(StageBackgroundInfo info) + { + if (info == null) + { + return; + } + + if (info.Sprite != null && info.Sprite.texture != null) + { + m_StageBackgroundService.SetBackground(info.Sprite.texture, m_CurrentBackgroundScale); + return; + } + + Debug.LogWarning($"[FUnity] 背景 '{info.DisplayName}' に Sprite が割り当てられていないため既定背景へフォールバックします。"); + m_StageBackgroundService.SetBackgroundFromResources(DefaultStageBackgroundName, m_CurrentBackgroundScale); + } + + /// + /// 背景数を返します。未設定時は 0 を返します。 + /// + /// 登録されている背景の数。 + private int GetBackgroundCountInternal() + { + return m_StageBackgrounds?.Count ?? 0; + } + + /// + /// 現在の背景番号(1 始まり)を返します。未設定時は 0。 + /// + /// 現在の背景番号。範囲外の場合は 0。 + private int GetCurrentBackgroundNumberInternal() + { + if (m_CurrentBackgroundIndex < 0) + { + return 0; + } + + return m_CurrentBackgroundIndex + 1; + } + + /// + /// 現在の背景名を返します。未設定時は空文字列。 + /// + /// 背景表示名。 + private string GetCurrentBackgroundNameInternal() + { + if (m_CurrentBackgroundIndex < 0 || m_StageBackgrounds == null || m_CurrentBackgroundIndex >= m_StageBackgrounds.Count) + { + return string.Empty; + } + + var info = m_StageBackgrounds[m_CurrentBackgroundIndex]; + return info != null ? info.DisplayName : string.Empty; + } + + /// + /// 1 始まりの番号指定で背景を切り替える。 + /// + /// ユーザー向けの背景番号。 + private void SetBackgroundByNumberInternal(int backgroundNumber) + { + if (m_StageBackgrounds == null || m_StageBackgrounds.Count == 0) + { + Debug.LogWarning("[FUnity] 背景が 1 件も登録されていないため切り替えできません。"); + return; + } + + var zeroBased = Mathf.Clamp(backgroundNumber - 1, 0, m_StageBackgrounds.Count - 1); + ApplyBackgroundByIndexInternal(zeroBased, false); + } + + /// + /// 背景名で一致するエントリを探し、見つかれば適用する。 + /// + /// 検索する背景名。 + private void SetBackgroundByNameInternal(string backgroundName) + { + if (string.IsNullOrWhiteSpace(backgroundName)) + { + Debug.LogWarning("[FUnity] 空の背景名が指定されたため切り替えをスキップします。"); + return; + } + + if (m_StageBackgrounds == null || m_StageBackgrounds.Count == 0) + { + Debug.LogWarning("[FUnity] 背景が 1 件も登録されていないため名前で切り替えできません。"); + return; + } + + for (var i = 0; i < m_StageBackgrounds.Count; i++) + { + var info = m_StageBackgrounds[i]; + if (info == null) + { + continue; + } + + if (string.Equals(info.DisplayName, backgroundName, StringComparison.Ordinal) || + string.Equals(info.DisplayName, backgroundName, StringComparison.OrdinalIgnoreCase)) + { + ApplyBackgroundByIndexInternal(i, false); + return; + } + } + + Debug.LogWarning($"[FUnity] 背景名 '{backgroundName}' に一致するエントリが見つかりません。"); + } + + /// + /// 現在の背景を 1 つ進め、末尾に達したら先頭へ戻す。 + /// + private void NextBackgroundInternal() + { + if (m_StageBackgrounds == null || m_StageBackgrounds.Count == 0) + { + Debug.LogWarning("[FUnity] 背景が 1 件も登録されていないため次の背景へ進めません。"); + return; + } + + var nextIndex = m_CurrentBackgroundIndex + 1; + ApplyBackgroundByIndexInternal(nextIndex, true); } /// @@ -2159,6 +2347,101 @@ public static ActorPresenter CloneActor(ActorPresenter original) return manager.CloneActorInternal(original); } + /// + /// 登録済みの背景数を返します。マネージャー未初期化時は 0。 + /// + /// 背景の総数。 + public static int GetBackgroundCount() + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.GetBackgroundCount: FUnityManager が見つからないため背景数を取得できません。"); + return 0; + } + + return manager.GetBackgroundCountInternal(); + } + + /// + /// 現在表示している背景番号(1 始まり)を返します。 + /// + /// 現在の背景番号。背景が無い場合は 0。 + public static int GetCurrentBackgroundNumber() + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.GetCurrentBackgroundNumber: FUnityManager が見つからないため背景番号を取得できません。"); + return 0; + } + + return manager.GetCurrentBackgroundNumberInternal(); + } + + /// + /// 現在表示している背景名を返します。 + /// + /// 背景名。未設定時は空文字列。 + public static string GetCurrentBackgroundName() + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.GetCurrentBackgroundName: FUnityManager が見つからないため背景名を取得できません。"); + return string.Empty; + } + + return manager.GetCurrentBackgroundNameInternal(); + } + + /// + /// 1 始まりの番号で背景を切り替えます。範囲外の値はクランプします。 + /// + /// ユーザー向けの背景番号。 + public static void SetBackgroundByNumber(int backgroundNumber) + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.SetBackgroundByNumber: FUnityManager が見つからないため背景を切り替えできません。"); + return; + } + + manager.SetBackgroundByNumberInternal(backgroundNumber); + } + + /// + /// 背景名で一致するエントリへ切り替えます。一致しない場合は変更しません。 + /// + /// 検索する背景名。 + public static void SetBackgroundByName(string backgroundName) + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.SetBackgroundByName: FUnityManager が見つからないため背景を切り替えできません。"); + return; + } + + manager.SetBackgroundByNameInternal(backgroundName); + } + + /// + /// 次の背景へ進め、末尾では先頭へ戻します。 + /// + public static void NextBackground() + { + var manager = Instance != null ? Instance : FindObjectOfType(); + if (manager == null) + { + Debug.LogWarning("[FUnity] FUnityManager.NextBackground: FUnityManager が見つからないため背景を進められません。"); + return; + } + + manager.NextBackgroundInternal(); + } + /// /// Scratch の「緑の旗が押されたとき」イベントを全俳優(クローン除く)へ配信します。 /// diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs new file mode 100644 index 0000000..7d185d3 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs @@ -0,0 +1,235 @@ +// Updated: 2025-11-05 +using Unity.VisualScripting; +using FUnity.Runtime.Core; +using FUnity.Runtime.Integrations.VisualScripting; + +namespace FUnity.Runtime.Integrations.VisualScripting.Units.ScratchUnits +{ + /// + /// Scratch の「背景を (背景の番号) にする」に対応し、ステージ背景を番号指定で切り替える Unit です。 + /// + [UnitTitle("背景を〇にする")] + [UnitCategory("FUnity/Blocks/見た目")] + [UnitSubtitle("見た目")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class SetBackgroundByIndexUnit : Unit + { + /// フロー入力ポートです。 + [DoNotSerialize] + private ControlInput m_Input; + + /// フロー出力ポートです。 + [DoNotSerialize] + private ControlOutput m_Output; + + /// 背景番号(1 始まり)を受け取る入力ポートです。 + [DoNotSerialize] + private ValueInput m_Index; + + /// 入力ポートへの参照です。 + public ControlInput Input => m_Input; + + /// 出力ポートへの参照です。 + public ControlOutput Output => m_Output; + + /// 背景番号ポートへの参照です。 + public ValueInput Index => m_Index; + + /// + /// ポート定義を行い、背景番号入力と制御フローを登録します。 + /// + protected override void Definition() + { + m_Input = ControlInput("enter", OnEnter); + m_Output = ControlOutput("exit"); + m_Index = ValueInput("index", 1); + + Succession(m_Input, m_Output); + Requirement(m_Index, m_Input); + } + + /// + /// フロー入力時に背景番号を取得し、FUnityManager へ切り替えを依頼します。 + /// + /// 現在のフロー情報。 + /// 後続へ続く ControlOutput。 + private ControlOutput OnEnter(Flow flow) + { + var index = flow.GetValue(m_Index); + FUnityManager.SetBackgroundByNumber(index); + return m_Output; + } + } + + /// + /// Scratch の「背景を (背景の名前) にする」に対応し、表示名でステージ背景を切り替える Unit です。 + /// + [UnitTitle("背景を (名前) にする")] + [UnitCategory("FUnity/Blocks/見た目")] + [UnitSubtitle("見た目")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class SetBackgroundByNameUnit : Unit + { + /// フロー入力ポートです。 + [DoNotSerialize] + private ControlInput m_Input; + + /// フロー出力ポートです。 + [DoNotSerialize] + private ControlOutput m_Output; + + /// 背景名を受け取る入力ポートです。 + [DoNotSerialize] + private ValueInput m_Name; + + /// 入力ポートへの参照です。 + public ControlInput Input => m_Input; + + /// 出力ポートへの参照です。 + public ControlOutput Output => m_Output; + + /// 背景名ポートへの参照です。 + public ValueInput Name => m_Name; + + /// + /// ポート定義を行い、背景名入力と制御フローを登録します。 + /// + protected override void Definition() + { + m_Input = ControlInput("enter", OnEnter); + m_Output = ControlOutput("exit"); + m_Name = ValueInput("name", string.Empty); + + Succession(m_Input, m_Output); + Requirement(m_Name, m_Input); + } + + /// + /// フロー入力時に背景名を取得し、FUnityManager へ切り替えを依頼します。 + /// + /// 現在のフロー情報。 + /// 後続へ続く ControlOutput。 + private ControlOutput OnEnter(Flow flow) + { + var name = flow.GetValue(m_Name); + FUnityManager.SetBackgroundByName(name); + return m_Output; + } + } + + /// + /// Scratch の「次の背景にする」に対応し、背景を循環切り替えする Unit です。 + /// + [UnitTitle("次の背景にする")] + [UnitCategory("FUnity/Blocks/見た目")] + [UnitSubtitle("見た目")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class NextBackgroundUnit : Unit + { + /// フロー入力ポートです。 + [DoNotSerialize] + private ControlInput m_Input; + + /// フロー出力ポートです。 + [DoNotSerialize] + private ControlOutput m_Output; + + /// 入力ポートへの参照です。 + public ControlInput Input => m_Input; + + /// 出力ポートへの参照です。 + public ControlOutput Output => m_Output; + + /// + /// ポート定義を行い、制御フローのみを登録します。 + /// + protected override void Definition() + { + m_Input = ControlInput("enter", OnEnter); + m_Output = ControlOutput("exit"); + + Succession(m_Input, m_Output); + } + + /// + /// フロー入力時に次の背景へ進めます。 + /// + /// 現在のフロー情報。 + /// 後続へ続く ControlOutput。 + private ControlOutput OnEnter(Flow flow) + { + FUnityManager.NextBackground(); + return m_Output; + } + } + + /// + /// Scratch の「背景の番号」に対応し、現在の背景番号を返すレポーターユニットです。 + /// + [UnitTitle("背景の番号")] + [UnitCategory("FUnity/Blocks/見た目")] + [UnitSubtitle("見た目")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class GetBackgroundIndexUnit : Unit + { + /// 背景番号を出力するポートです。 + [DoNotSerialize] + private ValueOutput m_Index; + + /// 背景番号ポートへの参照です。 + public ValueOutput Index => m_Index; + + /// + /// ポート定義を行い、背景番号出力を登録します。 + /// + protected override void Definition() + { + m_Index = ValueOutput("index", GetBackgroundNumber); + } + + /// + /// 現在の背景番号(1 始まり)を取得します。 + /// + /// 現在のフロー情報。 + /// 背景番号。背景未設定時は 0。 + private int GetBackgroundNumber(Flow flow) + { + return FUnityManager.GetCurrentBackgroundNumber(); + } + } + + /// + /// Scratch の「背景の名前」に対応し、現在の背景名を返すレポーターユニットです。 + /// + [UnitTitle("背景の名前")] + [UnitCategory("FUnity/Blocks/見た目")] + [UnitSubtitle("見た目")] + [TypeIcon(typeof(FUnityScratchUnitIcon))] + public sealed class GetBackgroundNameUnit : Unit + { + /// 背景名を出力するポートです。 + [DoNotSerialize] + private ValueOutput m_Name; + + /// 背景名ポートへの参照です。 + public ValueOutput Name => m_Name; + + /// + /// ポート定義を行い、背景名出力を登録します。 + /// + protected override void Definition() + { + m_Name = ValueOutput("name", GetBackgroundName); + } + + /// + /// 現在の背景名を取得します。 + /// + /// 現在のフロー情報。 + /// 背景名。未設定時は空文字列。 + private string GetBackgroundName(Flow flow) + { + return FUnityManager.GetCurrentBackgroundName(); + } + } +} diff --git a/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs.meta b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs.meta new file mode 100644 index 0000000..dfa2b37 --- /dev/null +++ b/Runtime/Integrations/VisualScripting/Units/ScratchUnits/BackgroundUnits.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 3cc0b4b380dd4e0b8e6b9fa8ea70b6c6