Files
Gen_Hack-and-Slash-Roguelite/Client/Assets/Scripts/Item/WeaponResource.cs

239 lines
11 KiB
C#
Raw Normal View History

using Base;
using Data;
using Entity;
using Prefab;
2025-08-28 16:20:24 +08:00
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; }
2025-08-28 16:20:24 +08:00
public DrawNodeDef AttackAnimationDef { get; private set; }
public float AttackAnimationTime { get; private set; }
public float AttackDetectionTime { get; private set; }
/// <summary>
/// 构造函数:通过 WeaponDef 对象初始化 WeaponResource。
/// </summary>
/// <param name="def">武器的定义对象。</param>
/// <exception cref="ArgumentNullException">如果传入的 WeaponDef 为 null则抛出此异常。</exception>
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;
}
/// <summary>
/// 逻辑修改说明 2新增公共方法根据 WeaponResource 的定义创建攻击动画的运行时 GameObject 实例。
/// 这是 WeaponResource 的工厂方法,用于按需创建动画对象。
/// </summary>
/// <param name="parent">动画 GameObject 的父 Transform。</param>
/// <returns>包含动画根 GameObject 和所有 ITick 组件的元组。</returns>
public (GameObject root, ITick[] animationComponents) InstantiateAttackAnimation(Transform parent)
{
if (AttackAnimationDef == null)
{
Debug.LogWarning($"WeaponResource: 尝试实例化攻击动画但 _attackAnimationDef 为 null. 武器类型: {Type}");
return (null, Array.Empty<ITick>());
}
// 逻辑修改说明 4在此处统一加载 ImagePrefab 和 SpriteAnimator避免性能损耗。
var imagePrefab = Resources.Load<ImagePrefab>("Prefab/Image");
var animatorPrefab = Resources.Load<SpriteAnimator>("Prefab/Animation");
if (imagePrefab == null)
{
Debug.LogError("InstantiateAttackAnimation: 无法加载 Prefab/Image.");
return (null, Array.Empty<ITick>());
}
if (animatorPrefab == null)
{
Debug.LogError("InstantiateAttackAnimation: 无法加载 Prefab/Animation.");
return (null, Array.Empty<ITick>());
}
2025-08-28 16:20:24 +08:00
// 逻辑修改说明 2调用修改后的私有辅助方法来创建 GameObject 层次结构。
var animationRoot = _CreateBodyPartGameObject(
2025-08-28 16:20:24 +08:00
AttackAnimationDef,
parent,
imagePrefab,
animatorPrefab);
if (animationRoot == null)
{
Debug.LogWarning($"WeaponResource: 创建攻击动画根节点失败. 武器类型: {Type}");
return (null, Array.Empty<ITick>());
}
// 逻辑修改说明 3收集所有实现了 ITick 接口的组件。
2025-08-28 16:20:24 +08:00
var tickComponents = animationRoot.GetComponentsInChildren<ITick>(true);
// GetComponentsInChildren(true) 会查找包括自身在内的所有子对象上的组件,即使它们是处于非活动状态。
2025-08-28 16:20:24 +08:00
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)
{
2025-08-28 16:20:24 +08:00
// 逻辑修改说明:这里直接返回 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<ImagePrefab>();
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<SpriteAnimator>();
if (animator == null)
{
Debug.LogWarning($"CreateBodyPartGameObject: 无法获取SpriteAnimator组件 (节点名: {nodeObject.name})");
break;
}
animator.SetFPS(drawNode.FPS);
var animatedSprites = new List<Sprite>();
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;
}
}
}
}