924 lines
33 KiB
Python
924 lines
33 KiB
Python
"""
|
||
高级骨骼动画系统 - 高级功能模块
|
||
提供动画状态机、曲线编辑器、物理集成等高级功能
|
||
"""
|
||
|
||
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("高级动画功能模块加载完成") |