diff --git a/docs/performance_optimization.md b/docs/performance_optimization.md new file mode 100644 index 0000000..df61a17 --- /dev/null +++ b/docs/performance_optimization.md @@ -0,0 +1,507 @@ +# 性能优化指南 + +本文档提供 Whale Town 项目的性能优化策略和最佳实践。 + +## 🎯 优化目标 + +### 性能指标 +- **帧率**: 保持60FPS稳定运行 +- **内存使用**: 控制在合理范围内 +- **加载时间**: 场景切换<2秒 +- **响应时间**: UI交互<100ms + +### 优化原则 +1. **测量优先** - 先测量再优化 +2. **渐进优化** - 逐步改进性能 +3. **平衡取舍** - 在质量和性能间平衡 +4. **用户体验** - 优化用户感知性能 + +## 🔧 渲染优化 + +### 纹理优化 +```gdscript +# 使用合适的纹理格式 +# 小图标使用 RGBA8 +# 大背景使用 DXT1/DXT5 +# UI元素使用 RGBA4444 + +# 纹理压缩设置 +func optimize_texture_import(): + # 在导入设置中启用压缩 + # 设置合适的最大尺寸 + # 启用Mipmaps(3D纹理) + 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: 减少网络请求频率,压缩传输数据,使用预测和插值,实现客户端预测。 + +--- + +**记住:性能优化是一个持续的过程,需要在开发的各个阶段都保持关注!** \ No newline at end of file