Files
whale-town-front/docs/performance_optimization.md
moyin c0f5d6a537 docs:添加性能优化指南
- 创建全面的性能优化文档
- 涵盖渲染、内存、脚本、网络等各方面优化
- 提供具体的代码示例和最佳实践
- 包含性能监控和调试工具使用方法
- 为开发者提供系统的性能优化指导
2025-12-24 20:51:45 +08:00

507 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 性能优化指南
本文档提供 Whale Town 项目的性能优化策略和最佳实践。
## 🎯 优化目标
### 性能指标
- **帧率**: 保持60FPS稳定运行
- **内存使用**: 控制在合理范围内
- **加载时间**: 场景切换<2秒
- **响应时间**: UI交互<100ms
### 优化原则
1. **测量优先** - 先测量再优化
2. **渐进优化** - 逐步改进性能
3. **平衡取舍** - 在质量和性能间平衡
4. **用户体验** - 优化用户感知性能
## 🔧 渲染优化
### 纹理优化
```gdscript
# 使用合适的纹理格式
# 小图标使用 RGBA8
# 大背景使用 DXT1/DXT5
# UI元素使用 RGBA4444
# 纹理压缩设置
func optimize_texture_import():
# 在导入设置中启用压缩
# 设置合适的最大尺寸
# 启用Mipmaps3D纹理
pass
```
### 批处理优化
```gdscript
# 合并相同材质的对象
# 使用MultiMesh渲染大量相同对象
func create_multimesh_instances():
var multimesh = MultiMesh.new()
multimesh.transform_format = MultiMesh.TRANSFORM_2D
multimesh.instance_count = 100
var mesh_instance = MeshInstance2D.new()
mesh_instance.multimesh = multimesh
add_child(mesh_instance)
```
### 视锥剔除
```gdscript
# 只渲染可见区域的对象
func update_visibility():
var camera = get_viewport().get_camera_2d()
var screen_rect = get_viewport_rect()
for child in get_children():
if child is Node2D:
var visible = screen_rect.intersects(child.get_rect())
child.visible = visible
```
## 💾 内存优化
### 对象池模式
```gdscript
# ObjectPool.gd
class_name ObjectPool
extends Node
var pools: Dictionary = {}
func get_object(type: String) -> Node:
if not pools.has(type):
pools[type] = []
var pool = pools[type]
if pool.size() > 0:
return pool.pop_back()
else:
return _create_new_object(type)
func return_object(obj: Node, type: String):
obj.reset() # 重置对象状态
pools[type].append(obj)
func _create_new_object(type: String) -> Node:
match type:
"Bullet":
return preload("res://prefabs/Bullet.tscn").instantiate()
"Enemy":
return preload("res://prefabs/Enemy.tscn").instantiate()
_:
return null
```
### 资源管理
```gdscript
# ResourceManager.gd
class_name ResourceManager
extends Node
var loaded_resources: Dictionary = {}
var resource_usage: Dictionary = {}
func load_resource(path: String) -> Resource:
if loaded_resources.has(path):
resource_usage[path] += 1
return loaded_resources[path]
var resource = load(path)
loaded_resources[path] = resource
resource_usage[path] = 1
return resource
func unload_resource(path: String):
if resource_usage.has(path):
resource_usage[path] -= 1
if resource_usage[path] <= 0:
loaded_resources.erase(path)
resource_usage.erase(path)
```
### 内存监控
```gdscript
# MemoryMonitor.gd
extends Node
func _ready():
# 每秒检查一次内存使用
var timer = Timer.new()
timer.wait_time = 1.0
timer.timeout.connect(_check_memory)
add_child(timer)
timer.start()
func _check_memory():
var memory_usage = OS.get_static_memory_usage_by_type()
var total_memory = OS.get_static_memory_peak_usage()
print("内存使用: ", memory_usage / 1024 / 1024, "MB")
print("峰值内存: ", total_memory / 1024 / 1024, "MB")
# 内存使用过高时触发垃圾回收
if total_memory > 200 * 1024 * 1024: # 200MB
print("触发垃圾回收")
# 清理不必要的资源
_cleanup_resources()
func _cleanup_resources():
# 清理缓存
# 卸载未使用的资源
# 强制垃圾回收
pass
```
## ⚡ 脚本优化
### 避免频繁计算
```gdscript
# ❌ 错误示例:每帧计算
func _process(delta):
var distance = global_position.distance_to(target.global_position)
if distance < 100:
attack_target()
# ✅ 正确示例:缓存计算结果
var cached_distance: float = 0.0
var distance_update_timer: float = 0.0
func _process(delta):
distance_update_timer += delta
if distance_update_timer >= 0.1: # 每100ms更新一次
cached_distance = global_position.distance_to(target.global_position)
distance_update_timer = 0.0
if cached_distance < 100:
attack_target()
```
### 优化循环
```gdscript
# ❌ 错误示例:嵌套循环
func find_nearest_enemy():
var nearest = null
var min_distance = INF
for enemy in enemies:
for player in players:
var distance = enemy.global_position.distance_to(player.global_position)
if distance < min_distance:
min_distance = distance
nearest = enemy
# ✅ 正确示例:优化算法
func find_nearest_enemy():
var player_pos = player.global_position
var nearest = null
var min_distance = INF
for enemy in enemies:
var distance = enemy.global_position.distance_squared_to(player_pos)
if distance < min_distance:
min_distance = distance
nearest = enemy
```
### 事件优化
```gdscript
# 使用信号代替轮询
# ❌ 错误示例:轮询检查
func _process(delta):
if player.health <= 0:
game_over()
# ✅ 正确示例:事件驱动
func _ready():
player.health_changed.connect(_on_health_changed)
func _on_health_changed(new_health: int):
if new_health <= 0:
game_over()
```
## 🎮 游戏逻辑优化
### 状态机优化
```gdscript
# StateMachine.gd
class_name StateMachine
extends Node
var current_state: State
var states: Dictionary = {}
func change_state(state_name: String):
if current_state:
current_state.exit()
current_state = states[state_name]
current_state.enter()
func _process(delta):
if current_state:
current_state.update(delta)
```
### AI优化
```gdscript
# EnemyAI.gd
extends Node
var update_interval: float = 0.2 # 每200ms更新一次AI
var update_timer: float = 0.0
func _process(delta):
update_timer += delta
if update_timer >= update_interval:
update_ai()
update_timer = 0.0
func update_ai():
# AI逻辑更新
# 路径寻找
# 决策制定
pass
```
### 碰撞检测优化
```gdscript
# 使用空间分区优化碰撞检测
class_name SpatialGrid
extends Node
var grid_size: int = 64
var grid: Dictionary = {}
func add_object(obj: Node2D):
var grid_pos = Vector2(
int(obj.global_position.x / grid_size),
int(obj.global_position.y / grid_size)
)
if not grid.has(grid_pos):
grid[grid_pos] = []
grid[grid_pos].append(obj)
func get_nearby_objects(pos: Vector2) -> Array:
var grid_pos = Vector2(
int(pos.x / grid_size),
int(pos.y / grid_size)
)
var nearby = []
for x in range(-1, 2):
for y in range(-1, 2):
var check_pos = grid_pos + Vector2(x, y)
if grid.has(check_pos):
nearby.append_array(grid[check_pos])
return nearby
```
## 🌐 网络优化
### 数据压缩
```gdscript
# NetworkManager.gd
func send_player_data(data: Dictionary):
# 只发送变化的数据
var delta_data = get_changed_data(data)
# 压缩数据
var compressed = compress_data(delta_data)
# 发送数据
send_to_server(compressed)
func compress_data(data: Dictionary) -> PackedByteArray:
var json_string = JSON.stringify(data)
var bytes = json_string.to_utf8_buffer()
return bytes.compress(FileAccess.COMPRESSION_GZIP)
```
### 批量更新
```gdscript
# 批量发送网络更新
var pending_updates: Array = []
var update_timer: float = 0.0
func _process(delta):
update_timer += delta
if update_timer >= 0.05: # 每50ms发送一次
if pending_updates.size() > 0:
send_batch_updates(pending_updates)
pending_updates.clear()
update_timer = 0.0
func queue_update(update_data: Dictionary):
pending_updates.append(update_data)
```
## 📊 性能监控
### FPS监控
```gdscript
# FPSMonitor.gd
extends Control
@onready var fps_label: Label = $FPSLabel
var fps_history: Array = []
func _process(delta):
var current_fps = Engine.get_frames_per_second()
fps_history.append(current_fps)
if fps_history.size() > 60: # 保留60帧历史
fps_history.pop_front()
var avg_fps = 0
for fps in fps_history:
avg_fps += fps
avg_fps /= fps_history.size()
fps_label.text = "FPS: %d (平均: %d)" % [current_fps, avg_fps]
# FPS过低时警告
if avg_fps < 30:
modulate = Color.RED
elif avg_fps < 50:
modulate = Color.YELLOW
else:
modulate = Color.WHITE
```
### 性能分析器
```gdscript
# Profiler.gd
class_name Profiler
extends Node
var timers: Dictionary = {}
func start_timer(name: String):
timers[name] = Time.get_time_dict_from_system()
func end_timer(name: String) -> float:
if not timers.has(name):
return 0.0
var start_time = timers[name]
var end_time = Time.get_time_dict_from_system()
var duration = (end_time.hour * 3600 + end_time.minute * 60 + end_time.second) - \
(start_time.hour * 3600 + start_time.minute * 60 + start_time.second)
timers.erase(name)
return duration
# 使用示例
func expensive_function():
Profiler.start_timer("expensive_function")
# 执行耗时操作
for i in range(10000):
pass
var duration = Profiler.end_timer("expensive_function")
print("函数执行时间: ", duration, "")
```
## 🛠️ 调试工具
### 性能调试面板
```gdscript
# DebugPanel.gd
extends Control
@onready var memory_label: Label = $VBox/MemoryLabel
@onready var fps_label: Label = $VBox/FPSLabel
@onready var objects_label: Label = $VBox/ObjectsLabel
func _process(delta):
# 更新内存使用
var memory = OS.get_static_memory_usage_by_type()
memory_label.text = "内存: %.1f MB" % (memory / 1024.0 / 1024.0)
# 更新FPS
fps_label.text = "FPS: %d" % Engine.get_frames_per_second()
# 更新对象数量
var object_count = get_tree().get_node_count()
objects_label.text = "对象数: %d" % object_count
```
### 热点分析
```gdscript
# HotspotAnalyzer.gd
extends Node
var function_calls: Dictionary = {}
var function_times: Dictionary = {}
func profile_function(func_name: String, callable: Callable):
var start_time = Time.get_time_dict_from_system()
callable.call()
var end_time = Time.get_time_dict_from_system()
var duration = (end_time.hour * 3600 + end_time.minute * 60 + end_time.second) - \
(start_time.hour * 3600 + start_time.minute * 60 + start_time.second)
if not function_calls.has(func_name):
function_calls[func_name] = 0
function_times[func_name] = 0.0
function_calls[func_name] += 1
function_times[func_name] += duration
func print_profile_report():
print("=== 性能分析报告 ===")
for func_name in function_calls.keys():
var calls = function_calls[func_name]
var total_time = function_times[func_name]
var avg_time = total_time / calls
print("%s: 调用%d次, 总时间%.3fs, 平均%.3fs" % [func_name, calls, total_time, avg_time])
```
## 📚 最佳实践
### 开发阶段
1. **早期优化** - 在设计阶段考虑性能
2. **渐进开发** - 逐步添加功能并测试性能
3. **定期测试** - 定期进行性能测试
4. **文档记录** - 记录性能优化决策
### 测试阶段
1. **多设备测试** - 在不同性能设备上测试
2. **压力测试** - 测试极限情况下的性能
3. **长时间测试** - 测试内存泄漏和性能衰减
4. **用户测试** - 收集真实用户的性能反馈
### 发布阶段
1. **性能监控** - 监控线上性能指标
2. **快速响应** - 快速修复性能问题
3. **持续优化** - 根据数据持续优化
4. **版本对比** - 对比不同版本的性能表现
## 🔍 常见问题
### Q: 如何识别性能瓶颈?
A: 使用Godot的内置分析器添加自定义性能监控分析FPS和内存使用情况。
### Q: 内存使用过高怎么办?
A: 检查资源加载,使用对象池,及时释放不需要的对象,优化纹理大小。
### Q: 如何优化大量对象的渲染?
A: 使用MultiMesh批处理实现视锥剔除使用LOD系统合并相同材质的对象。
### Q: 网络延迟如何优化?
A: 减少网络请求频率,压缩传输数据,使用预测和插值,实现客户端预测。
---
**记住:性能优化是一个持续的过程,需要在开发的各个阶段都保持关注!**