(client) chore:修改了角色的身体结构的定义方式,现在图片资源统一使用ImageDef加载,使用了更节省资源的初始化方式;fix:修复了定义加载数组时只能初始化数组而不能初始化列表的问题
This commit is contained in:
@ -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>
|
||||
|
Reference in New Issue
Block a user