using Base; using Data; using Entity; using Prefab; using System; using System.Collections.Generic; using UnityEngine; using Object = UnityEngine.Object; namespace Item { public class WeaponResource : ItemResource { public Attributes Attributes { get; private set; } public BulletDef Bullet { get; private set; } public WeaponType Type { get; private set; } public DrawNodeDef AttackAnimationDef { get; private set; } public float AttackAnimationTime { get; private set; } public float AttackDetectionTime { get; private set; } /// /// 构造函数:通过 WeaponDef 对象初始化 WeaponResource。 /// /// 武器的定义对象。 /// 如果传入的 WeaponDef 为 null,则抛出此异常。 public WeaponResource(WeaponDef def) : base(def) // 调用基类 ItemResource 的构造函数,直接传入 WeaponDef。 { // 参数校验:确保 WeaponDef 对象不为空,避免空引用异常。 if (def == null) { throw new ArgumentNullException(nameof(def), "创建 WeaponResource 时,WeaponDef 不能为 null。"); } Bullet = def.bullet; // 初始化武器的属性。Attributes 对象通过 WeaponDef 中的属性数据进行构建。 Attributes = new Attributes(def.attributes); // 初始化武器类型,直接从 WeaponDef 定义中获取。 Type = def.type; AttackDetectionTime = def.attackDetectionTime; // 逻辑修改说明 1:存储 DrawNodeDef,而不是直接创建 GameObject。 AttackAnimationDef = def.attackAnimation; AttackAnimationTime = AttackAnimationDef?.GetAnimationCycleDuration() ?? 0.1f; } /// /// 逻辑修改说明 2:新增公共方法:根据 WeaponResource 的定义创建攻击动画的运行时 GameObject 实例。 /// 这是 WeaponResource 的工厂方法,用于按需创建动画对象。 /// /// 动画 GameObject 的父 Transform。 /// 包含动画根 GameObject 和所有 ITick 组件的元组。 public (GameObject root, ITick[] animationComponents) InstantiateAttackAnimation(Transform parent) { if (AttackAnimationDef == null) { Debug.LogWarning($"WeaponResource: 尝试实例化攻击动画但 _attackAnimationDef 为 null. 武器类型: {Type}"); return (null, Array.Empty()); } // 逻辑修改说明 4:在此处统一加载 ImagePrefab 和 SpriteAnimator,避免性能损耗。 var imagePrefab = Resources.Load("Prefab/Image"); var animatorPrefab = Resources.Load("Prefab/Animation"); if (imagePrefab == null) { Debug.LogError("InstantiateAttackAnimation: 无法加载 Prefab/Image."); return (null, Array.Empty()); } if (animatorPrefab == null) { Debug.LogError("InstantiateAttackAnimation: 无法加载 Prefab/Animation."); return (null, Array.Empty()); } // 逻辑修改说明 2:调用修改后的私有辅助方法来创建 GameObject 层次结构。 var animationRoot = _CreateBodyPartGameObject( AttackAnimationDef, parent, imagePrefab, animatorPrefab); if (animationRoot == null) { Debug.LogWarning($"WeaponResource: 创建攻击动画根节点失败. 武器类型: {Type}"); return (null, Array.Empty()); } // 逻辑修改说明 3:收集所有实现了 ITick 接口的组件。 var tickComponents = animationRoot.GetComponentsInChildren(true); // GetComponentsInChildren(true) 会查找包括自身在内的所有子对象上的组件,即使它们是处于非活动状态。 return (animationRoot, tickComponents); } // 逻辑修改说明 4:修改 InitBodyPart 为 _CreateBodyPartGameObject,并设为 private static 方法。 // 它现在接收 ImagePrefab 和 SpriteAnimator 作为参数,不再在内部加载。 private static GameObject _CreateBodyPartGameObject(DrawNodeDef drawNode, Transform parent, ImagePrefab imagePrefab, SpriteAnimator animatorPrefab) { try { // 参数验证 if (drawNode == null) { Debug.LogWarning("CreateBodyPartGameObject: drawNode参数为null"); return null; } if (parent == null) { // 逻辑修改说明:这里直接返回 null 更合适,因为没有父节点无法创建实例。 Debug.LogWarning($"CreateBodyPartGameObject: 父节点为null (节点名: {drawNode.nodeName ?? "Unnamed DrawNode"})"); return null; } GameObject nodeObject = null; // 根据纹理数量创建不同类型的节点 switch (drawNode.textures?.Count ?? 0) { case 0: // 无纹理节点 nodeObject = new GameObject(drawNode.nodeName); nodeObject.transform.SetParent(parent.transform, false); break; case 1: // 单纹理节点 // imagePrefab 已作为参数传入,无需再次加载 nodeObject = Object.Instantiate(imagePrefab.gameObject, parent.transform); nodeObject.name = drawNode.nodeName ?? "UnnamedNode"; // 提前设置名称以在警告中显示 var texture = Managers.PackagesImageManager.Instance?.GetSprite(drawNode.packID, drawNode.textures[0]); if (!texture) { Debug.LogWarning( $"CreateBodyPartGameObject: 无法获取纹理 (节点名: {nodeObject.name}, 纹理ID: {drawNode.textures[0]})"); } var imagePrefabCom = nodeObject.GetComponent(); if (imagePrefabCom != null) { imagePrefabCom.SetSprite(texture); } else { Debug.LogWarning($"CreateBodyPartGameObject: 无法获取ImagePrefab组件 (节点名: {nodeObject.name})"); } break; default: // 多纹理动画节点 // animatorPrefab 已作为参数传入,无需再次加载 nodeObject = Object.Instantiate(animatorPrefab.gameObject, parent.transform); nodeObject.name = drawNode.nodeName ?? "UnnamedNode"; // 提前设置名称以在警告中显示 var animator = nodeObject.GetComponent(); if (animator == null) { Debug.LogWarning($"CreateBodyPartGameObject: 无法获取SpriteAnimator组件 (节点名: {nodeObject.name})"); break; } animator.SetFPS(drawNode.FPS); var animatedSprites = new List(); foreach (var textureId in drawNode.textures) { try { var sprite = Managers.PackagesImageManager.Instance?.GetSprite(drawNode.packID, textureId); if (sprite != null) { animatedSprites.Add(sprite); } else { Debug.LogWarning( $"CreateBodyPartGameObject: 无法获取动画纹理 (节点名: {nodeObject.name}, 纹理ID: {textureId})"); } } catch (Exception ex) { Debug.LogError( $"CreateBodyPartGameObject: 加载动画纹理时出错 (节点名: {nodeObject.name}, 纹理ID: {textureId}): {ex.Message}"); } } if (animatedSprites.Count > 0) { animator.SetSprites(animatedSprites.ToArray()); } else { Debug.LogWarning($"CreateBodyPartGameObject: 没有有效的动画纹理 (节点名: {nodeObject.name})"); } break; } // 设置节点属性 (对于case 1, default 已经在上面设置了名称,这里可以作为 fallback 或统一处理) if (nodeObject == null) return null; // 如果nodeObject在上面某些分支没有成功创建,提前返回 nodeObject.transform.localPosition = drawNode.position; // nodeObject.name = drawNode.nodeName ?? "UnnamedNode"; // 不再需要,已在上面处理 // 递归初始化子节点 if (drawNode.nodes == null) return nodeObject; foreach (var child in drawNode.nodes) { try { // 逻辑修改说明 4:递归调用时,传递 prefab 参数。 _CreateBodyPartGameObject(child, nodeObject.transform, imagePrefab, animatorPrefab); } catch (Exception ex) { Debug.LogError($"CreateBodyPartGameObject: 初始化子节点失败 (父节点: {nodeObject.name ?? drawNode.nodeName ?? "Unnamed Parent"}): {ex.Message}"); } } return nodeObject; } catch (Exception ex) { Debug.LogError($"CreateBodyPartGameObject: 初始化节点时发生未处理的异常 (节点名: {drawNode?.nodeName ?? "Unknown DrawNode"}): {ex}"); return null; } } } }