""" 高级骨骼动画系统 - 高级功能模块 提供动画状态机、曲线编辑器、物理集成等高级功能 """ import math import json from typing import Dict, List, Optional, Callable, Any, Tuple, Union from dataclasses import dataclass, field from enum import Enum from panda3d.core import * from direct.actor.Actor import Actor from direct.interval.IntervalGlobal import * # 定义常量 ADVANCED_ANIMATION_SETTINGS = { 'state_machine_max_transitions': 100, 'curve_editor_max_keyframes': 1000, 'physics_integration_enabled': True, 'physics_update_rate': 60 } # 枚举定义 class StateTransitionType(Enum): """状态转换类型枚举""" IMMEDIATE = 0 SMOOTH = 1 BLEND = 2 CONDITIONAL = 3 class CurveInterpolation(Enum): """曲线插值类型枚举""" LINEAR = 0 BEZIER = 1 HERMITE = 2 STEP = 3 SMOOTH = 4 class PhysicsIntegrationMode(Enum): """物理集成模式枚举""" KINEMATIC = 0 DYNAMIC = 1 CONSTRAINT = 2 @dataclass class StateMachineState: """状态机状态数据类""" name: str animations: List[str] loop: bool = True play_rate: float = 1.0 blend_time: float = 0.3 enter_callbacks: List[Callable] = field(default_factory=list) exit_callbacks: List[Callable] = field(default_factory=list) update_callbacks: List[Callable] = field(default_factory=list) transitions: Dict[str, 'StateTransition'] = field(default_factory=dict) @dataclass class StateTransition: """状态转换数据类""" to_state: str transition_type: StateTransitionType condition: Optional[Callable] = None transition_time: float = 0.3 blend_mode: str = 'linear' @dataclass class AnimationCurve: """动画曲线数据类""" name: str keyframes: List[Tuple[float, float]] = field(default_factory=list) interpolation: CurveInterpolation = CurveInterpolation.LINEAR min_value: float = 0.0 max_value: float = 1.0 loop: bool = False @dataclass class PhysicsConstraint: """物理约束数据类""" bone_name: str constraint_type: str # 'point', 'hinge', 'slider', 'fixed' target_node: Optional[str] = None stiffness: float = 1.0 damping: float = 0.1 max_force: float = 1000.0 class AnimationStateMachine: """ 动画状态机 管理复杂的动画状态转换和混合 """ def __init__(self, actor: Actor): """ 初始化动画状态机 :param actor: Actor对象 """ self.actor = actor self.states: Dict[str, StateMachineState] = {} self.current_state: Optional[str] = None self.previous_state: Optional[str] = None self.transition_in_progress = False self.transition_timer = 0.0 self.transition_duration = 0.0 self.state_timers: Dict[str, float] = {} self.global_timer = 0.0 print(f"动画状态机为Actor {actor.getName() if hasattr(actor, 'getName') else 'Unknown'} 初始化完成") def add_state(self, state_name: str, animations: Union[str, List[str]], loop: bool = True, play_rate: float = 1.0, blend_time: float = 0.3) -> None: """ 添加动画状态 :param state_name: 状态名称 :param animations: 动画列表或单个动画名称 :param loop: 是否循环 :param play_rate: 播放速度 :param blend_time: 状态切换混合时间 """ if isinstance(animations, str): animations = [animations] # 验证动画是否存在 valid_animations = [anim for anim in animations if anim in self.actor.getAnimNames()] if len(valid_animations) != len(animations): invalid_anims = set(animations) - set(valid_animations) print(f"警告: 以下动画不存在: {invalid_anims}") state = StateMachineState( name=state_name, animations=valid_animations, loop=loop, play_rate=play_rate, blend_time=blend_time ) self.states[state_name] = state self.state_timers[state_name] = 0.0 print(f"添加动画状态: {state_name} (动画: {valid_animations})") def remove_state(self, state_name: str) -> None: """ 移除动画状态 :param state_name: 状态名称 """ if state_name in self.states: # 如果是当前状态,先退出 if self.current_state == state_name: self.exit_state(state_name) del self.states[state_name] if state_name in self.state_timers: del self.state_timers[state_name] print(f"移除动画状态: {state_name}") def set_transition(self, from_state: str, to_state: str, transition_type: StateTransitionType = StateTransitionType.SMOOTH, condition: Optional[Callable] = None, transition_time: float = 0.3, blend_mode: str = 'linear') -> None: """ 设置状态转换 :param from_state: 起始状态 :param to_state: 目标状态 :param transition_type: 转换类型 :param condition: 转换条件函数,返回True时执行转换 :param transition_time: 转换时间 :param blend_mode: 混合模式 """ if from_state not in self.states: print(f"错误: 起始状态 {from_state} 不存在") return if to_state not in self.states: print(f"错误: 目标状态 {to_state} 不存在") return transition = StateTransition( to_state=to_state, transition_type=transition_type, condition=condition, transition_time=transition_time, blend_mode=blend_mode ) self.states[from_state].transitions[to_state] = transition print(f"设置状态转换: {from_state} -> {to_state}") def add_state_callback(self, state_name: str, callback_type: str, callback: Callable) -> None: """ 添加状态回调 :param state_name: 状态名称 :param callback_type: 回调类型 ('enter', 'exit', 'update') :param callback: 回调函数 """ if state_name not in self.states: print(f"错误: 状态 {state_name} 不存在") return state = self.states[state_name] if callback_type == 'enter': state.enter_callbacks.append(callback) elif callback_type == 'exit': state.exit_callbacks.append(callback) elif callback_type == 'update': state.update_callbacks.append(callback) else: print(f"错误: 无效的回调类型 {callback_type}") def enter_state(self, state_name: str) -> bool: """ 进入指定状态 :param state_name: 状态名称 :return: 是否成功进入 """ if state_name not in self.states: print(f"错误: 状态 {state_name} 不存在") return False # 退出当前状态 if self.current_state: self.exit_state(self.current_state) # 进入新状态 state = self.states[state_name] # 播放状态动画 for anim_name in state.animations: if state.loop: self.actor.loop(anim_name, restart=False) else: self.actor.play(anim_name) self.actor.setPlayRate(state.play_rate, anim_name) # 调用进入回调 for callback in state.enter_callbacks: try: callback(self.actor, state_name) except Exception as e: print(f"执行状态进入回调失败: {e}") self.previous_state = self.current_state self.current_state = state_name self.state_timers[state_name] = 0.0 print(f"进入动画状态: {state_name}") return True def exit_state(self, state_name: str) -> None: """ 退出指定状态 :param state_name: 状态名称 """ if state_name not in self.states or state_name != self.current_state: return state = self.states[state_name] # 停止状态动画 for anim_name in state.animations: self.actor.stop(anim_name) # 调用退出回调 for callback in state.exit_callbacks: try: callback(self.actor, state_name) except Exception as e: print(f"执行状态退出回调失败: {e}") print(f"退出动画状态: {state_name}") self.current_state = None def update(self, dt: float) -> None: """ 更新状态机 :param dt: 时间增量 """ self.global_timer += dt if not self.current_state: return # 更新当前状态计时器 self.state_timers[self.current_state] += dt # 调用更新回调 state = self.states[self.current_state] for callback in state.update_callbacks: try: callback(self.actor, self.current_state, dt, self.state_timers[self.current_state]) except Exception as e: print(f"执行状态更新回调失败: {e}") # 检查状态转换 self._check_transitions() def _check_transitions(self) -> None: """ 检查状态转换条件 """ if not self.current_state or self.transition_in_progress: return state = self.states[self.current_state] # 检查所有可能的转换 for transition in state.transitions.values(): # 检查转换条件 if transition.condition is None or transition.condition(self.actor, self.current_state): self._start_transition(transition) break def _start_transition(self, transition: StateTransition) -> None: """ 开始状态转换 :param transition: 转换信息 """ if self.transition_in_progress: return self.transition_in_progress = True self.transition_timer = 0.0 self.transition_duration = transition.transition_time print(f"开始状态转换: {self.current_state} -> {transition.to_state}") # 根据转换类型处理 if transition.transition_type == StateTransitionType.IMMEDIATE: self._complete_transition(transition) elif transition.transition_type == StateTransitionType.SMOOTH: # 平滑转换,开始混合 self._start_smooth_transition(transition) elif transition.transition_type == StateTransitionType.BLEND: # 动画混合转换 self._start_blend_transition(transition) elif transition.transition_type == StateTransitionType.CONDITIONAL: # 条件转换(已经在条件检查中处理) self._complete_transition(transition) def _start_smooth_transition(self, transition: StateTransition) -> None: """ 开始平滑转换 :param transition: 转换信息 """ # 这里可以实现更复杂的平滑转换逻辑 # 简化处理:直接完成转换 self._complete_transition(transition) def _start_blend_transition(self, transition: StateTransition) -> None: """ 开始混合转换 :param transition: 转换信息 """ # 这里可以实现动画混合逻辑 # 简化处理:直接完成转换 self._complete_transition(transition) def _update_transition(self, dt: float) -> None: """ 更新转换过程 :param dt: 时间增量 """ if not self.transition_in_progress: return self.transition_timer += dt # 检查转换是否完成 if self.transition_timer >= self.transition_duration: # 查找当前转换的目标状态 if self.current_state and self.current_state in self.states: state = self.states[self.current_state] for transition in state.transitions.values(): if transition.to_state: self._complete_transition(transition) break def _complete_transition(self, transition: StateTransition) -> None: """ 完成状态转换 :param transition: 转换信息 """ self.transition_in_progress = False self.transition_timer = 0.0 self.transition_duration = 0.0 # 进入目标状态 self.enter_state(transition.to_state) def get_current_state(self) -> Optional[str]: """ 获取当前状态 :return: 当前状态名称 """ return self.current_state def get_available_states(self) -> List[str]: """ 获取所有可用状态 :return: 状态名称列表 """ return list(self.states.keys()) def get_state_info(self, state_name: str) -> Optional[Dict[str, Any]]: """ 获取状态信息 :param state_name: 状态名称 :return: 状态信息字典 """ if state_name not in self.states: return None state = self.states[state_name] return { 'name': state.name, 'animations': state.animations.copy(), 'loop': state.loop, 'play_rate': state.play_rate, 'blend_time': state.blend_time, 'transitions': list(state.transitions.keys()), 'time_in_state': self.state_timers.get(state_name, 0.0) } class AnimationCurveEditor: """ 动画曲线编辑器 支持对动画属性进行曲线编辑和自定义 """ def __init__(self, actor: Actor): """ 初始化动画曲线编辑器 :param actor: Actor对象 """ self.actor = actor self.curves: Dict[str, AnimationCurve] = {} self.curve_targets: Dict[str, Dict[str, Any]] = {} self.active_curves: Dict[str, float] = {} # {curve_name: current_time} self.curve_values: Dict[str, float] = {} # {target_name: current_value} print(f"动画曲线编辑器为Actor {actor.getName() if hasattr(actor, 'getName') else 'Unknown'} 初始化完成") def create_curve(self, curve_name: str, keyframes: List[Tuple[float, float]], interpolation: CurveInterpolation = CurveInterpolation.LINEAR, min_value: float = 0.0, max_value: float = 1.0, loop: bool = False) -> None: """ 创建动画曲线 :param curve_name: 曲线名称 :param keyframes: 关键帧列表 [(time, value), ...] :param interpolation: 插值方式 :param min_value: 最小值 :param max_value: 最大值 :param loop: 是否循环 """ if len(keyframes) == 0: print("错误: 关键帧列表不能为空") return # 验证关键帧 sorted_keyframes = sorted(keyframes, key=lambda x: x[0]) curve = AnimationCurve( name=curve_name, keyframes=sorted_keyframes, interpolation=interpolation, min_value=min_value, max_value=max_value, loop=loop ) self.curves[curve_name] = curve self.active_curves[curve_name] = 0.0 print(f"创建动画曲线: {curve_name} ({len(keyframes)} 个关键帧)") def remove_curve(self, curve_name: str) -> None: """ 移除动画曲线 :param curve_name: 曲线名称 """ if curve_name in self.curves: del self.curves[curve_name] if curve_name in self.active_curves: del self.active_curves[curve_name] print(f"移除动画曲线: {curve_name}") def add_curve_target(self, target_name: str, curve_name: str, property_path: str, transform_type: str = 'scale') -> None: """ 添加曲线目标 :param target_name: 目标名称 :param curve_name: 曲线名称 :param property_path: 属性路径(如骨骼名称) :param transform_type: 变换类型 ('scale', 'position', 'rotation') """ if curve_name not in self.curves: print(f"错误: 曲线 {curve_name} 不存在") return self.curve_targets[target_name] = { 'curve_name': curve_name, 'property_path': property_path, 'transform_type': transform_type } print(f"添加曲线目标: {target_name} -> {curve_name}") def remove_curve_target(self, target_name: str) -> None: """ 移除曲线目标 :param target_name: 目标名称 """ if target_name in self.curve_targets: del self.curve_targets[target_name] print(f"移除曲线目标: {target_name}") def evaluate_curve(self, curve_name: str, time: float) -> float: """ 计算曲线在指定时间的值 :param curve_name: 曲线名称 :param time: 时间 :return: 曲线值 """ if curve_name not in self.curves: return 0.0 curve = self.curves[curve_name] keyframes = curve.keyframes if len(keyframes) == 0: return 0.0 # 处理循环 if curve.loop and len(keyframes) > 1: duration = keyframes[-1][0] - keyframes[0][0] if duration > 0: time = keyframes[0][0] + ((time - keyframes[0][0]) % duration) # 边界情况 if time <= keyframes[0][0]: return max(curve.min_value, min(curve.max_value, keyframes[0][1])) if time >= keyframes[-1][0]: return max(curve.min_value, min(curve.max_value, keyframes[-1][1])) # 查找相邻关键帧 for i in range(len(keyframes) - 1): if keyframes[i][0] <= time <= keyframes[i + 1][0]: t1, v1 = keyframes[i] t2, v2 = keyframes[i + 1] # 根据插值方式计算值 if curve.interpolation == CurveInterpolation.STEP: return max(curve.min_value, min(curve.max_value, v1)) elif curve.interpolation == CurveInterpolation.BEZIER: # 简化的贝塞尔插值 t = (time - t1) / (t2 - t1) if t2 != t1 else 0 # 贝塞尔曲线计算 (简化版) return max(curve.min_value, min(curve.max_value, v1 + (v2 - v1) * (3 * t * t - 2 * t * t * t))) elif curve.interpolation == CurveInterpolation.HERMITE: # 埃尔米特插值 t = (time - t1) / (t2 - t1) if t2 != t1 else 0 t2_sq = t * t t3_sq = t2_sq * t h00 = 2 * t3_sq - 3 * t2_sq + 1 h10 = t3_sq - 2 * t2_sq + t h01 = -2 * t3_sq + 3 * t2_sq h11 = t3_sq - t2_sq value = h00 * v1 + h10 * (t2 - t1) + h01 * v2 + h11 * (t2 - t1) return max(curve.min_value, min(curve.max_value, value)) elif curve.interpolation == CurveInterpolation.SMOOTH: # 平滑插值 t = (time - t1) / (t2 - t1) if t2 != t1 else 0 # 使用平滑步进函数 t = t * t * (3 - 2 * t) return max(curve.min_value, min(curve.max_value, v1 + (v2 - v1) * t)) else: # LINEAR t = (time - t1) / (t2 - t1) if t2 != t1 else 0 return max(curve.min_value, min(curve.max_value, v1 + (v2 - v1) * t)) return 0.0 def update_curves(self, dt: float) -> None: """ 更新所有曲线 :param dt: 时间增量 """ # 更新活动曲线时间 for curve_name in self.active_curves.keys(): self.active_curves[curve_name] += dt # 计算曲线值并应用到目标 for target_name, target_data in self.curve_targets.items(): curve_name = target_data['curve_name'] property_path = target_data['property_path'] transform_type = target_data['transform_type'] # 获取当前时间 current_time = self.active_curves.get(curve_name, 0.0) # 计算曲线值 value = self.evaluate_curve(curve_name, current_time) # 应用到目标 self._apply_curve_value(property_path, transform_type, value) # 存储当前值 self.curve_values[target_name] = value def _apply_curve_value(self, property_path: str, transform_type: str, value: float) -> None: """ 应用曲线值到目标属性 :param property_path: 属性路径 :param transform_type: 变换类型 :param value: 值 """ # 查找目标骨骼 joint = self.actor.exposeJoint(None, "modelRoot", property_path) if not joint: return # 根据变换类型应用值 if transform_type == 'scale': joint.setScale(value, value, value) elif transform_type == 'position': current_pos = joint.getPos() # 简化处理,实际应该根据具体轴向应用 joint.setPos(current_pos.x + value * 0.01, current_pos.y, current_pos.z) elif transform_type == 'rotation': # 简化处理,实际应该分解为具体的旋转轴 joint.setHpr(joint.getHpr() + Vec3(value * 10, value * 5, value * 2)) def get_curve_value(self, target_name: str) -> float: """ 获取曲线目标的当前值 :param target_name: 目标名称 :return: 当前值 """ return self.curve_values.get(target_name, 0.0) def get_curve_info(self, curve_name: str) -> Optional[Dict[str, Any]]: """ 获取曲线信息 :param curve_name: 曲线名称 :return: 曲线信息字典 """ if curve_name not in self.curves: return None curve = self.curves[curve_name] return { 'name': curve.name, 'keyframes_count': len(curve.keyframes), 'interpolation': curve.interpolation.name, 'min_value': curve.min_value, 'max_value': curve.max_value, 'loop': curve.loop, 'current_time': self.active_curves.get(curve_name, 0.0) } class PhysicsAnimationIntegration: """ 物理动画集成系统 将物理模拟与骨骼动画结合 """ def __init__(self, actor: Actor): """ 初始化物理动画集成系统 :param actor: Actor对象 """ self.actor = actor self.constraints: Dict[str, PhysicsConstraint] = {} self.physics_enabled = True self.integration_mode = PhysicsIntegrationMode.KINEMATIC self.update_rate = 60 self.update_timer = 0.0 print(f"物理动画集成系统为Actor {actor.getName() if hasattr(actor, 'getName') else 'Unknown'} 初始化完成") def add_constraint(self, constraint_name: str, bone_name: str, constraint_type: str, target_node: Optional[str] = None, stiffness: float = 1.0, damping: float = 0.1, max_force: float = 1000.0) -> None: """ 添加物理约束 :param constraint_name: 约束名称 :param bone_name: 骨骼名称 :param constraint_type: 约束类型 ('point', 'hinge', 'slider', 'fixed') :param target_node: 目标节点名称 :param stiffness: 刚度 :param damping: 阻尼 :param max_force: 最大力 """ # 验证骨骼是否存在 joint = self.actor.exposeJoint(None, "modelRoot", bone_name) if not joint: print(f"警告: 骨骼 {bone_name} 不存在") constraint = PhysicsConstraint( bone_name=bone_name, constraint_type=constraint_type, target_node=target_node, stiffness=stiffness, damping=damping, max_force=max_force ) self.constraints[constraint_name] = constraint print(f"添加物理约束: {constraint_name} ({constraint_type}) 到骨骼 {bone_name}") def remove_constraint(self, constraint_name: str) -> None: """ 移除物理约束 :param constraint_name: 约束名称 """ if constraint_name in self.constraints: del self.constraints[constraint_name] print(f"移除物理约束: {constraint_name}") def set_integration_mode(self, mode: PhysicsIntegrationMode) -> None: """ 设置集成模式 :param mode: 集成模式 """ self.integration_mode = mode print(f"物理集成模式设置为: {mode.name}") def enable_physics(self, enabled: bool = True) -> None: """ 启用/禁用物理集成 :param enabled: 是否启用 """ self.physics_enabled = enabled status = "启用" if enabled else "禁用" print(f"物理集成已{status}") def update(self, dt: float) -> None: """ 更新物理集成 :param dt: 时间增量 """ if not self.physics_enabled: return self.update_timer += dt # 根据更新率限制更新频率 if self.update_timer < 1.0 / self.update_rate: return self.update_timer = 0.0 # 更新所有约束 for constraint_name, constraint in self.constraints.items(): self._update_constraint(constraint) def _update_constraint(self, constraint: PhysicsConstraint) -> None: """ 更新物理约束 :param constraint: 约束信息 """ # 获取骨骼关节 joint = self.actor.exposeJoint(None, "modelRoot", constraint.bone_name) if not joint: return # 根据约束类型应用物理效果 if constraint.constraint_type == 'point': self._apply_point_constraint(joint, constraint) elif constraint.constraint_type == 'hinge': self._apply_hinge_constraint(joint, constraint) elif constraint.constraint_type == 'slider': self._apply_slider_constraint(joint, constraint) elif constraint.constraint_type == 'fixed': self._apply_fixed_constraint(joint, constraint) def _apply_point_constraint(self, joint, constraint: PhysicsConstraint) -> None: """ 应用点约束 :param joint: 关节节点 :param constraint: 约束信息 """ # 点约束实现 - 简化版本 # 在实际实现中,这里会与物理引擎交互 pass def _apply_hinge_constraint(self, joint, constraint: PhysicsConstraint) -> None: """ 应用铰链约束 :param joint: 关节节点 :param constraint: 约束信息 """ # 铰链约束实现 - 简化版本 pass def _apply_slider_constraint(self, joint, constraint: PhysicsConstraint) -> None: """ 应用滑动约束 :param joint: 关节节点 :param constraint: 约束信息 """ # 滑动约束实现 - 简化版本 pass def _apply_fixed_constraint(self, joint, constraint: PhysicsConstraint) -> None: """ 应用固定约束 :param joint: 关节节点 :param constraint: 约束信息 """ # 固定约束实现 - 简化版本 pass def get_constraint_info(self, constraint_name: str) -> Optional[Dict[str, Any]]: """ 获取约束信息 :param constraint_name: 约束名称 :return: 约束信息字典 """ if constraint_name not in self.constraints: return None constraint = self.constraints[constraint_name] return { 'name': constraint_name, 'bone_name': constraint.bone_name, 'constraint_type': constraint.constraint_type, 'target_node': constraint.target_node, 'stiffness': constraint.stiffness, 'damping': constraint.damping, 'max_force': constraint.max_force } # 使用示例和测试代码 def example_state_machine_usage(actor: Actor): """ 状态机使用示例 """ print("=== 动画状态机使用示例 ===") # 创建状态机 state_machine = AnimationStateMachine(actor) # 添加状态 state_machine.add_state("idle", "idle_anim", loop=True) state_machine.add_state("walk", "walk_anim", loop=True) state_machine.add_state("run", "run_anim", loop=True) state_machine.add_state("jump", ["jump_start", "jump_loop", "jump_end"], loop=False) # 设置状态转换 state_machine.set_transition("idle", "walk", StateTransitionType.SMOOTH) state_machine.set_transition("walk", "run", StateTransitionType.SMOOTH) state_machine.set_transition("run", "idle", StateTransitionType.SMOOTH) # 添加回调 def on_enter_walk(actor, state_name): print(f"进入行走状态: {state_name}") state_machine.add_state_callback("walk", "enter", on_enter_walk) # 进入初始状态 state_machine.enter_state("idle") print("状态机示例设置完成") return state_machine def example_curve_editor_usage(actor: Actor): """ 曲线编辑器使用示例 """ print("=== 动画曲线编辑器使用示例 ===") # 创建曲线编辑器 curve_editor = AnimationCurveEditor(actor) # 创建曲线 scale_curve_frames = [(0, 1.0), (1, 1.5), (2, 1.0), (3, 0.8), (4, 1.0)] curve_editor.create_curve("bounce_scale", scale_curve_frames, CurveInterpolation.SMOOTH) position_curve_frames = [(0, 0), (1, 0.5), (2, 0), (3, -0.3), (4, 0)] curve_editor.create_curve("bounce_position", position_curve_frames, CurveInterpolation.SMOOTH) # 添加曲线目标 curve_editor.add_curve_target("character_scale", "bounce_scale", "character", "scale") curve_editor.add_curve_target("character_height", "bounce_position", "character", "position") print("曲线编辑器示例设置完成") return curve_editor def example_physics_integration_usage(actor: Actor): """ 物理集成使用示例 """ print("=== 物理动画集成使用示例 ===") # 创建物理集成系统 physics_integration = PhysicsAnimationIntegration(actor) # 添加约束 physics_integration.add_constraint( "arm_constraint", "right_arm", "hinge", stiffness=0.8, damping=0.2 ) physics_integration.add_constraint( "leg_constraint", "left_leg", "point", stiffness=0.9, damping=0.1 ) # 启用物理集成 physics_integration.enable_physics(True) physics_integration.set_integration_mode(PhysicsIntegrationMode.DYNAMIC) print("物理集成示例设置完成") return physics_integration if __name__ == "__main__": print("高级动画功能模块加载完成")