docs:添加性能优化指南

- 创建全面的性能优化文档
- 涵盖渲染、内存、脚本、网络等各方面优化
- 提供具体的代码示例和最佳实践
- 包含性能监控和调试工具使用方法
- 为开发者提供系统的性能优化指导
This commit is contained in:
2025-12-24 20:51:45 +08:00
parent 0b6b1c2040
commit c0f5d6a537

View File

@@ -0,0 +1,507 @@
# 性能优化指南
本文档提供 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: 减少网络请求频率,压缩传输数据,使用预测和插值,实现客户端预测。
---
**记住:性能优化是一个持续的过程,需要在开发的各个阶段都保持关注!**