296 lines
10 KiB
C#
296 lines
10 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text.RegularExpressions;
|
||
using System.Xml.Linq;
|
||
using UnityEngine;
|
||
|
||
namespace Data
|
||
{
|
||
public enum Orientation
|
||
{
|
||
Down,
|
||
Left,
|
||
Right,
|
||
Up
|
||
}
|
||
|
||
public enum EntityState
|
||
{
|
||
Idle,
|
||
Walking,
|
||
MeleeAttack,
|
||
RangedAttack,
|
||
}
|
||
|
||
public class DrawingOrderDef : Define
|
||
{
|
||
public DrawNodeDef idle_down;
|
||
public DrawNodeDef idle_up;
|
||
public DrawNodeDef idle_left;
|
||
public DrawNodeDef idle_right;
|
||
|
||
public DrawNodeDef walk_down;
|
||
public DrawNodeDef walk_up;
|
||
public DrawNodeDef walk_left;
|
||
public DrawNodeDef walk_right;
|
||
|
||
public DrawNodeDef meleeAttack_down;
|
||
public DrawNodeDef meleeAttack_up;
|
||
public DrawNodeDef meleeAttack_left;
|
||
public DrawNodeDef meleeAttack_right;
|
||
|
||
public DrawNodeDef rangedAttack_down;
|
||
public DrawNodeDef rangedAttack_up;
|
||
public DrawNodeDef rangedAttack_left;
|
||
public DrawNodeDef rangedAttack_right;
|
||
|
||
public DrawNodeDef GetDrawNodeDef(EntityState state, Orientation orientation,
|
||
out Orientation? fallbackOrientation)
|
||
{
|
||
fallbackOrientation = null;
|
||
|
||
// 根据状态和方向获取对应的DrawNodeDef
|
||
var result = GetDrawNodeDefInternal(state, orientation);
|
||
|
||
if (result != null)
|
||
{
|
||
return result;
|
||
}
|
||
|
||
// 如果找不到,按照规则查找替补
|
||
switch (orientation)
|
||
{
|
||
case Orientation.Up:
|
||
// 上方向优先找下方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Down);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Down;
|
||
return result;
|
||
}
|
||
|
||
// 其次找左右方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Left);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Left;
|
||
return result;
|
||
}
|
||
|
||
result = GetDrawNodeDefInternal(state, Orientation.Right);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Right;
|
||
return result;
|
||
}
|
||
|
||
break;
|
||
|
||
case Orientation.Down:
|
||
// 下方向优先找上方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Up);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Up;
|
||
return result;
|
||
}
|
||
|
||
// 其次找左右方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Left);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Left;
|
||
return result;
|
||
}
|
||
|
||
result = GetDrawNodeDefInternal(state, Orientation.Right);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Right;
|
||
return result;
|
||
}
|
||
|
||
break;
|
||
|
||
case Orientation.Left:
|
||
// 左方向优先找右方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Right);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Right;
|
||
return result;
|
||
}
|
||
|
||
// 其次找上下方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Up);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Up;
|
||
return result;
|
||
}
|
||
|
||
result = GetDrawNodeDefInternal(state, Orientation.Down);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Down;
|
||
return result;
|
||
}
|
||
|
||
break;
|
||
|
||
case Orientation.Right:
|
||
// 右方向优先找左方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Left);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Left;
|
||
return result;
|
||
}
|
||
|
||
// 其次找上下方向
|
||
result = GetDrawNodeDefInternal(state, Orientation.Up);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Up;
|
||
return result;
|
||
}
|
||
|
||
result = GetDrawNodeDefInternal(state, Orientation.Down);
|
||
if (result != null)
|
||
{
|
||
fallbackOrientation = Orientation.Down;
|
||
return result;
|
||
}
|
||
|
||
break;
|
||
default:
|
||
throw new ArgumentOutOfRangeException(nameof(orientation), orientation, null);
|
||
}
|
||
|
||
// 如果所有替补都找不到,返回null
|
||
return null;
|
||
}
|
||
|
||
private DrawNodeDef GetDrawNodeDefInternal(EntityState state, Orientation orientation)
|
||
{
|
||
// 根据状态和方向获取对应的DrawNodeDef
|
||
switch (state)
|
||
{
|
||
case EntityState.Idle:
|
||
switch (orientation)
|
||
{
|
||
case Orientation.Down: return idle_down;
|
||
case Orientation.Up: return idle_up;
|
||
case Orientation.Left: return idle_left;
|
||
case Orientation.Right: return idle_right;
|
||
}
|
||
|
||
break;
|
||
|
||
case EntityState.Walking:
|
||
switch (orientation)
|
||
{
|
||
case Orientation.Down: return walk_down;
|
||
case Orientation.Up: return walk_up;
|
||
case Orientation.Left: return walk_left;
|
||
case Orientation.Right: return walk_right;
|
||
}
|
||
|
||
break;
|
||
|
||
case EntityState.MeleeAttack:
|
||
switch (orientation)
|
||
{
|
||
case Orientation.Down: return meleeAttack_down;
|
||
case Orientation.Up: return meleeAttack_up;
|
||
case Orientation.Left: return meleeAttack_left;
|
||
case Orientation.Right: return meleeAttack_right;
|
||
}
|
||
|
||
break;
|
||
|
||
case EntityState.RangedAttack:
|
||
switch (orientation)
|
||
{
|
||
case Orientation.Down: return rangedAttack_down;
|
||
case Orientation.Up: return rangedAttack_up;
|
||
case Orientation.Left: return rangedAttack_left;
|
||
case Orientation.Right: return rangedAttack_right;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
return null;
|
||
}
|
||
}
|
||
|
||
public class DrawNodeDef : Define
|
||
{
|
||
public List<string> textures = new();
|
||
public List<DrawNodeDef> nodes = new();
|
||
public string nodeName;
|
||
public Vector2 position = new(0, 0);
|
||
public float FPS = 0.5f;
|
||
|
||
public override bool Init(XElement xmlDef)
|
||
{
|
||
base.Init(xmlDef);
|
||
|
||
nodeName = xmlDef.Attribute("name")?.Value ?? "noName";
|
||
position = StringToVector(xmlDef.Attribute("position")?.Value ?? "(0 0)");
|
||
FPS = float.TryParse(xmlDef.Attribute("FPS")?.Value, out var result) ? result : 1.0f;
|
||
return false;
|
||
}
|
||
public Vector2 StringToVector(string vectorDef)
|
||
{
|
||
// 去掉可能存在的括号和多余的空格
|
||
var cleanedInput = vectorDef.Replace("(", "").Replace(")", "").Trim();
|
||
|
||
// 使用正则表达式匹配两个浮点数
|
||
var match = Regex.Match(cleanedInput, @"\s*(-?\d+(\.\d*)?)\s*[, ]\s*(-?\d+(\.\d*)?)\s*");
|
||
|
||
if (match.Success)
|
||
{
|
||
// 提取匹配到的两个浮点数
|
||
var x = float.Parse(match.Groups[1].Value);
|
||
var y = float.Parse(match.Groups[3].Value);
|
||
|
||
// 返回 Vector2 对象
|
||
return new Vector2(x, y);
|
||
}
|
||
|
||
return Vector2.zero;
|
||
}
|
||
/// <summary>
|
||
/// 计算动画执行一个周期的总时间(包括子对象)。
|
||
/// 如果自身没有纹理,自身动画时间为0。
|
||
/// 总周期取自身动画时间和所有子动画周期中的最大值。
|
||
/// </summary>
|
||
/// <returns>动画执行一个周期的总时间(秒)。</returns>
|
||
public float GetAnimationCycleDuration()
|
||
{
|
||
if (FPS < 0.01)
|
||
{
|
||
return 1;
|
||
}
|
||
float ownDuration = 0f;
|
||
// 计算当前节点自身的动画周期时间
|
||
// 由于 Init 方法已经处理了 FPS 的校验,FPS 保证大于 0
|
||
if (textures.Count > 0)
|
||
{
|
||
ownDuration = textures.Count / FPS;
|
||
}
|
||
// 递归计算所有子节点的动画周期,并取其中最长的
|
||
float maxChildDuration = 0f;
|
||
foreach (var childNode in nodes)
|
||
{
|
||
float childDuration = childNode.GetAnimationCycleDuration();
|
||
maxChildDuration = Math.Max(maxChildDuration, childDuration);
|
||
}
|
||
// 整个 DrawNodeDef 的动画周期是自身动画周期和所有子动画周期中的最大值
|
||
return Math.Max(ownDuration, maxChildDuration);
|
||
}
|
||
}
|
||
|
||
} |