forked from datawhale/whale-town-front
新的目录结构: 01-项目入门/ # 新人必读,项目基础 02-开发规范/ # 编码标准和规范 03-技术实现/ # 具体开发指导 04-高级开发/ # 进阶开发技巧 05-部署运维/ # 发布和部署 06-功能模块/ # 特定功能文档 新增导航文档: - docs/README.md - 完整的文档导航和使用指南 - 各目录下的README.md - 分类说明和使用指导 优化效果: - 开发者可以按阶段快速定位需要的文档 - 新人有清晰的学习路径 - 不同角色有针对性的文档推荐 - 提供了问题导向的快速查找功能
13 KiB
13 KiB
性能优化指南
本文档提供 Whale Town 项目的性能优化策略和最佳实践。
🎯 优化目标
性能指标
- 帧率: 保持60FPS稳定运行
- 内存使用: 控制在合理范围内
- 加载时间: 场景切换<2秒
- 响应时间: UI交互<100ms
优化原则
- 测量优先 - 先测量再优化
- 渐进优化 - 逐步改进性能
- 平衡取舍 - 在质量和性能间平衡
- 用户体验 - 优化用户感知性能
🔧 渲染优化
纹理优化
# 使用合适的纹理格式
# 小图标使用 RGBA8
# 大背景使用 DXT1/DXT5
# UI元素使用 RGBA4444
# 纹理压缩设置
func optimize_texture_import():
# 在导入设置中启用压缩
# 设置合适的最大尺寸
# 启用Mipmaps(3D纹理)
pass
批处理优化
# 合并相同材质的对象
# 使用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)
视锥剔除
# 只渲染可见区域的对象
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
💾 内存优化
对象池模式
# 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
资源管理
# 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)
内存监控
# 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
⚡ 脚本优化
避免频繁计算
# ❌ 错误示例:每帧计算
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()
优化循环
# ❌ 错误示例:嵌套循环
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
事件优化
# 使用信号代替轮询
# ❌ 错误示例:轮询检查
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()
🎮 游戏逻辑优化
状态机优化
# 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优化
# 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
碰撞检测优化
# 使用空间分区优化碰撞检测
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
🌐 网络优化
数据压缩
# 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)
批量更新
# 批量发送网络更新
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监控
# 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
性能分析器
# 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, "秒")
🛠️ 调试工具
性能调试面板
# 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
热点分析
# 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])
📚 最佳实践
开发阶段
- 早期优化 - 在设计阶段考虑性能
- 渐进开发 - 逐步添加功能并测试性能
- 定期测试 - 定期进行性能测试
- 文档记录 - 记录性能优化决策
测试阶段
- 多设备测试 - 在不同性能设备上测试
- 压力测试 - 测试极限情况下的性能
- 长时间测试 - 测试内存泄漏和性能衰减
- 用户测试 - 收集真实用户的性能反馈
发布阶段
- 性能监控 - 监控线上性能指标
- 快速响应 - 快速修复性能问题
- 持续优化 - 根据数据持续优化
- 版本对比 - 对比不同版本的性能表现
🔍 常见问题
Q: 如何识别性能瓶颈?
A: 使用Godot的内置分析器,添加自定义性能监控,分析FPS和内存使用情况。
Q: 内存使用过高怎么办?
A: 检查资源加载,使用对象池,及时释放不需要的对象,优化纹理大小。
Q: 如何优化大量对象的渲染?
A: 使用MultiMesh批处理,实现视锥剔除,使用LOD系统,合并相同材质的对象。
Q: 网络延迟如何优化?
A: 减少网络请求频率,压缩传输数据,使用预测和插值,实现客户端预测。
记住:性能优化是一个持续的过程,需要在开发的各个阶段都保持关注!