Skip to content

13 · 自定义 UI 面板 ​

游戏需要存档界面、设置面板、CG 鉴赏等 UI。本章讲解如何用 C# 实现自定义 Overlay 面板。

Overlay 系统 ​

灵泛引擎的 Overlay 系统允许在场景之上叠加 UI 面板。这些面板独立于 DSL 场景,由 C# 管理。

面板类型 ​

面板用途触发方式
存档/读档面板槽位选择、缩略图展示cmd="open_save" / cmd="open_load"
设置面板音量、文字速度、全屏cmd="open_settings"
历史面板对话历史列表cmd="open_history"
CG 鉴赏面板已解锁 CG 展示cmd="open_gallery"
快捷菜单右键菜单右键点击

button 的 cmd 参数 ​

DSL 中的 button 通过 cmd= 触发 C# Overlay 命令:

dsl
button "保存" x=50% y=50% cmd="open_save"
button "设置" x=50% y=60% cmd="open_settings"
button "退出" x=50% y=70% cmd="do_exit"

引擎将这些命令字符串映射到 C# 侧的处理器。

实现自定义面板 ​

1. 创建面板控件 ​

继承 Avalonia 的 UserControl:

csharp
public class SaveLoadPanel : UserControl
{
    private readonly IGameController _game;

    public SaveLoadPanel(IGameController game)
    {
        _game = game;
        // 构建 UI:槽位列表、缩略图、按钮
        var grid = new Grid();
        // ... 添加 UI 元素
        Content = grid;
    }

    public async Task SaveAsync(string slot)
    {
        await _game.SaveAsync(slot);
        // 更新 UI 显示
    }
}

2. 注册到 OverlayManager ​

csharp
public class OverlayManager
{
    private readonly SaveLoadPanel _savePanel;

    public void ShowSavePanel()
    {
        _savePanel.IsVisible = true;
    }

    public void HideAll()
    {
        _savePanel.IsVisible = false;
    }
}

3. 处理 cmd 命令 ​

在 MainWindow.cs 或命令处理器中,将 cmd 字符串映射到面板显示:

csharp
private void HandleCommand(string cmd)
{
    switch (cmd)
    {
        case "open_save":
            _overlayManager.ShowSavePanel();
            break;
        case "open_load":
            _overlayManager.ShowLoadPanel();
            break;
        case "open_settings":
            _overlayManager.ShowSettingsPanel();
            break;
        case "do_exit":
            // 引擎不提供统一退出 API;退出由宿主层负责。
            // Demo 标准做法(Avalonia):
            if (Avalonia.Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                desktop.Shutdown();
            break;
    }
}

call_screen 方式 ​

除了 cmd,还可以用 call_screen 调用 UI 场景并获取返回值:

dsl
label main:
  call_screen "save_panel" store="result"
  if {result == "saved"}
    say "存档成功!" speaker="系统"
  navigate "next"

call_screen 会暂停当前剧情,显示 UI 场景,玩家操作完成后返回。

Demo 项目的面板实现 ​

Demo 项目(src/Demo/LingFanEngine.Entry/UI/)包含完整的面板实现可供参考:

文件功能
SaveLoadPanel.cs存档/读档界面
SettingsPanel.cs设置面板
HistoryPanel.cs对话历史
GalleryPanel.csCG 鉴赏
QuickMenuPanel.cs右键快捷菜单
DebugConsolePanel.cs调试控制台
OverlayManager.csOverlay 统一管理

参考实现

建议直接阅读 Demo 项目的这些文件,它们展示了完整的面板实现模式。

游戏内工具栏 ​

Demo 项目还实现了一个游戏内工具栏,包含常用功能按钮:

csharp
public class GameToolbar : UserControl
{
    // 返回 / 历史 / 跳过 / 自动 / 保存 / 快存 / 快读 / 设置
}

工具栏通过快捷键和 UI 按钮触发:

快捷键功能
Ctrl+S存档
Ctrl+L读档
Esc关闭面板
右键快捷菜单
滚轮上/下回溯/前进

自定义 cmd 命令 ​

你可以扩展 cmd 命令系统,添加自定义命令:

csharp
private void HandleCommand(string cmd)
{
    switch (cmd)
    {
        case "open_shop":
            _overlayManager.ShowShopPanel();
            break;
        case "open_inventory":
            _overlayManager.ShowInventoryPanel();
            break;
        // ... 自定义命令
    }
}

然后在 DSL 中使用:

dsl
button "商店" cmd="open_shop"
button "背包" cmd="open_inventory"

动手练习 ​

实现一个简单的确认对话框:

csharp
public class ConfirmDialog : UserControl
{
    public Task<bool> ShowAsync(string message)
    {
        var tcs = new TaskCompletionSource<bool>();

        var text = new TextBlock { Text = message };
        var yesBtn = new Button { Content = "是" };
        var noBtn = new Button { Content = "否" };

        yesBtn.Click += (_, _) => { tcs.SetResult(true); IsVisible = false; };
        noBtn.Click += (_, _) => { tcs.SetResult(false); IsVisible = false; };

        // 布局...
        Content = new StackPanel { Children = { text, yesBtn, noBtn } };
        IsVisible = true;

        return tcs.Task;
    }
}

下一步 ​

UI 面板掌握了!下一章学习自定义对话框模板——创建完全自定义的对话框外观。

基于 .NET 10 + Avalonia 12 · Apache-2.0 License