EG/plugins/user/advanced_skeletal_animation/tools.py
2025-12-12 16:16:15 +08:00

1019 lines
37 KiB
Python
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.

"""
高级骨骼动画系统 - 工具模块
提供动画编辑、调试和分析工具
"""
import math
import json
from typing import Dict, List, Optional, Tuple, Any, Callable
from dataclasses import dataclass, field
from enum import Enum
from panda3d.core import *
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
# 定义常量
TOOL_SETTINGS = {
'analyzer_update_rate': 10,
'debug_visualization_enabled': True,
'profiler_sampling_rate': 60
}
# 枚举定义
class AnimationAnalysisType(Enum):
"""动画分析类型枚举"""
PERFORMANCE = 0
QUALITY = 1
MEMORY = 2
COMPRESSION = 3
class DebugVisualizationMode(Enum):
"""调试可视化模式枚举"""
SKELETON = 0
BOUNDS = 1
NORMALS = 2
WIREFRAME = 3
COLLISION = 4
class AnimationEditMode(Enum):
"""动画编辑模式枚举"""
VIEW = 0
EDIT_KEYFRAME = 1
EDIT_CURVE = 2
EDIT_BONE = 3
@dataclass
class AnimationAnalysisResult:
"""动画分析结果数据类"""
actor_id: str
anim_name: str
analysis_type: AnimationAnalysisType
metrics: Dict[str, Any]
timestamp: float
recommendations: List[str] = field(default_factory=list)
@dataclass
class AnimationEditorState:
"""动画编辑器状态数据类"""
actor_id: str
anim_name: str
current_frame: int
selected_bones: List[str]
edit_mode: AnimationEditMode
keyframe_selection: List[int]
curve_editor_state: Dict[str, Any]
@dataclass
class PerformanceMetrics:
"""性能指标数据类"""
fps: float
frame_time: float
animation_update_time: float
memory_usage: int
actor_count: int
active_animations: int
class AnimationAnalyzer:
"""
动画分析器
提供动画性能、质量和内存使用分析
"""
def __init__(self, core_system):
"""
初始化动画分析器
:param core_system: 核心动画系统
"""
self.core_system = core_system
self.analysis_history: List[AnimationAnalysisResult] = []
self.is_analyzing = False
self.update_rate = TOOL_SETTINGS['analyzer_update_rate']
self.last_update_time = 0.0
print("动画分析器初始化完成")
def start_analysis(self) -> None:
"""
开始分析
"""
self.is_analyzing = True
print("动画分析已启动")
def stop_analysis(self) -> None:
"""
停止分析
"""
self.is_analyzing = False
print("动画分析已停止")
def analyze_animation(self, actor_id: str, anim_name: str,
analysis_type: AnimationAnalysisType = AnimationAnalysisType.PERFORMANCE) -> Optional[AnimationAnalysisResult]:
"""
分析指定动画
:param actor_id: Actor ID
:param anim_name: 动画名称
:param analysis_type: 分析类型
:return: 分析结果
"""
if actor_id not in self.core_system.actors:
print(f"错误: Actor {actor_id} 不存在")
return None
actor_info = self.core_system.actors[actor_id]
actor = actor_info.actor
if anim_name not in actor.getAnimNames():
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id}")
return None
metrics = {}
recommendations = []
# 根据分析类型执行不同的分析
if analysis_type == AnimationAnalysisType.PERFORMANCE:
result = self._analyze_performance(actor_info, anim_name)
metrics.update(result[0])
recommendations.extend(result[1])
elif analysis_type == AnimationAnalysisType.QUALITY:
result = self._analyze_quality(actor_info, anim_name)
metrics.update(result[0])
recommendations.extend(result[1])
elif analysis_type == AnimationAnalysisType.MEMORY:
result = self._analyze_memory(actor_info, anim_name)
metrics.update(result[0])
recommendations.extend(result[1])
elif analysis_type == AnimationAnalysisType.COMPRESSION:
result = self._analyze_compression(actor_info, anim_name)
metrics.update(result[0])
recommendations.extend(result[1])
# 创建分析结果
analysis_result = AnimationAnalysisResult(
actor_id=actor_id,
anim_name=anim_name,
analysis_type=analysis_type,
metrics=metrics,
timestamp=globalClock.getRealTime(),
recommendations=recommendations
)
# 添加到历史记录
self.analysis_history.append(analysis_result)
if len(self.analysis_history) > 100: # 限制历史记录大小
self.analysis_history.pop(0)
return analysis_result
def _analyze_performance(self, actor_info: Any, anim_name: str) -> Tuple[Dict[str, Any], List[str]]:
"""
分析动画性能
:param actor_info: Actor信息
:param anim_name: 动画名称
:return: (指标, 建议)
"""
actor = actor_info.actor
metrics = {}
recommendations = []
# 获取动画控制
control = actor.getAnimControl(anim_name)
if control:
# 基本性能指标
metrics['frame_rate'] = actor.getFrameRate(anim_name)
metrics['num_frames'] = actor.getNumFrames(anim_name)
metrics['duration'] = control.getDuration()
metrics['is_looping'] = control.getLoop()
# 性能建议
if metrics['frame_rate'] and metrics['frame_rate'] < 15:
recommendations.append("动画帧率过低,建议优化关键帧")
if metrics['num_frames'] and metrics['num_frames'] > 1000:
recommendations.append("动画帧数过多,考虑分割或简化")
# 骨骼数量分析
bundle = actor.getPartBundleDict().get('modelRoot')
if bundle:
bone_count = bundle.getNumChildren()
metrics['bone_count'] = bone_count
if bone_count > 100:
recommendations.append("骨骼数量较多,可能影响性能")
return metrics, recommendations
def _analyze_quality(self, actor_info: Any, anim_name: str) -> Tuple[Dict[str, Any], List[str]]:
"""
分析动画质量
:param actor_info: Actor信息
:param anim_name: 动画名称
:return: (指标, 建议)
"""
actor = actor_info.actor
metrics = {}
recommendations = []
# 检查动画是否存在突变
prev_frame_data = None
large_changes = 0
control = actor.getAnimControl(anim_name)
if control:
num_frames = control.getNumFrames()
for frame in range(min(100, num_frames)): # 采样前100帧
actor.pose(anim_name, frame)
# 检查所有骨骼的变化
bundle = actor.getPartBundleDict().get('modelRoot')
if bundle:
for i in range(min(10, bundle.getNumChildren())): # 检查前10个骨骼
child = bundle.getChild(i)
if hasattr(child, 'getName'):
bone_name = child.getName()
joint = actor.exposeJoint(None, "modelRoot", bone_name)
if joint:
current_pos = joint.getPos()
if prev_frame_data and bone_name in prev_frame_data:
prev_pos = prev_frame_data[bone_name]
distance = (current_pos - prev_pos).length()
if distance > 1.0: # 阈值
large_changes += 1
if bone_name not in prev_frame_data:
prev_frame_data = {}
prev_frame_data[bone_name] = current_pos
metrics['large_position_changes'] = large_changes
if large_changes > 10:
recommendations.append("检测到大量位置突变,可能存在动画质量问题")
return metrics, recommendations
def _analyze_memory(self, actor_info: Any, anim_name: str) -> Tuple[Dict[str, Any], List[str]]:
"""
分析动画内存使用
:param actor_info: Actor信息
:param anim_name: 动画名称
:return: (指标, 建议)
"""
actor = actor_info.actor
metrics = {}
recommendations = []
# 估算内存使用 (简化)
control = actor.getAnimControl(anim_name)
if control:
num_frames = control.getNumFrames()
bundle = actor.getPartBundleDict().get('modelRoot')
bone_count = bundle.getNumChildren() if bundle else 0
# 简化的内存估算公式
estimated_memory = num_frames * bone_count * 64 # 每个骨骼关键帧约64字节
metrics['estimated_memory_bytes'] = estimated_memory
metrics['estimated_memory_kb'] = estimated_memory / 1024
if estimated_memory > 1024 * 1024: # 超过1MB
recommendations.append("动画内存使用较大,考虑压缩或简化")
return metrics, recommendations
def _analyze_compression(self, actor_info: Any, anim_name: str) -> Tuple[Dict[str, Any], List[str]]:
"""
分析动画压缩潜力
:param actor_info: Actor信息
:param anim_name: 动画名称
:return: (指标, 建议)
"""
actor = actor_info.actor
metrics = {}
recommendations = []
# 检查关键帧密度
control = actor.getAnimControl(anim_name)
if control:
num_frames = control.getNumFrames()
duration = control.getDuration()
if duration > 0:
fps = num_frames / duration
metrics['actual_fps'] = fps
# 检查是否可以降低帧率
if fps > 30:
metrics['recommended_fps'] = 30
recommendations.append(f"可以将帧率从{fps:.1f}降低到30以节省内存")
elif fps > 15:
metrics['recommended_fps'] = 15
recommendations.append(f"可以将帧率从{fps:.1f}降低到15以节省内存")
return metrics, recommendations
def get_actor_analysis_summary(self, actor_id: str) -> Optional[Dict[str, Any]]:
"""
获取Actor分析摘要
:param actor_id: Actor ID
:return: 分析摘要
"""
if actor_id not in self.core_system.actors:
return None
# 获取最新的分析结果
actor_analyses = [a for a in self.analysis_history if a.actor_id == actor_id]
if not actor_analyses:
return None
# 统计各类分析结果
performance_issues = len([a for a in actor_analyses
if a.analysis_type == AnimationAnalysisType.PERFORMANCE and a.recommendations])
quality_issues = len([a for a in actor_analyses
if a.analysis_type == AnimationAnalysisType.QUALITY and a.recommendations])
memory_issues = len([a for a in actor_analyses
if a.analysis_type == AnimationAnalysisType.MEMORY and a.recommendations])
return {
'actor_id': actor_id,
'total_analyses': len(actor_analyses),
'performance_issues': performance_issues,
'quality_issues': quality_issues,
'memory_issues': memory_issues,
'last_analysis_time': actor_analyses[-1].timestamp if actor_analyses else 0
}
def get_system_analysis_summary(self) -> Dict[str, Any]:
"""
获取系统分析摘要
:return: 分析摘要
"""
total_analyses = len(self.analysis_history)
actors_with_issues = len(set(a.actor_id for a in self.analysis_history if a.recommendations))
return {
'total_analyses': total_analyses,
'actors_analyzed': len(set(a.actor_id for a in self.analysis_history)),
'actors_with_issues': actors_with_issues,
'performance_analyses': len([a for a in self.analysis_history
if a.analysis_type == AnimationAnalysisType.PERFORMANCE]),
'quality_analyses': len([a for a in self.analysis_history
if a.analysis_type == AnimationAnalysisType.QUALITY]),
'memory_analyses': len([a for a in self.analysis_history
if a.analysis_type == AnimationAnalysisType.MEMORY])
}
class AnimationEditor:
"""
动画编辑器
提供关键帧编辑、曲线编辑等功能
"""
def __init__(self, core_system):
"""
初始化动画编辑器
:param core_system: 核心动画系统
"""
self.core_system = core_system
self.editor_states: Dict[str, AnimationEditorState] = {}
self.is_editing = False
self.edit_callbacks: List[Callable] = []
print("动画编辑器初始化完成")
def start_editing(self, actor_id: str, anim_name: str) -> bool:
"""
开始编辑动画
:param actor_id: Actor ID
:param anim_name: 动画名称
:return: 是否成功开始编辑
"""
if actor_id not in self.core_system.actors:
print(f"错误: Actor {actor_id} 不存在")
return False
actor_info = self.core_system.actors[actor_id]
actor = actor_info.actor
if anim_name not in actor.getAnimNames():
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id}")
return False
# 创建编辑器状态
editor_state = AnimationEditorState(
actor_id=actor_id,
anim_name=anim_name,
current_frame=0,
selected_bones=[],
edit_mode=AnimationEditMode.VIEW,
keyframe_selection=[],
curve_editor_state={}
)
self.editor_states[f"{actor_id}:{anim_name}"] = editor_state
self.is_editing = True
print(f"开始编辑动画: {anim_name} (Actor: {actor_id})")
return True
def stop_editing(self, actor_id: str, anim_name: str) -> None:
"""
停止编辑动画
:param actor_id: Actor ID
:param anim_name: 动画名称
"""
key = f"{actor_id}:{anim_name}"
if key in self.editor_states:
del self.editor_states[key]
if not self.editor_states:
self.is_editing = False
print(f"停止编辑动画: {anim_name} (Actor: {actor_id})")
def set_edit_mode(self, actor_id: str, anim_name: str, mode: AnimationEditMode) -> None:
"""
设置编辑模式
:param actor_id: Actor ID
:param anim_name: 动画名称
:param mode: 编辑模式
"""
key = f"{actor_id}:{anim_name}"
if key in self.editor_states:
self.editor_states[key].edit_mode = mode
print(f"设置编辑模式为: {mode.name} (Actor: {actor_id}, 动画: {anim_name})")
def select_bones(self, actor_id: str, anim_name: str, bone_names: List[str]) -> None:
"""
选择骨骼
:param actor_id: Actor ID
:param anim_name: 动画名称
:param bone_names: 骨骼名称列表
"""
key = f"{actor_id}:{anim_name}"
if key in self.editor_states:
self.editor_states[key].selected_bones = bone_names
print(f"选择骨骼: {bone_names} (Actor: {actor_id}, 动画: {anim_name})")
def set_current_frame(self, actor_id: str, anim_name: str, frame: int) -> None:
"""
设置当前帧
:param actor_id: Actor ID
:param anim_name: 动画名称
:param frame: 帧号
"""
key = f"{actor_id}:{anim_name}"
if key in self.editor_states:
editor_state = self.editor_states[key]
editor_state.current_frame = frame
# 定位到指定帧
actor_info = self.core_system.actors[actor_id]
actor_info.actor.pose(anim_name, frame)
print(f"设置当前帧为: {frame} (Actor: {actor_id}, 动画: {anim_name})")
def add_keyframe(self, actor_id: str, anim_name: str, bone_name: str,
frame: int, position: Optional[Tuple[float, float, float]] = None,
rotation: Optional[Tuple[float, float, float]] = None,
scale: Optional[Tuple[float, float, float]] = None) -> None:
"""
添加关键帧
:param actor_id: Actor ID
:param anim_name: 动画名称
:param bone_name: 骨骼名称
:param frame: 帧号
:param position: 位置
:param rotation: 旋转
:param scale: 缩放
"""
# 这是一个简化的实现,实际项目中需要直接操作动画数据
print(f"添加关键帧: 骨骼={bone_name}, 帧={frame} (Actor: {actor_id}, 动画: {anim_name})")
print(f" 位置: {position}")
print(f" 旋转: {rotation}")
print(f" 缩放: {scale}")
# 触发编辑回调
for callback in self.edit_callbacks:
try:
callback('add_keyframe', {
'actor_id': actor_id,
'anim_name': anim_name,
'bone_name': bone_name,
'frame': frame,
'position': position,
'rotation': rotation,
'scale': scale
})
except Exception as e:
print(f"执行编辑回调失败: {e}")
def remove_keyframe(self, actor_id: str, anim_name: str, bone_name: str, frame: int) -> None:
"""
移除关键帧
:param actor_id: Actor ID
:param anim_name: 动画名称
:param bone_name: 骨骼名称
:param frame: 帧号
"""
print(f"移除关键帧: 骨骼={bone_name}, 帧={frame} (Actor: {actor_id}, 动画: {anim_name})")
# 触发编辑回调
for callback in self.edit_callbacks:
try:
callback('remove_keyframe', {
'actor_id': actor_id,
'anim_name': anim_name,
'bone_name': bone_name,
'frame': frame
})
except Exception as e:
print(f"执行编辑回调失败: {e}")
def modify_keyframe(self, actor_id: str, anim_name: str, bone_name: str, frame: int,
position: Optional[Tuple[float, float, float]] = None,
rotation: Optional[Tuple[float, float, float]] = None,
scale: Optional[Tuple[float, float, float]] = None) -> None:
"""
修改关键帧
:param actor_id: Actor ID
:param anim_name: 动画名称
:param bone_name: 骨骼名称
:param frame: 帧号
:param position: 新位置
:param rotation: 新旋转
:param scale: 新缩放
"""
print(f"修改关键帧: 骨骼={bone_name}, 帧={frame} (Actor: {actor_id}, 动画: {anim_name})")
print(f" 新位置: {position}")
print(f" 新旋转: {rotation}")
print(f" 新缩放: {scale}")
# 触发编辑回调
for callback in self.edit_callbacks:
try:
callback('modify_keyframe', {
'actor_id': actor_id,
'anim_name': anim_name,
'bone_name': bone_name,
'frame': frame,
'position': position,
'rotation': rotation,
'scale': scale
})
except Exception as e:
print(f"执行编辑回调失败: {e}")
def add_edit_callback(self, callback: Callable) -> None:
"""
添加编辑回调
:param callback: 回调函数
"""
self.edit_callbacks.append(callback)
print("添加编辑回调")
def remove_edit_callback(self, callback: Callable) -> None:
"""
移除编辑回调
:param callback: 回调函数
"""
if callback in self.edit_callbacks:
self.edit_callbacks.remove(callback)
print("移除编辑回调")
class DebugVisualizer:
"""
调试可视化工具
提供骨骼、边界框等可视化功能
"""
def __init__(self, world):
"""
初始化调试可视化工具
:param world: 世界对象
"""
self.world = world
self.visualizations: Dict[str, NodePath] = {}
self.enabled = TOOL_SETTINGS['debug_visualization_enabled']
self.mode = DebugVisualizationMode.SKELETON
print("调试可视化工具初始化完成")
def set_mode(self, mode: DebugVisualizationMode) -> None:
"""
设置可视化模式
:param mode: 模式
"""
self.mode = mode
print(f"调试可视化模式设置为: {mode.name}")
def enable_visualization(self, enabled: bool = True) -> None:
"""
启用/禁用可视化
:param enabled: 是否启用
"""
self.enabled = enabled
if not enabled:
self.clear_all_visualizations()
print(f"调试可视化已{'启用' if enabled else '禁用'}")
def visualize_actor_skeleton(self, actor_id: str, actor: Actor) -> None:
"""
可视化Actor骨骼
:param actor_id: Actor ID
:param actor: Actor对象
"""
if not self.enabled or self.mode != DebugVisualizationMode.SKELETON:
return
# 清除旧的可视化
self.clear_visualization(actor_id)
# 创建骨骼可视化节点
skeleton_node = self.world.render.attachNewNode(f"skeleton_vis_{actor_id}")
self.visualizations[actor_id] = skeleton_node
# 获取骨骼信息并绘制连线
bundle = actor.getPartBundleDict().get('modelRoot')
if bundle:
self._draw_skeleton_hierarchy(actor, bundle, skeleton_node)
def _draw_skeleton_hierarchy(self, actor: Actor, bundle, parent_node: NodePath) -> None:
"""
绘制骨骼层级
:param actor: Actor对象
:param bundle: 骨骼包
:param parent_node: 父节点
"""
# 这里简化实现,实际项目中需要遍历骨骼树并绘制连线
# 可以使用LineSegs或其他Panda3D绘图功能
# 示例:绘制一个简单的可视化表示
from panda3d.core import LineSegs
segs = LineSegs(f"skeleton_lines_{actor.getName()}")
segs.setColor(0, 1, 0, 1) # 绿色
segs.setThickness(2.0)
# 从根节点到各个子节点绘制线段(简化)
root_pos = Point3(0, 0, 0)
segs.moveTo(root_pos)
for i in range(min(5, bundle.getNumChildren())): # 只绘制前5个骨骼
child = bundle.getChild(i)
if hasattr(child, 'getName'):
bone_name = child.getName()
joint = actor.exposeJoint(None, "modelRoot", bone_name)
if joint:
joint_pos = joint.getPos()
segs.drawTo(joint_pos)
segs.moveTo(root_pos)
# 创建节点并附加到场景
lines_node = segs.create()
np = parent_node.attachNewNode(lines_node)
np.setBin("fixed", 40)
np.setDepthTest(False)
np.setDepthWrite(False)
def visualize_actor_bounds(self, actor_id: str, actor: Actor) -> None:
"""
可视化Actor边界框
:param actor_id: Actor ID
:param actor: Actor对象
"""
if not self.enabled or self.mode != DebugVisualizationMode.BOUNDS:
return
# 清除旧的可视化
self.clear_visualization(actor_id)
# 获取边界框
bounds = actor.getTightBounds()
if bounds:
min_point, max_point = bounds
# 创建边界框可视化
from panda3d.core import LineSegs
segs = LineSegs(f"bounds_vis_{actor_id}")
segs.setColor(1, 0, 0, 1) # 红色
segs.setThickness(1.0)
# 绘制立方体边界框
self._draw_wire_cube(segs, min_point, max_point)
# 创建节点并附加到场景
bounds_node = self.world.render.attachNewNode(segs.create())
bounds_node.setBin("fixed", 40)
bounds_node.setDepthTest(False)
bounds_node.setDepthWrite(False)
self.visualizations[actor_id] = bounds_node
def _draw_wire_cube(self, segs: LineSegs, min_point: Point3, max_point: Point3) -> None:
"""
绘制线框立方体
:param segs: LineSegs对象
:param min_point: 最小点
:param max_point: 最大点
"""
# 获取立方体的8个顶点
vertices = [
Point3(min_point.x, min_point.y, min_point.z),
Point3(max_point.x, min_point.y, min_point.z),
Point3(max_point.x, max_point.y, min_point.z),
Point3(min_point.x, max_point.y, min_point.z),
Point3(min_point.x, min_point.y, max_point.z),
Point3(max_point.x, min_point.y, max_point.z),
Point3(max_point.x, max_point.y, max_point.z),
Point3(min_point.x, max_point.y, max_point.z)
]
# 绘制12条边
edges = [
(0, 1), (1, 2), (2, 3), (3, 0), # 底面
(4, 5), (5, 6), (6, 7), (7, 4), # 顶面
(0, 4), (1, 5), (2, 6), (3, 7) # 垂直边
]
for start, end in edges:
segs.moveTo(vertices[start])
segs.drawTo(vertices[end])
def clear_visualization(self, actor_id: str) -> None:
"""
清除指定Actor的可视化
:param actor_id: Actor ID
"""
if actor_id in self.visualizations:
self.visualizations[actor_id].removeNode()
del self.visualizations[actor_id]
def clear_all_visualizations(self) -> None:
"""
清除所有可视化
"""
for np in self.visualizations.values():
np.removeNode()
self.visualizations.clear()
def update_visualizations(self, actors: Dict[str, Any]) -> None:
"""
更新所有可视化
:param actors: Actor字典
"""
if not self.enabled:
return
for actor_id, actor_info in actors.items():
actor = actor_info.actor
if self.mode == DebugVisualizationMode.SKELETON:
self.visualize_actor_skeleton(actor_id, actor)
elif self.mode == DebugVisualizationMode.BOUNDS:
self.visualize_actor_bounds(actor_id, actor)
class AnimationProfiler:
"""
动画性能分析器
提供详细的性能分析和优化建议
"""
def __init__(self, core_system):
"""
初始化动画性能分析器
:param core_system: 核心动画系统
"""
self.core_system = core_system
self.sampling_rate = TOOL_SETTINGS['profiler_sampling_rate']
self.is_profiling = False
self.profile_data: List[PerformanceMetrics] = []
self.last_sample_time = 0.0
print("动画性能分析器初始化完成")
def start_profiling(self) -> None:
"""
开始性能分析
"""
self.is_profiling = True
print("动画性能分析已启动")
def stop_profiling(self) -> None:
"""
停止性能分析
"""
self.is_profiling = False
print("动画性能分析已停止")
def sample_performance(self) -> None:
"""
采样性能数据
"""
if not self.is_profiling:
return
current_time = globalClock.getRealTime()
if current_time - self.last_sample_time < 1.0 / self.sampling_rate:
return
self.last_sample_time = current_time
# 收集性能指标
metrics = self._collect_metrics()
self.profile_data.append(metrics)
# 限制数据大小
if len(self.profile_data) > 1000:
self.profile_data.pop(0)
def _collect_metrics(self) -> PerformanceMetrics:
"""
收集性能指标
:return: 性能指标
"""
# 获取FPS和帧时间
fps = globalClock.getAverageFrameRate()
frame_time = globalClock.getDt()
# 估算动画更新时间(简化)
animation_update_time = frame_time * 0.3 # 假设动画更新占总帧时间的30%
# 估算内存使用(简化)
memory_usage = len(self.core_system.actors) * 1024 * 1024 # 假设每个Actor约1MB
# 统计Actor和动画数量
actor_count = len(self.core_system.actors)
active_animations = sum(len(info.active_animations) for info in self.core_system.actors.values())
return PerformanceMetrics(
fps=fps,
frame_time=frame_time,
animation_update_time=animation_update_time,
memory_usage=memory_usage,
actor_count=actor_count,
active_animations=active_animations
)
def get_performance_report(self) -> Dict[str, Any]:
"""
获取性能报告
:return: 性能报告
"""
if not self.profile_data:
return {}
# 计算统计数据
fps_values = [m.fps for m in self.profile_data]
frame_time_values = [m.frame_time for m in self.profile_data]
update_time_values = [m.animation_update_time for m in self.profile_data]
memory_values = [m.memory_usage for m in self.profile_data]
return {
'sample_count': len(self.profile_data),
'duration_seconds': len(self.profile_data) / self.sampling_rate,
'fps': {
'average': sum(fps_values) / len(fps_values),
'min': min(fps_values),
'max': max(fps_values),
'std_dev': self._calculate_std_dev(fps_values)
},
'frame_time': {
'average': sum(frame_time_values) / len(frame_time_values),
'min': min(frame_time_values),
'max': max(frame_time_values),
'std_dev': self._calculate_std_dev(frame_time_values)
},
'animation_update_time': {
'average': sum(update_time_values) / len(update_time_values),
'min': min(update_time_values),
'max': max(update_time_values),
'std_dev': self._calculate_std_dev(update_time_values)
},
'memory_usage': {
'average': sum(memory_values) / len(memory_values),
'min': min(memory_values),
'max': max(memory_values),
'current': memory_values[-1] if memory_values else 0
},
'actor_stats': {
'current_actor_count': self.profile_data[-1].actor_count if self.profile_data else 0,
'current_active_animations': self.profile_data[-1].active_animations if self.profile_data else 0
}
}
def _calculate_std_dev(self, values: List[float]) -> float:
"""
计算标准差
:param values: 数值列表
:return: 标准差
"""
if len(values) < 2:
return 0.0
mean = sum(values) / len(values)
variance = sum((x - mean) ** 2 for x in values) / (len(values) - 1)
return math.sqrt(variance)
def get_optimization_recommendations(self) -> List[str]:
"""
获取优化建议
:return: 优化建议列表
"""
if not self.profile_data:
return []
latest = self.profile_data[-1]
recommendations = []
# FPS相关建议
if latest.fps < 30:
recommendations.append("FPS过低建议优化动画系统或减少同时播放的动画数量")
# 内存相关建议
if latest.memory_usage > 512 * 1024 * 1024: # 超过512MB
recommendations.append("内存使用过高建议检查Actor加载和卸载逻辑")
# Actor数量建议
if latest.actor_count > 100:
recommendations.append("Actor数量较多建议使用LOD或视锥剔除优化")
# 动画数量建议
if latest.active_animations > 200:
recommendations.append("同时播放的动画过多,建议限制同时活动的动画数量")
return recommendations
# 使用示例和测试代码
def example_analyzer_usage(core_system):
"""
动画分析器使用示例
"""
print("=== 动画分析器使用示例 ===")
# 创建分析器
analyzer = AnimationAnalyzer(core_system)
analyzer.start_analysis()
# 分析示例假设有Actor
# 在实际使用中这里会分析真实的Actor和动画
print("动画分析器示例完成")
return analyzer
def example_editor_usage(core_system):
"""
动画编辑器使用示例
"""
print("=== 动画编辑器使用示例 ===")
# 创建编辑器
editor = AnimationEditor(core_system)
# 添加编辑回调
def edit_callback(event_type, data):
print(f"编辑事件: {event_type}, 数据: {data}")
editor.add_edit_callback(edit_callback)
print("动画编辑器示例完成")
return editor
def example_visualizer_usage(world):
"""
调试可视化工具使用示例
"""
print("=== 调试可视化工具使用示例 ===")
# 创建可视化工具
visualizer = DebugVisualizer(world)
visualizer.enable_visualization(True)
visualizer.set_mode(DebugVisualizationMode.SKELETON)
print("调试可视化工具示例完成")
return visualizer
def example_profiler_usage(core_system):
"""
动画性能分析器使用示例
"""
print("=== 动画性能分析器使用示例 ===")
# 创建分析器
profiler = AnimationProfiler(core_system)
profiler.start_profiling()
# 采样性能数据
profiler.sample_performance()
# 获取性能报告
report = profiler.get_performance_report()
print(f"性能报告: {report}")
# 获取优化建议
recommendations = profiler.get_optimization_recommendations()
print(f"优化建议: {recommendations}")
print("动画性能分析器示例完成")
return profiler
if __name__ == "__main__":
print("动画工具模块加载完成")