优化播放动画场景卡顿问题
This commit is contained in:
parent
9bbcc89daa
commit
2822dfee71
@ -118,6 +118,102 @@ class AnimationTools:
|
||||
else:
|
||||
setattr(self.app, name, value)
|
||||
|
||||
def _ensure_animation_runtime_state(self):
|
||||
if not hasattr(self, "_animation_sync_tasks") or self._animation_sync_tasks is None:
|
||||
self._animation_sync_tasks = {}
|
||||
if not hasattr(self, "_animation_debug_enabled"):
|
||||
self._animation_debug_enabled = False
|
||||
|
||||
def _anim_log(self, message):
|
||||
self._ensure_animation_runtime_state()
|
||||
if self._animation_debug_enabled:
|
||||
print(message)
|
||||
|
||||
def _acquire_playback_actor(self, origin_model, owner_model, force_rebuild=False):
|
||||
"""
|
||||
获取用于播放的 Actor。
|
||||
默认优先复用缓存,只有缓存失效或明确要求时才重建,避免点击播放时阻塞。
|
||||
"""
|
||||
self._ensure_animation_runtime_state()
|
||||
|
||||
if force_rebuild:
|
||||
self._cleanup_conflicting_animation_actors(owner_model)
|
||||
self._discard_cached_actor(owner_model, restore_owner=True)
|
||||
|
||||
actor = self._getActor(owner_model)
|
||||
if not actor:
|
||||
return None
|
||||
|
||||
# 若仍在“无路径的内存代理”,仅在确实恢复出路径后重建一次真实 Actor。
|
||||
if isinstance(actor, _BoundAnimationProxy):
|
||||
try:
|
||||
owner_has_path = owner_model.hasTag("model_path") and bool(owner_model.getTag("model_path"))
|
||||
except Exception:
|
||||
owner_has_path = False
|
||||
|
||||
if not owner_has_path:
|
||||
recovered_path = self._recover_model_path_from_tags(owner_model) or self._recover_model_path_from_tags(origin_model)
|
||||
if recovered_path:
|
||||
try:
|
||||
owner_model.setTag("model_path", recovered_path)
|
||||
if (not owner_model.hasTag("original_path")) or (not owner_model.getTag("original_path")):
|
||||
owner_model.setTag("original_path", recovered_path)
|
||||
except Exception:
|
||||
pass
|
||||
self._clear_animation_cache(owner_model)
|
||||
actor = self._getActor(owner_model)
|
||||
return actor
|
||||
|
||||
def _sync_actor_transform_for_playback(self, owner_model, actor):
|
||||
is_scene_bound_proxy = isinstance(actor, _BoundAnimationProxy) and (not getattr(actor, "_owns_node", False))
|
||||
if is_scene_bound_proxy:
|
||||
return True
|
||||
|
||||
actor.setPos(self.render, owner_model.getPos(self.render))
|
||||
actor.setHpr(self.render, owner_model.getHpr(self.render))
|
||||
actor.setScale(self.render, owner_model.getScale(self.render))
|
||||
|
||||
actor.setPos(owner_model.getPos())
|
||||
actor.setHpr(owner_model.getHpr())
|
||||
actor.setScale(owner_model.getScale())
|
||||
return False
|
||||
|
||||
def _ensure_actor_sync_task(self, owner_model, actor, enabled):
|
||||
self._ensure_animation_runtime_state()
|
||||
|
||||
old_task_name = self._animation_sync_tasks.get(owner_model)
|
||||
if old_task_name and old_task_name != f"maintain_anim_pos_{id(actor)}":
|
||||
try:
|
||||
taskMgr.remove(old_task_name)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not enabled:
|
||||
self._animation_sync_tasks.pop(owner_model, None)
|
||||
return
|
||||
|
||||
task_name = f"maintain_anim_pos_{id(actor)}"
|
||||
self._animation_sync_tasks[owner_model] = task_name
|
||||
|
||||
def maintainWorldPosition(task):
|
||||
try:
|
||||
if not actor.isEmpty() and not owner_model.isEmpty():
|
||||
owner_transform = owner_model.getTransform(self.render)
|
||||
last_transform = owner_model.getPythonTag("_last_anim_sync_transform")
|
||||
if last_transform == owner_transform:
|
||||
return task.cont
|
||||
owner_model.setPythonTag("_last_anim_sync_transform", owner_transform)
|
||||
actor.setTransform(self.render, owner_transform)
|
||||
return task.cont
|
||||
self._animation_sync_tasks.pop(owner_model, None)
|
||||
return task.done
|
||||
except Exception:
|
||||
self._animation_sync_tasks.pop(owner_model, None)
|
||||
return task.done
|
||||
|
||||
taskMgr.remove(task_name)
|
||||
taskMgr.add(maintainWorldPosition, task_name)
|
||||
|
||||
def _is_scene_root_node(self, node):
|
||||
try:
|
||||
if not node or node.isEmpty():
|
||||
@ -931,7 +1027,7 @@ class AnimationTools:
|
||||
pass
|
||||
|
||||
anims = actor.getAnimNames()
|
||||
print(f"[Actor加载] {source_desc} 检测到动画: {anims}")
|
||||
self._anim_log(f"[Actor加载] {source_desc} 检测到动画: {anims}")
|
||||
if not anims:
|
||||
_cleanup_actor(actor)
|
||||
return None
|
||||
@ -948,7 +1044,7 @@ class AnimationTools:
|
||||
continue
|
||||
|
||||
if not playable:
|
||||
print(f"[Actor加载] {source_desc} 动画控制无效,尝试其他加载路径")
|
||||
self._anim_log(f"[Actor加载] {source_desc} 动画控制无效,尝试其他加载路径")
|
||||
_cleanup_actor(actor)
|
||||
return None
|
||||
|
||||
@ -959,7 +1055,7 @@ class AnimationTools:
|
||||
except Exception:
|
||||
has_geom = False
|
||||
if not has_geom:
|
||||
print(f"[Actor加载] {source_desc} 无可见几何体,尝试其他加载路径")
|
||||
self._anim_log(f"[Actor加载] {source_desc} 无可见几何体,尝试其他加载路径")
|
||||
_cleanup_actor(actor)
|
||||
return None
|
||||
|
||||
@ -967,7 +1063,7 @@ class AnimationTools:
|
||||
actor.hide()
|
||||
return actor
|
||||
except Exception as e:
|
||||
print(f"[Actor加载] {source_desc} 失败: {e}")
|
||||
self._anim_log(f"[Actor加载] {source_desc} 失败: {e}")
|
||||
return None
|
||||
|
||||
def _try_create_autobind_proxy(model_np, source_desc, owns_node=False):
|
||||
@ -1056,13 +1152,13 @@ class AnimationTools:
|
||||
except Exception:
|
||||
has_geom = False
|
||||
if not has_geom:
|
||||
print(f"[Actor加载] {source_desc} autoBind 检测到动画但无可见几何体,尝试其他节点")
|
||||
self._anim_log(f"[Actor加载] {source_desc} autoBind 检测到动画但无可见几何体,尝试其他节点")
|
||||
return None
|
||||
|
||||
print(f"[Actor加载] {source_desc} autoBind 检测到动画: {list(controls.keys())}")
|
||||
self._anim_log(f"[Actor加载] {source_desc} autoBind 检测到动画: {list(controls.keys())}")
|
||||
return _BoundAnimationProxy(model_np, controls, owns_node=owns_node)
|
||||
except Exception as e:
|
||||
print(f"[Actor加载] {source_desc} autoBind 失败: {e}")
|
||||
self._anim_log(f"[Actor加载] {source_desc} autoBind 失败: {e}")
|
||||
return None
|
||||
|
||||
def _try_create_actor_via_gltf_path(path):
|
||||
@ -1097,7 +1193,7 @@ class AnimationTools:
|
||||
succeeded = True
|
||||
return proxy
|
||||
except Exception as e:
|
||||
print(f"[Actor加载] GLTF专用加载失败 ({path}): {e}")
|
||||
self._anim_log(f"[Actor加载] GLTF专用加载失败 ({path}): {e}")
|
||||
finally:
|
||||
# 仅在失败且本地临时节点仍存在时清理,避免泄漏
|
||||
try:
|
||||
@ -1109,16 +1205,6 @@ class AnimationTools:
|
||||
|
||||
# 检查缓存(无效缓存自动清理,避免“无动画”结果被永久缓存)
|
||||
cached_actor = self._actor_cache.get(owner_model)
|
||||
if cached_actor:
|
||||
# 若已获得模型路径,优先重建真实 Actor,避免长期停留在 autoBind 代理导致“能触发但不可见”
|
||||
try:
|
||||
owner_has_path = owner_model.hasTag("model_path") and bool(owner_model.getTag("model_path"))
|
||||
except Exception:
|
||||
owner_has_path = False
|
||||
if owner_has_path and isinstance(cached_actor, _BoundAnimationProxy):
|
||||
_cleanup_actor(cached_actor)
|
||||
self._actor_cache.pop(owner_model, None)
|
||||
cached_actor = None
|
||||
|
||||
if cached_actor:
|
||||
try:
|
||||
@ -1249,7 +1335,7 @@ class AnimationTools:
|
||||
owner_model.setTag("has_animations", "true")
|
||||
return mem_actor
|
||||
except Exception as e:
|
||||
print(f"[Actor加载] 创建内存模型副本失败: {e}")
|
||||
self._anim_log(f"[Actor加载] 创建内存模型副本失败: {e}")
|
||||
finally:
|
||||
# _try_create_actor_from_source 失败时,清理临时副本
|
||||
try:
|
||||
@ -1269,27 +1355,27 @@ class AnimationTools:
|
||||
self._actor_cache[owner_model] = mem_proxy
|
||||
owner_model.setTag("has_animations", "true")
|
||||
if source_node != owner_model:
|
||||
print(f"[Actor加载] 使用可见节点作为动画源: {owner_model.getName()} -> {source_node.getName()}")
|
||||
self._anim_log(f"[Actor加载] 使用可见节点作为动画源: {owner_model.getName()} -> {source_node.getName()}")
|
||||
return mem_proxy
|
||||
return None
|
||||
|
||||
# 始终优先尝试从文件路径加载,因为底层 gltf 插件只有在加载文件时才能抽取完整的动画名称。
|
||||
print(f"[Actor加载调试] 传入的 origin_model: {origin_model.getName() if origin_model else 'None'}")
|
||||
print(f"[Actor加载调试] origin_model tags: {origin_model.getTags() if origin_model else 'None'}")
|
||||
print(f"[Actor加载调试] 解析出的 owner_model: {owner_model.getName() if owner_model else 'None'}")
|
||||
self._anim_log(f"[Actor加载调试] 传入的 origin_model: {origin_model.getName() if origin_model else 'None'}")
|
||||
self._anim_log(f"[Actor加载调试] origin_model tags: {origin_model.getTags() if origin_model else 'None'}")
|
||||
self._anim_log(f"[Actor加载调试] 解析出的 owner_model: {owner_model.getName() if owner_model else 'None'}")
|
||||
try:
|
||||
print(f"[Actor加载调试] owner_model tags: {owner_model.getTags() if owner_model else 'None'}")
|
||||
print(f"[Actor加载调试] owner_model path tag: {owner_model.getTag('model_path') if owner_model.hasTag('model_path') else 'MISSING'}")
|
||||
self._anim_log(f"[Actor加载调试] owner_model tags: {owner_model.getTags() if owner_model else 'None'}")
|
||||
self._anim_log(f"[Actor加载调试] owner_model path tag: {owner_model.getTag('model_path') if owner_model.hasTag('model_path') else 'MISSING'}")
|
||||
except Exception as e:
|
||||
print(f"[Actor加载调试] 获取 tags 异常: {e}")
|
||||
self._anim_log(f"[Actor加载调试] 获取 tags 异常: {e}")
|
||||
|
||||
filepath = owner_model.getTag("model_path") if owner_model.hasTag("model_path") else ""
|
||||
if not filepath and owner_model.hasTag("original_path"):
|
||||
filepath = owner_model.getTag("original_path")
|
||||
|
||||
print(f"[Actor加载调试] 获取到的 filepath: '{filepath}'")
|
||||
self._anim_log(f"[Actor加载调试] 获取到的 filepath: '{filepath}'")
|
||||
if not filepath:
|
||||
print(f"[Actor加载调试] filepath为空,触发 _try_memory_fallback()")
|
||||
self._anim_log(f"[Actor加载调试] filepath为空,触发 _try_memory_fallback()")
|
||||
return _try_memory_fallback()
|
||||
|
||||
# 针对Actor加载,必须使用Panda3D规范的Unix风格路径,否则Windows绝对路径会导致加载彻底崩溃并返回空节点
|
||||
@ -1340,7 +1426,7 @@ class AnimationTools:
|
||||
load_paths = existing_paths + [p for p in unique_paths if p not in existing_paths]
|
||||
|
||||
for p in load_paths:
|
||||
print(f"[Actor加载验证] 正在尝试通过路径读取骨骼和动画文件: {p}")
|
||||
self._anim_log(f"[Actor加载验证] 正在尝试通过路径读取骨骼和动画文件: {p}")
|
||||
actor = _try_create_actor_from_source(p, f"文件路径({p})")
|
||||
if actor:
|
||||
owner_model.setTag("model_path", p)
|
||||
@ -1408,7 +1494,7 @@ class AnimationTools:
|
||||
format_info = self._getModelFormat(origin_model)
|
||||
processed = []
|
||||
|
||||
print(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||
self._anim_log(f"[动画分析] 格式: {format_info}, 原始动画名称: {anim_names}")
|
||||
|
||||
for name in anim_names:
|
||||
display_name = name
|
||||
@ -1439,7 +1525,7 @@ class AnimationTools:
|
||||
# FBX 格式可能需要特殊处理
|
||||
if self._isLikelyBoneGroup(name):
|
||||
# 检查是否是骨骼组而非动画
|
||||
print(f"[警告] '{name}' 可能不是真正的动画序列,而是骨骼组")
|
||||
self._anim_log(f"[警告] '{name}' 可能不是真正的动画序列,而是骨骼组")
|
||||
display_name = f"⚠️ {name} (可能非动画)"
|
||||
else:
|
||||
display_name = name
|
||||
@ -1449,7 +1535,7 @@ class AnimationTools:
|
||||
display_name = name
|
||||
|
||||
processed.append((display_name, original_name))
|
||||
print(f"[动画分析] {original_name} → {display_name}")
|
||||
self._anim_log(f"[动画分析] {original_name} → {display_name}")
|
||||
|
||||
return processed
|
||||
|
||||
@ -1630,6 +1716,12 @@ class AnimationTools:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
self._ensure_animation_runtime_state()
|
||||
self._animation_sync_tasks.pop(owner_model, None)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
actor.hide()
|
||||
except Exception:
|
||||
@ -1694,70 +1786,19 @@ class AnimationTools:
|
||||
pass
|
||||
|
||||
owner_model = self._resolve_animation_owner_model(origin_model)
|
||||
self._stop_all_active_animations()
|
||||
self._cleanup_conflicting_animation_actors(owner_model)
|
||||
self._discard_cached_actor(owner_model, restore_owner=True)
|
||||
actor = self._getActor(owner_model)
|
||||
self._stop_all_active_animations(except_owner=owner_model)
|
||||
actor = self._acquire_playback_actor(origin_model, owner_model, force_rebuild=False)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
# 若仍在“无路径的内存代理”,播放前强制再尝试一次路径恢复并重建真实 Actor
|
||||
if isinstance(actor, _BoundAnimationProxy):
|
||||
owner_has_path = owner_model.hasTag("model_path") and bool(owner_model.getTag("model_path"))
|
||||
if not owner_has_path:
|
||||
recovered_path = self._recover_model_path_from_tags(owner_model) or self._recover_model_path_from_tags(origin_model)
|
||||
if recovered_path:
|
||||
try:
|
||||
owner_model.setTag("model_path", recovered_path)
|
||||
if (not owner_model.hasTag("original_path")) or (not owner_model.getTag("original_path")):
|
||||
owner_model.setTag("original_path", recovered_path)
|
||||
except Exception:
|
||||
pass
|
||||
self._clear_animation_cache(owner_model)
|
||||
actor = self._getActor(owner_model)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
is_scene_bound_proxy = isinstance(actor, _BoundAnimationProxy) and (not getattr(actor, "_owns_node", False))
|
||||
|
||||
if not is_scene_bound_proxy:
|
||||
actor.setPos(self.render, owner_model.getPos(self.render))
|
||||
actor.setHpr(self.render, owner_model.getHpr(self.render))
|
||||
actor.setScale(self.render, owner_model.getScale(self.render))
|
||||
|
||||
# original_world_pos = owner_model.getPos(self.render)
|
||||
# original_world_hpr = owner_model.getHpr(self.render)
|
||||
# original_world_scale = owner_model.getScale(self.render)
|
||||
|
||||
if not is_scene_bound_proxy:
|
||||
actor.setPos(owner_model.getPos())
|
||||
actor.setHpr(owner_model.getHpr())
|
||||
actor.setScale(owner_model.getScale())
|
||||
is_scene_bound_proxy = self._sync_actor_transform_for_playback(owner_model, actor)
|
||||
|
||||
if self._should_swap_visibility_for_actor(owner_model, actor):
|
||||
owner_model.hide()
|
||||
else:
|
||||
owner_model.show()
|
||||
actor.show()
|
||||
|
||||
task_name = f"maintain_anim_pos_{id(actor)}"
|
||||
|
||||
# 维持 Actor 的世界变换,避免父节点层级差异导致位置抖动/漂移
|
||||
def maintainWorldPosition(task):
|
||||
try:
|
||||
if not actor.isEmpty() and not owner_model.isEmpty():
|
||||
actor.setPos(self.render, owner_model.getPos(self.render))
|
||||
actor.setHpr(self.render, owner_model.getHpr(self.render))
|
||||
actor.setScale(self.render, owner_model.getScale(self.render))
|
||||
return task.cont
|
||||
else:
|
||||
return task.done
|
||||
except:
|
||||
return task.done
|
||||
|
||||
taskMgr.remove(task_name)
|
||||
if not is_scene_bound_proxy:
|
||||
taskMgr.add(maintainWorldPosition, task_name)
|
||||
self._ensure_actor_sync_task(owner_model, actor, enabled=(not is_scene_bound_proxy))
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = self._resolve_selected_animation_name(origin_model, actor, owner_model)
|
||||
@ -1849,47 +1890,19 @@ class AnimationTools:
|
||||
pass
|
||||
|
||||
owner_model = self._resolve_animation_owner_model(origin_model)
|
||||
self._stop_all_active_animations()
|
||||
self._cleanup_conflicting_animation_actors(owner_model)
|
||||
self._discard_cached_actor(owner_model, restore_owner=True)
|
||||
actor = self._getActor(owner_model)
|
||||
self._stop_all_active_animations(except_owner=owner_model)
|
||||
actor = self._acquire_playback_actor(origin_model, owner_model, force_rebuild=False)
|
||||
if not actor:
|
||||
return
|
||||
|
||||
is_scene_bound_proxy = isinstance(actor, _BoundAnimationProxy) and (not getattr(actor, "_owns_node", False))
|
||||
|
||||
original_world_pos = owner_model.getPos(self.render)
|
||||
original_world_hpr = owner_model.getHpr(self.render)
|
||||
original_world_scale = owner_model.getScale(self.render)
|
||||
|
||||
if not is_scene_bound_proxy:
|
||||
actor.setPos(self.render, owner_model.getPos(self.render))
|
||||
actor.setHpr(self.render, owner_model.getHpr(self.render))
|
||||
actor.setScale(self.render, owner_model.getScale(self.render))
|
||||
is_scene_bound_proxy = self._sync_actor_transform_for_playback(owner_model, actor)
|
||||
|
||||
if self._should_swap_visibility_for_actor(owner_model, actor):
|
||||
owner_model.hide()
|
||||
else:
|
||||
owner_model.show()
|
||||
actor.show()
|
||||
|
||||
task_name = f"maintain_anim_pos_{id(actor)}"
|
||||
|
||||
def maintainWorldPosition(task):
|
||||
try:
|
||||
if not actor.isEmpty() and not owner_model.isEmpty():
|
||||
actor.setPos(self.render, owner_model.getPos(self.render))
|
||||
actor.setHpr(self.render, owner_model.getHpr(self.render))
|
||||
actor.setScale(self.render, owner_model.getScale(self.render))
|
||||
return task.cont
|
||||
else:
|
||||
return task.done
|
||||
except:
|
||||
return task.done
|
||||
|
||||
taskMgr.remove(task_name)
|
||||
if not is_scene_bound_proxy:
|
||||
taskMgr.add(maintainWorldPosition, task_name)
|
||||
self._ensure_actor_sync_task(owner_model, actor, enabled=(not is_scene_bound_proxy))
|
||||
|
||||
# 获取当前选中的动画
|
||||
current_anim = self._resolve_selected_animation_name(origin_model, actor, owner_model)
|
||||
@ -1928,6 +1941,11 @@ class AnimationTools:
|
||||
node.setPythonTag("cached_processed_names", None)
|
||||
node.setPythonTag("cached_has_animation_nodes", None)
|
||||
node.setPythonTag("animation", None) # 同时清除动画检测结果
|
||||
if owner_node and owner_node != node:
|
||||
owner_node.setPythonTag("cached_anim_info", None)
|
||||
owner_node.setPythonTag("cached_processed_names", None)
|
||||
owner_node.setPythonTag("cached_has_animation_nodes", None)
|
||||
owner_node.setPythonTag("animation", None)
|
||||
|
||||
# 如果Actor在缓存中,也需要清理
|
||||
if owner_node in self._actor_cache:
|
||||
|
||||
@ -658,7 +658,9 @@ class EditorPanelsRightMixin(
|
||||
pass
|
||||
|
||||
has_animation_tag = anim_node.hasTag("has_animations") and anim_node.getTag("has_animations").lower() == "true"
|
||||
has_cached_animation = anim_node.getPythonTag("animation") is True
|
||||
cached_animation_state = anim_node.getPythonTag("animation")
|
||||
has_cached_animation = cached_animation_state is True
|
||||
cached_no_animation = cached_animation_state is False
|
||||
has_animation_nodes = (
|
||||
has_animation_tag or
|
||||
has_cached_animation or
|
||||
@ -691,13 +693,15 @@ class EditorPanelsRightMixin(
|
||||
anim_node.setPythonTag("animation", None)
|
||||
|
||||
should_force_probe = False
|
||||
if not (has_animation_tag or has_animation_nodes or has_cached_animation):
|
||||
if not (has_animation_tag or has_animation_nodes or has_cached_animation or cached_no_animation):
|
||||
should_force_probe = True
|
||||
anim_node.setPythonTag("cached_anim_info", None)
|
||||
anim_node.setPythonTag("cached_processed_names", None)
|
||||
anim_node.setPythonTag("cached_has_animation_nodes", None)
|
||||
has_animation_nodes = self._get_cached_animation_structure_state(anim_node, force_refresh=True)
|
||||
|
||||
if not has_animation_nodes:
|
||||
cached_no_animation = True
|
||||
anim_node.setPythonTag("animation", False)
|
||||
anim_node.setPythonTag("cached_anim_info", "无动画")
|
||||
anim_node.setPythonTag("cached_processed_names", [])
|
||||
|
||||
# 只有在没有缓存时才进行完整的动画检测和处理
|
||||
if cached_anim_info is None or cached_processed_names is None:
|
||||
# 获取Actor
|
||||
@ -710,8 +714,18 @@ class EditorPanelsRightMixin(
|
||||
anim_node.setPythonTag("animation", False)
|
||||
anim_node.setPythonTag("cached_processed_names", [])
|
||||
anim_node.setPythonTag("cached_anim_info", "无动画")
|
||||
try:
|
||||
anim_node.setTag("has_animations", "false")
|
||||
anim_node.setTag("has_animations_checked", "true")
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "此模型不包含动画")
|
||||
anim_node.setPythonTag("animation", False)
|
||||
if cached_processed_names is None:
|
||||
anim_node.setPythonTag("cached_processed_names", [])
|
||||
if cached_anim_info is None:
|
||||
anim_node.setPythonTag("cached_anim_info", "无动画")
|
||||
return
|
||||
|
||||
# 获取和分析动画名称
|
||||
@ -724,6 +738,12 @@ class EditorPanelsRightMixin(
|
||||
if not (has_animation_tag or has_animation_nodes):
|
||||
anim_node.setPythonTag("cached_processed_names", [])
|
||||
anim_node.setPythonTag("cached_anim_info", "无动画")
|
||||
anim_node.setPythonTag("animation", False)
|
||||
try:
|
||||
anim_node.setTag("has_animations", "false")
|
||||
anim_node.setTag("has_animations_checked", "true")
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
anim_node.setTag("has_animations", "true")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user