EG/plugins/user/skeletal_animation_system/plugin.py
2025-12-12 16:16:15 +08:00

1224 lines
45 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.

"""
骨骼动画系统插件
提供完整的骨骼动画支持,包括加载、播放、控制、混合等高级功能
"""
from plugins.plugin_manager import BasePlugin
from direct.actor.Actor import Actor
from direct.interval.IntervalGlobal import *
from panda3d.core import Filename, ConfigVariableBool
from panda3d.core import NodePath, Vec3, Point3
import os
import json
import math
# 导入高级功能模块
from .advanced_features import AdvancedAnimationManager
from .animation_optimization import AnimationCompression, AnimationCaching, AnimationLOD
from .network_sync import DistributedAnimationManager, AnimationStateSerializer
class SkeletalAnimationManager:
"""
骨骼动画管理器类
负责管理场景中所有的骨骼动画Actor
"""
def __init__(self, world):
self.world = world
self.actors = {} # 存储所有Actor {actor_id: actor_object}
self.actor_configs = {} # 存储Actor配置 {actor_id: config_dict}
self.active_animations = {} # 存储活动动画 {actor_id: {anim_name: control_object}}
self.animation_intervals = {} # 存储动画间隔 {actor_id: {interval_name: interval_object}}
self.blend_intervals = {} # 存储混合动画 {actor_id: {blend_name: blend_object}}
# 高级功能管理器
self.advanced_managers = {} # {actor_id: AdvancedAnimationManager}
self.compression_system = AnimationCompression()
self.caching_system = AnimationCaching()
self.lod_system = AnimationLOD(world)
self.distributed_manager = DistributedAnimationManager(world)
self.serializer = AnimationStateSerializer()
def load_actor(self, model_path, anim_dict=None, actor_id=None, position=None, scale=None):
"""
加载Actor模型
:param model_path: 模型文件路径
:param anim_dict: 动画字典,格式 {'anim_name': 'anim_path'}
:param actor_id: Actor标识符如果为None则自动生成
:param position: 位置坐标 (x, y, z)
:param scale: 缩放比例 (x, y, z) 或单个数值
:return: Actor对象和actor_id
"""
try:
if not os.path.exists(model_path):
print(f"错误: 模型文件不存在: {model_path}")
return None, None
if actor_id is None:
actor_id = f"actor_{len(self.actors)}"
# 检查缓存
cached_actor = self.caching_system.get_cached_animation(actor_id, "model")
if cached_actor:
actor = cached_actor
else:
# 创建Actor
if anim_dict:
# 验证动画文件存在性
validated_anims = {}
for anim_name, anim_path in anim_dict.items():
if os.path.exists(anim_path):
validated_anims[anim_name] = anim_path
else:
print(f"警告: 动画文件不存在: {anim_path}")
actor = Actor(model_path, validated_anims)
else:
actor = Actor(model_path)
# 缓存Actor
self.caching_system.cache_animation(actor_id, "model", actor)
# 设置位置和缩放
if position:
actor.setPos(*position)
if scale:
if isinstance(scale, (int, float)):
actor.setScale(scale)
else:
actor.setScale(*scale)
# 将Actor添加到场景中
actor.reparentTo(self.world.render)
actor.setName(actor_id)
# 存储Actor和配置
self.actors[actor_id] = actor
self.actor_configs[actor_id] = {
'model_path': model_path,
'anim_dict': anim_dict,
'position': position,
'scale': scale
}
# 创建高级动画管理器
self.advanced_managers[actor_id] = AdvancedAnimationManager(actor)
print(f"成功加载Actor: {actor_id}")
print(f" 模型: {model_path}")
print(f" 位置: {position}")
print(f" 缩放: {scale}")
print(f" 可用动画: {actor.getAnimNames()}")
return actor, actor_id
except Exception as e:
print(f"加载Actor失败: {e}")
import traceback
traceback.print_exc()
return None, None
def unload_actor(self, actor_id):
"""
卸载Actor
:param actor_id: 要卸载的Actor标识符
"""
if actor_id in self.actors:
# 停止所有活动动画
self.stop_all_animations(actor_id)
# 清理混合动画
if actor_id in self.blend_intervals:
for blend in self.blend_intervals[actor_id].values():
if blend:
blend.finish()
del self.blend_intervals[actor_id]
# 清理动画间隔
if actor_id in self.animation_intervals:
for interval in self.animation_intervals[actor_id].values():
if interval:
interval.finish()
del self.animation_intervals[actor_id]
# 清理高级动画管理器
if actor_id in self.advanced_managers:
del self.advanced_managers[actor_id]
# 清理Actor
actor = self.actors[actor_id]
actor.cleanup()
del self.actors[actor_id]
# 清理配置
if actor_id in self.actor_configs:
del self.actor_configs[actor_id]
# 清理活动动画记录
if actor_id in self.active_animations:
del self.active_animations[actor_id]
# 使缓存失效
self.caching_system.invalidate_cache(actor_id)
print(f"已卸载Actor: {actor_id}")
def get_actor(self, actor_id):
"""
获取Actor对象
:param actor_id: Actor标识符
:return: Actor对象或None
"""
return self.actors.get(actor_id)
def get_actor_animations(self, actor_id):
"""
获取Actor的所有动画名称
:param actor_id: Actor标识符
:return: 动画名称列表
"""
if actor_id in self.actors:
return self.actors[actor_id].getAnimNames()
return []
def play_animation(self, actor_id, anim_name, loop=False, play_rate=1.0, start_frame=None, end_frame=None):
"""
播放动画
:param actor_id: Actor标识符
:param anim_name: 动画名称
:param loop: 是否循环播放
:param play_rate: 播放速度
:param start_frame: 起始帧
:param end_frame: 结束帧
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return False
actor = self.actors[actor_id]
# 检查动画是否存在
if anim_name not in actor.getAnimNames():
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id}")
print(f" 可用动画: {actor.getAnimNames()}")
return False
try:
# 设置播放速度
actor.setPlayRate(play_rate, anim_name)
# 播放动画
if loop:
if start_frame is not None and end_frame is not None:
actor.loop(anim_name, restart=False, fromFrame=start_frame, toFrame=end_frame)
else:
actor.loop(anim_name, restart=False)
else:
if start_frame is not None and end_frame is not None:
actor.play(anim_name, fromFrame=start_frame, toFrame=end_frame)
else:
actor.play(anim_name)
# 记录活动动画
if actor_id not in self.active_animations:
self.active_animations[actor_id] = {}
self.active_animations[actor_id][anim_name] = actor.getAnimControl(anim_name)
print(f"播放动画: {anim_name} (Actor: {actor_id}, 循环: {loop}, 速度: {play_rate}x)")
return True
except Exception as e:
print(f"播放动画失败: {e}")
import traceback
traceback.print_exc()
return False
def stop_animation(self, actor_id, anim_name=None):
"""
停止动画
:param actor_id: Actor标识符
:param anim_name: 动画名称如果为None则停止所有动画
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return
actor = self.actors[actor_id]
try:
if anim_name:
# 停止指定动画
if anim_name in actor.getAnimNames():
actor.stop(anim_name)
# 从活动动画中移除
if actor_id in self.active_animations and anim_name in self.active_animations[actor_id]:
del self.active_animations[actor_id][anim_name]
print(f"停止动画: {anim_name} (Actor: {actor_id})")
else:
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id}")
else:
# 停止所有动画
actor.stop()
# 清空活动动画记录
if actor_id in self.active_animations:
self.active_animations[actor_id].clear()
print(f"停止所有动画 (Actor: {actor_id})")
except Exception as e:
print(f"停止动画失败: {e}")
import traceback
traceback.print_exc()
def stop_all_animations(self, actor_id):
"""
停止指定Actor的所有动画
:param actor_id: Actor标识符
"""
self.stop_animation(actor_id)
def set_animation_speed(self, actor_id, anim_name, speed):
"""
设置动画播放速度
:param actor_id: Actor标识符
:param anim_name: 动画名称
:param speed: 播放速度
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return
actor = self.actors[actor_id]
try:
if anim_name in actor.getAnimNames():
actor.setPlayRate(speed, anim_name)
print(f"设置动画速度: {anim_name}{speed}x (Actor: {actor_id})")
else:
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id}")
except Exception as e:
print(f"设置动画速度失败: {e}")
import traceback
traceback.print_exc()
def pause_animation(self, actor_id, anim_name=None):
"""
暂停动画
:param actor_id: Actor标识符
:param anim_name: 动画名称如果为None则暂停所有动画
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return
actor = self.actors[actor_id]
try:
if anim_name:
if anim_name in actor.getAnimNames():
control = actor.getAnimControl(anim_name)
if control and control.isPlaying():
control.setPlayRate(0) # 通过设置播放速度为0来模拟暂停
print(f"暂停动画: {anim_name} (Actor: {actor_id})")
else:
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id}")
else:
# 暂停所有活动动画
if actor_id in self.active_animations:
for anim_name, control in self.active_animations[actor_id].items():
if control and control.isPlaying():
control.setPlayRate(0)
print(f"暂停所有动画 (Actor: {actor_id})")
except Exception as e:
print(f"暂停动画失败: {e}")
import traceback
traceback.print_exc()
def resume_animation(self, actor_id, anim_name=None):
"""
恢复动画播放
:param actor_id: Actor标识符
:param anim_name: 动画名称如果为None则恢复所有动画
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return
actor = self.actors[actor_id]
try:
if anim_name:
if anim_name in actor.getAnimNames():
control = actor.getAnimControl(anim_name)
if control and control.getPlayRate() == 0:
control.setPlayRate(1.0) # 恢复正常播放速度
print(f"恢复动画: {anim_name} (Actor: {actor_id})")
else:
print(f"警告: 动画 {anim_name} 不存在于Actor {actor_id}")
else:
# 恢复所有暂停的动画
if actor_id in self.active_animations:
for anim_name, control in self.active_animations[actor_id].items():
if control and control.getPlayRate() == 0:
control.setPlayRate(1.0)
print(f"恢复所有动画 (Actor: {actor_id})")
except Exception as e:
print(f"恢复动画失败: {e}")
import traceback
traceback.print_exc()
def get_animation_info(self, actor_id, anim_name):
"""
获取动画信息
:param actor_id: Actor标识符
:param anim_name: 动画名称
:return: 动画信息字典
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return None
actor = self.actors[actor_id]
if anim_name not in actor.getAnimNames():
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id}")
return None
try:
control = actor.getAnimControl(anim_name)
info = {
'name': anim_name,
'frame_rate': actor.getFrameRate(anim_name),
'num_frames': actor.getNumFrames(anim_name),
'play_rate': actor.getPlayRate(anim_name),
'is_playing': control.isPlaying() if control else False,
'is_paused': control.getPlayRate() == 0 if control else False,
'current_frame': actor.getCurrentFrame(anim_name) if control else 0
}
return info
except Exception as e:
print(f"获取动画信息失败: {e}")
import traceback
traceback.print_exc()
return None
def blend_animations(self, actor_id, from_anim, to_anim, blend_time=1.0, loop=True):
"""
混合两个动画
:param actor_id: Actor标识符
:param from_anim: 起始动画名称
:param to_anim: 目标动画名称
:param blend_time: 混合时间
:param loop: 是否循环混合
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return None
actor = self.actors[actor_id]
# 检查动画是否存在
if from_anim not in actor.getAnimNames():
print(f"错误: 起始动画 {from_anim} 不存在于Actor {actor_id}")
return None
if to_anim not in actor.getAnimNames():
print(f"错误: 目标动画 {to_anim} 不存在于Actor {actor_id}")
return None
try:
# 创建动画混合
blend_name = f"{from_anim}_to_{to_anim}"
blend = actor.actorInterval(from_anim, endTime=actor.getDuration(from_anim)) \
+ actor.actorInterval(to_anim, loop=loop)
# 存储混合动画
if actor_id not in self.blend_intervals:
self.blend_intervals[actor_id] = {}
self.blend_intervals[actor_id][blend_name] = blend
# 播放混合动画
blend.start()
print(f"开始动画混合: {from_anim} -> {to_anim} (Actor: {actor_id}, 时间: {blend_time}s)")
return blend
except Exception as e:
print(f"动画混合失败: {e}")
import traceback
traceback.print_exc()
return None
def stop_blend(self, actor_id, blend_name=None):
"""
停止动画混合
:param actor_id: Actor标识符
:param blend_name: 混合动画名称如果为None则停止所有混合
"""
if actor_id not in self.blend_intervals:
return
try:
if blend_name:
if blend_name in self.blend_intervals[actor_id]:
blend = self.blend_intervals[actor_id][blend_name]
if blend:
blend.finish()
del self.blend_intervals[actor_id][blend_name]
print(f"停止动画混合: {blend_name} (Actor: {actor_id})")
else:
# 停止所有混合
for blend in self.blend_intervals[actor_id].values():
if blend:
blend.finish()
self.blend_intervals[actor_id].clear()
print(f"停止所有动画混合 (Actor: {actor_id})")
except Exception as e:
print(f"停止动画混合失败: {e}")
import traceback
traceback.print_exc()
def create_animation_sequence(self, actor_id, anim_list, loop=False):
"""
创建动画序列
:param actor_id: Actor标识符
:param anim_list: 动画名称列表
:param loop: 是否循环播放序列
:return: 序列间隔对象
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return None
actor = self.actors[actor_id]
try:
# 验证动画存在性
for anim_name in anim_list:
if anim_name not in actor.getAnimNames():
print(f"错误: 动画 {anim_name} 不存在于Actor {actor_id}")
return None
# 创建动画序列
sequence = Sequence()
for anim_name in anim_list:
interval = actor.actorInterval(anim_name)
sequence.append(interval)
# 存储序列
sequence_name = f"seq_{'_'.join(anim_list)}"
if actor_id not in self.animation_intervals:
self.animation_intervals[actor_id] = {}
self.animation_intervals[actor_id][sequence_name] = sequence
# 设置循环
if loop:
sequence.loop()
else:
sequence.start()
print(f"创建动画序列: {sequence_name} (Actor: {actor_id})")
return sequence
except Exception as e:
print(f"创建动画序列失败: {e}")
import traceback
traceback.print_exc()
return None
def stop_animation_sequence(self, actor_id, sequence_name=None):
"""
停止动画序列
:param actor_id: Actor标识符
:param sequence_name: 序列名称如果为None则停止所有序列
"""
if actor_id not in self.animation_intervals:
return
try:
if sequence_name:
if sequence_name in self.animation_intervals[actor_id]:
sequence = self.animation_intervals[actor_id][sequence_name]
if sequence:
sequence.finish()
del self.animation_intervals[actor_id][sequence_name]
print(f"停止动画序列: {sequence_name} (Actor: {actor_id})")
else:
# 停止所有序列
for sequence in self.animation_intervals[actor_id].values():
if sequence:
sequence.finish()
self.animation_intervals[actor_id].clear()
print(f"停止所有动画序列 (Actor: {actor_id})")
except Exception as e:
print(f"停止动画序列失败: {e}")
import traceback
traceback.print_exc()
def get_actor_info(self, actor_id):
"""
获取Actor详细信息
:param actor_id: Actor标识符
:return: Actor信息字典
"""
if actor_id not in self.actors:
print(f"错误: Actor {actor_id} 不存在")
return None
actor = self.actors[actor_id]
config = self.actor_configs.get(actor_id, {})
try:
info = {
'id': actor_id,
'model_path': config.get('model_path'),
'position': actor.getPos().toTuple(),
'scale': actor.getScale().toTuple(),
'available_animations': actor.getAnimNames(),
'active_animations': list(self.active_animations.get(actor_id, {}).keys()),
'has_character': actor.hasCharacter(),
'num_parts': actor.getNumParts(),
'bounds': {
'min': actor.getTightBounds()[0].toTuple() if actor.getTightBounds() else None,
'max': actor.getTightBounds()[1].toTuple() if actor.getTightBounds() else None
}
}
return info
except Exception as e:
print(f"获取Actor信息失败: {e}")
import traceback
traceback.print_exc()
return None
def set_actor_position(self, actor_id, x, y, z):
"""
设置Actor位置
:param actor_id: Actor标识符
:param x: X坐标
:param y: Y坐标
:param z: Z坐标
"""
if actor_id in self.actors:
self.actors[actor_id].setPos(x, y, z)
# 更新配置
if actor_id in self.actor_configs:
self.actor_configs[actor_id]['position'] = (x, y, z)
print(f"设置Actor {actor_id} 位置: ({x}, {y}, {z})")
def set_actor_scale(self, actor_id, scale):
"""
设置Actor缩放
:param actor_id: Actor标识符
:param scale: 缩放值 (数值或(x, y, z)元组)
"""
if actor_id in self.actors:
if isinstance(scale, (int, float)):
self.actors[actor_id].setScale(scale)
else:
self.actors[actor_id].setScale(*scale)
# 更新配置
if actor_id in self.actor_configs:
self.actor_configs[actor_id]['scale'] = scale
print(f"设置Actor {actor_id} 缩放: {scale}")
def get_advanced_manager(self, actor_id):
"""
获取指定Actor的高级动画管理器
:param actor_id: Actor标识符
:return: AdvancedAnimationManager实例
"""
return self.advanced_managers.get(actor_id)
def compress_animation(self, actor_id, anim_name, output_path=None):
"""
压缩指定动画
:param actor_id: Actor标识符
:param anim_name: 动画名称
:param output_path: 输出路径
"""
if actor_id in self.actors:
actor = self.actors[actor_id]
return self.compression_system.compress_animation(actor, anim_name, output_path)
return None
def get_cache_stats(self):
"""
获取缓存统计信息
:return: 缓存统计
"""
return self.caching_system.get_cache_stats()
def update_lod(self, camera_pos):
"""
更新LOD系统
:param camera_pos: 摄像机位置
"""
self.lod_system.update_lod_levels(camera_pos)
def get_lod_stats(self):
"""
获取LOD统计信息
:return: LOD统计
"""
return self.lod_system.get_lod_stats()
def enable_distributed_mode(self, mode='server'):
"""
启用分布式模式
:param mode: 模式 ('server''client')
"""
self.distributed_manager.enable_distributed_mode(mode)
def create_distributed_actor(self, actor_id, model_path, anim_dict=None):
"""
创建分布式Actor
:param actor_id: Actor ID
:param model_path: 模型路径
:param anim_dict: 动画字典
:return: Actor对象
"""
return self.distributed_manager.create_distributed_actor(actor_id, model_path, anim_dict)
def serialize_actor_state(self, actor_id, file_path=None):
"""
序列化Actor状态
:param actor_id: Actor ID
:param file_path: 文件路径
"""
if actor_id in self.actors:
actor = self.actors[actor_id]
if file_path:
self.serializer.save_state_to_file(actor, file_path)
else:
return self.serializer.serialize_actor_state(actor)
return None
class AnimationEventManager:
"""
动画事件管理器
处理动画事件和回调
"""
def __init__(self, world, animation_manager):
self.world = world
self.animation_manager = animation_manager
self.event_callbacks = {} # 存储事件回调 {event_name: [callback_list]}
def register_event_callback(self, event_name, callback):
"""
注册事件回调
:param event_name: 事件名称
:param callback: 回调函数
"""
if event_name not in self.event_callbacks:
self.event_callbacks[event_name] = []
self.event_callbacks[event_name].append(callback)
print(f"注册事件回调: {event_name}")
def unregister_event_callback(self, event_name, callback):
"""
注销事件回调
:param event_name: 事件名称
:param callback: 回调函数
"""
if event_name in self.event_callbacks:
if callback in self.event_callbacks[event_name]:
self.event_callbacks[event_name].remove(callback)
print(f"注销事件回调: {event_name}")
def trigger_event(self, event_name, *args, **kwargs):
"""
触发事件
:param event_name: 事件名称
:param args: 位置参数
:param kwargs: 关键字参数
"""
if event_name in self.event_callbacks:
for callback in self.event_callbacks[event_name]:
try:
callback(*args, **kwargs)
except Exception as e:
print(f"执行事件回调失败 {event_name}: {e}")
import traceback
traceback.print_exc()
def setup_default_events(self):
"""
设置默认事件处理器
"""
# 注册基本动画控制事件
self.world.accept("skeletal.load_actor", self._handle_load_actor)
self.world.accept("skeletal.play_animation", self._handle_play_animation)
self.world.accept("skeletal.stop_animation", self._handle_stop_animation)
self.world.accept("skeletal.set_animation_speed", self._handle_set_animation_speed)
self.world.accept("skeletal.pause_animation", self._handle_pause_animation)
self.world.accept("skeletal.resume_animation", self._handle_resume_animation)
print("已设置默认动画事件处理器")
def _handle_load_actor(self, model_path, anim_dict=None, actor_id=None, position=None, scale=None):
"""处理加载Actor事件"""
self.animation_manager.load_actor(model_path, anim_dict, actor_id, position, scale)
def _handle_play_animation(self, actor_id, anim_name, loop=False, play_rate=1.0):
"""处理播放动画事件"""
self.animation_manager.play_animation(actor_id, anim_name, loop, play_rate)
def _handle_stop_animation(self, actor_id, anim_name=None):
"""处理停止动画事件"""
self.animation_manager.stop_animation(actor_id, anim_name)
def _handle_set_animation_speed(self, actor_id, anim_name, speed):
"""处理设置动画速度事件"""
self.animation_manager.set_animation_speed(actor_id, anim_name, speed)
def _handle_pause_animation(self, actor_id, anim_name=None):
"""处理暂停动画事件"""
self.animation_manager.pause_animation(actor_id, anim_name)
def _handle_resume_animation(self, actor_id, anim_name=None):
"""处理恢复动画事件"""
self.animation_manager.resume_animation(actor_id, anim_name)
class SkeletalAnimationGUI:
"""
骨骼动画GUI界面
提供可视化控制界面
"""
def __init__(self, world, animation_manager, event_manager):
self.world = world
self.animation_manager = animation_manager
self.event_manager = event_manager
self.panel = None
self.actor_list_elements = {} # 存储Actor列表元素
self.animation_list_elements = {} # 存储动画列表元素
def create_control_panel(self):
"""
创建控制面板
"""
if not hasattr(self.world, 'gui_manager') or not self.world.gui_manager:
print("警告: GUI管理器不可用无法创建控制面板")
return
try:
# 创建主面板
self.panel = self.world.gui_manager.createGUIPanel(
pos=(0.7, 0, 0.5),
size=(0.25, 0.4),
title="骨骼动画控制面板"
)
# 创建Actor控制区域
actor_label = self.world.gui_manager.createGUIText(
parent=self.panel,
text="Actor列表:",
pos=(0.02, 0, 0.35),
scale=0.04
)
# 创建Actor列表框
self.actor_listbox = self.world.gui_manager.createGUIScrollableFrame(
parent=self.panel,
pos=(0.02, 0, 0.15),
size=(0.22, 0.18)
)
# 创建动画控制区域
anim_label = self.world.gui_manager.createGUIText(
parent=self.panel,
text="动画控制:",
pos=(0.02, 0, 0.1),
scale=0.04
)
# 创建控制按钮
button_y = 0.02
button_spacing = 0.06
self.play_button = self.world.gui_manager.createGUIButton(
parent=self.panel,
pos=(0.03, 0, button_y),
size=0.05,
text="播放"
)
self.world.accept("skeletal_gui_play", self._on_play_clicked)
self.stop_button = self.world.gui_manager.createGUIButton(
parent=self.panel,
pos=(0.09, 0, button_y),
size=0.05,
text="停止"
)
self.world.accept("skeletal_gui_stop", self._on_stop_clicked)
self.pause_button = self.world.gui_manager.createGUIButton(
parent=self.panel,
pos=(0.15, 0, button_y),
size=0.05,
text="暂停"
)
self.world.accept("skeletal_gui_pause", self._on_pause_clicked)
# 创建速度控制
speed_label = self.world.gui_manager.createGUIText(
parent=self.panel,
text="速度:",
pos=(0.02, 0, button_y - button_spacing),
scale=0.03
)
self.speed_slider = self.world.gui_manager.createGUISlider(
parent=self.panel,
pos=(0.08, 0, button_y - button_spacing),
size=0.15,
min_value=0.0,
max_value=3.0,
initial_value=1.0
)
print("骨骼动画控制面板创建成功")
except Exception as e:
print(f"创建控制面板失败: {e}")
import traceback
traceback.print_exc()
def update_actor_list(self):
"""
更新Actor列表
"""
if not hasattr(self, 'actor_listbox') or not self.actor_listbox:
return
try:
# 清除现有元素
for element in self.actor_list_elements.values():
self.world.gui_manager.deleteGUIElement(element)
self.actor_list_elements.clear()
# 添加Actor列表项
actors = self.animation_manager.actors
y_offset = 0.15
y_spacing = 0.03
for i, (actor_id, actor) in enumerate(actors.items()):
# 创建Actor列表项
actor_button = self.world.gui_manager.createGUIButton(
parent=self.actor_listbox,
pos=(0.01, 0, y_offset - i * y_spacing),
size=0.04,
text=actor_id
)
# 注册点击事件
self.world.accept(f"skeletal_select_actor_{actor_id}",
lambda aid=actor_id: self._on_actor_selected(aid))
self.actor_list_elements[actor_id] = actor_button
except Exception as e:
print(f"更新Actor列表失败: {e}")
import traceback
traceback.print_exc()
def _on_actor_selected(self, actor_id):
"""
当Actor被选中时的处理函数
:param actor_id: 选中的Actor ID
"""
print(f"选中Actor: {actor_id}")
# 更新动画列表
self.update_animation_list(actor_id)
def update_animation_list(self, actor_id):
"""
更新动画列表
:param actor_id: Actor ID
"""
if not hasattr(self, 'actor_listbox') or not self.actor_listbox:
return
try:
# 清除现有元素
for element in self.animation_list_elements.values():
self.world.gui_manager.deleteGUIElement(element)
self.animation_list_elements.clear()
# 获取Actor动画列表
animations = self.animation_manager.get_actor_animations(actor_id)
y_offset = -0.05
y_spacing = 0.03
for i, anim_name in enumerate(animations):
# 创建动画列表项
anim_button = self.world.gui_manager.createGUIButton(
parent=self.actor_listbox,
pos=(0.01, 0, y_offset - i * y_spacing),
size=0.04,
text=anim_name
)
# 注册点击事件
self.world.accept(f"skeletal_select_anim_{actor_id}_{anim_name}",
lambda aid=actor_id, an=anim_name: self._on_animation_selected(aid, an))
self.animation_list_elements[f"{actor_id}_{anim_name}"] = anim_button
except Exception as e:
print(f"更新动画列表失败: {e}")
import traceback
traceback.print_exc()
def _on_animation_selected(self, actor_id, anim_name):
"""
当动画被选中时的处理函数
:param actor_id: Actor ID
:param anim_name: 动画名称
"""
print(f"选中动画: {anim_name} (Actor: {actor_id})")
def _on_play_clicked(self):
"""
当播放按钮被点击时的处理函数
"""
print("播放按钮被点击")
# 这里应该实现实际的播放逻辑
def _on_stop_clicked(self):
"""
当停止按钮被点击时的处理函数
"""
print("停止按钮被点击")
# 这里应该实现实际的停止逻辑
def _on_pause_clicked(self):
"""
当暂停按钮被点击时的处理函数
"""
print("暂停按钮被点击")
# 这里应该实现实际的暂停逻辑
def destroy(self):
"""
销毁GUI界面
"""
try:
if self.panel:
self.world.gui_manager.deleteGUIElement(self.panel)
self.panel = None
print("骨骼动画GUI界面已销毁")
except Exception as e:
print(f"销毁GUI界面失败: {e}")
class Plugin(BasePlugin):
"""
骨骼动画系统插件
提供完整的骨骼动画支持,包括加载、播放、控制、混合等高级功能
"""
def __init__(self, plugin_manager, name):
super().__init__(plugin_manager, name)
self.config = {
"version": "1.0.0",
"author": "EG Team",
"description": "骨骼动画系统,支持模型动画加载、播放控制、混合等高级功能"
}
self.animation_manager = None
self.event_manager = None
self.gui = None
self.is_enabled = False
self.update_task = None
def initialize(self) -> bool:
"""
初始化插件
"""
print(f"初始化骨骼动画插件: {self.name}")
try:
# 创建动画管理器
self.animation_manager = SkeletalAnimationManager(self.plugin_manager.world)
# 创建事件管理器
self.event_manager = AnimationEventManager(self.plugin_manager.world, self.animation_manager)
# 创建GUI
self.gui = SkeletalAnimationGUI(self.plugin_manager.world,
self.animation_manager,
self.event_manager)
print(f"骨骼动画插件初始化成功: {self.name}")
return True
except Exception as e:
print(f"骨骼动画插件初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用插件
"""
if not super().enable():
return False
if self.is_enabled:
print(f"骨骼动画插件已启用: {self.name}")
return True
print(f"启用骨骼动画插件: {self.name}")
try:
# 设置默认事件处理器
self.event_manager.setup_default_events()
# 创建GUI控制面板
self.gui.create_control_panel()
# 启动更新任务
self.update_task = self.plugin_manager.world.taskMgr.add(
self._update_task,
f"skeletal_animation_update_{self.name}",
sort=30
)
# 标记为已启用
self.is_enabled = True
print(f"骨骼动画插件启用成功: {self.name}")
return True
except Exception as e:
print(f"骨骼动画插件启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self) -> bool:
"""
禁用插件
"""
if not super().disable():
return False
if not self.is_enabled:
print(f"骨骼动画插件已禁用: {self.name}")
return True
print(f"禁用骨骼动画插件: {self.name}")
try:
# 停止更新任务
if self.update_task:
self.plugin_manager.world.taskMgr.remove(self.update_task)
self.update_task = None
# 移除事件处理器
self.plugin_manager.world.ignore("skeletal.load_actor")
self.plugin_manager.world.ignore("skeletal.play_animation")
self.plugin_manager.world.ignore("skeletal.stop_animation")
self.plugin_manager.world.ignore("skeletal.set_animation_speed")
self.plugin_manager.world.ignore("skeletal.pause_animation")
self.plugin_manager.world.ignore("skeletal.resume_animation")
# 销毁GUI
self.gui.destroy()
# 标记为已禁用
self.is_enabled = False
print(f"骨骼动画插件禁用成功: {self.name}")
return True
except Exception as e:
print(f"骨骼动画插件禁用失败: {e}")
import traceback
traceback.print_exc()
return False
def finalize(self):
"""
清理插件资源
"""
print(f"清理骨骼动画插件资源: {self.name}")
try:
# 停止更新任务
if self.update_task:
self.plugin_manager.world.taskMgr.remove(self.update_task)
self.update_task = None
# 停止所有动画
for actor_id in list(self.animation_manager.actors.keys()):
self.animation_manager.unload_actor(actor_id)
# 清理事件处理器
self.event_manager = None
# 清理GUI
self.gui = None
# 清理动画管理器
self.animation_manager = None
print(f"骨骼动画插件资源清理完成: {self.name}")
except Exception as e:
print(f"清理骨骼动画插件资源失败: {e}")
import traceback
traceback.print_exc()
def _update_task(self, task):
"""
插件更新任务
"""
try:
# 获取摄像机位置用于LOD计算
if hasattr(self.plugin_manager.world, 'camera'):
camera_pos = self.plugin_manager.world.camera.getPos()
self.animation_manager.update_lod(camera_pos)
# 更新高级动画系统
dt = globalClock.getDt()
time = globalClock.getRealTime()
for actor_id, advanced_manager in self.animation_manager.advanced_managers.items():
advanced_manager.update(dt, time)
except Exception as e:
print(f"插件更新任务出错: {e}")
return task.cont
def get_animation_manager(self):
"""
获取动画管理器
:return: SkeletalAnimationManager实例
"""
return self.animation_manager
def get_event_manager(self):
"""
获取事件管理器
:return: AnimationEventManager实例
"""
return self.event_manager
def get_gui(self):
"""
获取GUI界面
:return: SkeletalAnimationGUI实例
"""
return self.gui