mirror of
http://47.107.252.169:3000/Roguelite-Game-Developing-Team/Gen_Hack-and-Slash-Roguelite.git
synced 2025-11-20 01:07: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:
54
Client/Assets/Scripts/Test/RuleTileFieldInspector.cs
Normal file
54
Client/Assets/Scripts/Test/RuleTileFieldInspector.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
// 确保你已经通过 Package Manager 或手动导入了 Newtonsoft.Json
|
||||
using Newtonsoft.Json;
|
||||
using UnityEngine.Tilemaps;
|
||||
|
||||
public class RuleTileInspector : MonoBehaviour
|
||||
{
|
||||
[Tooltip("将一个 RuleTile 资产拖拽到这里以检查其内容。")]
|
||||
public RuleTile ruleTile;
|
||||
|
||||
// 使用 ContextMenu 属性在 Inspector 组件右键菜单或齿轮菜单中添加按钮
|
||||
[ContextMenu("打印 RuleTile 内容 (JSON)")]
|
||||
private void PrintRuleTileContentAsJson()
|
||||
{
|
||||
if (ruleTile == null)
|
||||
{
|
||||
Debug.LogWarning("RuleTile 字段未分配,请拖拽一个 RuleTile 资产。", this);
|
||||
return;
|
||||
}
|
||||
// 创建序列化设置
|
||||
var settings = new JsonSerializerSettings
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore, // 忽略 null 值属性,防止 UnassignedReferenceException
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore, // 忽略循环引用(作为安全网)
|
||||
Formatting = Formatting.Indented, // 格式化输出,可读性更好
|
||||
// 应用自定义的 ContractResolver 来处理 Unity 值类型属性
|
||||
ContractResolver = new UnityPropertyContractResolver(),
|
||||
|
||||
// 应用自定义的 Converter 来处理 UnityEngine.Object 引用类型
|
||||
Converters = { new UnityObjectConverter() }
|
||||
};
|
||||
try
|
||||
{
|
||||
// 序列化 ruleTile.m_TilingRules 列表
|
||||
string json = JsonConvert.SerializeObject(ruleTile.m_TilingRules, settings);
|
||||
Debug.Log("Serialized RuleTile Tiling Rules:\n" + json);
|
||||
}
|
||||
catch (JsonSerializationException ex)
|
||||
{
|
||||
Debug.LogError($"JSON Serialization Error: {ex.Message}\nPath: {ex.Path}\nStackTrace: {ex.StackTrace}");
|
||||
if (ex.InnerException != null)
|
||||
{
|
||||
Debug.LogError(
|
||||
$"Inner Exception: {ex.InnerException.Message}\nInner StackTrace: {ex.InnerException.StackTrace}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Unexpected Error during serialization: {ex.Message}\nStackTrace: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e12bac1ea0ab44e79a24cdb1101397aa
|
||||
timeCreated: 1758009087
|
||||
33
Client/Assets/Scripts/Test/TempAnimationTest.cs
Normal file
33
Client/Assets/Scripts/Test/TempAnimationTest.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using Data;
|
||||
using Managers;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class TempAnimationTest:MonoBehaviour
|
||||
{
|
||||
|
||||
public string testMessage="测试消息";
|
||||
public PromptDisplayCategory displayCategory;
|
||||
public Color messsageColor = Color.white;
|
||||
private void Start()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
TemporaryAnimationManager.Instance.GenerateTemporaryAnimation("1,2,3,4,5,1,2,3,4,5",
|
||||
Camera.main.ScreenToWorldPoint(Input.mousePosition));
|
||||
}
|
||||
|
||||
if (Input.GetMouseButtonDown(1))
|
||||
{
|
||||
MessageManager.Instance.DisplayMessage(testMessage,displayCategory,messsageColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Client/Assets/Scripts/Test/TempAnimationTest.cs.meta
Normal file
3
Client/Assets/Scripts/Test/TempAnimationTest.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7a743a0b262a45d8bb1203e776cbbe00
|
||||
timeCreated: 1757066158
|
||||
100
Client/Assets/Scripts/Test/TestInit.cs
Normal file
100
Client/Assets/Scripts/Test/TestInit.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Managers;
|
||||
using UI;
|
||||
using UnityEngine;
|
||||
using Map;
|
||||
|
||||
namespace Test
|
||||
{
|
||||
public class TestInit : MonoBehaviour
|
||||
{
|
||||
public SkillTreeUI skillTreePageUI;
|
||||
|
||||
private Task _currentMapGenerationTask = Task.CompletedTask; // 初始化为已完成任务
|
||||
private readonly object _mapGenerationLock = new object();
|
||||
public MapGenerator mapGenerator;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Reload();
|
||||
mapGenerator = Program.Instance.FocusedDimension?.mapGenerator;
|
||||
}
|
||||
|
||||
public async void Reload() // Make Reload async to await the previous task
|
||||
{
|
||||
// 在清理之前,等待任何正在进行的地图生成任务完成
|
||||
// 注意:这会使 Reload() 异步,并且在等待期间 UI 可能会被阻塞,
|
||||
// 或者如果你不await,后续代码可能在任务完成前执行。
|
||||
Task oldMapTask;
|
||||
lock (_mapGenerationLock)
|
||||
{
|
||||
oldMapTask = _currentMapGenerationTask;
|
||||
}
|
||||
|
||||
if (!oldMapTask.IsCompleted)
|
||||
{
|
||||
Debug.Log("TestInit: 等待之前的地图生成任务完成,然后再进行清理和新生成...");
|
||||
try
|
||||
{
|
||||
await oldMapTask; // 等待之前的任务完成
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"TestInit: 之前的地图生成任务在等待时失败: {ex.Message}");
|
||||
// 可以选择忽略或者重新抛出
|
||||
}
|
||||
}
|
||||
|
||||
mapGenerator?.Clear();
|
||||
|
||||
// --- 清理和初始化阶段 ---
|
||||
DefineManager.Instance.Clear();
|
||||
PackagesImageManager.Instance.Clear();
|
||||
TileManager.Instance.Clear();
|
||||
|
||||
DefineManager.Instance.Init();
|
||||
PackagesImageManager.Instance.Init();
|
||||
TileManager.Instance.Init();
|
||||
// --- 清理和初始化阶段结束 ---
|
||||
|
||||
// 启动新的地图生成任务
|
||||
StartNewMapGeneration(); // 这是一个非 async 的包装方法
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动新的地图生成任务,并更新 _currentMapGenerationTask。
|
||||
/// </summary>
|
||||
private void StartNewMapGeneration()
|
||||
{
|
||||
lock (_mapGenerationLock)
|
||||
{
|
||||
_currentMapGenerationTask = InitializeMapGenerationAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步初始化并应用所有地图生成器。
|
||||
/// </summary>
|
||||
private async Task InitializeMapGenerationAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 异步应用地图生成器,并且按顺序等待它们完成
|
||||
await TileManager.Instance.ApplyMapGenerator("DefaultMapGenerator");
|
||||
// await TileManager.Instance.ApplyMapGenerator("WaterMapGenerator");
|
||||
await TileManager.Instance.ApplyMapGenerator("TreeGenerator");
|
||||
|
||||
Debug.Log("TestInit: 所有地图生成器已成功应用并完成。");
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
Debug.LogWarning("TestInit: 地图生成任务被取消。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"TestInit: 在地图生成过程中发生未处理的错误: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Client/Assets/Scripts/Test/TestInit.cs.meta
Normal file
3
Client/Assets/Scripts/Test/TestInit.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d9f93d1858fb4a46b362523ed93ddd4e
|
||||
timeCreated: 1757851653
|
||||
116
Client/Assets/Scripts/Test/jsonRule.cs
Normal file
116
Client/Assets/Scripts/Test/jsonRule.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
// 确保你已经通过 Package Manager 或手动导入了 Newtonsoft.Json
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
using Object = UnityEngine.Object; // 用于自定义序列化,可选但推荐
|
||||
|
||||
public class UnityObjectConverter : JsonConverter<Object>
|
||||
{
|
||||
public override void WriteJson(JsonWriter writer, Object value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 对于 Unity 对象,最简单的序列化方式是它们的名称。
|
||||
// 也可以选择序列化 InstanceID 或 Asset Path (在编辑器环境下)。
|
||||
writer.WriteValue(value.name);
|
||||
// 如果你需要 Asset Path (仅在 Unity 编辑器中可用)
|
||||
// #if UNITY_EDITOR
|
||||
// string path = UnityEditor.AssetDatabase.GetAssetPath(value);
|
||||
// writer.WriteValue(path);
|
||||
// #else
|
||||
// writer.WriteValue(value.name);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
public override Object ReadJson(JsonReader reader, Type objectType, Object existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.Null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
string objectIdentifier = reader.Value?.ToString();
|
||||
if (string.IsNullOrEmpty(objectIdentifier))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// 示例反序列化:尝试从 Resources 文件夹加载同名对象
|
||||
// 这需要你的对象位于 "Resources/" 路径下
|
||||
// 并且 objectType 必须是正确的目标类型 (如 Sprite, GameObject等)
|
||||
return Resources.Load(objectIdentifier, objectType);
|
||||
// 如果你的对象不是通过 Resources 加载的,你可能需要自定义查找逻辑
|
||||
// 例如:
|
||||
// if (objectType == typeof(Sprite))
|
||||
// {
|
||||
// // 假设你有一个方法可以根据名称找到 Sprite Asset
|
||||
// return MyAssetManager.LoadSpriteByName(objectIdentifier);
|
||||
// }
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
public class UnityPropertyContractResolver : DefaultContractResolver
|
||||
{
|
||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||
{
|
||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||
// --- 1. 处理 Unity 数学结构体及其循环引用问题 ---
|
||||
// Vector3, Vector2, Bounds, Rect 都是 struct,但它们的某些属性返回新实例,导致 JsonConvert 误判循环。
|
||||
if (member.DeclaringType == typeof(Vector3) || member.DeclaringType == typeof(Vector2))
|
||||
{
|
||||
// 明确忽略 normalized, magnitude, sqrMagnitude 等计算属性
|
||||
if (member.Name == "normalized" || member.Name == "magnitude" || member.Name == "sqrMagnitude")
|
||||
{
|
||||
property.ShouldSerialize = instance => false;
|
||||
}
|
||||
}
|
||||
else if (member.DeclaringType == typeof(Bounds))
|
||||
{
|
||||
// Bounds 结构体在内部引用 Vector3,也可能有一些我们不希望序列化的计算属性
|
||||
// 例如,min/max 可以通过 center 和 extents 计算得出,通常不需单独序列化
|
||||
if (member.Name == "min" || member.Name == "max")
|
||||
{
|
||||
property.ShouldSerialize = instance => false;
|
||||
}
|
||||
}
|
||||
else if (member.DeclaringType == typeof(Rect))
|
||||
{
|
||||
// Rect 也有一些计算属性,如果不需要可以忽略
|
||||
// 例如,xMax, yMax, center, position, size
|
||||
if (member.Name.EndsWith("Max") || member.Name == "center" || member.Name == "position" || member.Name == "size")
|
||||
{
|
||||
property.ShouldSerialize = instance => false;
|
||||
}
|
||||
}
|
||||
// --- 2. 处理 UnityEditor 命名空间中的类型 (在运行时通常不序列化) ---
|
||||
// 这些通常是编辑器专属数据,不应包含在运行时序列化中。
|
||||
if (property.DeclaringType != null && property.DeclaringType.Namespace != null && property.DeclaringType.Namespace.StartsWith("UnityEditor"))
|
||||
{
|
||||
property.ShouldSerialize = instance => false;
|
||||
}
|
||||
// --- 3. 处理 Unity.Mathematics 命名空间中的类型 (如果有使用 DOTS/Job System) ---
|
||||
// 尽管 Unity.Mathematics 类型通常没有循环引用,但如果你只想序列化原始字段,可以这样做。
|
||||
// if (property.DeclaringType != null && property.DeclaringType.Namespace != null && property.DeclaringType.Namespace.StartsWith("Unity.Mathematics"))
|
||||
// { /* ... */ }
|
||||
// --- 4. 确保 Unity Object 引用由专门的 Converter 处理,而不是默认序列化器 ---
|
||||
// 虽然我们已经提供了 UnityObjectConverter,但这一步可以作为补充。
|
||||
// 如果某个属性的类型是 UnityEngine.Object 或其派生,但又没有显式地被 Converter 捕获
|
||||
// 我们可以禁用其默认序列化,以避免尝试序列化内部数据。
|
||||
// 但由于我们注册了 UnityObjectConverter,它应该会优先处理。
|
||||
// 这一部分通常是多余的,除非你的 Converter 只能处理特定的 Unity Object 类型 (例如只处理 Sprite)。
|
||||
/*
|
||||
if (typeof(Object).IsAssignableFrom(property.PropertyType) && !property.PropertyType.IsPrimitive && !property.PropertyType.IsEnum)
|
||||
{
|
||||
// 除非你有一个针对特定 Unity Object 类型的自定义转换器,否则通常不应该直接序列化它们。
|
||||
// 这里我们假设 UnityObjectConverter 会处理所有 Object 类型。
|
||||
// 如果你有特定的 Object 类型需要排除,可以在这里添加逻辑。
|
||||
}
|
||||
*/
|
||||
return property;
|
||||
}
|
||||
}
|
||||
3
Client/Assets/Scripts/Test/jsonRule.cs.meta
Normal file
3
Client/Assets/Scripts/Test/jsonRule.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9293ba2273e64c27af650873933707c8
|
||||
timeCreated: 1758011099
|
||||
Reference in New Issue
Block a user