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