Skip to content

性能优化技巧 ​

本章总结灵泛引擎的性能优化最佳实践。

AOT 编译 ​

启用 AOT ​

xml
<!-- 在平台 .csproj 中 -->
<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>

AOT 友好原则 ​

避免推荐
反射显式类型匹配
动态代码生成Source Generator
dynamic 类型具体类型
运行时 JsonSerializerJsonSerializerContext

引擎已内置

灵泛引擎的核心代码已经完全 AOT 友好——命令处理器用显式 switch 类型匹配,JSON 序列化用 source generator。

内存优化 ​

避免频繁分配 ​

csharp
// ❌ 每帧分配
public void Update()
{
    var list = new List<int>();  // 每帧 new
    // ...
}

// ✅ 复用
private readonly List<int> _list = new();

public void Update()
{
    _list.Clear();
    // ...
}

使用 Span / Memory ​

csharp
// ❌ 分配数组
byte[] data = new byte[256];

// ✅ 栈分配(小数据)
Span<byte> data = stackalloc byte[256];

ArrayPool(大数据) ​

csharp
// 大数组用 ArrayPool
byte[] buffer = ArrayPool<byte>.Shared.Rent(8192);
try
{
    // 使用 buffer
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

不可变集合 ​

csharp
// ✅ 不可变数组,遍历快
ImmutableArray<string> names = ImmutableArray.Create("A", "B", "C");

// ✅ 原子更新
ImmutableInterlocked.Update(ref _items, arr => arr.Add("D"));

图片优化 ​

纹理大小 ​

用途建议尺寸
背景图1920×1080
立绘1024×1536
CG1920×1080
图标64×64 / 128×128

图片格式 ​

格式推荐说明
PNG✅无损,适合 UI / 立绘
JPG✅压缩,适合背景 / CG
WebP✅更好的压缩率

按需加载 ​

灵泛引擎的 AvaloniaAssetAccessor 会按需加载图片。避免在场景定义中引用过多大图。

音频优化 ​

格式选择 ​

用途推荐格式理由
BGMMP3压缩,体积小
SEWAV加载快,延迟低
VoiceWAV / MP3根据量决定

音频池 ​

频繁播放的 SE 用音频池避免重复加载:

csharp
// 引擎内置音频池,无需手动管理

DSL 优化 ​

减少场景内命令 ​

dsl
// ❌ 场景内大量命令
scene "town"
  set "a" 1
  set "b" 2
  set "c" 3
  // ... 50 行 set

// ✅ 用 label 组织
scene "town"
  call "init_town_vars"
  text "小镇" x=50% y=10%

label init_town_vars:
  set "a" 1
  set "b" 2
  set "c" 3
  return

避免深层嵌套 ​

dsl
// ❌ 深层嵌套
if {a}
  if {b}
    if {c}
      // ...

// ✅ 合并条件
if {a && b && c}
  // ...

渲染优化 ​

减少 ZIndex 切换 ​

频繁切换 ZIndex 会导致重绘。尽量保持层级稳定。

透明度 vs 可见性 ​

dsl
// ❌ 用透明度隐藏(仍参与渲染)
image "bg.png" opacity=0

// ✅ 用 hide 隐藏(不参与渲染)
hide "bg"

合并图片 ​

多个小图标可以合并为一张图集(sprite sheet),减少绘制调用。

状态管理优化 ​

避免高频 _state.Set ​

csharp
// ❌ 每帧更新
public void Update()
{
    _state.Set("fps", currentFps);  // 每帧
}

// ✅ 节流更新
private double _fpsUpdateTimer;
public void Update(double delta)
{
    _fpsUpdateTimer += delta;
    if (_fpsUpdateTimer >= 0.5)  // 0.5 秒更新一次
    {
        _state.Set("fps", currentFps);
        _fpsUpdateTimer = 0;
    }
}

局部变量优先 ​

dsl
// ❌ 用全局变量存临时数据
set "temp.x" 10
set "temp.y" 20
set "result" {temp.x + temp.y}

// ✅ 用局部变量
let "x" 10
let "y" 20
set "result" {_local_x + _local_y}

移动端优化 ​

目标帧率 ​

csharp
// 在配置中
options.MobileTargetFps = 60;  // 移动端 60 帧足够

资源预热 ​

在 loading 场景预热资源:

dsl
label loading:
  // 预加载图片
  image "Images/chapter1_bg.jpg" x=-100% y=-100% opacity=0
  image "Images/hero.png" x=-100% y=-100% opacity=0
  // 预加载音频
  bgm "Media/bgm_chapter1.mp3" volume=0
  // BGM 会在场景切换时自动停止(DefaultAutoStopBgm=true)
  navigate "chapter1"

内存监控 ​

dsl
debug "Memory: {stats.memory_mb} MB" level=Debug

性能分析 ​

内置性能 HUD ​

csharp
options.ShowPerformanceHud = true;

显示 FPS、内存、绘制调用数。

调试日志 ​

dsl
debug "Checkpoint created at {story.progress}" level=Debug
debug "State keys: {state.count}" level=Trace

发布优化 ​

去除调试符号 ​

xml
<PropertyGroup>
  <DebugType>none</DebugType>
  <DebugSymbols>false</DebugSymbols>
</PropertyGroup>

裁剪未使用代码 ​

xml
<PropertyGroup>
  <PublishTrimmed>true</PublishTrimmed>
  <TrimMode>full</TrimMode>
</PropertyGroup>

单文件发布 ​

xml
<PropertyGroup>
  <PublishSingleFile>true</PublishSingleFile>
  <SelfContained>true</SelfContained>
</PropertyGroup>

性能检查清单 ​

  • [ ] 启用 AOT 编译
  • [ ] 移除调试符号
  • [ ] 图片尺寸合理
  • [ ] 音频格式合适
  • [ ] 避免每帧 _state.Set
  • [ ] 局部变量优先
  • [ ] 场景内命令精简
  • [ ] 移动端帧率适配
  • [ ] 资源预热
  • [ ] 性能 HUD 检查

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