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

239 lines
11 KiB
C#
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.

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; }
/// <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>());
}
// 逻辑修改说明 2调用修改后的私有辅助方法来创建 GameObject 层次结构。
var animationRoot = _CreateBodyPartGameObject(
AttackAnimationDef,
parent,
imagePrefab,
animatorPrefab);
if (animationRoot == null)
{
Debug.LogWarning($"WeaponResource: 创建攻击动画根节点失败. 武器类型: {Type}");
return (null, Array.Empty<ITick>());
}
// 逻辑修改说明 3收集所有实现了 ITick 接口的组件。
var tickComponents = animationRoot.GetComponentsInChildren<ITick>(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<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;
}
}
}
}