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

924 lines
33 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, 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("高级动画功能模块加载完成")