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

684 lines
23 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 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("动画网络同步模块加载完成")