(client) chore:修改了角色的身体结构的定义方式,现在图片资源统一使用ImageDef加载,使用了更节省资源的初始化方式;fix:修复了定义加载数组时只能初始化数组而不能初始化列表的问题

This commit is contained in:
m0_75251201
2025-08-22 20:43:55 +08:00
parent 3e099137a1
commit 8916440e7e
28 changed files with 1411 additions and 954 deletions

View File

@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AI;
using Base;
using Data;
@ -98,7 +99,7 @@ namespace Entity
/// 表示实体是否已经死亡(生命值小于等于零)。
/// </summary>
public bool IsDead => attributes.health <= 0;
public bool IsShowingOfHitBarUI=>hitBarUIShowTimer > 0;
public bool IsShowingHealthBarUI=>hitBarUIShowTimer > 0;
public bool IsAttacking => attackCoroutine != null;
@ -108,18 +109,21 @@ namespace Entity
/// <summary>
/// 存储不同朝向下的动画节点集合。
/// </summary>
public Dictionary<Orientation, List<ITick>> bodyAnimationNode = new();
public Dictionary<EntityState, Dictionary<Orientation, List<ITick>>> bodyAnimationNode = new();
private List<ITick> currentAnimatorCache=new ();
/// <summary>
/// 存储不同朝向下的身体节点对象。
/// </summary>
private Dictionary<Orientation, GameObject> bodyNodes = new();
private Dictionary<EntityState, Dictionary<Orientation,GameObject>> bodyNodes = new();
/// <summary>
/// 当前实体的朝向。
/// </summary>
private Orientation currentOrientation = Orientation.Down;
/// <summary>
/// 当前实体的状态
/// </summary>
private EntityState currentState = EntityState.Idle;
/// <summary>
@ -162,83 +166,218 @@ namespace Entity
/// <param name="drawingOrder">绘制顺序定义。</param>
public virtual void InitBody(DrawingOrderDef drawingOrder)
{
// 定义方向枚举和对应的 GetDrawingOrder 调用
Orientation[] orientations = { Orientation.Down, Orientation.Up, Orientation.Left, Orientation.Right };
// 预缓存枚举值(避免每次循环重复调用 Enum.GetValues
var states = Enum.GetValues(typeof(EntityState)).Cast<EntityState>().ToArray();
var orientations = Enum.GetValues(typeof(Orientation)).Cast<Orientation>().ToArray();
foreach (var orientation in orientations)
// 预初始化字典结构(减少内层循环的字典检查)
foreach (var state in states)
{
currentOrientation = orientation;
bodyAnimationNode[orientation] = new();
// 获取当前方向的绘图节点
var drawNode = drawingOrder.GetDrawingOrder(orientation, out var realOrientation);
if (drawNode == null) continue;
var directionRoot = new GameObject(orientation.ToString());
directionRoot.transform.SetParent(body.transform, false);
InitBodyPart(drawNode, directionRoot, drawingOrder.texturePath,realOrientation);
bodyNodes[orientation] = directionRoot;
bodyNodes.TryAdd(state, new Dictionary<Orientation, GameObject>());
bodyAnimationNode.TryAdd(state, new Dictionary<Orientation, List<ITick>>());
}
currentOrientation = Orientation.Down;
foreach (var bodyNode in bodyNodes)
// 主初始化逻辑
foreach (var state in states)
{
bodyNode.Value.SetActive(false);
var stateBodyNodes = bodyNodes[state];
var stateAnimNodes = bodyAnimationNode[state];
foreach (var orientation in orientations)
{
// 获取节点定义(避免重复调用)
var nodeDef = drawingOrder.GetDrawNodeDef(state, orientation, out var original);
// 处理空节点定义(直接创建空对象)
if (nodeDef == null)
{
var obj = new GameObject { name = $"{state}_Empty" };
obj.transform.SetParent(body.transform, false);
stateBodyNodes[orientation] = obj;
continue; // 跳过后续动画处理
}
// 处理有效节点定义
GameObject targetObj;
if (original.HasValue && stateBodyNodes.TryGetValue(original.Value, out var reusedObj))
{
targetObj = reusedObj; // 复用已有对象
}
else
{
targetObj = InitBodyPart(nodeDef, body); // 创建新对象
}
stateBodyNodes[orientation] = targetObj;
// 提取动画组件(安全处理空组件情况)
var animators = targetObj.GetComponentsInChildren<SpriteAnimator>();
if (animators.Length > 0)
{
stateAnimNodes[orientation] = animators.Cast<ITick>().ToList();
}
// 无动画组件时保持 stateAnimNodes[orientation] 为 null符合原始逻辑
}
}
SetOrientation(Orientation.Down);
// 批量隐藏所有节点(使用字典值集合直接操作)
foreach (var nodeDict in bodyNodes.Values)
{
foreach (var obj in nodeDict.Values)
{
obj.SetActive(false);
}
}
SetBodyTexture(EntityState.Idle,Orientation.Down); // 激活默认朝向
}
/// <summary>
/// 递归初始化单个绘图节点及其子节点。
/// 递归初始化单个绘图节点及其子节点,具有更强的健壮性和错误处理
/// </summary>
/// <param name="drawNode">绘图节点定义。</param>
/// <param name="parent">父节点对象。</param>
/// <param name="folderPath">纹理资源路径。</param>
public virtual void InitBodyPart(DrawNodeDef drawNode, GameObject parent, string folderPath,Orientation realOrientation)
/// <returns>创建的GameObject如果失败则返回null</returns>
public virtual GameObject InitBodyPart(DrawNodeDef drawNode, GameObject parent)
{
if (drawNode == null) return;
GameObject nodeObject;
if (drawNode.nodeName == "noName")
try
{
nodeObject = new();
nodeObject.transform.SetParent(parent.transform);
}
else
{
switch (drawNode.drawNodeType)
// 参数验证
if (drawNode == null)
{
case DrawNodeType.Image:
Debug.LogWarning("InitBodyPart: drawNode参数为null");
return null;
}
if (parent == null)
{
Debug.LogWarning($"InitBodyPart: 父节点为null (节点名: {drawNode.nodeName})");
return null;
}
GameObject nodeObject = null;
// 根据纹理数量创建不同类型的节点
switch (drawNode.animationTextures?.Count ?? 0)
{
case 0:
// 无纹理节点
nodeObject = new GameObject(drawNode.nodeName);
nodeObject.transform.SetParent(parent.transform, false);
break;
case 1:
// 单纹理节点
if (imagePrefab == null)
{
Debug.LogError($"InitBodyPart: imagePrefab未设置 (节点名: {drawNode.nodeName})");
return null;
}
nodeObject = Instantiate(imagePrefab.gameObject, parent.transform);
var texture =
Managers.PackagesImageManager.Instance.FindBodyTextures(drawNode.packID, folderPath,
$"{drawNode.nodeName}_{realOrientation}");
var image = nodeObject.GetComponent<ImagePrefab>();
image.SetSprite(texture.Length > 0
? texture[0]
: Managers.PackagesImageManager.Instance.defaultSprite);
Managers.PackagesImageManager.Instance?.GetSprite(drawNode.packID,
drawNode.animationTextures[0]);
if (!texture)
{
Debug.LogWarning(
$"InitBodyPart: 无法获取纹理 (节点名: {drawNode.nodeName}, 纹理ID: {drawNode.animationTextures[0]})");
}
var imagePrefabCom = nodeObject.GetComponent<ImagePrefab>();
if (imagePrefabCom != null)
{
imagePrefabCom.SetSprite(texture);
}
else
{
Debug.LogWarning($"InitBodyPart: 无法获取ImagePrefab组件 (节点名: {drawNode.nodeName})");
}
break;
case DrawNodeType.Animation:
nodeObject = Instantiate(animatorPrefab.gameObject, parent.transform);
ITick tick = nodeObject.GetComponent<SpriteAnimator>();
if (tick != null)
bodyAnimationNode[currentOrientation].Add(tick);
var textures = Managers.PackagesImageManager.Instance.FindBodyTextures(drawNode.packID,
folderPath,
$"{drawNode.nodeName}_{realOrientation}");
var animator = nodeObject.GetComponent<SpriteAnimator>();
animator.SetSprites(textures);
break;
default:
throw new ArgumentOutOfRangeException();
// 多纹理动画节点
if (!animatorPrefab)
{
Debug.LogError($"InitBodyPart: animatorPrefab未设置 (节点名: {drawNode.nodeName})");
return null;
}
nodeObject = Instantiate(animatorPrefab.gameObject, parent.transform);
var animator = nodeObject.GetComponent<SpriteAnimator>();
if (animator == null)
{
Debug.LogWarning($"InitBodyPart: 无法获取SpriteAnimator组件 (节点名: {drawNode.nodeName})");
break;
}
animator.SetFPS(drawNode.FPS);
var animatedSprites = new List<Sprite>();
foreach (var textureId in drawNode.animationTextures)
{
try
{
var sprite =
Managers.PackagesImageManager.Instance?.GetSprite(drawNode.packID, textureId);
if (sprite != null)
{
animatedSprites.Add(sprite);
}
else
{
Debug.LogWarning(
$"InitBodyPart: 无法获取动画纹理 (节点名: {drawNode.nodeName}, 纹理ID: {textureId})");
}
}
catch (Exception ex)
{
Debug.LogError(
$"InitBodyPart: 加载动画纹理时出错 (节点名: {drawNode.nodeName}, 纹理ID: {textureId}): {ex.Message}");
}
}
if (animatedSprites.Count > 0)
{
animator.SetSprites(animatedSprites.ToArray());
}
else
{
Debug.LogWarning($"InitBodyPart: 没有有效的动画纹理 (节点名: {drawNode.nodeName})");
}
break;
}
// 设置节点属性
if (!nodeObject) return nodeObject;
nodeObject.transform.localPosition = drawNode.position;
nodeObject.name = drawNode.nodeName ?? "UnnamedNode";
// 递归初始化子节点
if (drawNode.nodes == null) return nodeObject;
foreach (var child in drawNode.nodes)
{
try
{
InitBodyPart(child, nodeObject);
}
catch (Exception ex)
{
Debug.LogError($"InitBodyPart: 初始化子节点失败 (父节点: {drawNode.nodeName}): {ex.Message}");
}
}
return nodeObject;
}
nodeObject.transform.localPosition = drawNode.position;
nodeObject.name = drawNode.nodeName;
// 递归初始化子节点
foreach (var child in drawNode.children)
catch (Exception ex)
{
InitBodyPart(child, nodeObject, folderPath,realOrientation);
Debug.LogError($"InitBodyPart: 初始化节点时发生未处理的异常 (节点名: {drawNode?.nodeName}): {ex}");
return null;
}
}
@ -255,15 +394,17 @@ namespace Entity
{
AutoBehave();
}
if (bodyAnimationNode.TryGetValue(currentOrientation, out var ticks))
if (currentAnimatorCache!=null)
{
foreach (var tick in ticks)
foreach (var animator in currentAnimatorCache)
{
tick.Tick();
animator.Tick();
}
}
if (IsShowingOfHitBarUI)
if (IsShowingHealthBarUI)
{
hitBarUIShowTimer -= Time.deltaTime;
if (hitBarUIShowTimer <= 0)
@ -281,24 +422,34 @@ namespace Entity
if(!IsAttacking)
attackCoroutine = StartCoroutine(AttackFlow());
}
/// <summary>
/// 设置实体的朝向。
/// </summary>
/// <param name="orientation">新的朝向。</param>
public virtual void SetOrientation(Orientation orientation)
public virtual void SetBodyTexture(EntityState state, Orientation orientation)
{
// 禁用当前朝向的节点
if (bodyNodes.TryGetValue(currentOrientation, out var currentNode))
if (bodyNodes.TryGetValue(currentState, out var stateNode))
{
currentNode.SetActive(false);
if (stateNode.TryGetValue(currentOrientation, out var node))
{
node.SetActive(false);
}
}
// 设置新的朝向
currentOrientation = orientation;
// 激活新朝向的节点
if (bodyNodes.TryGetValue(orientation, out var newNode))
if (bodyNodes.TryGetValue(state, out var showStateNode))
{
newNode.SetActive(true);
if (showStateNode.TryGetValue(orientation, out var showNode))
{
showNode.SetActive(true);
}
}
currentState = state;
currentOrientation = orientation;
if (bodyAnimationNode.TryGetValue(currentState, out var animationNode))
{
if (animationNode.TryGetValue(currentOrientation, out var value))
{
currentAnimatorCache=value;
}
}
}
@ -369,7 +520,8 @@ namespace Entity
// 水平方向优先
ori = direction.x > 0 ? Orientation.Right : Orientation.Left;
}
SetOrientation(ori);
SetBodyTexture(currentState, ori);
}
/// <summary>