mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
docs(guidelines): 更新错误处理和移动端优化最佳实践
- 添加完整的错误处理最佳实践指南,涵盖Result<T>和Option<T>使用方式 - 补充移动端性能优化策略,包括纹理压缩、对象池、内存管理和电池优化 - 更新单元测试教程中的相关文档链接 - 完善错误处理层次结构和测试示例代码 - 增加移动端UI优化和平台适配最佳实践
This commit is contained in:
parent
519e3a480b
commit
8554f01423
@ -1301,3 +1301,385 @@ public class CircuitBreaker
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 选择合适的错误处理方式
|
||||
|
||||
根据错误类型选择处理方式:
|
||||
|
||||
| 错误类型 | 处理方式 | 示例 |
|
||||
|-----------|-----------------|-------------|
|
||||
| 可预期的业务错误 | Result<T> | 用户输入验证、权限检查 |
|
||||
| 可能不存在的值 | Option<T> | 查找操作、配置读取 |
|
||||
| 不可预期的系统错误 | Exception | 文件 IO、网络错误 |
|
||||
| 不可恢复的错误 | Exception + 日志 | 初始化失败、资源耗尽 |
|
||||
|
||||
### 2. 错误处理的层次结构
|
||||
|
||||
```csharp
|
||||
// 底层:使用 Result 处理业务错误
|
||||
public class InventoryRepository
|
||||
{
|
||||
public Result<Item> GetItem(string itemId)
|
||||
{
|
||||
var item = _items.FirstOrDefault(i => i.Id == itemId);
|
||||
return item != null
|
||||
? Result<Item>.Success(item)
|
||||
: Result<Item>.Failure("物品不存在");
|
||||
}
|
||||
}
|
||||
|
||||
// 中层:组合多个操作
|
||||
public class InventorySystem : AbstractSystem
|
||||
{
|
||||
public Result<Item> UseItem(string itemId)
|
||||
{
|
||||
return _repository.GetItem(itemId)
|
||||
.Bind(item => ValidateItemUsage(item))
|
||||
.Bind(item => ConsumeItem(item))
|
||||
.OnSuccess(item => this.SendEvent(new ItemUsedEvent { Item = item }))
|
||||
.OnFailure(ex => _logger.Warn($"使用物品失败: {ex.Message}"));
|
||||
}
|
||||
}
|
||||
|
||||
// 上层:处理用户交互
|
||||
public class InventoryController : IController
|
||||
{
|
||||
public void OnUseItemButtonClicked(string itemId)
|
||||
{
|
||||
var result = _inventorySystem.UseItem(itemId);
|
||||
|
||||
result.Match(
|
||||
succ: item => ShowSuccessMessage($"使用了 {item.Name}"),
|
||||
fail: ex => ShowErrorMessage(ex.Message)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 避免过度使用异常
|
||||
|
||||
```csharp
|
||||
// ❌ 避免:用异常处理正常流程
|
||||
public Player GetPlayerById(string playerId)
|
||||
{
|
||||
var player = _players.FirstOrDefault(p => p.Id == playerId);
|
||||
if (player == null)
|
||||
throw new PlayerNotFoundException(playerId); // 不应该用异常
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
// ✅ 好的做法:使用 Option
|
||||
public Option<Player> GetPlayerById(string playerId)
|
||||
{
|
||||
var player = _players.FirstOrDefault(p => p.Id == playerId);
|
||||
return player != null
|
||||
? Option<Player>.Some(player)
|
||||
: Option<Player>.None;
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 提供有意义的错误消息
|
||||
|
||||
```csharp
|
||||
// ❌ 避免:模糊的错误消息
|
||||
return Result<Item>.Failure("错误");
|
||||
return Result<Item>.Failure("操作失败");
|
||||
|
||||
// ✅ 好的做法:具体的错误消息
|
||||
return Result<Item>.Failure("物品不存在");
|
||||
return Result<Item>.Failure($"背包空间不足,需要 {required} 格,当前 {available} 格");
|
||||
return Result<Item>.Failure($"物品 {itemName} 不可交易");
|
||||
```
|
||||
|
||||
### 5. 记录错误上下文
|
||||
|
||||
```csharp
|
||||
// ❌ 避免:缺少上下文
|
||||
_logger.Error("保存失败", ex);
|
||||
|
||||
// ✅ 好的做法:包含完整上下文
|
||||
_logger.Log(
|
||||
LogLevel.Error,
|
||||
"保存玩家数据失败",
|
||||
ex,
|
||||
("playerId", playerId),
|
||||
("saveSlot", saveSlot),
|
||||
("dataSize", data.Length),
|
||||
("timestamp", DateTime.UtcNow)
|
||||
);
|
||||
```
|
||||
|
||||
### 6. 优雅降级
|
||||
|
||||
```csharp
|
||||
// ✅ 好的做法:提供降级方案
|
||||
public async Task<Texture> LoadTextureAsync(string path)
|
||||
{
|
||||
// 1. 尝试加载指定纹理
|
||||
var result = await TryLoadTextureAsync(path);
|
||||
if (result.IsSuccess)
|
||||
return result.Match(succ: t => t, fail: _ => null);
|
||||
|
||||
// 2. 尝试加载备用纹理
|
||||
_logger.Warn($"加载纹理失败: {path},使用备用纹理");
|
||||
var fallbackResult = await TryLoadTextureAsync(_fallbackTexturePath);
|
||||
if (fallbackResult.IsSuccess)
|
||||
return fallbackResult.Match(succ: t => t, fail: _ => null);
|
||||
|
||||
// 3. 使用默认纹理
|
||||
_logger.Error("所有纹理加载失败,使用默认纹理");
|
||||
return _defaultTexture;
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 测试错误处理
|
||||
|
||||
```csharp
|
||||
[TestFixture]
|
||||
public class InventorySystemTests
|
||||
{
|
||||
[Test]
|
||||
public void UseItem_ItemNotFound_ShouldReturnFailure()
|
||||
{
|
||||
// Arrange
|
||||
var system = new InventorySystem();
|
||||
var invalidItemId = "invalid_item";
|
||||
|
||||
// Act
|
||||
var result = system.UseItem(invalidItemId);
|
||||
|
||||
// Assert
|
||||
Assert.That(result.IsFaulted, Is.True);
|
||||
Assert.That(result.Exception.Message, Contains.Substring("物品不存在"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UseItem_ItemNotUsable_ShouldReturnFailure()
|
||||
{
|
||||
// Arrange
|
||||
var system = new InventorySystem();
|
||||
var item = new Item { Id = "item1", IsUsable = false };
|
||||
system.AddItem(item);
|
||||
|
||||
// Act
|
||||
var result = system.UseItem("item1");
|
||||
|
||||
// Assert
|
||||
Assert.That(result.IsFaulted, Is.True);
|
||||
Assert.That(result.Exception.Message, Contains.Substring("不可使用"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void UseItem_ValidItem_ShouldReturnSuccess()
|
||||
{
|
||||
// Arrange
|
||||
var system = new InventorySystem();
|
||||
var item = new Item { Id = "item1", IsUsable = true };
|
||||
system.AddItem(item);
|
||||
|
||||
// Act
|
||||
var result = system.UseItem("item1");
|
||||
|
||||
// Assert
|
||||
Assert.That(result.IsSuccess, Is.True);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1: 何时使用 Result,何时使用 Option?
|
||||
|
||||
**A:**
|
||||
|
||||
- 使用 **Result<T>** 当操作可能失败,需要返回错误信息时
|
||||
- 使用 **Option<T>** 当值可能不存在,但不需要错误信息时
|
||||
|
||||
```csharp
|
||||
// 使用 Result:需要知道为什么失败
|
||||
public Result<User> RegisterUser(string username, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username))
|
||||
return Result<User>.Failure("用户名不能为空");
|
||||
|
||||
if (password.Length < 8)
|
||||
return Result<User>.Failure("密码长度至少为 8 个字符");
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
// 使用 Option:只需要知道是否存在
|
||||
public Option<User> FindUserById(string userId)
|
||||
{
|
||||
var user = _users.FirstOrDefault(u => u.Id == userId);
|
||||
return user != null ? Option<User>.Some(user) : Option<User>.None;
|
||||
}
|
||||
```
|
||||
|
||||
### Q2: 如何处理异步操作中的错误?
|
||||
|
||||
**A:** 使用 Result 的异步扩展方法:
|
||||
|
||||
```csharp
|
||||
public async Task<Result<PlayerData>> LoadPlayerDataAsync(string playerId)
|
||||
{
|
||||
return await ResultExtensions.TryAsync(async () =>
|
||||
{
|
||||
var data = await _httpClient.GetStringAsync($"/api/players/{playerId}");
|
||||
return JsonSerializer.Deserialize<PlayerData>(data);
|
||||
});
|
||||
}
|
||||
|
||||
// 链式异步操作
|
||||
public async Task<Result<Player>> LoadAndValidatePlayerAsync(string playerId)
|
||||
{
|
||||
var result = await LoadPlayerDataAsync(playerId);
|
||||
return await result.BindAsync(async data =>
|
||||
{
|
||||
var isValid = await ValidatePlayerDataAsync(data);
|
||||
return isValid
|
||||
? Result<Player>.Success(CreatePlayer(data))
|
||||
: Result<Player>.Failure("玩家数据验证失败");
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Q3: 如何在 Command 和 Query 中处理错误?
|
||||
|
||||
**A:** Command 和 Query 可以返回 Result:
|
||||
|
||||
```csharp
|
||||
// Command 返回 Result
|
||||
public class SaveGameCommand : AbstractCommand<Result<SaveData>>
|
||||
{
|
||||
protected override Result<SaveData> OnDo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = CollectSaveData();
|
||||
var saveSystem = this.GetSystem<SaveSystem>();
|
||||
return saveSystem.SaveGame(data);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.GetUtility<ILogger>().Error("保存游戏失败", ex);
|
||||
return Result<SaveData>.Failure(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Query 返回 Option
|
||||
public class GetPlayerQuery : AbstractQuery<Option<Player>>
|
||||
{
|
||||
public string PlayerId { get; set; }
|
||||
|
||||
protected override Option<Player> OnDo()
|
||||
{
|
||||
var playerSystem = this.GetSystem<PlayerSystem>();
|
||||
return playerSystem.FindPlayerById(PlayerId);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Q4: 如何处理多个可能失败的操作?
|
||||
|
||||
**A:** 使用 Result 的链式操作:
|
||||
|
||||
```csharp
|
||||
public Result<Trade> ExecuteTrade(string sellerId, string buyerId, string itemId, int price)
|
||||
{
|
||||
return ValidateSeller(sellerId)
|
||||
.Bind(_ => ValidateBuyer(buyerId))
|
||||
.Bind(_ => ValidateItem(itemId))
|
||||
.Bind(_ => ValidatePrice(price))
|
||||
.Bind(_ => TransferItem(sellerId, buyerId, itemId))
|
||||
.Bind(_ => TransferCurrency(buyerId, sellerId, price))
|
||||
.Map(_ => CreateTradeRecord(sellerId, buyerId, itemId, price));
|
||||
}
|
||||
```
|
||||
|
||||
### Q5: 如何避免 Result 嵌套过深?
|
||||
|
||||
**A:** 使用 Bind 扁平化嵌套:
|
||||
|
||||
```csharp
|
||||
// ❌ 避免:嵌套过深
|
||||
public Result<string> ProcessData(string input)
|
||||
{
|
||||
var result1 = Step1(input);
|
||||
if (result1.IsSuccess)
|
||||
{
|
||||
var result2 = Step2(result1.Match(succ: v => v, fail: _ => ""));
|
||||
if (result2.IsSuccess)
|
||||
{
|
||||
var result3 = Step3(result2.Match(succ: v => v, fail: _ => ""));
|
||||
return result3;
|
||||
}
|
||||
return Result<string>.Failure(result2.Exception);
|
||||
}
|
||||
return Result<string>.Failure(result1.Exception);
|
||||
}
|
||||
|
||||
// ✅ 好的做法:使用 Bind 扁平化
|
||||
public Result<string> ProcessData(string input)
|
||||
{
|
||||
return Step1(input)
|
||||
.Bind(Step2)
|
||||
.Bind(Step3);
|
||||
}
|
||||
```
|
||||
|
||||
### Q6: 如何在 UI 层处理错误?
|
||||
|
||||
**A:** 将错误转换为用户友好的消息:
|
||||
|
||||
```csharp
|
||||
public class UIController : IController
|
||||
{
|
||||
private readonly ErrorMessageService _errorMessageService;
|
||||
|
||||
public void OnSaveButtonClicked()
|
||||
{
|
||||
var result = this.SendCommand(new SaveGameCommand());
|
||||
|
||||
result.Match(
|
||||
succ: data => {
|
||||
ShowSuccessToast("游戏保存成功");
|
||||
},
|
||||
fail: ex => {
|
||||
var userMessage = _errorMessageService.GetUserFriendlyMessage(ex);
|
||||
ShowErrorDialog("保存失败", userMessage);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
良好的错误处理是构建健壮应用的基础。遵循本指南的最佳实践:
|
||||
|
||||
- ✅ 使用 **Result<T>** 处理可预期的业务错误
|
||||
- ✅ 使用 **Option<T>** 处理可能不存在的值
|
||||
- ✅ 使用 **异常** 处理不可预期的系统错误
|
||||
- ✅ 记录详细的 **日志** 信息
|
||||
- ✅ 提供友好的 **用户反馈**
|
||||
- ✅ 实现 **错误恢复** 和降级策略
|
||||
- ✅ 编写 **测试** 验证错误处理逻辑
|
||||
|
||||
通过这些实践,你将构建出更加稳定、可维护、用户友好的游戏应用。
|
||||
|
||||
---
|
||||
|
||||
**相关文档**:
|
||||
|
||||
- [架构模式最佳实践](./architecture-patterns.md)
|
||||
- [扩展方法使用指南](../core/extensions.md)
|
||||
- [日志系统](../core/logging.md)
|
||||
|
||||
**文档版本**: 1.0.0
|
||||
**更新日期**: 2026-03-07
|
||||
|
||||
@ -382,3 +382,549 @@ public enum ResourcePriority
|
||||
|
||||
### 2. 纹理压缩和优化
|
||||
|
||||
使用合适的纹理格式和压缩:
|
||||
|
||||
```csharp
|
||||
public class TextureOptimizer
|
||||
{
|
||||
public static TextureSettings GetOptimalSettings(string platform)
|
||||
{
|
||||
return platform switch
|
||||
{
|
||||
"iOS" => new TextureSettings
|
||||
{
|
||||
Format = TextureFormat.PVRTC_RGB4,
|
||||
MaxSize = 2048,
|
||||
MipmapEnabled = true,
|
||||
Compression = TextureCompression.High
|
||||
},
|
||||
"Android" => new TextureSettings
|
||||
{
|
||||
Format = TextureFormat.ETC2_RGB,
|
||||
MaxSize = 2048,
|
||||
MipmapEnabled = true,
|
||||
Compression = TextureCompression.High
|
||||
},
|
||||
_ => new TextureSettings
|
||||
{
|
||||
Format = TextureFormat.RGB24,
|
||||
MaxSize = 4096,
|
||||
MipmapEnabled = true,
|
||||
Compression = TextureCompression.Normal
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 对象池优化
|
||||
|
||||
针对移动平台优化对象池,限制池大小并定期清理:
|
||||
|
||||
```csharp
|
||||
public class MobileObjectPool<T> : AbstractObjectPoolSystem<string, T>
|
||||
where T : IPoolableObject
|
||||
{
|
||||
private const int MaxPoolSize = 50;
|
||||
|
||||
public new void Release(string key, T obj)
|
||||
{
|
||||
if (Pools.TryGetValue(key, out var pool) && pool.Count >= MaxPoolSize)
|
||||
{
|
||||
obj.OnPoolDestroy();
|
||||
return;
|
||||
}
|
||||
base.Release(key, obj);
|
||||
}
|
||||
|
||||
protected override T Create(string key)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 避免内存泄漏
|
||||
|
||||
确保正确释放资源和取消事件订阅:
|
||||
|
||||
```csharp
|
||||
public class LeakFreeSystem : AbstractSystem
|
||||
{
|
||||
private IResourceHandle<Texture>? _textureHandle;
|
||||
private IUnRegister? _eventUnregister;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
_eventUnregister = this.RegisterEvent<GameEvent>(OnGameEvent);
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
// 释放资源句柄
|
||||
_textureHandle?.Dispose();
|
||||
_textureHandle = null;
|
||||
|
||||
// 取消事件订阅
|
||||
_eventUnregister?.UnRegister();
|
||||
_eventUnregister = null;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
private void OnGameEvent(GameEvent e)
|
||||
{
|
||||
// 处理事件
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 1. CPU 优化
|
||||
|
||||
减少 CPU 计算负担:
|
||||
|
||||
```csharp
|
||||
public class CPUOptimizer : AbstractSystem
|
||||
{
|
||||
private const int UpdateInterval = 5; // 每 5 帧更新一次
|
||||
private int _frameCount;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
this.RegisterEvent<GameUpdateEvent>(OnUpdate);
|
||||
}
|
||||
|
||||
private void OnUpdate(GameUpdateEvent e)
|
||||
{
|
||||
_frameCount++;
|
||||
|
||||
// 降低更新频率
|
||||
if (_frameCount % UpdateInterval == 0)
|
||||
{
|
||||
UpdateNonCriticalSystems();
|
||||
}
|
||||
|
||||
// 关键系统每帧更新
|
||||
UpdateCriticalSystems();
|
||||
}
|
||||
|
||||
private void UpdateCriticalSystems()
|
||||
{
|
||||
// 玩家输入、物理等关键系统
|
||||
}
|
||||
|
||||
private void UpdateNonCriticalSystems()
|
||||
{
|
||||
// AI、动画等非关键系统
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 批量处理
|
||||
|
||||
使用批量操作减少函数调用:
|
||||
|
||||
```csharp
|
||||
public class BatchProcessor : AbstractSystem
|
||||
{
|
||||
private readonly List<Entity> _entitiesToUpdate = new();
|
||||
|
||||
public void ProcessEntities()
|
||||
{
|
||||
// 批量处理,减少函数调用开销
|
||||
for (int i = 0; i < _entitiesToUpdate.Count; i++)
|
||||
{
|
||||
var entity = _entitiesToUpdate[i];
|
||||
entity.Update();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 缓存计算结果
|
||||
|
||||
避免重复计算:
|
||||
|
||||
```csharp
|
||||
public class CachedCalculator
|
||||
{
|
||||
private readonly Dictionary<string, float> _cache = new();
|
||||
|
||||
public float GetDistance(Vector3 a, Vector3 b)
|
||||
{
|
||||
var key = $"{a}_{b}";
|
||||
|
||||
if (_cache.TryGetValue(key, out var distance))
|
||||
{
|
||||
return distance;
|
||||
}
|
||||
|
||||
distance = Vector3.Distance(a, b);
|
||||
_cache[key] = distance;
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
public void ClearCache()
|
||||
{
|
||||
_cache.Clear();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 电池优化
|
||||
|
||||
### 1. 动态帧率调整
|
||||
|
||||
根据场景复杂度调整帧率:
|
||||
|
||||
```csharp
|
||||
public class DynamicFrameRateSystem : AbstractSystem
|
||||
{
|
||||
private int _targetFrameRate = 60;
|
||||
|
||||
public void AdjustFrameRate(SceneComplexity complexity)
|
||||
{
|
||||
_targetFrameRate = complexity switch
|
||||
{
|
||||
SceneComplexity.Low => 60,
|
||||
SceneComplexity.Medium => 45,
|
||||
SceneComplexity.High => 30,
|
||||
_ => 60
|
||||
};
|
||||
|
||||
Application.targetFrameRate = _targetFrameRate;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SceneComplexity
|
||||
{
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 后台优化
|
||||
|
||||
应用进入后台时降低性能消耗:
|
||||
|
||||
```csharp
|
||||
public class BackgroundOptimizer : AbstractSystem
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
Application.focusChanged += OnFocusChanged;
|
||||
}
|
||||
|
||||
private void OnFocusChanged(bool hasFocus)
|
||||
{
|
||||
if (hasFocus)
|
||||
{
|
||||
OnApplicationForeground();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnApplicationBackground();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationBackground()
|
||||
{
|
||||
// 降低帧率
|
||||
Application.targetFrameRate = 10;
|
||||
|
||||
// 暂停音频
|
||||
AudioListener.pause = true;
|
||||
|
||||
// 暂停非关键系统
|
||||
PauseNonCriticalSystems();
|
||||
}
|
||||
|
||||
private void OnApplicationForeground()
|
||||
{
|
||||
// 恢复帧率
|
||||
Application.targetFrameRate = 60;
|
||||
|
||||
// 恢复音频
|
||||
AudioListener.pause = false;
|
||||
|
||||
// 恢复系统
|
||||
ResumeNonCriticalSystems();
|
||||
}
|
||||
|
||||
private void PauseNonCriticalSystems()
|
||||
{
|
||||
SendEvent(new PauseNonCriticalSystemsEvent());
|
||||
}
|
||||
|
||||
private void ResumeNonCriticalSystems()
|
||||
{
|
||||
SendEvent(new ResumeNonCriticalSystemsEvent());
|
||||
}
|
||||
|
||||
protected override void OnDestroy()
|
||||
{
|
||||
Application.focusChanged -= OnFocusChanged;
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## UI 优化
|
||||
|
||||
### 1. 触摸优化
|
||||
|
||||
优化触摸输入处理:
|
||||
|
||||
```csharp
|
||||
public class TouchInputSystem : AbstractSystem
|
||||
{
|
||||
private const float TouchThreshold = 10f; // 最小移动距离
|
||||
private Vector2 _lastTouchPosition;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
this.RegisterEvent<TouchEvent>(OnTouch);
|
||||
}
|
||||
|
||||
private void OnTouch(TouchEvent e)
|
||||
{
|
||||
switch (e.Phase)
|
||||
{
|
||||
case TouchPhase.Began:
|
||||
_lastTouchPosition = e.Position;
|
||||
break;
|
||||
|
||||
case TouchPhase.Moved:
|
||||
var delta = e.Position - _lastTouchPosition;
|
||||
if (delta.magnitude > TouchThreshold)
|
||||
{
|
||||
ProcessTouchMove(delta);
|
||||
_lastTouchPosition = e.Position;
|
||||
}
|
||||
break;
|
||||
|
||||
case TouchPhase.Ended:
|
||||
ProcessTouchEnd(e.Position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessTouchMove(Vector2 delta)
|
||||
{
|
||||
SendEvent(new TouchMoveEvent { Delta = delta });
|
||||
}
|
||||
|
||||
private void ProcessTouchEnd(Vector2 position)
|
||||
{
|
||||
SendEvent(new TouchEndEvent { Position = position });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. UI 元素池化
|
||||
|
||||
复用 UI 元素减少创建开销:
|
||||
|
||||
```csharp
|
||||
public class UIElementPool : AbstractObjectPoolSystem<string, UIElement>
|
||||
{
|
||||
protected override UIElement Create(string key)
|
||||
{
|
||||
return new UIElement(key);
|
||||
}
|
||||
|
||||
public UIElement GetButton()
|
||||
{
|
||||
return Acquire("button");
|
||||
}
|
||||
|
||||
public void ReturnButton(UIElement button)
|
||||
{
|
||||
Release("button", button);
|
||||
}
|
||||
}
|
||||
|
||||
public class UIElement : IPoolableObject
|
||||
{
|
||||
public string Type { get; }
|
||||
|
||||
public UIElement(string type)
|
||||
{
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public void OnAcquire()
|
||||
{
|
||||
// 激活 UI 元素
|
||||
}
|
||||
|
||||
public void OnRelease()
|
||||
{
|
||||
// 重置状态
|
||||
}
|
||||
|
||||
public void OnPoolDestroy()
|
||||
{
|
||||
// 清理资源
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 平台适配
|
||||
|
||||
### iOS 优化
|
||||
|
||||
```csharp
|
||||
public class iOSOptimizer
|
||||
{
|
||||
public static void ApplyOptimizations()
|
||||
{
|
||||
// 使用 Metal 渲染
|
||||
SystemInfo.graphicsDeviceType = GraphicsDeviceType.Metal;
|
||||
|
||||
// 优化纹理格式
|
||||
QualitySettings.masterTextureLimit = 0;
|
||||
|
||||
// 启用多线程渲染
|
||||
PlayerSettings.MTRendering = true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Android 优化
|
||||
|
||||
```csharp
|
||||
public class AndroidOptimizer
|
||||
{
|
||||
public static void ApplyOptimizations()
|
||||
{
|
||||
// 使用 Vulkan 或 OpenGL ES 3
|
||||
SystemInfo.graphicsDeviceType = GraphicsDeviceType.Vulkan;
|
||||
|
||||
// 优化纹理格式
|
||||
QualitySettings.masterTextureLimit = 0;
|
||||
|
||||
// 启用多线程渲染
|
||||
PlayerSettings.MTRendering = true;
|
||||
|
||||
// 优化 GC
|
||||
GarbageCollector.GCMode = GarbageCollector.Mode.Disabled;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 资源管理
|
||||
|
||||
- 使用资源优先级系统
|
||||
- 及时卸载不用的资源
|
||||
- 使用资源压缩
|
||||
- 实现资源预加载
|
||||
|
||||
### 2. 内存管理
|
||||
|
||||
- 监控内存使用
|
||||
- 限制对象池大小
|
||||
- 避免内存泄漏
|
||||
- 定期执行 GC
|
||||
|
||||
### 3. 性能优化
|
||||
|
||||
- 降低更新频率
|
||||
- 使用批量处理
|
||||
- 缓存计算结果
|
||||
- 优化算法复杂度
|
||||
|
||||
### 4. 电池优化
|
||||
|
||||
- 动态调整帧率
|
||||
- 后台降低性能
|
||||
- 减少渲染开销
|
||||
- 优化网络请求
|
||||
|
||||
### 5. UI 优化
|
||||
|
||||
- 优化触摸处理
|
||||
- 池化 UI 元素
|
||||
- 减少 UI 层级
|
||||
- 使用异步加载
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 问题:如何监控移动设备的内存使用?
|
||||
|
||||
**解答**:
|
||||
|
||||
使用 `GC.GetTotalMemory()` 监控托管内存,结合平台 API 监控总内存:
|
||||
|
||||
```csharp
|
||||
var managedMemory = GC.GetTotalMemory(false);
|
||||
Console.WriteLine($"托管内存: {managedMemory / 1024 / 1024}MB");
|
||||
```
|
||||
|
||||
### 问题:如何优化移动游戏的启动时间?
|
||||
|
||||
**解答**:
|
||||
|
||||
- 延迟加载非关键资源
|
||||
- 使用异步初始化
|
||||
- 减少启动时的计算
|
||||
- 优化资源包大小
|
||||
|
||||
### 问题:如何处理不同设备的性能差异?
|
||||
|
||||
**解答**:
|
||||
|
||||
实现设备性能分级系统:
|
||||
|
||||
```csharp
|
||||
public enum DevicePerformance
|
||||
{
|
||||
Low,
|
||||
Medium,
|
||||
High
|
||||
}
|
||||
|
||||
public class DeviceProfiler
|
||||
{
|
||||
public static DevicePerformance GetDevicePerformance()
|
||||
{
|
||||
var memory = SystemInfo.systemMemorySize;
|
||||
var processorCount = SystemInfo.processorCount;
|
||||
|
||||
if (memory < 2048 || processorCount < 4)
|
||||
return DevicePerformance.Low;
|
||||
else if (memory < 4096 || processorCount < 6)
|
||||
return DevicePerformance.Medium;
|
||||
else
|
||||
return DevicePerformance.High;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 问题:如何优化移动游戏的网络性能?
|
||||
|
||||
**解答**:
|
||||
|
||||
- 使用数据压缩
|
||||
- 批量发送请求
|
||||
- 实现请求队列
|
||||
- 处理网络中断
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [资源管理系统](/zh-CN/core/resource) - 资源管理详细说明
|
||||
- [对象池系统](/zh-CN/core/pool) - 对象池优化
|
||||
- [协程系统](/zh-CN/core/coroutine) - 异步操作优化
|
||||
- [架构模式最佳实践](/zh-CN/best-practices/architecture-patterns) - 架构设计
|
||||
|
||||
---
|
||||
|
||||
**文档版本**: 1.0.0
|
||||
**更新日期**: 2026-03-07
|
||||
|
||||
|
||||
@ -1143,5 +1143,5 @@ public void CalculateBonus_Should_Return_Double(int input, int expected)
|
||||
|
||||
- [NUnit 官方文档](https://docs.nunit.org/)
|
||||
- [Moq 快速入门](https://github.com/moq/moq4/wiki/Quickstart)
|
||||
- [测试驱动开发实践](/zh-CN/best-practices/tdd)
|
||||
- [持续集成配置](/zh-CN/best-practices/ci-cd)
|
||||
- [架构设计模式](/zh-CN/best-practices/architecture-patterns)
|
||||
- [性能优化最佳实践](/zh-CN/best-practices/performance)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user