yyl
2025-05-07 f43e38880a7255d07cccfc313146c3f397fcf505
UI配置等
4个文件已修改
55个文件已添加
3333 ■■■■■ 已修改文件
Main/Battle.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character/CharacterBase.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character/CharacterBase.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character/Motion.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character/Motion/MotionBase.cs 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Character/Motion/MotionBase.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigBase.cs 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigBase.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigParse.cs 305 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigParse.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.asmdef 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/ConfigManager.cs 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/ConfigManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/BattleManager.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/BattleManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/PlaceManager.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/PlaceManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/TeamManager.cs 400 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/GameSystemManager/TeamManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/IManager.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/IManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/LoginManager.cs 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/LoginManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/ManagerBase.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/ManagerBase.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/StageManager.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Manager/StageManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceDataStructs.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceDataStructs.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceDrop.cs 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceDrop.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceField.cs 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceField.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceUnit.cs 516 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Place/PlaceUnit.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/LoadingWin.cs 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/LoadingWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Login.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Login/LoginWin.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Login/LoginWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Main.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Main/MainWin.cs 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Main/MainWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Place.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Place/PlaceWin.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/Place/PlaceWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/UIBase.cs 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/UI/UIManager.cs 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Utility/DontDestroyOnLoad.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Utility/DontDestroyOnLoad.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Battle.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f97380ec7fe308f4fb5bb6658c9b2604
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Character.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 236e2b98d9a024546a3d5b0ee59c257e
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Character/CharacterBase.cs
New file
@@ -0,0 +1,9 @@
public class CharacterBase
{
}
Main/Character/CharacterBase.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 446cfdee2e270e2459fa4dbb2c33b152
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Character/Motion.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 613ebae9f200e9d43b75a053fd050971
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Character/Motion/MotionBase.cs
New file
@@ -0,0 +1,58 @@
using Spine.Unity;
using Spine;
using System;
using System.Collections.Generic;
using UnityEngine;
//  动作控制
public class MotionBase
{
    protected SkeletonAnimation skeletonAnimation;
    public virtual void Init(SkeletonAnimation _skeletonAnimation)
    {
        skeletonAnimation = _skeletonAnimation;
    }
    public virtual void Play(string _motionName, bool _isLoop = false)
    {
        skeletonAnimation.AnimationState.SetAnimation(0, _motionName, _isLoop);
    }
    public virtual void Pause()
    {
        skeletonAnimation.AnimationState.TimeScale = 0;
    }
    public virtual void Resume()
    {
        skeletonAnimation.AnimationState.TimeScale = 1;
    }
    public virtual void MixBlend(string _motionName, float _duration)
    {
        // 获取当前轨道上的动画
        TrackEntry currentTrack = skeletonAnimation.AnimationState.GetCurrent(0);
        if (currentTrack != null)
        {
            // 添加一个新的动画到轨道0,并设置混合时间
            TrackEntry newTrack = skeletonAnimation.AnimationState.SetAnimation(0, _motionName, true);
            // 设置混合持续时间
            newTrack.MixDuration = _duration;
            // 可选:设置混合类型
            // newTrack.MixBlend = MixBlend.Replace; // 默认值
            // 可选:如果需要在混合完成后执行某些操作
            // newTrack.Complete += OnMixComplete;
        }
        else
        {
            // 如果当前没有动画在播放,直接播放新动画
            skeletonAnimation.AnimationState.SetAnimation(0, _motionName, true);
        }
    }
}
Main/Character/Motion/MotionBase.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7d0ffbc3831c9564481db79412785ff9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: da9ffef894f7c304c8f0de2f85657112
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/ConfigBase.cs
New file
@@ -0,0 +1,141 @@
using System.Collections.Generic;
using UnityEngine;
public class ConfigBase<U, T> where T : ConfigBase<U, T>, new()
{
    public static Dictionary<U, T> dic = new Dictionary<U, T>();
    protected static bool isInit = false;
    public static T Get(U id)
    {
        if (!isInit)
        {
            Debug.LogError("ConfigBase 没有初始化");
            return null; // 或者抛出异常,视情况而定
        }
        if (dic.ContainsKey(id))
        {
            return dic[id];
        }
        return null;
    }
    public List<T> GetValues()
    {
        if (!isInit)
        {
            Debug.LogError(typeof(T).Name + " 没有初始化 GetValues");
            return null; // 或者抛出异常,视情况而定
        }
        List<T> result = new List<T>();
        result.AddRange(dic.Values);
        return result;
    }
    public static bool HasKey(U key)
    {
        if (!isInit)
        {
            Debug.LogError(typeof(T).Name + " 没有初始化 HasKey");
            return false; // 或者抛出异常,视情况而定
        }
        return dic.ContainsKey(key);
    }
    public static void Init(string[] lines)
    {
        Dictionary<string, string> rawDatas = new Dictionary<string, string>();
        for (int i = 3; i < lines.Length; i++)
        {
            string line = lines[i];
            var index = line.IndexOf("\t");
            if (index == -1)
            {
                continue;
            }
            string strKey = line.Substring(0, index);
            T config = new T();
            U key = config.LoadKey(strKey);
            config.LoadConfig(line);
            dic.Add(key, config);
        }
        isInit = true;
    }
    public virtual U LoadKey(string line)
    {
        return default(U);
    }
    public virtual void LoadConfig(string line)
    {
    }
    protected int ParseInt(string str)
    {
        int result = 0;
        int.TryParse(str, out result);
        return result;
    }
    protected float ParseFloat(string str)
    {
        float result = 0f;
        float.TryParse(str, out result);
        return result;
    }
    protected string[] Split(string str, char split)
    {
        return str.Split(split);
    }
    protected List<string> ParseStrList(string str, char split)
    {
        List<string> result = new List<string>();
        string[] strs = Split(str, split);
        for (int i = 0; i < strs.Length; i++)
        {
            result.Add(strs[i]);
        }
        return result;
    }
    protected List<int> ParseIntList(string str, char split)
    {
        List<int> result = new List<int>();
        string[] strs = Split(str, split);
        for (int i = 0; i < strs.Length; i++)
        {
            result.Add(ParseInt(strs[i]));
        }
        return result;
    }
    protected U GetKey(string key)
    {
        if (typeof(U) == typeof(string))
        {
            return (U)(object)key;
        }
        else if (typeof(U) == typeof(int))
        {
            return (U)(object)ParseInt(key);
        }
        else
        {
            Debug.LogError("GetKey 类型错误");
            return default(U);
        }
    }
}
Main/Config/ConfigBase.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: beeab167082007f42b3eedbb8a3c19e8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/ConfigParse.cs
New file
@@ -0,0 +1,305 @@
using LitJson;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
public class ConfigParse
{
    public enum SegStrType
    {
        Multiple,
        KeyValue,
    }
    public static T? GetSegValue<T>(string msg, string key, SegStrType type) where T : struct
    {
        T? result = null;
        switch (type)
        {
            case SegStrType.Multiple:
                {
                    uint index = 0;
                    uint.TryParse(key, out index);
                    result = (T)Convert.ChangeType(GetMultipleStr(msg, index), typeof(T));
                }
                break;
            case SegStrType.KeyValue:
                {
                    result = (T)Convert.ChangeType(GetKeyValue(msg, key), typeof(T));
                }
                break;
        }
        return result;
    }
    public static string GetSegValue(string msg, string key, SegStrType type)
    {
        switch (type)
        {
            case SegStrType.Multiple:
                {
                    uint index = 0;
                    uint.TryParse(key, out index);
                    return GetMultipleStr(msg, index);
                }
            case SegStrType.KeyValue:
                {
                    return GetKeyValue(msg, key);
                }
        }
        return string.Empty;
    }
    private static string GetMultipleStr(string msg, uint index)
    {
        string[] segs = GetMultipleStr(msg);
        if (segs != null && index < segs.Length)
        {
            return segs[index];
        }
        return string.Empty;
    }
    public static T[] GetMultipleStr<T>(string msg) where T : struct
    {
        string[] segs = GetMultipleStr(msg);
        if (segs != null && segs.Length > 0)
        {
            T[] array = new T[segs.Length];
            for (int i = 0; i < segs.Length; i++)
            {
                array[i] = (T)Convert.ChangeType(segs[i], typeof(T));
            }
            return array;
        }
        return null;
    }
    public static string[] GetMultipleStr(string msg)
    {
        string[] segs = msg.Split('|');
        if (segs.Length == 1 && segs[0].Equals(string.Empty)) return null;
        return segs;
    }
    private static string GetKeyValue(string msg, string key)
    {
        string[] segs = GetMultipleStr(msg);
        for (int i = 0; i < segs.Length; i++)
        {
            string[] pair = GetKeyValue(segs[i]);
            if (pair.Length > 1)
            {
                if (!pair[0].Equals(key)) continue;
                else return pair[1];
            }
            else continue;
        }
        return string.Empty;
    }
    private static string[] GetKeyValue(string msg)
    {
        return msg.Split('_');
    }
    public static T[] GetKeyValueKeys<T>(string msg) where T : struct
    {
        string[] segs = GetMultipleStr(msg);
        if (segs != null && segs.Length > 0)
        {
            T[] array = new T[segs.Length];
            for (int i = 0; i < segs.Length; i++)
            {
                string[] pair = GetKeyValue(segs[i]);
                if (pair.Length > 1)
                {
                    array[i] = (T)Convert.ChangeType(pair[0], typeof(T));
                }
            }
            return array;
        }
        return null;
    }
    public static T[] GetKeyValueValues<T>(string msg) where T : struct
    {
        string[] segs = GetMultipleStr(msg);
        if (segs != null && segs.Length > 0)
        {
            T[] array = new T[segs.Length];
            for (int i = 0; i < segs.Length; i++)
            {
                string[] pair = GetKeyValue(segs[i]);
                if (pair.Length > 1)
                {
                    array[i] = (T)Convert.ChangeType(pair[1], typeof(T));
                }
            }
            return array;
        }
        return null;
    }
    public static Dictionary<T, P> GetDic<T, P>(string msg)
    {
        Dictionary<T, P> dic = null;
        string[] segs = GetMultipleStr(msg);
        if (segs != null && segs.Length > 0)
        {
            dic = new Dictionary<T, P>();
            for (int i = 0; i < segs.Length; i++)
            {
                string[] pair = GetKeyValue(segs[i]);
                if (pair.Length > 1)
                {
                    dic.Add((T)Convert.ChangeType(pair[0], typeof(T)), (P)Convert.ChangeType(pair[1], typeof(P)));
                }
            }
        }
        return dic;
    }
    public static string ServerStringTrim(string str)
    {
        if (!string.IsNullOrEmpty(str))
        {
            str = str.Replace("\0", "");
            str = str.Replace("\x00", "");
            return str;
        }
        else
        {
            return string.Empty;
        }
    }
    //{'17':['63','6','27'],'65':['800'],'55':['139'],'19':['1000','2600','130']}
    public static Regex userDataRegex = new Regex(@"'([0-9]+)':\[(.*?)\]", RegexOptions.Singleline);
    public static Dictionary<int, List<int>> Analysis(string val)//正则表达式的字符串分割
    {
        string s = ServerStringTrim(val);
        if (string.IsNullOrEmpty(s))
        {
            return null;
        }
        s = s.Replace(" ", string.Empty);
        if (!userDataRegex.IsMatch(s))
        {
            return null;
        }
        else
        {
            Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
            foreach (Match match in userDataRegex.Matches(s))
            {
                int id = int.Parse(match.Groups[1].Value);
                string str = match.Groups[2].Value;
                string[] vals = str.Split(',');
                List<int> list = new List<int>();
                for (int i = 0; i < vals.Length; i++)
                {
                    int intval = int.Parse(vals[i].Replace('\'', ' '));
                    list.Add(intval);
                }
                if (!dics.ContainsKey(id))
                {
                    dics.Add(id, list);
                }
            }
            return dics;
        }
    }
    public static Dictionary<int, List<int>> ParseJsonDict(string jsonStr)
    {
        if (jsonStr == "{}" || string.IsNullOrEmpty(jsonStr))
        {
            return new Dictionary<int, List<int>>();
        }
        var dict = JsonMapper.ToObject<Dictionary<string, List<int>>>(jsonStr);
        Dictionary<int, List<int>> result = new Dictionary<int, List<int>>();
        foreach (var item in dict)
        {
            result[int.Parse(item.Key)] = item.Value;
        }
        return result;
    }
    public static Dictionary<int, int> ParseIntDict(string jsonStr)
    {
        if (jsonStr == "{}" || string.IsNullOrEmpty(jsonStr))
        {
            return new Dictionary<int, int>();
        }
        var dict = JsonMapper.ToObject<Dictionary<string, int>>(jsonStr);
        Dictionary<int, int> result = new Dictionary<int, int>();
        foreach (var item in dict)
        {
            result[int.Parse(item.Key)] = item.Value;
        }
        return result;
    }
    public static Dictionary<int, int[]> ParseIntArrayDict(string jsonStr)
    {
        if (jsonStr == "{}" || string.IsNullOrEmpty(jsonStr))
        {
            return new Dictionary<int, int[]>();
        }
        var dict = JsonMapper.ToObject<Dictionary<string, int[]>>(jsonStr);
        Dictionary<int, int[]> result = new Dictionary<int, int[]>();
        foreach (var item in dict)
        {
            result[int.Parse(item.Key)] = item.Value;
        }
        return result;
    }
    public static Dictionary<int, int[][]> ParseIntArray2Dict(string jsonStr)
    {
        if (jsonStr == "{}" || string.IsNullOrEmpty(jsonStr))
        {
            return new Dictionary<int, int[][]>();
        }
        var dict = JsonMapper.ToObject<Dictionary<string, int[][]>>(jsonStr);
        Dictionary<int, int[][]> result = new Dictionary<int, int[][]>();
        foreach (var item in dict)
        {
            result[int.Parse(item.Key)] = item.Value;
        }
        return result;
    }
    //万分率转为每个id对应的概率 [[万分概率,id1],[万分概率,id2]]
    public static Dictionary<int, int> GetRateDict(int[][] rateArray)
    {
        Dictionary<int, int> dic = new Dictionary<int, int>();
        //概率为 减去上一个概率的值即为当前ID概率
        for (int i = 0;i< rateArray.Length; i++)
        {
            if (i > 0)
            {
                dic[rateArray[i][1]] = rateArray[i][0] - rateArray[i - 1][0];
            }
            else
            {
                dic[rateArray[i][1]] = rateArray[i][0];
            }
        }
        return dic;
    }
}
Main/Config/ConfigParse.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6f9144568a1b6674da727f6a1a88aed9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 776d1c99148d30f4799a606e5cd0f5de
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Main.asmdef
@@ -4,7 +4,12 @@
    "references": [
        "GUID:3ffa07c58a98b0445a7a34376b165fd1",
        "GUID:4129704b5a1a13841ba16f230bf24a57",
        "GUID:f51ebe6a0ceec4240a699833d6309b23"
        "GUID:f51ebe6a0ceec4240a699833d6309b23",
        "GUID:e34a5702dd353724aa315fb8011f08c3",
        "GUID:4d1926c9df5b052469a1c63448b7609a",
        "GUID:68765d262e2128e4ab49c983f3411946",
        "GUID:173464ddf4cdb6640a4dfa8a9281ad69",
        "GUID:72d1fea872bd7a449bf3818f2b0a6708"
    ],
    "includePlatforms": [],
    "excludePlatforms": [],
Main/Main.cs
@@ -4,49 +4,81 @@
using UnityEngine;
using Cysharp.Threading;
using Cysharp.Threading.Tasks;
using System.Threading.Tasks;
/// <summary>
/// Main类,作为热更新程序集的入口点
/// </summary>
public class Main
{
    public static List<IGameSystemManager> managers = new List<IGameSystemManager>();
    /// <summary>
    /// 初始化函数,由Launch类调用
    /// </summary>
    public static void Init()
    public static async UniTask Init()
    {
        Debug.Log("Main.Init() 被调用");
        // 1. 初始化游戏系统
        InitializeGameSystems();
        await InitSystems();
        
        // 2. 加载配置
        LoadConfigurations();
        Debug.Log("关闭加载界面");
        LaunchLoadingWin.Instance.CloseWindow();
        
        // 3. 打开登录界面
        OpenLoginUI();
        SwitchToLoginScene();
    }
    
    /// <summary>
    /// 初始化游戏系统
    /// </summary>
    private static void InitializeGameSystems()
    private static async UniTask InitSystems()
    {
        Debug.Log("初始化游戏系统");
        // 这里可以初始化各种游戏系统,如资源管理、UI管理、音频管理等
        // 这里可以初始化大型系统 贯穿整个游戏的系统
        await ResManager.Instance.Init();
        UIManager.Instance.Init();
        StageManager.Instance.Init();
        LoginManager.Instance.Init();
        
    }
    
    /// <summary>
    /// 加载配置
    /// </summary>
    private static void LoadConfigurations()
    private static void SwitchToLoginScene()
    {
        Debug.Log("加载游戏配置");
        // Debug.Log("打开登录界面");
        // UIManager.Instance.OpenWindow<LoginWin>();
        Debug.Log("切换到登录场景");
        ConfigManager.Instance.Init();
        StageManager.Instance.ToLoginScene();
    }
        
        // 这里可以加载游戏配置,如游戏参数、表格数据等
    public static void OnEnterGameScene()
    {
        // 初始化游戏场景
        Debug.Log("初始化游戏场景");
        
        // 初始化游戏系统
        managers.Add(PlaceManager.Instance);
        managers.Add(BattleManager.Instance);
        managers.Add(TeamManager.Instance);
        foreach (var manager in managers)
        {
            manager.Init();
        }
        foreach (var manager in managers)
        {
            manager.RequestNessaryData();
        }
    }
    public static void OnSwitchToLoginScene()
    {
        foreach (var manager in managers)
        {
            manager.Release();
        }
        managers.Clear();
    }
    
    /// <summary>
@@ -54,27 +86,8 @@
    /// </summary>
    private static void OpenLoginUI()
    {
        Debug.Log("关闭加载界面");
        LaunchLoadingWin.Instance.CloseWindow();
        Debug.Log("打开登录界面");
        
        // 获取Launch中的IsUseSDK标志
        bool isUseSDK = Launch.Instance.IsUseSDK;
        // 根据IsUseSDK标志显示不同的登录界面
        if (isUseSDK)
        {
            Debug.Log("显示SDK登录界面");
            // 在这里实现SDK登录界面的显示逻辑
            // 例如:UIManager.Instance.OpenUI("SDKLoginUI");
        }
        else
        {
            Debug.Log("显示普通登录界面");
            // 在这里实现普通登录界面的显示逻辑
            // 例如:UIManager.Instance.OpenUI("NormalLoginUI");
        }
    }
}
Main/Manager.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d17f7001c7a25da4b8f7c0051a479e37
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/ConfigManager.cs
New file
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using Cysharp.Threading.Tasks;
using System.Reflection;
public class ConfigManager : ManagerBase<ConfigManager>
{
    public bool isLoadFinished
    {
        get;
        private set;
    }
    public virtual async UniTask InitConfigs()
    {
        // 加载配置文件
        await LoadConfigs();
    }
    protected async UniTask LoadConfigs()
    {
        isLoadFinished = false;
        // 加载配置文件
        // 加载完成后设置isLoadFinished为true
        isLoadFinished = true;
    }
    private async UniTask LoadConfig<T>() where T : class
    {
        string configName = typeof(T).Name;
        TextAsset textAsset = await ResManager.Instance.LoadAsset<TextAsset>("Config", configName);
        if (textAsset != null)
        {
            string[] lines = textAsset.text.Split('\n');
            var methodInfo = typeof(T).GetMethod("Init", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
            if (methodInfo != null)
            {
                methodInfo.Invoke(null, new object[] { lines });
                // 设置初始化标志
                var isInitField = typeof(T).GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                if (isInitField != null)
                {
                    isInitField.SetValue(null, true);
                }
                Debug.Log($"加载配置: {typeof(T).Name} 成功");
            }
            else
            {
                Debug.LogError($"配置类 {typeof(T).Name} 没有静态Init方法");
            }
        }
        else
        {
            Debug.LogError($"找不到配置文件: {configName}");
        }
    }
    private void ClearConfigDictionary<T>() where T : class
    {
        // 重置 T 初始化状态
        var isInitField = typeof(T).GetField("isInit", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
        if (isInitField != null)
        {
            isInitField.SetValue(null, false);
        }
    }
    public override void Release()
    {
    }
}
Main/Manager/ConfigManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 899d505772a98b44ba61e2e0d0a00072
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/GameSystemManager.cs
New file
@@ -0,0 +1,33 @@
// 游戏系统的管理基类
// 添加一个非泛型的基类或接口
public interface IGameSystemManager
{
    public void Init();
    public void Release();
    public void RequestNessaryData();
    public bool IsNessaryDataReady();
}
// 让泛型类继承自非泛型基类
public class GameSystemManager<T> : Singleton<T>, IGameSystemManager where T : GameSystemManager<T>, new()
{
    public virtual void Init()
    {
    }
    public virtual void Release()
    {
    }
    public virtual void RequestNessaryData()
    {
    }
    public virtual bool IsNessaryDataReady()
    {
        return true;
    }
}
Main/Manager/GameSystemManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 05ef44209a69865458086694ce7d6f67
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/GameSystemManager.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7b0931f43ab92084b8a1e5faa7fbfda2
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/GameSystemManager/BattleManager.cs
New file
@@ -0,0 +1,14 @@
public class BattleManager : GameSystemManager<BattleManager>
{
    public override void Init()
    {
        base.Init();
    }
    public override void Release()
    {
        base.Release();
    }
}
Main/Manager/GameSystemManager/BattleManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2eea1200b9fe6644a6907b4e042f222
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/GameSystemManager/PlaceManager.cs
New file
@@ -0,0 +1,21 @@
public class PlaceManager : GameSystemManager<PlaceManager>
{
    public PlaceField placeField;
    public override void Init()
    {
        base.Init();
    }
    public override void Release()
    {
        base.Release();
    }
    //  当队伍跟章节信息数据都有之后就可以创建场景了
    public void CreatePlaceField()
    {
    }
}
Main/Manager/GameSystemManager/PlaceManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc73049956fa1a241a3aeac4d7fdd7af
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/GameSystemManager/TeamManager.cs
New file
@@ -0,0 +1,400 @@
using System;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 阵型类型
/// </summary>
public enum FormationType
{
    None = 0,
    Type1 = 1,
    Type2 = 2,
    Type3 = 3,
    Type4 = 4,
    Type5 = 5
}
/// <summary>
/// 卡牌信息
/// </summary>
[Serializable]
public class CardInfo
{
    public int cardId;         // 卡牌ID
    public int level;          // 卡牌等级
    public int star;           // 卡牌星级
    public int quality;        // 卡牌品质
    // 其他卡牌属性...
    public CardInfo()
    {
        cardId = 0;
        level = 1;
        star = 1;
        quality = 1;
    }
    public CardInfo(int _cardId, int _level = 1, int _star = 1, int _quality = 1)
    {
        cardId = _cardId;
        level = _level;
        star = _star;
        quality = _quality;
    }
}
/// <summary>
/// 队伍卡牌信息
/// </summary>
[Serializable]
public class SlotInfo
{
    public CardInfo cardInfo;      // 卡牌信息
    public int teamIndex;      // 在队伍中的索引位置
    public SlotInfo()
    {
        cardInfo = new CardInfo();
        teamIndex = -1;
    }
    public SlotInfo(CardInfo _card, int _teamIndex)
    {
        cardInfo = _card;
        teamIndex = _teamIndex;
    }
    public bool IsEmptySlot()
    {
        return cardInfo == null || cardInfo.cardId == 0;
    }
}
/// <summary>
/// 队伍信息
/// </summary>
[Serializable]
public class TeamInfo
{
    public int teamId;                     // 队伍ID
    public FormationType formation;        // 阵型类型
    public List<SlotInfo> teamCards;       // 队伍卡牌列表
    public TeamInfo()
    {
        teamId = 0;
        formation = FormationType.Type1;
        teamCards = new List<SlotInfo>();
    }
    public TeamInfo(int _teamId, string _teamName, FormationType _formation)
    {
        teamId = _teamId;
        formation = _formation;
        teamCards = new List<SlotInfo>();
    }
    /// <summary>
    /// 添加卡牌到队伍
    /// </summary>
    public bool AddCard(CardInfo _card, int _teamIndex, bool _isLeader = false)
    {
        // 检查位置是否已被占用
        foreach (var teamCard in teamCards)
        {
            if (teamCard.teamIndex == _teamIndex)
            {
                Debug.LogWarning($"队伍位置 {_teamIndex} 已被占用");
                return false;
            }
        }
        SlotInfo newTeamCard = new SlotInfo(_card, _teamIndex);
        teamCards.Add(newTeamCard);
        return true;
    }
    /// <summary>
    /// 移除队伍中的卡牌
    /// </summary>
    public bool RemoveCard(int _teamIndex)
    {
        for (int i = 0; i < teamCards.Count; i++)
        {
            if (teamCards[i].teamIndex == _teamIndex)
            {
                teamCards.RemoveAt(i);
                return true;
            }
        }
        Debug.LogWarning($"未找到位置 {_teamIndex} 的卡牌");
        return false;
    }
}
/// <summary>
/// 队伍管理器
/// </summary>
public class TeamManager : GameSystemManager<TeamManager>
{
    // 所有队伍信息
    private List<TeamInfo> teamInfos = new List<TeamInfo>();
    // 当前选中的队伍索引
    private int currentTeamIndex = 0;
    // 最大队伍数量
    private const int MAX_TEAM_COUNT = 5;
    // 每个队伍的最大卡牌数量
    private const int MAX_CARD_PER_TEAM = 6;
    public override void Init()
    {
        base.Init();
        Debug.Log("TeamManager 初始化");
        // 初始化默认队伍
        if (teamInfos.Count == 0)
        {
            CreateDefaultTeam();
        }
    }
    public override void Release()
    {
        base.Release();
        Debug.Log("TeamManager 释放");
        // 清理数据
        teamInfos.Clear();
    }
    /// <summary>
    /// 创建默认队伍
    /// </summary>
    private void CreateDefaultTeam()
    {
        TeamInfo defaultTeam = new TeamInfo(1, "队伍1", FormationType.Type1);
        teamInfos.Add(defaultTeam);
    }
    /// <summary>
    /// 获取所有队伍信息
    /// </summary>
    public List<TeamInfo> GetAllTeams()
    {
        return teamInfos;
    }
    /// <summary>
    /// 获取指定ID的队伍
    /// </summary>
    public TeamInfo GetTeam(int _teamId)
    {
        return teamInfos.Find(team => team.teamId == _teamId);
    }
    /// <summary>
    /// 获取当前选中的队伍
    /// </summary>
    public TeamInfo GetCurrentTeam()
    {
        if (currentTeamIndex >= 0 && currentTeamIndex < teamInfos.Count)
        {
            return teamInfos[currentTeamIndex];
        }
        return null;
    }
    /// <summary>
    /// 设置当前选中的队伍
    /// </summary>
    public bool SetCurrentTeam(int _teamId)
    {
        for (int i = 0; i < teamInfos.Count; i++)
        {
            if (teamInfos[i].teamId == _teamId)
            {
                currentTeamIndex = i;
                return true;
            }
        }
        return false;
    }
    /// <summary>
    /// 创建新队伍
    /// </summary>
    public TeamInfo CreateTeam(string _teamName, FormationType _formation)
    {
        if (teamInfos.Count >= MAX_TEAM_COUNT)
        {
            Debug.LogWarning("已达到最大队伍数量限制");
            return null;
        }
        // 生成新的队伍ID
        int newTeamId = 1;
        if (teamInfos.Count > 0)
        {
            newTeamId = teamInfos[teamInfos.Count - 1].teamId + 1;
        }
        TeamInfo newTeam = new TeamInfo(newTeamId, _teamName, _formation);
        teamInfos.Add(newTeam);
        return newTeam;
    }
    /// <summary>
    /// 删除队伍
    /// </summary>
    public bool DeleteTeam(int _teamId)
    {
        if (teamInfos.Count <= 1)
        {
            Debug.LogWarning("至少保留一个队伍");
            return false;
        }
        for (int i = 0; i < teamInfos.Count; i++)
        {
            if (teamInfos[i].teamId == _teamId)
            {
                teamInfos.RemoveAt(i);
                // 如果删除的是当前选中的队伍,则重新设置当前队伍
                if (currentTeamIndex >= teamInfos.Count)
                {
                    currentTeamIndex = teamInfos.Count - 1;
                }
                return true;
            }
        }
        return false;
    }
    /// <summary>
    /// 修改队伍阵型
    /// </summary>
    public bool ChangeFormation(int _teamId, FormationType _newFormation)
    {
        TeamInfo team = GetTeam(_teamId);
        if (team != null)
        {
            team.formation = _newFormation;
            return true;
        }
        return false;
    }
    /// <summary>
    /// 向队伍添加卡牌
    /// </summary>
    public bool AddCardToTeam(int _teamId, CardInfo _card, int _teamIndex, bool _isLeader = false)
    {
        TeamInfo team = GetTeam(_teamId);
        if (team == null)
        {
            Debug.LogWarning($"未找到ID为 {_teamId} 的队伍");
            return false;
        }
        if (team.teamCards.Count >= MAX_CARD_PER_TEAM)
        {
            Debug.LogWarning("队伍卡牌已达到最大数量");
            return false;
        }
        return team.AddCard(_card, _teamIndex, _isLeader);
    }
    /// <summary>
    /// 从队伍移除卡牌
    /// </summary>
    public bool RemoveCardFromTeam(int _teamId, int _teamIndex)
    {
        TeamInfo team = GetTeam(_teamId);
        if (team == null)
        {
            Debug.LogWarning($"未找到ID为 {_teamId} 的队伍");
            return false;
        }
        return team.RemoveCard(_teamIndex);
    }
    /// <summary>
    /// 交换队伍中两张卡牌的位置
    /// </summary>
    public bool SwapTeamCards(int _teamId, int _fromIndex, int _toIndex)
    {
        TeamInfo team = GetTeam(_teamId);
        if (team == null)
        {
            Debug.LogWarning($"未找到ID为 {_teamId} 的队伍");
            return false;
        }
        SlotInfo fromCard = null;
        SlotInfo toCard = null;
        foreach (var card in team.teamCards)
        {
            if (card.teamIndex == _fromIndex)
            {
                fromCard = card;
            }
            else if (card.teamIndex == _toIndex)
            {
                toCard = card;
            }
        }
        if (fromCard == null)
        {
            Debug.LogWarning($"未找到位置 {_fromIndex} 的卡牌");
            return false;
        }
        if (toCard == null)
        {
            // 如果目标位置没有卡牌,直接移动
            fromCard.teamIndex = _toIndex;
        }
        else
        {
            // 交换两张卡牌的位置
            fromCard.teamIndex = _toIndex;
            toCard.teamIndex = _fromIndex;
        }
        return true;
    }
    /// <summary>
    /// 保存队伍数据
    /// </summary>
    public void SaveTeamData()
    {
        // 实现队伍数据的保存逻辑
        // 可以使用 PlayerPrefs、JSON 序列化或其他方式
        Debug.Log("保存队伍数据");
    }
    /// <summary>
    /// 加载队伍数据
    /// </summary>
    public void LoadTeamData()
    {
        // 实现队伍数据的加载逻辑
        Debug.Log("加载队伍数据");
    }
}
Main/Manager/GameSystemManager/TeamManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8ac01de78a793cf4fb759dddc21c0bbe
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/IManager.cs
New file
@@ -0,0 +1,7 @@
public interface IManager
{
    void Init();
    void Release();
}
Main/Manager/IManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 15be84dd6a98de14cb553ac421b17fb9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/LoginManager.cs
New file
@@ -0,0 +1,16 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LoginManager : ManagerBase<LoginManager>
{
    public override void Init()
    {
    }
    public override void Release()
    {
    }
}
Main/Manager/LoginManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 344e28aa3edcfbd4f863a019c2cbaaac
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/ManagerBase.cs
New file
@@ -0,0 +1,13 @@
public class ManagerBase<T> : Singleton<T>, IManager where T : ManagerBase<T>, new()
{
    public virtual void Init()
    {
    }
    public virtual void Release()
    {
    }
}
Main/Manager/ManagerBase.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c651c6b070b83d48b0f07117ed43058
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Manager/StageManager.cs
New file
@@ -0,0 +1,119 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;
public enum StageName
{
    Login,
    Game,
}
public class StageManager : Singleton<StageManager>, IManager
{
    public StageName currentStage;
    public void Init()
    {
    }
    public void Release()
    {
    }
    public async UniTaskVoid ToLoginScene()
    {
        UIManager.Instance.DestroyAllUI();
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Login");
        await OnLoading(asyncOperation, () => 1f);
        Main.OnSwitchToLoginScene();
        currentStage = StageName.Login;
        UIManager.Instance.OpenWindow<LoginWin>();
    }
    protected float GetManagerRequestDataProgress()
    {
        if (Main.managers.Count == 0)
        {
            return 1f;
        }
        int count = 0;
        for (int i = 0; i < Main.managers.Count; i++)
        {
            var manager = Main.managers[i];
            if (manager.IsNessaryDataReady())
            {
                count++;
            }
        }
        return ((float)count) / ((float)Main.managers.Count);
    }
    public async UniTaskVoid ToGameScene()
    {
        UIManager.Instance.DestroyAllUI();
        ResManager.Instance.PrewarmResources();
        AsyncOperation asyncOperation = SceneManager.LoadSceneAsync("Game");
        await OnLoading(asyncOperation, GetManagerRequestDataProgress);
        //  加载初始化数据完成
        currentStage = StageName.Game;
        UIManager.Instance.OpenWindow<MainWin>();
    }
    protected async UniTask OnLoading(AsyncOperation asyncOperation, Func<float> getLoadingProgress, Func<UniTask> anthorTask = null)
    {
        asyncOperation.allowSceneActivation = false;
        LoadingWin loadingWin = UIManager.Instance.OpenWindow<LoadingWin>();
        while (!asyncOperation.isDone)
        {
            if (asyncOperation.progress >= 0.9f)
            {
                asyncOperation.allowSceneActivation = true;
            }
            loadingWin.SetProgress(asyncOperation.progress * 0.5f + getLoadingProgress() * 0.5f);
            await UniTask.Yield();
        }
        float managerProgress = getLoadingProgress();
        while (managerProgress < 1f)
        {
            loadingWin.SetProgress(asyncOperation.progress * 0.5f + managerProgress * 0.5f);
            await UniTask.Yield();
            managerProgress = getLoadingProgress();
        }
        if (anthorTask != null)
        {
            await anthorTask();
        }
        loadingWin.SetProgress(1f, true);
        await UniTask.Delay(TimeSpan.FromSeconds(0.5f));
        loadingWin.CloseWindow();
    }
}
Main/Manager/StageManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aac79594075c0524080fb21a459c7264
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Place.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1244e95cb51b1cb42a8adaba3a5dff53
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Place/PlaceDataStructs.cs
New file
@@ -0,0 +1,83 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 单位数据
/// </summary>
public class UnitData
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int HP { get; set; }
    public int Attack { get; set; }
    public float AttackSpeed { get; set; }
    public float MoveSpeed { get; set; }
    public string PrefabPath { get; set; }
}
/// <summary>
/// 章节数据
/// </summary>
public class ChapterData
{
    public int ID { get; set; }
    public string Name { get; set; }
    public int MapId { get; set; }
    public List<WaveData> Waves { get; set; }
    /// <summary>
    /// 获取指定波次的敌人列表
    /// </summary>
    public List<int> GetEnemiesForWave(int wave)
    {
        if (wave <= 0 || wave > Waves.Count)
            return new List<int>();
        return Waves[wave - 1].EnemyIds;
    }
    /// <summary>
    /// 获取指定波次的掉落物品
    /// </summary>
    public List<DropItemData> GetDropsForWave(int wave)
    {
        if (wave <= 0 || wave > Waves.Count)
            return new List<DropItemData>();
        return Waves[wave - 1].DropItems;
    }
}
/// <summary>
/// 波次数据
/// </summary>
public class WaveData
{
    public int WaveIndex { get; set; }
    public List<int> EnemyIds { get; set; }
    public List<DropItemData> DropItems { get; set; }
}
/// <summary>
/// 物品类型
/// </summary>
public enum ItemType
{
    Gold,
    Diamond,
    Item
}
/// <summary>
/// 掉落物品数据
/// </summary>
public class DropItemData
{
    public int ID { get; set; }
    public ItemType ItemType { get; set; }
    public int ItemId { get; set; }
    public int Amount { get; set; }
    public float DropRate { get; set; }
    public string IconPath { get; set; }
}
Main/Place/PlaceDataStructs.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4541140280fac3541b7ec64b9acd2f20
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Place/PlaceDrop.cs
New file
@@ -0,0 +1,143 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 放置战斗掉落物品类
/// </summary>
public class PlaceDrop
{
    // 掉落物品数据
    public DropItemData DropData { get; private set; }
    // 位置
    public Vector2 Position { get; private set; }
    // 状态
    private enum DropState
    {
        Dropping,   // 掉落中
        Waiting,    // 等待收集
        Flying      // 飞向UI
    }
    private DropState currentState = DropState.Dropping;
    // 计时器
    private float waitTimer = 0f;
    private float waitDuration = 1.5f; // 等待1.5秒后自动收集
    // 飞行相关
    private Vector2 targetPosition;
    private float flySpeed = 10f;
    // 是否应该被收集
    public bool ShouldCollect { get; private set; } = false;
    // 掉落动画计时器
    private float dropAnimTimer = 0f;
    private float dropAnimDuration = 0.5f;
    private Vector2 startPosition;
    private Vector2 endPosition;
    /// <summary>
    /// 构造函数
    /// </summary>
    public PlaceDrop(DropItemData dropData)
    {
        DropData = dropData;
        // 随机掉落位置(在场景中心附近)
        startPosition = new Vector2(Random.Range(-2f, 2f), 5f);
        endPosition = new Vector2(
            startPosition.x + Random.Range(-1f, 1f),
            Random.Range(-1f, 1f)
        );
        Position = startPosition;
        // 设置飞向的目标位置(UI上的货币栏位置)
        targetPosition = new Vector2(8f, 4f); // 示例位置,实际应该是UI中货币栏的世界坐标
    }
    /// <summary>
    /// 更新掉落物品状态
    /// </summary>
    public void Update(float deltaTime)
    {
        switch (currentState)
        {
            case DropState.Dropping:
                UpdateDropping(deltaTime);
                break;
            case DropState.Waiting:
                UpdateWaiting(deltaTime);
                break;
            case DropState.Flying:
                UpdateFlying(deltaTime);
                break;
        }
    }
    /// <summary>
    /// 更新掉落动画
    /// </summary>
    private void UpdateDropping(float deltaTime)
    {
        dropAnimTimer += deltaTime;
        float t = Mathf.Clamp01(dropAnimTimer / dropAnimDuration);
        // 使用二次贝塞尔曲线模拟掉落效果
        Position = Vector2.Lerp(startPosition, endPosition, t);
        // 掉落动画完成后切换到等待状态
        if (t >= 1.0f)
        {
            currentState = DropState.Waiting;
            waitTimer = 0f;
        }
    }
    /// <summary>
    /// 更新等待状态
    /// </summary>
    private void UpdateWaiting(float deltaTime)
    {
        // 等待一段时间后自动收集
        waitTimer += deltaTime;
        if (waitTimer >= waitDuration)
        {
            currentState = DropState.Flying;
        }
    }
    /// <summary>
    /// 更新飞行状态
    /// </summary>
    private void UpdateFlying(float deltaTime)
    {
        // 计算飞行方向
        Vector2 direction = (targetPosition - Position).normalized;
        // 更新位置
        Position += direction * flySpeed * deltaTime;
        // 检查是否到达目标
        float distanceToTarget = Vector2.Distance(Position, targetPosition);
        if (distanceToTarget < 0.1f)
        {
            // 到达目标,标记为应该被收集
            ShouldCollect = true;
        }
    }
    /// <summary>
    /// 手动收集物品
    /// </summary>
    public void Collect()
    {
        currentState = DropState.Flying;
    }
}
Main/Place/PlaceDrop.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 070f25c487448bb44ac2134e1290d5f0
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Place/PlaceField.cs
New file
@@ -0,0 +1,406 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
/// <summary>
/// 放置战斗场地类,管理战斗场景中的所有元素
/// </summary>
public class PlaceField
{
    // 当前章节信息
    private ChapterData currentChapter;
    // 场景中的所有单位
    private List<PlaceUnit> allUnits = new List<PlaceUnit>();
    // 玩家单位列表
    private List<PlaceUnit> playerUnits = new List<PlaceUnit>();
    // 敌人单位列表
    private List<PlaceUnit> enemyUnits = new List<PlaceUnit>();
    // 场景中的掉落物品
    private List<PlaceDrop> dropItems = new List<PlaceDrop>();
    // 战斗状态
    public enum BattleState
    {
        Preparing,  // 准备中
        Fighting,   // 战斗中
        Completed,  // 战斗完成
        Waiting     // 等待下一场
    }
    public BattleState CurrentState { get; private set; } = BattleState.Preparing;
    // 战斗完成事件
    public event Action OnBattleCompleted;
    // 掉落物品收集事件
    public event Action<DropItemData> OnItemCollected;
    // 等待计时器
    private float waitTimer = 0f;
    private const float WAIT_DURATION = 2f; // 战斗结束后等待2秒
    /// <summary>
    /// 初始化战斗场景
    /// </summary>
    /// <param name="chapterId">章节ID</param>
    public void InitBattleField(int chapterId)
    {
        Debug.Log($"初始化战斗场地: 章节{chapterId}");
        // 清空场景
        ClearField();
        // 加载章节数据
        LoadChapterData(chapterId);
        // 加载地图背景
        LoadMapBackground();
        // 生成玩家阵容
        SpawnPlayerUnits();
        // 生成敌方单位
        SpawnEnemyUnits();
        // 设置战斗状态为准备中
        CurrentState = BattleState.Preparing;
    }
    /// <summary>
    /// 开始战斗
    /// </summary>
    public void StartBattle()
    {
        if (CurrentState != BattleState.Preparing && CurrentState != BattleState.Waiting)
            return;
        Debug.Log("开始战斗");
        CurrentState = BattleState.Fighting;
        // 通知所有单位开始战斗
        foreach (var unit in allUnits)
        {
            unit.StartBattle();
        }
    }
    /// <summary>
    /// 更新战场状态
    /// </summary>
    public void UpdateField(float deltaTime)
    {
        switch (CurrentState)
        {
            case BattleState.Fighting:
                UpdateFighting(deltaTime);
                break;
            case BattleState.Completed:
                UpdateCompleted(deltaTime);
                break;
            case BattleState.Waiting:
                UpdateWaiting(deltaTime);
                break;
        }
    }
    /// <summary>
    /// 更新战斗状态
    /// </summary>
    private void UpdateFighting(float deltaTime)
    {
        // 更新所有单位
        for (int i = allUnits.Count - 1; i >= 0; i--)
        {
            if (i < allUnits.Count)
            {
                allUnits[i].UpdateUnit(deltaTime);
            }
        }
        // 检查战斗是否结束
        CheckBattleEnd();
        // 更新掉落物品
        UpdateDropItems(deltaTime);
    }
    /// <summary>
    /// 更新战斗完成状态
    /// </summary>
    private void UpdateCompleted(float deltaTime)
    {
        // 更新掉落物品
        UpdateDropItems(deltaTime);
        // 检查是否所有掉落物品都已收集
        bool allItemsCollected = true;
        foreach (var item in dropItems)
        {
            if (!item.ShouldCollect)
            {
                allItemsCollected = false;
                break;
            }
        }
        // 如果所有物品都已收集,进入等待状态
        if (allItemsCollected && dropItems.Count > 0)
        {
            CurrentState = BattleState.Waiting;
            waitTimer = 0f;
        }
        // 如果没有掉落物品,直接进入等待状态
        else if (dropItems.Count == 0)
        {
            CurrentState = BattleState.Waiting;
            waitTimer = 0f;
        }
    }
    /// <summary>
    /// 更新等待状态
    /// </summary>
    private void UpdateWaiting(float deltaTime)
    {
        waitTimer += deltaTime;
        // 等待时间结束后,开始下一场战斗
        if (waitTimer >= WAIT_DURATION)
        {
            // 清理战场
            ClearField();
            // 重新初始化战场
            InitBattleField(currentChapter.ID);
            // 开始新的战斗
            StartBattle();
        }
    }
    /// <summary>
    /// 加载章节数据
    /// </summary>
    private void LoadChapterData(int chapterId)
    {
        // 从配置管理器加载章节数据
        // currentChapter = ConfigManager.Instance.GetChapterData(chapterId);
        // 临时代码,创建一个测试章节
        currentChapter = new ChapterData
        {
            ID = chapterId,
            Name = $"测试章节 {chapterId}",
            MapId = chapterId
        };
        Debug.Log($"加载章节数据: {currentChapter.Name}");
    }
    /// <summary>
    /// 加载地图背景
    /// </summary>
    private void LoadMapBackground()
    {
        // 根据章节数据加载对应的地图背景
        // MapManager.Instance.LoadMap(currentChapter.MapId);
        Debug.Log($"加载地图背景: MapID={currentChapter.MapId}");
    }
    /// <summary>
    /// 生成玩家阵容
    /// </summary>
    private void SpawnPlayerUnits()
    {
        Debug.Log("生成玩家阵容");
        // 从玩家数据中获取当前阵容
        // var formation = PlayerManager.Instance.CurrentFormation;
        // 临时代码,创建测试玩家单位
        for (int i = 0; i < 3; i++)
        {
            UnitData unitData = new UnitData
            {
                ID = 1000 + i,
                Name = $"玩家角色 {i+1}",
                HP = 100,
                Attack = 10,
                AttackSpeed = 1.0f,
                MoveSpeed = 2.0f,
                PrefabPath = $"Characters/Player_{i+1}"
            };
            PlaceUnit unit = new PlaceUnit(unitData, true);
            playerUnits.Add(unit);
            allUnits.Add(unit);
            Debug.Log($"生成玩家单位: {unit.UnitData.Name}");
        }
    }
    /// <summary>
    /// 生成敌方单位
    /// </summary>
    private void SpawnEnemyUnits()
    {
        Debug.Log("生成敌方单位");
        // 根据章节数据生成敌方单位
        // var enemyIds = currentChapter.GetEnemiesForWave(1);
        // 临时代码,创建测试敌方单位
        for (int i = 0; i < 5; i++)
        {
            UnitData unitData = new UnitData
            {
                ID = 2000 + i,
                Name = $"敌方单位 {i+1}",
                HP = 50,
                Attack = 5,
                AttackSpeed = 0.8f,
                MoveSpeed = 1.5f,
                PrefabPath = $"Characters/Enemy_{i+1}"
            };
            PlaceUnit unit = new PlaceUnit(unitData, false);
            enemyUnits.Add(unit);
            allUnits.Add(unit);
            Debug.Log($"生成敌方单位: {unit.UnitData.Name}");
        }
    }
    /// <summary>
    /// 检查战斗是否结束
    /// </summary>
    private void CheckBattleEnd()
    {
        // 检查敌方单位是否全部阵亡
        bool allEnemyDead = true;
        foreach (var unit in enemyUnits)
        {
            if (!unit.IsDead)
            {
                allEnemyDead = false;
                break;
            }
        }
        // 如果所有敌人都已阵亡,战斗结束
        if (allEnemyDead)
        {
            CompleteBattle();
        }
    }
    /// <summary>
    /// 完成战斗
    /// </summary>
    private void CompleteBattle()
    {
        Debug.Log("战斗完成");
        CurrentState = BattleState.Completed;
        // 停止所有单位行动
        foreach (var unit in allUnits)
        {
            unit.StopBattle();
        }
        // 生成掉落物品
        GenerateDropItems();
        // 触发战斗完成事件
        OnBattleCompleted?.Invoke();
    }
    /// <summary>
    /// 生成掉落物品
    /// </summary>
    private void GenerateDropItems()
    {
        Debug.Log("生成掉落物品");
        // 根据章节掉落表生成掉落物品
        // var dropList = currentChapter.GetDropsForWave(1);
        // 临时代码,创建测试掉落物品
        for (int i = 0; i < 5; i++)
        {
            DropItemData dropData = new DropItemData
            {
                ID = i,
                ItemType = (i % 3 == 0) ? ItemType.Gold : (i % 3 == 1) ? ItemType.Diamond : ItemType.Item,
                ItemId = i,
                Amount = UnityEngine.Random.Range(10, 100),
                DropRate = 1.0f,
                IconPath = $"Icons/Item_{i}"
            };
            // 创建掉落物品
            PlaceDrop drop = new PlaceDrop(dropData);
            dropItems.Add(drop);
            Debug.Log($"生成掉落物品: 类型={dropData.ItemType}, 数量={dropData.Amount}");
        }
    }
    /// <summary>
    /// 更新掉落物品
    /// </summary>
    private void UpdateDropItems(float deltaTime)
    {
        for (int i = dropItems.Count - 1; i >= 0; i--)
        {
            dropItems[i].Update(deltaTime);
            // 检查是否需要收集物品
            if (dropItems[i].ShouldCollect)
            {
                // 触发物品收集事件
                OnItemCollected?.Invoke(dropItems[i].DropData);
                // 移除物品
                dropItems.RemoveAt(i);
            }
        }
    }
    /// <summary>
    /// 清空战场
    /// </summary>
    public void ClearField()
    {
        Debug.Log("清空战场");
        allUnits.Clear();
        playerUnits.Clear();
        enemyUnits.Clear();
        dropItems.Clear();
        CurrentState = BattleState.Preparing;
    }
    /// <summary>
    /// 获取玩家单位列表
    /// </summary>
    public List<PlaceUnit> GetPlayerUnits()
    {
        return new List<PlaceUnit>(playerUnits);
    }
    /// <summary>
    /// 获取敌方单位列表
    /// </summary>
    public List<PlaceUnit> GetEnemyUnits()
    {
        return new List<PlaceUnit>(enemyUnits);
    }
}
Main/Place/PlaceField.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b82dee68a4dcad54780acf1dad335d8c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Place/PlaceUnit.cs
New file
@@ -0,0 +1,516 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 放置战斗单位类,代表战场上的一个角色
/// </summary>
public class PlaceUnit
{
    // 单位数据
    public UnitData UnitData { get; private set; }
    // 单位属性
    public int HP { get; private set; }
    public int MaxHP { get; private set; }
    public int Attack { get; private set; }
    public float AttackSpeed { get; private set; }
    public float MoveSpeed { get; private set; }
    // 单位状态
    public bool IsPlayer { get; private set; }
    public bool IsDead { get; private set; }
    // 位置和朝向
    public Vector2 Position { get; private set; }
    public Vector2 Direction { get; private set; }
    // 战斗相关
    private PlaceUnit targetUnit;
    private float attackCooldown;
    // 单位行为状态
    public enum UnitState
    {
        Idle,       // 待机
        Moving,     // 移动中
        Attacking,  // 攻击中
        Dead        // 死亡
    }
    public UnitState CurrentState { get; private set; } = UnitState.Idle;
    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="data">单位数据</param>
    /// <param name="isPlayer">是否为玩家单位</param>
    public PlaceUnit(UnitData data, bool isPlayer)
    {
        UnitData = data;
        IsPlayer = isPlayer;
        // 初始化属性
        MaxHP = data.HP;
        HP = MaxHP;
        Attack = data.Attack;
        AttackSpeed = data.AttackSpeed;
        MoveSpeed = data.MoveSpeed;
        // 初始化攻击冷却
        attackCooldown = Random.Range(0f, 1f / AttackSpeed);
        // 设置初始位置
        Position = isPlayer ? new Vector2(-5f + Random.Range(-1f, 1f), Random.Range(-2f, 2f)) :
                             new Vector2(5f + Random.Range(-1f, 1f), Random.Range(-2f, 2f));
        Direction = isPlayer ? Vector2.right : Vector2.left;
        IsDead = false;
        CurrentState = UnitState.Idle;
    }
    /// <summary>
    /// 开始战斗
    /// </summary>
    public void StartBattle()
    {
        if (IsDead) return;
        CurrentState = UnitState.Idle;
        FindTarget();
    }
    /// <summary>
    /// 停止战斗
    /// </summary>
    public void StopBattle()
    {
        if (IsDead) return;
        CurrentState = UnitState.Idle;
        targetUnit = null;
    }
    /// <summary>
    /// 更新单位状态
    /// </summary>
    public void UpdateUnit(float deltaTime)
    {
        if (IsDead) return;
        // 如果没有目标,尝试寻找目标
        if (targetUnit == null || targetUnit.IsDead)
        {
            FindTarget();
            if (targetUnit == null)
            {
                CurrentState = UnitState.Idle;
                return;
            }
        }
        // 根据与目标的距离决定行为
        float distanceToTarget = Vector2.Distance(Position, targetUnit.Position);
        float attackRange = 1.5f; // 攻击范围
        if (distanceToTarget <= attackRange)
        {
            // 在攻击范围内,执行攻击
            AttackTarget(deltaTime);
        }
        else
        {
            // 不在攻击范围内,移动接近目标
            MoveToTarget(deltaTime);
        }
    }
    /// <summary>
    /// 寻找目标
    /// </summary>
    private void FindTarget()
    {
        // 从场景中寻找敌对单位作为目标
        List<PlaceUnit> potentialTargets = IsPlayer ?
            PlaceManager.Instance.placeField.GetEnemyUnits() :
            PlaceManager.Instance.placeField.GetPlayerUnits();
        float minDistance = float.MaxValue;
        PlaceUnit nearestTarget = null;
        foreach (var unit in potentialTargets)
        {
            if (!unit.IsDead)
            {
                float distance = Vector2.Distance(Position, unit.Position);
                if (distance < minDistance)
                {
                    minDistance = distance;
                    nearestTarget = unit;
                }
            }
        }
        targetUnit = nearestTarget;
    }
    /// <summary>
    /// 移动向目标
    /// </summary>
    private void MoveToTarget(float deltaTime)
    {
        CurrentState = UnitState.Moving;
        // 计算移动方向
        Vector2 moveDirection = (targetUnit.Position - Position).normalized;
        // 检查是否有障碍物,如果有则需要寻路
        if (HasObstacle(Position, targetUnit.Position))
        {
            // 简单的寻路逻辑
            moveDirection = FindPathDirection(targetUnit.Position);
        }
        // 检查是否与其他单位重叠
        Vector2 avoidanceDirection = Vector2.zero;
        int avoidanceCount = 0;
        // 获取所有单位
        List<PlaceUnit> allUnits = new List<PlaceUnit>();
        allUnits.AddRange(PlaceManager.Instance.placeField.GetPlayerUnits());
        allUnits.AddRange(PlaceManager.Instance.placeField.GetEnemyUnits());
        // 检查附近的单位
        foreach (var unit in allUnits)
        {
            if (unit != this && !unit.IsDead)
            {
                float distance = Vector2.Distance(Position, unit.Position);
                if (distance < 1.0f) // 避让距离
                {
                    // 计算避让方向
                    Vector2 dir = (Position - unit.Position).normalized;
                    avoidanceDirection += dir;
                    avoidanceCount++;
                }
            }
        }
        // 如果有需要避让的单位,调整移动方向
        if (avoidanceCount > 0)
        {
            avoidanceDirection /= avoidanceCount;
            moveDirection = Vector2.Lerp(moveDirection, avoidanceDirection, 0.5f).normalized;
        }
        // 更新位置
        Position += moveDirection * MoveSpeed * deltaTime;
        // 更新朝向
        Direction = moveDirection;
    }
    /// <summary>
    /// 检查是否有障碍物
    /// </summary>
    private bool HasObstacle(Vector2 start, Vector2 end)
    {
        // 简单的障碍物检测,实际项目中可能需要更复杂的碰撞检测
        // 这里可以使用Physics2D.Raycast来检测
        // 示例代码
        // RaycastHit2D hit = Physics2D.Raycast(start, end - start, Vector2.Distance(start, end), obstacleLayer);
        // return hit.collider != null;
        // 临时代码,随机生成障碍物
        return Random.value < 0.1f;
    }
    /// <summary>
    /// 寻找路径方向
    /// </summary>
    private Vector2 FindPathDirection(Vector2 target)
    {
        // 使用A*寻路算法
        List<Node> path = AStar(Position, target);
        if (path != null && path.Count > 0)
        {
            // 获取下一个节点的方向
            Vector2 nextNodePos = path[0].position;
            return (nextNodePos - Position).normalized;
        }
        // 如果寻路失败,直接返回目标方向
        return (target - Position).normalized;
    }
    /// <summary>
    /// A*寻路算法
    /// </summary>
    private List<Node> AStar(Vector2 start, Vector2 goal)
    {
        // 网格大小设置
        float gridSize = 0.5f;
        int gridWidth = 20;
        int gridHeight = 10;
        // 创建开放列表和关闭列表
        List<Node> openList = new List<Node>();
        List<Node> closedList = new List<Node>();
        // 创建起点和终点节点
        Node startNode = new Node(start, null);
        Node goalNode = new Node(goal, null);
        // 计算起点的f、g、h值
        startNode.g = 0;
        startNode.h = CalculateHeuristic(startNode, goalNode);
        startNode.f = startNode.g + startNode.h;
        // 将起点加入开放列表
        openList.Add(startNode);
        // 开始A*算法主循环
        while (openList.Count > 0)
        {
            // 找到开放列表中f值最小的节点
            Node currentNode = FindNodeWithLowestF(openList);
            // 如果当前节点是目标节点,则构建路径并返回
            if (Vector2.Distance(currentNode.position, goalNode.position) < gridSize)
            {
                return BuildPath(currentNode);
            }
            // 将当前节点从开放列表移除,加入关闭列表
            openList.Remove(currentNode);
            closedList.Add(currentNode);
            // 获取当前节点的相邻节点
            List<Node> neighbors = GetNeighbors(currentNode, gridSize, gridWidth, gridHeight);
            foreach (Node neighbor in neighbors)
            {
                // 如果相邻节点在关闭列表中,则跳过
                if (IsNodeInList(neighbor, closedList))
                {
                    continue;
                }
                // 如果相邻节点位置有障碍物,则跳过
                if (HasObstacle(currentNode.position, neighbor.position))
                {
                    continue;
                }
                // 计算从起点经过当前节点到相邻节点的代价
                float tentativeG = currentNode.g + Vector2.Distance(currentNode.position, neighbor.position);
                // 如果相邻节点不在开放列表中,或者新路径更优,则更新相邻节点
                if (!IsNodeInList(neighbor, openList) || tentativeG < neighbor.g)
                {
                    neighbor.parent = currentNode;
                    neighbor.g = tentativeG;
                    neighbor.h = CalculateHeuristic(neighbor, goalNode);
                    neighbor.f = neighbor.g + neighbor.h;
                    // 如果相邻节点不在开放列表中,则加入开放列表
                    if (!IsNodeInList(neighbor, openList))
                    {
                        openList.Add(neighbor);
                    }
                }
            }
        }
        // 如果开放列表为空,则寻路失败,返回null
        return null;
    }
    /// <summary>
    /// 计算启发式函数值(曼哈顿距离)
    /// </summary>
    private float CalculateHeuristic(Node node, Node goal)
    {
        // 使用曼哈顿距离作为启发式函数
        return Mathf.Abs(node.position.x - goal.position.x) + Mathf.Abs(node.position.y - goal.position.y);
    }
    /// <summary>
    /// 查找开放列表中f值最小的节点
    /// </summary>
    private Node FindNodeWithLowestF(List<Node> list)
    {
        Node lowestNode = list[0];
        for (int i = 1; i < list.Count; i++)
        {
            if (list[i].f < lowestNode.f)
            {
                lowestNode = list[i];
            }
        }
        return lowestNode;
    }
    /// <summary>
    /// 获取节点的相邻节点
    /// </summary>
    private List<Node> GetNeighbors(Node node, float gridSize, int gridWidth, int gridHeight)
    {
        List<Node> neighbors = new List<Node>();
        // 8个方向的相邻节点
        Vector2[] directions = new Vector2[]
        {
            new Vector2(1, 0),    // 右
            new Vector2(-1, 0),   // 左
            new Vector2(0, 1),    // 上
            new Vector2(0, -1),   // 下
            new Vector2(1, 1),    // 右上
            new Vector2(1, -1),   // 右下
            new Vector2(-1, 1),   // 左上
            new Vector2(-1, -1)   // 左下
        };
        foreach (Vector2 dir in directions)
        {
            Vector2 neighborPos = node.position + dir * gridSize;
            // 检查是否在网格范围内
            if (neighborPos.x >= -gridWidth/2 && neighborPos.x <= gridWidth/2 &&
                neighborPos.y >= -gridHeight/2 && neighborPos.y <= gridHeight/2)
            {
                Node neighbor = new Node(neighborPos, node);
                neighbors.Add(neighbor);
            }
        }
        return neighbors;
    }
    /// <summary>
    /// 检查节点是否在列表中
    /// </summary>
    private bool IsNodeInList(Node node, List<Node> list)
    {
        foreach (Node n in list)
        {
            if (Vector2.Distance(n.position, node.position) < 0.1f)
            {
                return true;
            }
        }
        return false;
    }
    /// <summary>
    /// 构建从起点到当前节点的路径
    /// </summary>
    private List<Node> BuildPath(Node node)
    {
        List<Node> path = new List<Node>();
        Node currentNode = node;
        // 从目标节点回溯到起点
        while (currentNode.parent != null)
        {
            path.Insert(0, currentNode);
            currentNode = currentNode.parent;
        }
        return path;
    }
    /// <summary>
    /// A*寻路的节点类
    /// </summary>
    private class Node
    {
        public Vector2 position;  // 节点位置
        public Node parent;       // 父节点
        public float g;           // 从起点到当前节点的代价
        public float h;           // 从当前节点到目标节点的估计代价
        public float f;           // f = g + h,总代价
        public Node(Vector2 pos, Node parent)
        {
            this.position = pos;
            this.parent = parent;
        }
    }
    /// <summary>
    /// 攻击目标
    /// </summary>
    private void AttackTarget(float deltaTime)
    {
        CurrentState = UnitState.Attacking;
        // 攻击冷却
        attackCooldown -= deltaTime;
        if (attackCooldown <= 0)
        {
            // 执行攻击
            PerformAttack();
            // 重置冷却
            attackCooldown = 1f / AttackSpeed;
        }
    }
    /// <summary>
    /// 执行攻击
    /// </summary>
    private void PerformAttack()
    {
        if (targetUnit != null && !targetUnit.IsDead)
        {
            // 造成伤害
            targetUnit.TakeDamage(Attack);
            // 播放攻击动画/特效
            Debug.Log($"{UnitData.Name} 攻击 {targetUnit.UnitData.Name}, 造成 {Attack} 点伤害");
        }
    }
    /// <summary>
    /// 受到伤害
    /// </summary>
    public void TakeDamage(int damage)
    {
        if (IsDead) return;
        // 计算实际伤害(考虑防御等因素)
        int defense = Mathf.Max(0, MaxHP / 20); // 简单防御计算,基于最大生命值
        int actualDamage = Mathf.Max(1, damage - defense); // 确保至少造成1点伤害
        // 扣除生命值
        HP -= actualDamage;
        // 检查是否死亡
        if (HP <= 0)
        {
            HP = 0;
            Die();
        }
        // 显示伤害数字
        Debug.Log($"{UnitData.Name} 受到 {actualDamage} 点伤害, 剩余HP: {HP}/{MaxHP}");
    }
    /// <summary>
    /// 死亡处理
    /// </summary>
    private void Die()
    {
        IsDead = true;
        CurrentState = UnitState.Dead;
        // 播放死亡动画
        Debug.Log($"{UnitData.Name} 死亡");
    }
}
Main/Place/PlaceUnit.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 84bf5729662617b4582cbe05bad8d53c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/LoadingWin.cs
New file
@@ -0,0 +1,73 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class LoadingWin : UIBase
{
    protected int currentProgress = 0;
    protected int targetProgress = 0;
    protected Text titleText;
    protected Text tipsText;
    protected Image progressBar;
    protected Text progressText;
    protected override void InitComponent()
    {
        base.InitComponent();
        titleText = transform.Find("Text_Loading").GetComponent<Text>();
        tipsText = transform.Find("Text_Tips").GetComponent<Text>();
        progressBar = transform.Find("Img_Progress/Img_Foreground").GetComponent<Image>();
        progressText = transform.Find("Text_Progress").GetComponent<Text>();
    }
    public override void HandleOpen()
    {
        base.HandleOpen();
        currentProgress = targetProgress = 0;
        Refresh();
    }
    public override void HandleClose()
    {
        base.HandleClose();
    }
    public override void Refresh()
    {
        base.Refresh();
        UpdateProgress();
    }
    public void SetProgress(float value, bool directly = false)
    {
        if (directly)
        {
            currentProgress = targetProgress = (int)(value * 100);
            UpdateProgress();
        }
        else
        {
            currentProgress = (int)(value * 100);
        }
    }
    protected void UpdateProgress()
    {
        progressText.text = currentProgress + "%";
        progressBar.fillAmount = currentProgress / 100f;
    }
    protected void Update()
    {
        if (currentProgress < targetProgress)
        {
            currentProgress = (int)Mathf.Lerp(currentProgress, targetProgress, 0.1f);
            UpdateProgress();
        }
    }
}
Main/UI/LoadingWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e85e62037b34d9944a44a4c93764c656
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Login.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bef9e971d62416147ae8006de5cbff6a
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Login/LoginWin.cs
New file
@@ -0,0 +1,62 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using UnityEngine;
using UnityEngine.UI;
public class LoginWin : UIBase
{
    private InputField username;
    private InputField password;
    private Button loginBtn;
    private Button registerBtn;
    protected override void InitComponent()
    {
        base.InitComponent();
        Debug.Log("初始化登录窗口");
        username = transform.Find("InputUserName").GetComponent<InputField>();
        password = transform.Find("InputPassword").GetComponent<InputField>();
        loginBtn = transform.Find("Buttons/ButtonLogin").GetComponent<Button>();
        registerBtn = transform.Find("Buttons/ButtonRegister").GetComponent<Button>();
        loginBtn.onClick.AddListener(OnLoginBtnClick);
        registerBtn.onClick.AddListener(OnRegisterBtnClick);
    }
    public override void HandleOpen()
    {
        base.HandleOpen();
        Debug.Log("打开登录窗口");
        ResManager.Instance.PrewarmResources();
        Refresh();
    }
    public override void HandleClose()
    {
        base.HandleClose();
        Debug.Log("关闭登录窗口");
    }
    public override void Refresh()
    {
        base.Refresh();
        Debug.Log("刷新登录窗口");
    }
    private void OnRegisterBtnClick()
    {
        //  TODO 注册
        Debug.Log("注册");
    }
    private void OnLoginBtnClick()
    {
        //  TODO 登录
        Debug.Log("登录" + username.text + " / " + password.text);
        UIManager.Instance.CloseWindow(this);
        StageManager.Instance.ToGameScene();
    }
}
Main/UI/Login/LoginWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1d9e1ce1eab28d84188a1c2db2c1c472
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Main.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 35cec4a6bce278e4b9c8efc66988e0e2
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Main/MainWin.cs
New file
@@ -0,0 +1,206 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 游戏主界面
/// </summary>
public class MainWin : UIBase
{
    private GameObject windowBackground;
    // 底部按钮组
    private Button[] bottomTabButtons;
    // 当前选中的底部标签索引
    private int currentTabIndex = 0;
    // 当前打开的子界面
    private UIBase currentSubUI;
    /// <summary>
    /// 初始化组件
    /// </summary>
    protected override void InitComponent()
    {
        base.InitComponent();
        windowBackground = transform.Find("RawImgBackground").gameObject;
        bottomTabButtons = new Button[5];
        for (int i = 1; i <= 5; i++)
        {
            string buttonName = "Buttons/Button" + i;
            bottomTabButtons[i-1] = transform.Find(buttonName).GetComponent<Button>();
        }
        // 初始化UI组件事件
        InitButtonEvents();
    }
    /// <summary>
    /// 初始化UI组件事件
    /// </summary>
    private void InitButtonEvents()
    {
        // 初始化底部按钮
        for (int i = 0; i < bottomTabButtons.Length; i++)
        {
            int index = i; // 捕获索引
            bottomTabButtons[i].onClick.AddListener(() => {
                OnBottomTabButtonClicked(index);
            });
        }
    }
    protected override void OnOpen()
    {
        base.OnOpen();
        // 默认选中第一个标签
        SelectBottomTab(0);
        // 刷新UI
        Refresh();
    }
    public override void Refresh()
    {
        UpdatePlayerInfo();
        UpdateCurrency();
    }
    /// <summary>
    /// 更新玩家信息
    /// </summary>
    private void UpdatePlayerInfo()
    {
        // 从玩家数据中获取信息并更新UI
        // 例如:
        // playerNameText.text = PlayerData.Instance.Name;
        // playerLevelText.text = "Lv." + PlayerData.Instance.Level;
        // powerText.text = PlayerData.Instance.Power.ToString();
        // expSlider.value = PlayerData.Instance.ExpRatio;
    }
    /// <summary>
    /// 更新货币信息
    /// </summary>
    private void UpdateCurrency()
    {
        // 从玩家数据中获取货币信息并更新UI
        // 例如:
        // goldText.text = PlayerData.Instance.Gold.ToString();
        // diamondText.text = PlayerData.Instance.Diamond.ToString();
        // energyText.text = PlayerData.Instance.Energy + "/" + PlayerData.Instance.MaxEnergy;
    }
    /// <summary>
    /// 底部标签按钮点击
    /// </summary>
    private void OnBottomTabButtonClicked(int index)
    {
        SelectBottomTab(index);
    }
    /// <summary>
    /// 选择底部标签
    /// </summary>
    private void SelectBottomTab(int index)
    {
        // 如果点击当前已选中的标签,不做处理
        if (currentTabIndex == index && currentSubUI != null)
        {
            return;
        }
        // 更新当前选中的标签索引
        currentTabIndex = index;
        // 更新按钮状态
        UpdateButtonsState();
        // 关闭当前打开的子界面
        CloseCurrentSubUI();
        // 根据选中的标签打开对应的界面
        OpenSubUIByTabIndex(index);
    }
    /// <summary>
    /// 更新按钮状态
    /// </summary>
    private void UpdateButtonsState()
    {
        // 遍历所有按钮,设置选中状态
        for (int i = 0; i < bottomTabButtons.Length; i++)
        {
            // 这里可以根据是否选中设置按钮的视觉效果
            // 例如:改变图片、颜色等
            // bottomTabButtons[i].GetComponent<Image>().color = (i == currentTabIndex) ? Color.blue : Color.white;
            // 或者激活/禁用选中图标
            bottomTabButtons[i].image.color = (i == currentTabIndex) ?  Color.white : Color.gray;
        }
    }
    /// <summary>
    /// 关闭当前打开的子界面
    /// </summary>
    private void CloseCurrentSubUI()
    {
        if (currentSubUI != null)
        {
            // 关闭当前界面
            UIManager.Instance.CloseWindow(currentSubUI);
            currentSubUI = null;
        }
    }
    /// <summary>
    /// 根据标签索引打开对应的子界面
    /// </summary>
    private void OpenSubUIByTabIndex(int index)
    {
        Debug.Log("打开子界面 : " + index);
        // 主城 阵容 同盟 福利 冒险
        //根据索引打开不同的界面
         switch (index)
        {
            case 0:
                // 例如:打开主页界面
                // currentSubUI = UIManager.Instance.OpenUI<HomeUI>();
                Debug.Log("打开主城界面");
                break;
            case 1:
                // 例如:打开角色界面
                // currentSubUI = UIManager.Instance.OpenUI<CharacterUI>();
                Debug.Log("打开阵容界面");
                break;
            case 2:
                // 例如:打开背包界面
                // currentSubUI = UIManager.Instance.OpenUI<BagUI>();
                Debug.Log("打开同盟界面");
                break;
            case 3:
                // 例如:打开任务界面
                // currentSubUI = UIManager.Instance.OpenUI<QuestUI>();
                Debug.Log("打开福利界面");
                break;
            case 4:
                // 例如:打开设置界面
                currentSubUI = UIManager.Instance.OpenWindow<PlaceWin>();
                Debug.Log("打开冒险界面");
                windowBackground.SetActive(false);
                break;
            default:
                Debug.LogWarning("未知的标签索引: " + index);
                break;
        }
    }
}
Main/UI/Main/MainWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0b14b415a3ac64548a78d163d6cb7e33
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Place.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2a75dbf92ff839445b4bf6be823add40
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/Place/PlaceWin.cs
New file
@@ -0,0 +1,62 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 场景界面
/// </summary>
public class PlaceWin : UIBase
{
    protected Button btnExplore;
    // protected
    /// <summary>
    /// 初始化组件
    /// </summary>
    protected override void InitComponent()
    {
        base.InitComponent();
        // 在这里初始化组件
        btnExplore = transform.Find("BtnExplore").GetComponent<Button>();
        btnExplore.onClick.AddListener(OnExploreClick);
    }
    /// <summary>
    /// 窗口打开时调用
    /// </summary>
    protected override void OnOpen()
    {
        base.OnOpen();
        // 窗口打开时的逻辑
    }
    /// <summary>
    /// 窗口关闭时调用
    /// </summary>
    protected override void OnClose()
    {
        base.OnClose();
        // 窗口关闭时的逻辑
    }
    /// <summary>
    /// 刷新界面
    /// </summary>
    public override void Refresh()
    {
        // 刷新界面的逻辑
    }
    private void OnExploreClick()
    {
        // UIManager.Instance.OpenUI<ExploreWin>();
    }
}
Main/UI/Place/PlaceWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 846b941e24d55594fac435ba7031cfa9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/UI/UIBase.cs
@@ -1,7 +1,8 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening; // DOTween 插件引用
using DG.Tweening;
using UnityEngine.UI; // DOTween 插件引用
public enum UILayer
{
@@ -23,24 +24,27 @@
    SlideFromRight // 从右侧滑入
}
[RequireComponent(typeof(Canvas))]
[RequireComponent(typeof(CanvasGroup))]
[RequireComponent(typeof(CanvasScaler))]
public class UIBase : MonoBehaviour
{
    #region 属性和变量
    // UI基本属性
    [SerializeField] public UILayer uiLayer = UILayer.Mid;
    [SerializeField] public string uiName;
    [SerializeField][HideInInspector] public string uiName;
    [SerializeField] public bool isMainUI = false;
    
    // 持久化相关
    [SerializeField] public bool isPersistent = false;
    [SerializeField] public int maxIdleRounds = 20;
    [SerializeField][HideInInspector] public int maxIdleRounds = 20;
    
    // 动画相关
    [SerializeField] public UIAnimationType openAnimationType = UIAnimationType.FadeInOut;
    [SerializeField] public UIAnimationType closeAnimationType = UIAnimationType.FadeInOut;
    [SerializeField] public float animationDuration = 0.3f;
    [SerializeField] public Ease animationEase = Ease.OutQuad; // 确保使用 DG.Tweening.Ease
    [SerializeField] public UIAnimationType openAnimationType = UIAnimationType.None;
    [SerializeField] public UIAnimationType closeAnimationType = UIAnimationType.None;
    [SerializeField][HideInInspector] public float animationDuration = 0.3f;
    [SerializeField][HideInInspector] public Ease animationEase = Ease.OutQuad; // 确保使用 DG.Tweening.Ease
    
    // 运行时状态
    [HideInInspector] public int lastUsedRound = 0;
@@ -132,13 +136,6 @@
        rectTransform = GetComponent<RectTransform>();
    }
    
    // 初始化UI
    public virtual void Init()
    {
        // 确保组件已初始化
        InitComponent();
    }
    #endregion
    #region UI操作方法
@@ -153,22 +150,23 @@
    }
    
    // 打开UI
    public virtual void Open()
    public virtual void HandleOpen()
    {
        // 如果正在播放动画,先停止
        StopCurrentAnimation();
        
        gameObject.SetActive(true);
        isActive = true;
        
        // 根据动画类型播放打开动画
        PlayOpenAnimation();
        
        isActive = true;
        OnOpen();
    }
    
    // 关闭UI
    public virtual void Close()
    public virtual void HandleClose()
    {
        // 如果正在播放动画,先停止
        StopCurrentAnimation();
@@ -176,17 +174,14 @@
        // 根据动画类型播放关闭动画
        PlayCloseAnimation();
        
        isActive = false;
        OnClose();
        gameObject.SetActive(false);
        isActive = false;
    }
        
        // 关闭所有子UI
        for (int i = childrenUI.Count - 1; i >= 0; i--)
    public void CloseWindow()
        {
            if (childrenUI[i] != null && childrenUI[i].isActive)
            {
                childrenUI[i].Close();
            }
        }
        UIManager.Instance.CloseWindow(this);
    }
    
    // 刷新UI
@@ -371,7 +366,8 @@
            }
            
            // 动画完成后的回调
            currentAnimation.OnComplete(() => {
            currentAnimation.OnComplete(() =>
            {
                isAnimating = false;
                
                // 启用交互
@@ -501,7 +497,8 @@
            }
            
            // 动画完成后的回调
            currentAnimation.OnComplete(() => {
            currentAnimation.OnComplete(() =>
            {
                isAnimating = false;
                
                // 确保UI不可见且不可交互
Main/UI/UIManager.cs
@@ -38,7 +38,7 @@
    #region 初始化方法
    
    // 初始化
    private void Init()
    public void Init()
    {
        InitUIRoot();
    }
@@ -46,7 +46,18 @@
    // 创建UI根节点
    private void InitUIRoot()
    {
        GameObject root = GameObject.Instantiate(Resources.Load<GameObject>("UIRoot"));
        GameObject root = GameObject.Find("UIRoot");
        if (root == null)
        {
            root = GameObject.Instantiate(Resources.Load<GameObject>("UI/UIRoot"));
            if (root == null)
            {
                Debug.LogError("无法找到UI根节点");
                return;
            }
        }
        uiRoot = root.transform;
        uiRoot.position = Vector3.zero;
@@ -118,8 +129,9 @@
    }
    
    // 获取UI实例,如果不存在则返回null
    private UIBase GetUI(string uiName)
    public T GetUI<T>() where T : UIBase
    {
        string uiName = typeof(T).Name;
        if (string.IsNullOrEmpty(uiName))
        {
            Debug.LogError("UI名称为空");
@@ -129,7 +141,7 @@
        UIBase ui;
        if (uiDict.TryGetValue(uiName, out ui))
        {
            return ui;
            return ui as T;
        }
        
        return null;
@@ -171,16 +183,10 @@
    #region UI资源管理
    
    // 加载UI预制体
    private UIBase LoadUIResource(string uiName)
    private T LoadUIResource<T>(string uiName) where T : UIBase
    {
        // 这里是资源加载部分,根据项目实际情况实现
        // 可以使用Resources.Load或者其他资源管理系统
        // 示例代码,实际项目中需要替换为真实的资源加载逻辑
        GameObject prefab = null;
        // TODO: 在这里实现资源加载逻辑
        // prefab = Resources.Load<GameObject>("UI/" + uiName);
        GameObject prefab = ResManager.Instance.LoadUI(uiName);
        if (prefab == null)
        {
@@ -189,7 +195,8 @@
        }
        GameObject uiObject = GameObject.Instantiate(prefab);
        UIBase uiBase = uiObject.GetComponent<UIBase>();
        uiObject.name = uiName;
        T uiBase = uiObject.GetComponent<T>();
        if (uiBase == null)
        {
@@ -202,8 +209,6 @@
        // 设置父节点为UI根节点
        uiObject.transform.SetParent(GetTransForLayer(uiBase.uiLayer), false);
        // 初始化UI
        uiBase.Init();
        // 设置排序顺序
        int baseSortingOrder = GetBaseSortingOrderForLayer(uiBase.uiLayer);
@@ -296,8 +301,9 @@
    #region 公共接口方法
    
    // 打开单个UI窗口
    public UIBase OpenWindow(string uiName)
    public T OpenWindow<T>() where T : UIBase
    {
        string uiName = typeof(T).Name;
        if (string.IsNullOrEmpty(uiName))
        {
            Debug.LogError("尝试打开空名称的UI窗口");
@@ -308,12 +314,12 @@
        currentRound++;
        
        // 先尝试获取已加载的UI
        UIBase ui = GetUI(uiName);
        T ui = GetUI<T>();
        
        // 如果UI不存在,则加载
        if (ui == null)
        {
            ui = LoadUIResource(uiName);
            ui = LoadUIResource<T>(uiName);
            if (ui == null)
                return null;
                
@@ -370,7 +376,7 @@
                    }
                    // 关闭当前UI链上的非System层UI
                    topUI.Close();
                    topUI.HandleClose();
                    // 清理父子关系
                    if (topUI.parentUI != null)
@@ -389,7 +395,7 @@
            }
        }
        ui.Open();
        ui.HandleOpen();
        
        // 如果栈中有其他UI,则将栈顶UI设为父UI
        if (!ui.isMainUI && ui.uiLayer != UILayer.System && uiStack.Count > 0)
@@ -449,8 +455,10 @@
            return;
        
        // 创建一个列表来存储需要关闭的UI
        List<UIBase> uisToClose = new List<UIBase>();
        uisToClose.Add(ui);
        List<UIBase> uisToClose = new List<UIBase>
        {
            ui
        };
        
        // 递归收集所有子UI
        CollectChildrenUI(ui, uisToClose);
@@ -465,7 +473,7 @@
        foreach (var uiToClose in uisToClose)
        {
            // 关闭UI
            uiToClose.Close();
            uiToClose.HandleClose();
            
            // 清理父子关系
            if (uiToClose.parentUI != null)
@@ -486,19 +494,6 @@
        RemoveFromUIStack(uisToClose);
    }
    
    // 关闭所有UI窗口
    public void CloseAllWindows()
    {
        foreach (var ui in uiDict.Values)
        {
            if (ui != null)
            {
                ui.Close();
            }
        }
        uiStack.Clear();
    }
    // 返回上一级UI
    public void GoBack()
@@ -508,7 +503,8 @@
        
        // 关闭当前UI
        UIBase currentUI = uiStack.Pop();
        currentUI.Close();
        CloseWindow(currentUI);
        
        // 更新排序顺序
        UpdateUISortingOrder();
@@ -545,7 +541,9 @@
    // 销毁所有UI
    public void DestroyAllUI()
    {
        foreach (var ui in uiDict.Values)
        List<string> uiNames = new List<string>(uiDict.Keys);
        foreach (var ui in uiNames)
        {
            if (ui != null)
            {
Utility/DontDestroyOnLoad.cs
New file
@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DontDestroyOnLoad : MonoBehaviour
{
    void Awake()
    {
        DontDestroyOnLoad(gameObject);
    }
}
Utility/DontDestroyOnLoad.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ebddc3e48e1baa844a56615981c5c152
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant: