(client) feat:添加基地界面到游玩界面的过程,添加存档管理,技能树变得可用 (#58)

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/58
This commit is contained in:
2025-10-03 00:31:34 +08:00
parent aff747be17
commit dd9d90439d
134 changed files with 10322 additions and 4872 deletions

View File

@@ -0,0 +1,64 @@
using System;
using AI;
using Data;
namespace Utils
{
public static class BehaviorTreeUtils
{
/// <summary>
/// 将行为树定义转换为 AIBase 类型。
/// </summary>
/// <param name="behaviorTreeDef">行为树定义。</param>
/// <returns>转换后的 AIBase 实例。</returns>
public static BehaviorTreeBase ConvertToAIBase(BehaviorTreeDef behaviorTreeDef)
{
if (behaviorTreeDef == null)
return null;
var aiBase = CreateAIBaseInstance(behaviorTreeDef.className);
return aiBase;
}
/// <summary>
/// 使用反射根据类名创建 AIBase 的具体子类实例。
/// </summary>
/// <param name="className">类名。</param>
/// <returns>创建的 AIBase 子类实例。</returns>
private static BehaviorTreeBase CreateAIBaseInstance(string className)
{
if (string.IsNullOrEmpty(className))
throw new ArgumentException("className 不能为空");
if (className.Equals("AIBase", StringComparison.OrdinalIgnoreCase))
{
return (BehaviorTreeBase)Activator.CreateInstance(typeof(BehaviorTreeBase));
}
// 定义可能的命名空间列表
var possibleNamespaces = new[] { "AI" };
foreach (var ns in possibleNamespaces)
{
try
{
// 获取当前程序集
var assembly = typeof(BehaviorTreeBase).Assembly;
// 尝试查找类型
var type = assembly.GetType($"{ns}.{className}");
if (type != null && typeof(BehaviorTreeBase).IsAssignableFrom(type))
{
// 如果找到合适的类型,则创建实例并返回
return (BehaviorTreeBase)Activator.CreateInstance(type);
}
}
catch
{
// 忽略单个命名空间的错误,继续尝试下一个命名空间
}
}
// 如果所有命名空间都未找到对应的类型,抛出异常
throw new InvalidOperationException($"无法找到类型 {className} 或该类型不是 AIBase 的子类");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6b711456a12f4bf6a29b0de14a2d7d8f
timeCreated: 1754982046

View File

@@ -6,16 +6,17 @@ namespace Utils
{
// 旋转对象到指定方向
public static void RotateTransformToDirection(Transform transform, Vector3 targetDirection)
public static float RotateTransformToDirection(Transform transform, Vector3 targetDirection)
{
// 确保目标方向不是零向量
if (targetDirection == Vector3.zero)
return;
return 0;
// 计算当前向上方向与目标方向之间的角度
var angle = Mathf.Atan2(targetDirection.y, targetDirection.x) * Mathf.Rad2Deg;
// 应用旋转
transform.rotation = Quaternion.Euler(0f, 0f, angle);
return angle;
}
}
}

View File

@@ -9,7 +9,6 @@ namespace Utils
{
public static class StringUtils
{
// 以下是你原始代码中未修改的部分,为了完整性列出
/// <summary>
/// 尝试从字符串中解析出指定数量的浮点数组件。
/// 这是StringToVector2和StringToVector3的核心辅助方法。
@@ -28,15 +27,12 @@ namespace Utils
parsedComponents = new float[expectedComponentCount];
if (string.IsNullOrWhiteSpace(source))
{
// 如果字符串为空或空白,则用默认值填充并失败
for (var i = 0; i < expectedComponentCount; i++)
{
parsedComponents[i] = defaultComponentValue;
}
return false;
}
// 移除所有可能存在的括号或方括号
// 采用Replace而不是Regex是因为简单字符替换的性能优势
var cleanedSource = source
@@ -52,30 +48,24 @@ namespace Utils
System.StringSplitOptions.RemoveEmptyEntries);
if (componentStrings.Length < expectedComponentCount)
{
// 如果组件数量不足,则用默认值填充并失败
for (var i = 0; i < expectedComponentCount; i++)
{
parsedComponents[i] = defaultComponentValue;
}
return false;
}
for (var i = 0; i < expectedComponentCount; i++)
{
// 尝试解析每个组件,并去除前后空格
if (!float.TryParse(componentStrings[i].Trim(), out parsedComponents[i]))
{
// 如果任何一个组件解析失败,则用默认值填充所有组件并返回 false
for (var j = 0; j < expectedComponentCount; j++)
{
parsedComponents[j] = defaultComponentValue;
}
return false;
}
}
return true; // 所有组件都成功解析
}
@@ -92,13 +82,11 @@ namespace Utils
{
defaultValue = Vector2.zero; // 如果未指定,则使用 Vector2.zero 作为默认值
}
// 默认浮点值为0f用于内部解析失败填充
if (TryParseVectorComponents(str, 2, out var components))
{
return new Vector2(components[0], components[1]);
}
Debug.LogWarning($"将字符串 \"{str}\" 解析为 Vector2 失败。返回默认值 {defaultValue}。");
return defaultValue;
}
@@ -115,18 +103,14 @@ namespace Utils
{
defaultValue = Vector3.zero; // 如果未指定,则使用 Vector3.zero 作为默认值
}
// 默认浮点值为0f用于内部解析失败填充
if (TryParseVectorComponents(str, 3, out var components))
{
return new Vector3(components[0], components[1], components[2]);
}
Debug.LogWarning($"将字符串 \"{str}\" 解析为 Vector3 失败。返回默认值 {defaultValue}。");
return defaultValue;
}
// 原始代码结束
// #region 新增代码
/// <summary>
/// 尝试从字符串中解析出指定数量的整数组件。
/// 这是StringToVector3Int的核心辅助方法。
@@ -145,15 +129,12 @@ namespace Utils
parsedComponents = new int[expectedComponentCount];
if (string.IsNullOrWhiteSpace(source))
{
// 如果字符串为空或空白,则用默认值填充并失败
for (var i = 0; i < expectedComponentCount; i++)
{
parsedComponents[i] = defaultComponentValue;
}
return false;
}
// 移除所有可能存在的括号或方括号
var cleanedSource = source
.Replace("[", "")
@@ -167,30 +148,24 @@ namespace Utils
System.StringSplitOptions.RemoveEmptyEntries);
if (componentStrings.Length < expectedComponentCount)
{
// 如果组件数量不足,则用默认值填充并失败
for (var i = 0; i < expectedComponentCount; i++)
{
parsedComponents[i] = defaultComponentValue;
}
return false;
}
for (var i = 0; i < expectedComponentCount; i++)
{
// 尝试解析每个组件,并去除前后空格
if (!int.TryParse(componentStrings[i].Trim(), out parsedComponents[i]))
{
// 如果任何一个组件解析失败,则用默认值填充所有组件并返回 false
for (var j = 0; j < expectedComponentCount; j++)
{
parsedComponents[j] = defaultComponentValue;
}
return false;
}
}
return true; // 所有组件都成功解析
}
@@ -203,22 +178,19 @@ namespace Utils
/// <returns>转换后的 Vector3Int 值或默认值。</returns>
public static Vector3Int StringToVector3Int(string str, Vector3Int defaultValue = default)
{
// C# 7.1 以上版本允许 'default' 用于值类型,对于引用类型为 null对于值类型为所有成员的默认值例如0
// 所以 Vector3Int.zero 可以作为显式默认值。
if (defaultValue == default)
{
defaultValue = Vector3Int.zero; // 如果未指定,则使用 Vector3Int.zero 作为默认值
}
// 默认整数值为0用于内部解析失败填充
if (TryParseVectorIntComponents(str, 3, out var components))
{
return new Vector3Int(components[0], components[1], components[2]);
}
Debug.LogWarning($"将字符串 \"{str}\" 解析为 Vector3Int 失败。返回默认值 {defaultValue}。");
return defaultValue;
}
/// <summary>
/// 根据类名字符串创建并返回一个继承自 MapGeneratorWorkClassBase 的实例。
/// </summary>