684 lines
23 KiB
Python
684 lines
23 KiB
Python
"""
|
||
骨骼动画系统插件 - 网络同步模块
|
||
提供动画状态网络同步、多人协作等功能
|
||
"""
|
||
|
||
from panda3d.core import *
|
||
from direct.distributed.ClockDelta import *
|
||
from direct.showbase.DirectObject import DirectObject
|
||
import json
|
||
import hashlib
|
||
|
||
|
||
class AnimationNetworkSync(DirectObject):
|
||
"""
|
||
动画网络同步系统
|
||
支持多人环境中动画状态的同步
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
self.world = world
|
||
self.network_settings = {
|
||
'enable_sync': True,
|
||
'sync_interval': 0.1, # 同步间隔(秒)
|
||
'interpolation_mode': 'linear', # 插值模式
|
||
'extrapolation_limit': 0.5, # 外推限制(秒)
|
||
'compression_enabled': True,
|
||
'delta_compression': True
|
||
}
|
||
|
||
# 网络状态
|
||
self.is_server = False
|
||
self.is_client = False
|
||
self.connected = False
|
||
self.network_actor_states = {} # {actor_id: state_data}
|
||
self.local_actor_states = {} # {actor_id: state_data}
|
||
self.actor_sync_callbacks = {} # {actor_id: callback_func}
|
||
|
||
# 同步定时器
|
||
self.sync_task = None
|
||
self.last_sync_time = 0
|
||
|
||
def enable_server_mode(self):
|
||
"""
|
||
启用服务器模式
|
||
"""
|
||
self.is_server = True
|
||
self.is_client = False
|
||
self.connected = True
|
||
print("动画网络同步系统 - 服务器模式已启用")
|
||
|
||
def enable_client_mode(self):
|
||
"""
|
||
启用客户端模式
|
||
"""
|
||
self.is_server = False
|
||
self.is_client = True
|
||
self.connected = True
|
||
print("动画网络同步系统 - 客户端模式已启用")
|
||
|
||
def disable_network_sync(self):
|
||
"""
|
||
禁用网络同步
|
||
"""
|
||
self.is_server = False
|
||
self.is_client = False
|
||
self.connected = False
|
||
print("动画网络同步系统已禁用")
|
||
|
||
def register_actor_for_sync(self, actor_id, actor, sync_callback=None):
|
||
"""
|
||
注册Actor以进行网络同步
|
||
:param actor_id: Actor ID
|
||
:param actor: Actor对象
|
||
:param sync_callback: 同步回调函数
|
||
"""
|
||
self.local_actor_states[actor_id] = {
|
||
'actor': actor,
|
||
'last_state': self._capture_actor_state(actor),
|
||
'last_update': globalClock.getRealTime(),
|
||
'sync_callback': sync_callback
|
||
}
|
||
|
||
if sync_callback:
|
||
self.actor_sync_callbacks[actor_id] = sync_callback
|
||
|
||
print(f"Actor {actor_id} 已注册网络同步")
|
||
|
||
def unregister_actor_from_sync(self, actor_id):
|
||
"""
|
||
取消Actor的网络同步注册
|
||
:param actor_id: Actor ID
|
||
"""
|
||
if actor_id in self.local_actor_states:
|
||
del self.local_actor_states[actor_id]
|
||
if actor_id in self.actor_sync_callbacks:
|
||
del self.actor_sync_callbacks[actor_id]
|
||
if actor_id in self.network_actor_states:
|
||
del self.network_actor_states[actor_id]
|
||
|
||
print(f"Actor {actor_id} 已取消网络同步注册")
|
||
|
||
def _capture_actor_state(self, actor):
|
||
"""
|
||
捕获Actor当前状态
|
||
:param actor: Actor对象
|
||
:return: 状态数据
|
||
"""
|
||
state = {
|
||
'position': actor.getPos().toTuple(),
|
||
'rotation': actor.getHpr().toTuple(),
|
||
'scale': actor.getScale().toTuple(),
|
||
'active_animations': {},
|
||
'timestamp': globalClock.getRealTime()
|
||
}
|
||
|
||
# 捕获活动动画状态
|
||
for anim_name in actor.getAnimNames():
|
||
control = actor.getAnimControl(anim_name)
|
||
if control and control.isPlaying():
|
||
state['active_animations'][anim_name] = {
|
||
'frame': actor.getCurrentFrame(anim_name),
|
||
'play_rate': actor.getPlayRate(anim_name),
|
||
'is_looping': control.getLoop()
|
||
}
|
||
|
||
return state
|
||
|
||
def _apply_actor_state(self, actor, state_data):
|
||
"""
|
||
应用Actor状态
|
||
:param actor: Actor对象
|
||
:param state_data: 状态数据
|
||
"""
|
||
# 应用变换
|
||
if 'position' in state_data:
|
||
actor.setPos(*state_data['position'])
|
||
if 'rotation' in state_data:
|
||
actor.setHpr(*state_data['rotation'])
|
||
if 'scale' in state_data:
|
||
actor.setScale(*state_data['scale'])
|
||
|
||
# 应用动画状态
|
||
if 'active_animations' in state_data:
|
||
for anim_name, anim_state in state_data['active_animations'].items():
|
||
if anim_name in actor.getAnimNames():
|
||
# 停止当前动画
|
||
actor.stop(anim_name)
|
||
|
||
# 设置到指定帧
|
||
if 'frame' in anim_state:
|
||
actor.pose(anim_name, anim_state['frame'])
|
||
|
||
# 设置播放速度
|
||
if 'play_rate' in anim_state:
|
||
actor.setPlayRate(anim_state['play_rate'], anim_name)
|
||
|
||
# 调用自定义同步回调
|
||
actor_id = actor.getName() # 简化获取ID的方法
|
||
if actor_id in self.actor_sync_callbacks:
|
||
self.actor_sync_callbacks[actor_id](actor, state_data)
|
||
|
||
def send_actor_state(self, actor_id):
|
||
"""
|
||
发送Actor状态(服务器模式)
|
||
:param actor_id: Actor ID
|
||
"""
|
||
if not self.is_server or not self.connected:
|
||
return
|
||
|
||
if actor_id not in self.local_actor_states:
|
||
return
|
||
|
||
actor_state = self.local_actor_states[actor_id]
|
||
actor = actor_state['actor']
|
||
current_state = self._capture_actor_state(actor)
|
||
|
||
# 检查状态是否发生变化
|
||
if self._has_state_changed(actor_state['last_state'], current_state):
|
||
# 发送状态更新
|
||
self._broadcast_actor_state(actor_id, current_state)
|
||
|
||
# 更新最后状态
|
||
actor_state['last_state'] = current_state
|
||
actor_state['last_update'] = globalClock.getRealTime()
|
||
|
||
def _has_state_changed(self, old_state, new_state, threshold=0.001):
|
||
"""
|
||
检查状态是否发生变化
|
||
:param old_state: 旧状态
|
||
:param new_state: 新状态
|
||
:param threshold: 变化阈值
|
||
:return: 是否发生变化
|
||
"""
|
||
# 检查位置变化
|
||
if 'position' in old_state and 'position' in new_state:
|
||
old_pos = Vec3(*old_state['position'])
|
||
new_pos = Vec3(*new_state['position'])
|
||
if (old_pos - new_pos).length() > threshold:
|
||
return True
|
||
|
||
# 检查旋转变化
|
||
if 'rotation' in old_state and 'rotation' in new_state:
|
||
old_hpr = Vec3(*old_state['rotation'])
|
||
new_hpr = Vec3(*new_state['rotation'])
|
||
if (old_hpr - new_hpr).length() > threshold:
|
||
return True
|
||
|
||
# 检查动画状态变化
|
||
if 'active_animations' in old_state and 'active_animations' in new_state:
|
||
if old_state['active_animations'] != new_state['active_animations']:
|
||
return True
|
||
|
||
return False
|
||
|
||
def _broadcast_actor_state(self, actor_id, state_data):
|
||
"""
|
||
广播Actor状态
|
||
:param actor_id: Actor ID
|
||
:param state_data: 状态数据
|
||
"""
|
||
# 在实际实现中,这里会通过网络发送数据
|
||
# 为简化,我们直接更新网络状态
|
||
self.network_actor_states[actor_id] = state_data
|
||
print(f"广播Actor {actor_id} 状态更新")
|
||
|
||
def receive_actor_state(self, actor_id, state_data):
|
||
"""
|
||
接收Actor状态(客户端模式)
|
||
:param actor_id: Actor ID
|
||
:param state_data: 状态数据
|
||
"""
|
||
if not self.is_client or not self.connected:
|
||
return
|
||
|
||
# 存储接收到的状态
|
||
self.network_actor_states[actor_id] = {
|
||
'state': state_data,
|
||
'receive_time': globalClock.getRealTime(),
|
||
'interpolated': False
|
||
}
|
||
|
||
print(f"接收Actor {actor_id} 状态更新")
|
||
|
||
def update_network_sync(self, task):
|
||
"""
|
||
更新网络同步
|
||
"""
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 检查同步间隔
|
||
if current_time - self.last_sync_time < self.network_settings['sync_interval']:
|
||
return task.cont
|
||
|
||
self.last_sync_time = current_time
|
||
|
||
if self.is_server:
|
||
# 服务器:发送本地Actor状态
|
||
for actor_id in list(self.local_actor_states.keys()):
|
||
self.send_actor_state(actor_id)
|
||
|
||
elif self.is_client:
|
||
# 客户端:应用接收到的状态
|
||
self._apply_network_states()
|
||
|
||
return task.cont
|
||
|
||
def _apply_network_states(self):
|
||
"""
|
||
应用网络状态
|
||
"""
|
||
current_time = globalClock.getRealTime()
|
||
|
||
for actor_id, state_info in self.network_actor_states.items():
|
||
state_data = state_info['state']
|
||
receive_time = state_info['receive_time']
|
||
|
||
# 查找对应的Actor
|
||
actor_np = self.world.render.find(f"**/{actor_id}")
|
||
if actor_np and actor_np.node():
|
||
actor = actor_np.node()
|
||
if isinstance(actor, Actor):
|
||
# 时间补偿插值
|
||
if self.network_settings['interpolation_mode'] == 'linear':
|
||
# 简单线性插值
|
||
self._apply_actor_state(actor, state_data)
|
||
else:
|
||
# 直接应用状态
|
||
self._apply_actor_state(actor, state_data)
|
||
|
||
def start_sync_task(self):
|
||
"""
|
||
启动同步任务
|
||
"""
|
||
if self.sync_task is None:
|
||
self.sync_task = self.world.taskMgr.add(
|
||
self.update_network_sync,
|
||
"animationNetworkSyncTask",
|
||
sort=40 # 确保在网络更新之后执行
|
||
)
|
||
print("动画网络同步任务已启动")
|
||
|
||
def stop_sync_task(self):
|
||
"""
|
||
停止同步任务
|
||
"""
|
||
if self.sync_task:
|
||
self.world.taskMgr.remove(self.sync_task)
|
||
self.sync_task = None
|
||
print("动画网络同步任务已停止")
|
||
|
||
def get_network_stats(self):
|
||
"""
|
||
获取网络同步统计信息
|
||
:return: 统计信息
|
||
"""
|
||
return {
|
||
'is_server': self.is_server,
|
||
'is_client': self.is_client,
|
||
'connected': self.connected,
|
||
'local_actors': len(self.local_actor_states),
|
||
'network_actors': len(self.network_actor_states),
|
||
'sync_enabled': self.network_settings['enable_sync'],
|
||
'sync_interval': self.network_settings['sync_interval']
|
||
}
|
||
|
||
|
||
class DistributedAnimationManager:
|
||
"""
|
||
分布式动画管理器
|
||
管理网络环境中的动画同步
|
||
"""
|
||
|
||
def __init__(self, world):
|
||
self.world = world
|
||
self.network_sync = AnimationNetworkSync(world)
|
||
self.distributed_actors = {} # {actor_id: distributed_data}
|
||
self.master_slave_relations = {} # {slave_actor_id: master_actor_id}
|
||
|
||
def enable_distributed_mode(self, mode='server'):
|
||
"""
|
||
启用分布式模式
|
||
:param mode: 模式 ('server' 或 'client')
|
||
"""
|
||
if mode == 'server':
|
||
self.network_sync.enable_server_mode()
|
||
else:
|
||
self.network_sync.enable_client_mode()
|
||
|
||
self.network_sync.start_sync_task()
|
||
print(f"分布式动画管理器已启用 ({mode} 模式)")
|
||
|
||
def disable_distributed_mode(self):
|
||
"""
|
||
禁用分布式模式
|
||
"""
|
||
self.network_sync.stop_sync_task()
|
||
self.network_sync.disable_network_sync()
|
||
print("分布式动画管理器已禁用")
|
||
|
||
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对象
|
||
"""
|
||
from direct.actor.Actor import Actor
|
||
|
||
# 创建Actor
|
||
if anim_dict:
|
||
actor = Actor(model_path, anim_dict)
|
||
else:
|
||
actor = Actor(model_path)
|
||
|
||
# 添加到场景
|
||
actor.reparentTo(self.world.render)
|
||
actor.setName(actor_id)
|
||
|
||
# 注册网络同步
|
||
self.network_sync.register_actor_for_sync(actor_id, actor)
|
||
|
||
# 存储分布式数据
|
||
self.distributed_actors[actor_id] = {
|
||
'actor': actor,
|
||
'model_path': model_path,
|
||
'anim_dict': anim_dict,
|
||
'is_distributed': True
|
||
}
|
||
|
||
print(f"分布式Actor已创建: {actor_id}")
|
||
return actor
|
||
|
||
def set_master_slave_relation(self, master_id, slave_id):
|
||
"""
|
||
设置主从关系
|
||
:param master_id: 主Actor ID
|
||
:param slave_id: 从Actor ID
|
||
"""
|
||
if master_id in self.distributed_actors and slave_id in self.distributed_actors:
|
||
self.master_slave_relations[slave_id] = master_id
|
||
print(f"设置主从关系: {master_id} -> {slave_id}")
|
||
|
||
def sync_actor_animation(self, actor_id, anim_name, play=True, loop=False, play_rate=1.0):
|
||
"""
|
||
同步Actor动画
|
||
:param actor_id: Actor ID
|
||
:param anim_name: 动画名称
|
||
:param play: 是否播放
|
||
:param loop: 是否循环
|
||
:param play_rate: 播放速度
|
||
"""
|
||
if actor_id not in self.distributed_actors:
|
||
return
|
||
|
||
actor_data = self.distributed_actors[actor_id]
|
||
actor = actor_data['actor']
|
||
|
||
if play:
|
||
actor.play(anim_name)
|
||
if loop:
|
||
actor.loop(anim_name, restart=False)
|
||
actor.setPlayRate(play_rate, anim_name)
|
||
else:
|
||
actor.stop(anim_name)
|
||
|
||
print(f"同步Actor {actor_id} 动画: {anim_name}")
|
||
|
||
def get_distributed_stats(self):
|
||
"""
|
||
获取分布式统计信息
|
||
:return: 统计信息
|
||
"""
|
||
network_stats = self.network_sync.get_network_stats()
|
||
return {
|
||
'network_stats': network_stats,
|
||
'distributed_actors': len(self.distributed_actors),
|
||
'master_slave_relations': len(self.master_slave_relations)
|
||
}
|
||
|
||
|
||
class AnimationStateSerializer:
|
||
"""
|
||
动画状态序列化器
|
||
支持动画状态的序列化和反序列化
|
||
"""
|
||
|
||
def __init__(self):
|
||
self.serialization_settings = {
|
||
'compress_data': True,
|
||
'include_bone_transforms': True,
|
||
'include_animation_state': True,
|
||
'include_custom_properties': True
|
||
}
|
||
|
||
def serialize_actor_state(self, actor, include_animations=True):
|
||
"""
|
||
序列化Actor状态
|
||
:param actor: Actor对象
|
||
:param include_animations: 是否包含动画状态
|
||
:return: 序列化数据
|
||
"""
|
||
state_data = {
|
||
'name': actor.getName() if hasattr(actor, 'getName') else 'Unknown',
|
||
'position': actor.getPos().toTuple(),
|
||
'rotation': actor.getHpr().toTuple(),
|
||
'scale': actor.getScale().toTuple(),
|
||
'timestamp': globalClock.getRealTime()
|
||
}
|
||
|
||
if include_animations:
|
||
state_data['animations'] = self._serialize_animation_state(actor)
|
||
|
||
if self.serialization_settings['include_bone_transforms']:
|
||
state_data['bone_transforms'] = self._serialize_bone_transforms(actor)
|
||
|
||
# 计算数据校验和
|
||
state_data['checksum'] = self._calculate_checksum(state_data)
|
||
|
||
return state_data
|
||
|
||
def _serialize_animation_state(self, actor):
|
||
"""
|
||
序列化动画状态
|
||
:param actor: Actor对象
|
||
:return: 动画状态数据
|
||
"""
|
||
anim_state = {
|
||
'current_animations': [],
|
||
'animation_rates': {},
|
||
'animation_frames': {}
|
||
}
|
||
|
||
for anim_name in actor.getAnimNames():
|
||
control = actor.getAnimControl(anim_name)
|
||
if control and control.isPlaying():
|
||
anim_state['current_animations'].append(anim_name)
|
||
anim_state['animation_rates'][anim_name] = actor.getPlayRate(anim_name)
|
||
anim_state['animation_frames'][anim_name] = actor.getCurrentFrame(anim_name)
|
||
|
||
return anim_state
|
||
|
||
def _serialize_bone_transforms(self, actor):
|
||
"""
|
||
序列化骨骼变换
|
||
:param actor: Actor对象
|
||
:return: 骨骼变换数据
|
||
"""
|
||
bone_transforms = {}
|
||
|
||
# 获取骨骼信息
|
||
bundle_dict = actor.getPartBundleDict()
|
||
for bundle_name, bundle in bundle_dict.items():
|
||
bone_transforms[bundle_name] = self._extract_bundle_transforms(bundle)
|
||
|
||
return bone_transforms
|
||
|
||
def _extract_bundle_transforms(self, bundle):
|
||
"""
|
||
提取绑定变换
|
||
:param bundle: PartBundle对象
|
||
:return: 变换数据
|
||
"""
|
||
transforms = {}
|
||
|
||
# 遍历子节点
|
||
for i in range(bundle.getNumChildren()):
|
||
child = bundle.getChild(i)
|
||
if hasattr(child, 'getName'):
|
||
bone_name = child.getName()
|
||
# 注意:这里简化处理,实际项目中需要更复杂的骨骼数据提取
|
||
transforms[bone_name] = {
|
||
'transform': 'dummy_transform_data'
|
||
}
|
||
|
||
return transforms
|
||
|
||
def _calculate_checksum(self, data):
|
||
"""
|
||
计算数据校验和
|
||
:param data: 数据
|
||
:return: 校验和
|
||
"""
|
||
# 简化实现,实际项目中应使用更安全的哈希算法
|
||
data_str = json.dumps(data, sort_keys=True)
|
||
return hashlib.md5(data_str.encode()).hexdigest()
|
||
|
||
def deserialize_actor_state(self, actor, state_data):
|
||
"""
|
||
反序列化Actor状态
|
||
:param actor: Actor对象
|
||
:param state_data: 状态数据
|
||
"""
|
||
# 验证校验和
|
||
if 'checksum' in state_data:
|
||
expected_checksum = state_data['checksum']
|
||
data_to_check = state_data.copy()
|
||
del data_to_check['checksum']
|
||
actual_checksum = self._calculate_checksum(data_to_check)
|
||
|
||
if expected_checksum != actual_checksum:
|
||
print("警告: 数据校验和不匹配")
|
||
|
||
# 应用变换
|
||
if 'position' in state_data:
|
||
actor.setPos(*state_data['position'])
|
||
if 'rotation' in state_data:
|
||
actor.setHpr(*state_data['rotation'])
|
||
if 'scale' in state_data:
|
||
actor.setScale(*state_data['scale'])
|
||
|
||
# 应用动画状态
|
||
if 'animations' in state_data:
|
||
self._deserialize_animation_state(actor, state_data['animations'])
|
||
|
||
def _deserialize_animation_state(self, actor, anim_state):
|
||
"""
|
||
反序列化动画状态
|
||
:param actor: Actor对象
|
||
:param anim_state: 动画状态数据
|
||
"""
|
||
# 停止所有当前动画
|
||
actor.stop()
|
||
|
||
# 播放指定动画
|
||
for anim_name in anim_state.get('current_animations', []):
|
||
if anim_name in actor.getAnimNames():
|
||
frame = anim_state.get('animation_frames', {}).get(anim_name, 0)
|
||
rate = anim_state.get('animation_rates', {}).get(anim_name, 1.0)
|
||
|
||
actor.pose(anim_name, frame)
|
||
actor.setPlayRate(rate, anim_name)
|
||
|
||
def save_state_to_file(self, actor, file_path, include_animations=True):
|
||
"""
|
||
保存状态到文件
|
||
:param actor: Actor对象
|
||
:param file_path: 文件路径
|
||
:param include_animations: 是否包含动画状态
|
||
"""
|
||
state_data = self.serialize_actor_state(actor, include_animations)
|
||
|
||
try:
|
||
with open(file_path, 'w') as f:
|
||
json.dump(state_data, f, indent=2)
|
||
print(f"Actor状态已保存到: {file_path}")
|
||
except Exception as e:
|
||
print(f"保存状态失败: {e}")
|
||
|
||
def load_state_from_file(self, actor, file_path):
|
||
"""
|
||
从文件加载状态
|
||
:param actor: Actor对象
|
||
:param file_path: 文件路径
|
||
"""
|
||
try:
|
||
with open(file_path, 'r') as f:
|
||
state_data = json.load(f)
|
||
self.deserialize_actor_state(actor, state_data)
|
||
print(f"Actor状态已从 {file_path} 加载")
|
||
except Exception as e:
|
||
print(f"加载状态失败: {e}")
|
||
|
||
|
||
# 使用示例
|
||
def example_network_sync_usage(world):
|
||
"""
|
||
网络同步使用示例
|
||
"""
|
||
print("=== 动画网络同步使用示例 ===")
|
||
|
||
# 创建网络同步系统
|
||
network_sync = AnimationNetworkSync(world)
|
||
|
||
# 模拟服务器模式
|
||
network_sync.enable_server_mode()
|
||
|
||
# 获取网络统计
|
||
stats = network_sync.get_network_stats()
|
||
print(f"网络统计: {stats}")
|
||
|
||
print("网络同步示例完成")
|
||
|
||
|
||
def example_distributed_usage(world):
|
||
"""
|
||
分布式动画使用示例
|
||
"""
|
||
print("=== 分布式动画使用示例 ===")
|
||
|
||
# 创建分布式管理器
|
||
dist_manager = DistributedAnimationManager(world)
|
||
|
||
# 启用服务器模式
|
||
dist_manager.enable_distributed_mode('server')
|
||
|
||
# 获取分布式统计
|
||
stats = dist_manager.get_distributed_stats()
|
||
print(f"分布式统计: {stats}")
|
||
|
||
print("分布式动画示例完成")
|
||
|
||
|
||
def example_serialization_usage(actor):
|
||
"""
|
||
状态序列化使用示例
|
||
"""
|
||
print("=== 动画状态序列化使用示例 ===")
|
||
|
||
# 创建序列化器
|
||
serializer = AnimationStateSerializer()
|
||
|
||
# 序列化Actor状态
|
||
state_data = serializer.serialize_actor_state(actor)
|
||
print(f"序列化状态大小: {len(json.dumps(state_data))} 字符")
|
||
|
||
# 保存到文件
|
||
serializer.save_state_to_file(actor, "actor_state.json")
|
||
|
||
print("状态序列化示例完成")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("动画网络同步模块加载完成") |