mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 11:27:14 +08:00
(client) feat:实现技能树界面,实现地图生成器,实现维度指定,实现规则瓦片定义,实现逃跑逻辑,实现消息定义,实现武器动画,实现受击动画 fix: 修复单攻击子弹击中多个目标,修复人物属性计算错误 (#56)
Co-authored-by: m0_75251201 <m0_75251201@noreply.gitcode.com> Reviewed-on: http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite/pulls/56
This commit is contained in:
213
Client/Assets/Scripts/Utils/StringUtils.cs
Normal file
213
Client/Assets/Scripts/Utils/StringUtils.cs
Normal file
@@ -0,0 +1,213 @@
|
||||
// 文件: Assets/Scripts/Utils/StringUtils.cs (或你的实际路径)
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Map;
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
using UnityEngine; // 引入Unity命名空间,以便使用Vector2和Vector3
|
||||
|
||||
public static class StringUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 尝试从字符串中解析出指定数量的浮点数组件。
|
||||
/// 这是StringToVector2和StringToVector3的核心辅助方法。
|
||||
/// </summary>
|
||||
/// <param name="source">要解析的源字符串。</param>
|
||||
/// <param name="expectedComponentCount">期望的组件数量(例如,Vector2为2,Vector3为3)。</param>
|
||||
/// <param name="parsedComponents">如果解析成功,包含解析出的浮点数;否则为 null。</param>
|
||||
/// <param name="defaultComponentValue">在解析失败时,用于填充parsedComponents数组的默认值。</param>
|
||||
/// <returns>如果所有组件都成功解析,则返回 true;否则返回 false。</returns>
|
||||
private static bool TryParseVectorComponents(
|
||||
string source,
|
||||
int expectedComponentCount,
|
||||
out float[] parsedComponents,
|
||||
float defaultComponentValue = 0f)
|
||||
{
|
||||
parsedComponents = new float[expectedComponentCount];
|
||||
|
||||
if (string.IsNullOrWhiteSpace(source))
|
||||
{
|
||||
// 如果字符串为空或空白,则用默认值填充并失败
|
||||
for (int i = 0; i < expectedComponentCount; i++)
|
||||
{
|
||||
parsedComponents[i] = defaultComponentValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// 移除所有可能存在的括号或方括号
|
||||
// 采用Replace而不是Regex是因为简单字符替换的性能优势
|
||||
string cleanedSource = source
|
||||
.Replace("[", "")
|
||||
.Replace("]", "")
|
||||
.Replace("(", "")
|
||||
.Replace(")", "");
|
||||
|
||||
// 定义所有可能的分隔符:逗号、空格、分号
|
||||
// 使用 StringSplitOptions.RemoveEmptyEntries 确保即使有多个分隔符连续出现,也不会创建空字符串项
|
||||
char[] delimiters = { ',', ' ', ';' };
|
||||
string[] componentStrings = cleanedSource.Split(
|
||||
delimiters,
|
||||
System.StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (componentStrings.Length < expectedComponentCount)
|
||||
{
|
||||
// 如果组件数量不足,则用默认值填充并失败
|
||||
for (int i = 0; i < expectedComponentCount; i++)
|
||||
{
|
||||
parsedComponents[i] = defaultComponentValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < expectedComponentCount; i++)
|
||||
{
|
||||
// 尝试解析每个组件,并去除前后空格
|
||||
if (!float.TryParse(componentStrings[i].Trim(), out parsedComponents[i]))
|
||||
{
|
||||
// 如果任何一个组件解析失败,则用默认值填充所有组件并返回 false
|
||||
for (int j = 0; j < expectedComponentCount; j++)
|
||||
{
|
||||
parsedComponents[j] = defaultComponentValue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true; // 所有组件都成功解析
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串转换为 Unity 的 Vector2。
|
||||
/// 支持格式如 "1,2", "[1 2]", "(1;2)",并能处理空格。
|
||||
/// </summary>
|
||||
/// <param name="str">要转换的字符串。</param>
|
||||
/// <param name="defaultValue">转换失败时返回的默认 Vector2 值。</param>
|
||||
/// <returns>转换后的 Vector2 值或默认值。</returns>
|
||||
public static Vector2 StringToVector2(string str, Vector2 defaultValue = default)
|
||||
{
|
||||
if (defaultValue == default)
|
||||
{
|
||||
defaultValue = Vector2.zero; // 如果未指定,则使用 Vector2.zero 作为默认值
|
||||
}
|
||||
|
||||
// 默认浮点值为0f,用于内部解析失败填充
|
||||
if (TryParseVectorComponents(str, 2, out var components))
|
||||
{
|
||||
return new Vector2(components[0], components[1]);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串转换为 Unity 的 Vector3。
|
||||
/// 支持格式如 "1,2,3", "[1 2 3]", "(1;2;3)",并能处理空格。
|
||||
/// </summary>
|
||||
/// <param name="str">要转换的字符串。</param>
|
||||
/// <param name="defaultValue">转换失败时返回的默认 Vector3 值。</param>
|
||||
/// <returns>转换后的 Vector3 值或默认值。</returns>
|
||||
public static Vector3 StringToVector3(string str, Vector3 defaultValue = default)
|
||||
{
|
||||
if (defaultValue == default)
|
||||
{
|
||||
defaultValue = Vector3.zero; // 如果未指定,则使用 Vector3.zero 作为默认值
|
||||
}
|
||||
|
||||
// 默认浮点值为0f,用于内部解析失败填充
|
||||
if (TryParseVectorComponents(str, 3, out var components))
|
||||
{
|
||||
return new Vector3(components[0], components[1], components[2]);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类名字符串创建并返回一个继承自 MapGeneratorWorkClassBase 的实例。
|
||||
/// </summary>
|
||||
/// <param name="className">不含命名空间的类名。</param>
|
||||
/// <returns>对应的 MapGeneratorWorkClassBase 实例,如果找不到或类型不匹配则返回 null。</returns>
|
||||
public static MapGeneratorWorkClassBase CreateMapGeneratorInstance(string className)
|
||||
{
|
||||
// 定义优先查找的命名空间
|
||||
const string preferredNamespace = "Map";
|
||||
Type foundType = null;
|
||||
// 第一遍:优先在指定的命名空间下查找
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
string fullClassNameInPreferredNamespace = preferredNamespace + "." + className;
|
||||
Type type = assembly.GetType(fullClassNameInPreferredNamespace);
|
||||
if (type != null)
|
||||
{
|
||||
foundType = type;
|
||||
break; // 找到后立即退出循环,因为我们找到了优先的类型
|
||||
}
|
||||
}
|
||||
|
||||
// 如果在优先命名空间中没有找到,则进行第二遍:在所有命名空间中查找(不指定命名空间,即只用className)
|
||||
if (foundType == null)
|
||||
{
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
Type type = assembly.GetType(className);
|
||||
if (type != null)
|
||||
{
|
||||
// 如果找到多个同名的类型(在不同命名空间,但都不在preferredNamespace),
|
||||
// 这里的处理是取第一个找到的。如果需要更复杂的选择逻辑,例如按顺序优先级,
|
||||
// 则需要收集所有可能类型并进行筛选。
|
||||
// 但对于通常的应用,如果className本身是不带命名空间的,这通常意味着它在全局作用域
|
||||
// 或者我们期望它在一个特定的命名空间(如preferredNamespace)中。
|
||||
foundType = type;
|
||||
// 注意:这里没有break;的话,如果不同程序集有同名类会继续查找并覆盖foundType。
|
||||
// 如果你期望找到第一个就返回,可以加上break;
|
||||
// 如果你期望找到一个满足条件的就返回,那么把后续的检查放到这里。
|
||||
// 为了简化,我们只在这里找到一个可用的Type,然后统一在后面检查。
|
||||
break; // 找到了一个,先用这个。
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对找到的类型进行有效性检查和实例创建
|
||||
if (foundType != null)
|
||||
{
|
||||
// 检查类型是否:
|
||||
// 1. 是 MapGeneratorWorkClassBase 的子类或本身就是 MapGeneratorWorkClassBase
|
||||
// 2. 不是抽象类
|
||||
// 3. 有一个无参构造函数
|
||||
if (typeof(MapGeneratorWorkClassBase).IsAssignableFrom(foundType) &&
|
||||
!foundType.IsAbstract &&
|
||||
foundType.GetConstructor(Type.EmptyTypes) != null) // 检查是否有无参构造函数
|
||||
{
|
||||
try
|
||||
{
|
||||
// 使用 Activator.CreateInstance 创建实例
|
||||
return (MapGeneratorWorkClassBase)Activator.CreateInstance(foundType);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error creating instance of {foundType.FullName}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
string foundTypeName = foundType.FullName;
|
||||
Debug.LogError($"Type '{foundTypeName}' found but does not meet criteria:" +
|
||||
$" IsAssignableFrom({nameof(MapGeneratorWorkClassBase)}): {typeof(MapGeneratorWorkClassBase).IsAssignableFrom(foundType)}," +
|
||||
$" IsAbstract: {foundType.IsAbstract}," +
|
||||
$" HasParameterlessConstructor: {foundType.GetConstructor(Type.EmptyTypes) != null}");
|
||||
return null; // 类型不满足条件
|
||||
}
|
||||
|
||||
Debug.LogError(
|
||||
$"Type '{className}' not found in preferred namespace '{preferredNamespace}' or any loaded assembly.");
|
||||
return null; // 类型未找到
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user