Skip to content

UIライブラリの実装 #94

Description

@EbiseLutica

概要

Prometeにビルトインの UIライブラリを実装する。既存のノードシステムの上に構築し、ボタン・テキスト入力・モーダルダイアログなどの基本的なUI要素を提供する。

設計方針

  • ノードシステムベース: UIElement は ContainableNode を継承し、既存の ZIndex・Transform・Trim・レンダリングパイプラインをそのまま活用する
  • Shapeベースのデフォルト描画: テクスチャアセットをエンジンに同梱せず、Shape による最低限の見た目をデフォルトとする。NineSliceSprite によるカスタムデザインはオプション
  • プラガブルなスタイル: IUIStyle<TElement> で描画ロジックを分離し、ゲームごとにデザインを自由に差し替え可能にする
  • レイアウトシステムとの共存余地: UIElement は通常の Node なので、将来 FlexBox 等のレイアウトライブラリを別途作った際にそのまま併用できる

主要コンポーネント

UIManager(DIプラグイン)

var app = PrometeApp.Create()
    .Use<Keyboard>()
    .Use<Mouse>()
    .Use<UIManager>()
    .BuildWithOpenGLDesktop();

責務:

  • ヒットテスト: 毎フレーム、マウス位置に対してUI要素をZIndex順(手前→奥)に走査
  • フォーカス管理: キーボード/ゲームパッドでのフォーカス移動、FocusGroup によるスコープ管理
  • モーダルスタック: モーダル表示中はモーダル以下の要素へのイベントを遮断
  • 入力ディスパッチ: 生の入力を UIイベント(Click, Hover, Submit, Cancel 等)に変換

UIElement 基底クラス

public abstract class UIElement : ContainableNode
{
    public virtual bool HitTest(VectorInt point);
    public bool IsInteractable { get; set; } = true;
    public bool IsFocusable { get; set; } = true;
    public int NavigationOrder { get; set; }
    
    // スタイル
    public UIElementState CurrentState { get; }
    
    // 仮想メソッド(継承でオーバーライド)
    protected virtual void OnPointerEnter();
    protected virtual void OnPointerLeave();
    protected virtual void OnPointerDown(PointerEventArgs e);
    protected virtual void OnPointerUp(PointerEventArgs e);
    protected virtual void OnFocus();
    protected virtual void OnBlur();
    protected virtual void OnSubmit();
    protected virtual void OnCancel();
    
    // 外部フック用イベント
    public event Action? Clicked;
}

IUIStyle(スタイルシステム)

public interface IUIStyle<TElement> where TElement : UIElement
{
    void Apply(TElement element, UIElementState state);
}
  • DefaultButtonStyle: Shape ベース(色指定のみ、テクスチャ不要)
  • NineSliceButtonStyle: ユーザー提供の Texture9Sliced で描画

状態変化時のみ Apply() を呼び出す(毎フレームではない)。

入力バインディング(InputMap)

マウス・キーボード・ゲームパッドの入力を統一的な「アクション」に抽象化する。

public class InputMap
{
    public InputAction CreateAction(string name);
    public void Bind(string actionName, KeyCode key);
    public void Bind(string actionName, MouseButtonType button);
    public void Bind(string actionName, GamepadButtonType button);
}

UIManager は内部的にデフォルトの InputMap を持ち、フォーカス移動や Submit/Cancel をバインドする。ユーザーが自由にリバインド可能。

プログラムからの仮想入力発火

InputMap はプログラムから直接アクションを発火できるAPIを提供する。

public class InputMap
{
    /// <summary>
    /// 指定したアクションをプログラムから発火する。
    /// 実際のデバイス入力と同等に扱われる。
    /// </summary>
    public void Fire(string actionName);
    
    /// <summary>
    /// 指定したアクションの押下状態を制御する(押し続け等のシミュレーション用)。
    /// </summary>
    public void SetPressed(string actionName, bool pressed);
}

用途:

  • 仮想入力: タッチスクリーンの仮想ボタン、アクセシビリティ支援など、アプリケーション側で独自の入力ソースを実装する場合
  • テスト: ヘッドレスバックエンドと組み合わせ、UIの自動テストをデバイス入力なしで実行
  • デバッグ: 開発中にスクリプトやデバッグコンソールからUI操作を再現
// テストでの使用例
var inputMap = app.GetPlugin<InputMap>();
inputMap.Fire("Submit");  // Enterキー押下と同等の効果

// 押し続けのシミュレーション
inputMap.SetPressed("MoveRight", true);
// ... 数フレーム後 ...
inputMap.SetPressed("MoveRight", false);

UI要素一覧

要素 デフォルト描画構成 備考
Button Shape(矩形+ボーダー) + Text 状態で色変更
TextInput Shape(矩形+ボーダー) + Text(値) + Text(placeholder) + Shape(カーソル) Keyboard.GetChar() でテキスト入力
Checkbox Shape(矩形) + Shape(チェックマーク) + Text(ラベル) トグル状態
Slider Shape(トラック) + Shape(つまみ) ドラッグで float 値操作
ScrollView Container(IsTrimmable) + Shape(スクロールバー) 既存の Trim 機構を活用
Panel Shape(矩形背景) 子ノードのグループ化
Modal Shape(全画面半透明オーバーレイ) + Panel UIManager.PushModal() で表示

モーダルとヒットテスト

  • モーダルスタックの最上位のみがイベントを受け取る
  • モーダル外クリックはイベントとして発火(閉じる動作に利用可能)
  • モーダルがない場合は全 UIElement を ZIndex 降順で走査し、最初にヒットした要素で停止

フォーカスナビゲーション

  • NavigationOrder による Tab/上下キーでの順次移動
  • 空間ナビゲーション(AbsoluteLocation ベースで方向キーの最近傍要素を探索)をオプション対応
  • FocusGroup(Container 単位)でフォーカススコープを区切る(モーダル内ではモーダルの子だけが対象)

プロジェクト構造

Promete/UI/
├── UIManager.cs
├── UIElement.cs
├── UIElementState.cs
├── FocusGroup.cs
├── InputMap.cs
├── InputAction.cs
├── Styles/
│   └── IUIStyle.cs
├── Elements/
│   ├── Button.cs
│   ├── TextInput.cs
│   ├── Checkbox.cs
│   ├── Slider.cs
│   ├── ScrollView.cs
│   ├── Panel.cs
│   └── Modal.cs
└── Events/
    └── PointerEventArgs.cs

Promete 本体 (Promete.csproj) に含める。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions