瀏覽代碼

根据配表自动生成怪物逻辑

wulifu 1 年之前
父節點
當前提交
6a1a145f85

+ 8 - 2
ActionTowerDefense/Assets/Gen/CfgEnemy.cs

@@ -16,23 +16,29 @@ namespace cfg
 
 public sealed partial class CfgEnemy
 {
+    private readonly Dictionary<int, SingleEnemyConfig> _dataMap;
     private readonly List<SingleEnemyConfig> _dataList;
     
-
     public CfgEnemy(JSONNode _json)
     {
+        _dataMap = new Dictionary<int, SingleEnemyConfig>();
         _dataList = new List<SingleEnemyConfig>();
         
         foreach(JSONNode _row in _json.Children)
         {
             var _v = SingleEnemyConfig.DeserializeSingleEnemyConfig(_row);
             _dataList.Add(_v);
+            _dataMap.Add(_v.ID, _v);
         }
         PostInit();
     }
 
+    public Dictionary<int, SingleEnemyConfig> DataMap => _dataMap;
     public List<SingleEnemyConfig> DataList => _dataList;
 
+    public SingleEnemyConfig GetOrDefault(int key) => _dataMap.TryGetValue(key, out var v) ? v : null;
+    public SingleEnemyConfig Get(int key) => _dataMap[key];
+    public SingleEnemyConfig this[int key] => _dataMap[key];
 
     public void Resolve(Dictionary<string, object> _tables)
     {
@@ -50,7 +56,7 @@ public sealed partial class CfgEnemy
             v.TranslateText(translator);
         }
     }
-
+    
     
     partial void PostInit();
     partial void PostResolve();

+ 15 - 1
ActionTowerDefense/Assets/Gen/SingleCreateEnemyConfig.cs

@@ -24,10 +24,12 @@ public sealed partial class SingleCreateEnemyConfig :  Bright.Config.BeanBase
         { if(!_json["Time"].IsNumber) { throw new SerializationException(); }  Time = _json["Time"]; }
         { if(!_json["TimeInterval"].IsNumber) { throw new SerializationException(); }  TimeInterval = _json["TimeInterval"]; }
         { var __json0 = _json["Position"]; if(!__json0.IsArray) { throw new SerializationException(); } Position = new System.Collections.Generic.List<float>(__json0.Count); foreach(JSONNode __e0 in __json0.Children) { float __v0;  { if(!__e0.IsNumber) { throw new SerializationException(); }  __v0 = __e0; }  Position.Add(__v0); }   }
+        { if(!_json["AttackRatio"].IsNumber) { throw new SerializationException(); }  AttackRatio = _json["AttackRatio"]; }
+        { if(!_json["HPRatio"].IsNumber) { throw new SerializationException(); }  HPRatio = _json["HPRatio"]; }
         PostInit();
     }
 
-    public SingleCreateEnemyConfig(int ID, int EnemyID, int Count, float Time, float TimeInterval, System.Collections.Generic.List<float> Position ) 
+    public SingleCreateEnemyConfig(int ID, int EnemyID, int Count, float Time, float TimeInterval, System.Collections.Generic.List<float> Position, float AttackRatio, float HPRatio ) 
     {
         this.ID = ID;
         this.EnemyID = EnemyID;
@@ -35,6 +37,8 @@ public sealed partial class SingleCreateEnemyConfig :  Bright.Config.BeanBase
         this.Time = Time;
         this.TimeInterval = TimeInterval;
         this.Position = Position;
+        this.AttackRatio = AttackRatio;
+        this.HPRatio = HPRatio;
         PostInit();
     }
 
@@ -67,6 +71,14 @@ public sealed partial class SingleCreateEnemyConfig :  Bright.Config.BeanBase
     /// 出怪位置
     /// </summary>
     public System.Collections.Generic.List<float> Position { get; private set; }
+    /// <summary>
+    /// 攻击力倍率
+    /// </summary>
+    public float AttackRatio { get; private set; }
+    /// <summary>
+    /// 血量倍率
+    /// </summary>
+    public float HPRatio { get; private set; }
 
     public const int __ID__ = 691467974;
     public override int GetTypeId() => __ID__;
@@ -89,6 +101,8 @@ public sealed partial class SingleCreateEnemyConfig :  Bright.Config.BeanBase
         + "Time:" + Time + ","
         + "TimeInterval:" + TimeInterval + ","
         + "Position:" + Bright.Common.StringUtil.CollectionToString(Position) + ","
+        + "AttackRatio:" + AttackRatio + ","
+        + "HPRatio:" + HPRatio + ","
         + "}";
     }
     

+ 15 - 8
ActionTowerDefense/Assets/Gen/SingleEnemyConfig.cs

@@ -21,16 +21,18 @@ public sealed partial class SingleEnemyConfig :  Bright.Config.BeanBase
         { if(!_json["ID"].IsNumber) { throw new SerializationException(); }  ID = _json["ID"]; }
         { if(!_json["EnemyPrefab"].IsString) { throw new SerializationException(); }  EnemyPrefab = _json["EnemyPrefab"]; }
         { if(!_json["HP"].IsNumber) { throw new SerializationException(); }  HP = _json["HP"]; }
-        { if(!_json["Damage"].IsNumber) { throw new SerializationException(); }  Damage = _json["Damage"]; }
+        { var __json0 = _json["Attack1"]; if(!__json0.IsArray) { throw new SerializationException(); } Attack1 = new System.Collections.Generic.List<int>(__json0.Count); foreach(JSONNode __e0 in __json0.Children) { int __v0;  { if(!__e0.IsNumber) { throw new SerializationException(); }  __v0 = __e0; }  Attack1.Add(__v0); }   }
+        { var __json0 = _json["Attack2"]; if(!__json0.IsArray) { throw new SerializationException(); } Attack2 = new System.Collections.Generic.List<int>(__json0.Count); foreach(JSONNode __e0 in __json0.Children) { int __v0;  { if(!__e0.IsNumber) { throw new SerializationException(); }  __v0 = __e0; }  Attack2.Add(__v0); }   }
         PostInit();
     }
 
-    public SingleEnemyConfig(int ID, string EnemyPrefab, int HP, int Damage ) 
+    public SingleEnemyConfig(int ID, string EnemyPrefab, int HP, System.Collections.Generic.List<int> Attack1, System.Collections.Generic.List<int> Attack2 ) 
     {
         this.ID = ID;
         this.EnemyPrefab = EnemyPrefab;
         this.HP = HP;
-        this.Damage = Damage;
+        this.Attack1 = Attack1;
+        this.Attack2 = Attack2;
         PostInit();
     }
 
@@ -40,7 +42,7 @@ public sealed partial class SingleEnemyConfig :  Bright.Config.BeanBase
     }
 
     /// <summary>
-    /// 出怪批次
+    /// 怪物ID
     /// </summary>
     public int ID { get; private set; }
     /// <summary>
@@ -48,13 +50,17 @@ public sealed partial class SingleEnemyConfig :  Bright.Config.BeanBase
     /// </summary>
     public string EnemyPrefab { get; private set; }
     /// <summary>
-    /// 怪物血量
+    /// 基础血量
     /// </summary>
     public int HP { get; private set; }
     /// <summary>
-    /// 攻击力
+    /// Attack1攻击力
     /// </summary>
-    public int Damage { get; private set; }
+    public System.Collections.Generic.List<int> Attack1 { get; private set; }
+    /// <summary>
+    /// Attack2攻击力
+    /// </summary>
+    public System.Collections.Generic.List<int> Attack2 { get; private set; }
 
     public const int __ID__ = 491839330;
     public override int GetTypeId() => __ID__;
@@ -74,7 +80,8 @@ public sealed partial class SingleEnemyConfig :  Bright.Config.BeanBase
         + "ID:" + ID + ","
         + "EnemyPrefab:" + EnemyPrefab + ","
         + "HP:" + HP + ","
-        + "Damage:" + Damage + ","
+        + "Attack1:" + Bright.Common.StringUtil.CollectionToString(Attack1) + ","
+        + "Attack2:" + Bright.Common.StringUtil.CollectionToString(Attack2) + ","
         + "}";
     }
     

+ 60 - 62
ActionTowerDefense/Assets/Scenes/SampleScene.unity

@@ -286,7 +286,7 @@ Transform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 0}
-  m_RootOrder: 4
+  m_RootOrder: 3
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!1 &519420028
 GameObject:
@@ -428,67 +428,6 @@ Transform:
   m_Father: {fileID: 0}
   m_RootOrder: 2
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1001 &792970844
-PrefabInstance:
-  m_ObjectHideFlags: 0
-  serializedVersion: 2
-  m_Modification:
-    m_TransformParent: {fileID: 0}
-    m_Modifications:
-    - target: {fileID: 2070274865187464477, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_Mesh
-      value: 
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462361, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_Name
-      value: Enemy_Sword
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_RootOrder
-      value: 3
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalPosition.x
-      value: 8.01
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalPosition.y
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalPosition.z
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalRotation.w
-      value: 1
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalRotation.x
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalRotation.y
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalRotation.z
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalEulerAnglesHint.x
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalEulerAnglesHint.y
-      value: 0
-      objectReference: {fileID: 0}
-    - target: {fileID: 2437299196472462364, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
-      propertyPath: m_LocalEulerAnglesHint.z
-      value: 0
-      objectReference: {fileID: 0}
-    m_RemovedComponents: []
-  m_SourcePrefab: {fileID: 100100000, guid: aaf35db4e76c3004fb462f102cf8535f, type: 3}
 --- !u!114 &1192490554 stripped
 MonoBehaviour:
   m_CorrespondingSourceObject: {fileID: 3571941038519084336, guid: 5b538f610930dd743a096c582e2810f4, type: 3}
@@ -791,6 +730,65 @@ MeshFilter:
   m_PrefabAsset: {fileID: 0}
   m_GameObject: {fileID: 1687724998}
   m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &1714821084
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1714821086}
+  - component: {fileID: 1714821085}
+  - component: {fileID: 1714821087}
+  m_Layer: 0
+  m_Name: GameManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1714821085
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1714821084}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a63e480014f84f04c8f1bb1145122988, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  gameTime: 0
+--- !u!4 &1714821086
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1714821084}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1714821087
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1714821084}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 0b88ebc6e4d3df6439f5464df5614ec5, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  created: 
 --- !u!1 &2045679344
 GameObject:
   m_ObjectHideFlags: 0

+ 35 - 1
ActionTowerDefense/Assets/Scripts/AttackTrigger.cs

@@ -31,7 +31,41 @@ public class AttackTrigger : MonoBehaviour
             if (!triged)
             {
                 trigedObjs.Add(hitTrigger);
-                hitTrigger.BeHit(damage, force);
+                switch (owner.tag)
+                {
+                    case "Player":
+                        if (hitTrigger.owner.tag == "Enemy" || hitTrigger.owner.tag == "EnemyTower")
+                        {
+                            hitTrigger.BeHit(damage, force);
+                        }
+                        break;
+                    case "Demonic":
+                        if (hitTrigger.owner.tag == "Enemy" || hitTrigger.owner.tag == "EnemyTower")
+                        {
+                            hitTrigger.BeHit(damage, force);
+                        }
+                        break;
+                    case "Tower":
+                        if (hitTrigger.owner.tag == "Enemy" || hitTrigger.owner.tag == "EnemyTower")
+                        {
+                            hitTrigger.BeHit(damage, force);
+                        }
+                        break;
+                    case "Enemy":
+                        if (hitTrigger.owner.tag == "Player" || hitTrigger.owner.tag == "Demonic" || hitTrigger.owner.tag == "Tower")
+                        {
+                            hitTrigger.BeHit(damage, force);
+                        }
+                        break;
+                    case "EnemyTower":
+                        if (hitTrigger.owner.tag == "Player" || hitTrigger.owner.tag == "Demonic" || hitTrigger.owner.tag == "Tower")
+                        {
+                            hitTrigger.BeHit(damage, force);
+                        }
+                        break;
+                    default:
+                        break;
+                }
             }
         }
     }

+ 1 - 2
ActionTowerDefense/Assets/Scripts/Enemy.cs

@@ -34,12 +34,11 @@ public class Enemy : MoveCharacter
     public bool canHitFly = false;
 
     public Character targetCharacter;
+    public float attackRatio;
 
     private void Awake()
     {
         Init();
-        Debug.Log("²âÊÔ");
-        totalHp = 100;
         Reload();
     }
 

+ 79 - 0
ActionTowerDefense/Assets/Scripts/EnemyCreater.cs

@@ -0,0 +1,79 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using cfg;
+using System.Threading.Tasks;
+using Base.Common;
+
+public class EnemyCreater : MonoBehaviour
+{
+    public static EnemyCreater instance;
+    public List<SingleCreateEnemyConfig> cfgCreateEnemy;
+    public List<bool> created;
+
+    private void Awake()
+    {
+        if (!instance)
+        {
+            instance = this;
+        }
+        else
+        {
+            DestroyImmediate(gameObject);
+            return;
+        }
+    }
+
+    private void Start()
+    {
+        cfgCreateEnemy = GameManager.instance.allCfgData.CfgCreateEnemy.DataList;
+        created = new List<bool>();
+        for (int i = 0; i < cfgCreateEnemy.Count; i++)
+        {
+            created.Add(false);
+        }
+    }
+
+    public void OnGameTimeChange(float gameTime)
+    {
+        for (int i = 0; i < cfgCreateEnemy.Count; i++)
+        {
+            if (cfgCreateEnemy[i].Time <= gameTime && !created[i])
+            {
+                created[i] = true;
+                StartCreateEnemy(i);
+            }
+        }
+    }
+
+    public async void StartCreateEnemy(int id)
+    {
+        SingleCreateEnemyConfig singleCreateEnemy = cfgCreateEnemy[id];
+        for (int i = 0; i < singleCreateEnemy.Count; i++)
+        {
+            Vector3 pos = new Vector3(singleCreateEnemy.Position[0], singleCreateEnemy.Position[1], singleCreateEnemy.Position[2]);
+            CreateEnemy(singleCreateEnemy.EnemyID, pos, singleCreateEnemy.HPRatio, singleCreateEnemy.AttackRatio);
+            await Task.Delay((int)(singleCreateEnemy.TimeInterval * 1000));
+        }
+    }
+
+    public void CreateEnemy(int enemyId, Vector3 pos, float hpRatio, float attackRatio)
+    {
+        SingleEnemyConfig cfgEnemy = GameManager.instance.allCfgData.CfgEnemy.Get(enemyId);
+        GameObject enemyObj = Util.Instantiate(cfgEnemy.EnemyPrefab, pos);
+        Enemy enemy = enemyObj.GetComponent<Enemy>();
+        enemy.totalHp = (int)(cfgEnemy.HP * hpRatio);
+        for (int i = 0; i < cfgEnemy.Attack1.Count; i++)
+        {
+            AttackInfo attackInfo = enemy.attack1Infos[i];
+            attackInfo.damage = (int)(cfgEnemy.Attack1[i] * attackRatio);
+            enemy.attack1Infos[i] = attackInfo;
+        }
+        for (int i = 0; i < cfgEnemy.Attack1.Count; i++)
+        {
+            AttackInfo attackInfo = enemy.attack2Infos[i];
+            attackInfo.damage = (int)(cfgEnemy.Attack2[i] * attackRatio);
+            enemy.attack2Infos[i] = attackInfo;
+        }
+    }
+}

+ 11 - 0
ActionTowerDefense/Assets/Scripts/EnemyCreater.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0b88ebc6e4d3df6439f5464df5614ec5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 100
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 48 - 0
ActionTowerDefense/Assets/Scripts/GameManager.cs

@@ -0,0 +1,48 @@
+using cfg;
+using SimpleJSON;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+
+public class GameManager : MonoBehaviour
+{
+    public static GameManager instance;
+    public float gameTime;
+    public Tables allCfgData;
+
+    private JSONNode Loader(string fileName)
+    {
+        return JSON.Parse(File.ReadAllText("GenerateDatas/json/" + fileName + ".json"));
+    }
+
+    public void GetAllExcel()
+    {
+        allCfgData = new Tables(Loader);
+    }
+
+    private void Awake()
+    {
+        if (!instance)
+        {
+            instance = this;
+        }
+        else
+        {
+            DestroyImmediate(gameObject);
+            return;
+        }
+    }
+
+    private void Start()
+    {
+        gameTime = 0;
+        GetAllExcel();
+    }
+
+    private void FixedUpdate()
+    {
+        gameTime += Time.deltaTime;
+        EnemyCreater.instance.OnGameTimeChange(gameTime);
+    }
+}

+ 11 - 0
ActionTowerDefense/Assets/Scripts/GameManager.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a63e480014f84f04c8f1bb1145122988
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 5
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 21 - 0
ActionTowerDefense/Assets/Scripts/PoolItem.cs

@@ -0,0 +1,21 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class PoolItem : MonoBehaviour
+{
+    public GameObject prefab;
+    private void OnDisable()
+    {
+        if (gameObject.activeSelf)
+        {
+            return;//防止是父物体去激活的情况
+        }
+        PoolManager.instance.Recycle(this);
+    }
+
+    private void OnDestroy()
+    {
+        PoolManager.instance.DestroyItem(this);
+    }
+}

+ 11 - 0
ActionTowerDefense/Assets/Scripts/PoolItem.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c4559615cb7cb3e4482cff794908cb04
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 66 - 0
ActionTowerDefense/Assets/Scripts/PoolManager.cs

@@ -0,0 +1,66 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class PoolManager : MonoBehaviour
+{
+    public static PoolManager instance;
+    public Dictionary<GameObject, List<GameObject>> pools;
+
+    void Awake()
+    {
+        if (instance)
+        {
+            Destroy(gameObject);
+            return;
+        }
+        instance = this;
+
+        pools = new Dictionary<GameObject, List<GameObject>>();
+    }
+
+    public static GameObject Instantiate(GameObject prefab, Vector3 pos = default, Quaternion rotation = default, Transform parent = null)
+    {
+        GameObject go;
+        if (!instance)
+        {
+            go = Object.Instantiate(prefab, pos, rotation, parent);
+            go.SetActive(true);
+            return go;
+        }
+        if (instance.pools.ContainsKey(prefab) && instance.pools[prefab].Count > 0)
+        {
+            go = instance.pools[prefab][0];
+            go.transform.parent = parent;
+            go.transform.position = pos;
+            go.transform.rotation = rotation;
+            instance.pools[prefab].RemoveAt(0);
+        }
+        else
+        {
+            go = Object.Instantiate(prefab, pos, rotation, parent);
+            PoolItem poolItem = go.AddComponent<PoolItem>();
+            poolItem.prefab = prefab;
+        }
+        go.SetActive(true);
+        return go;
+    }
+
+    public void Recycle(PoolItem poolItem)
+    {
+        if (!pools.ContainsKey(poolItem.prefab))
+        {
+            pools.Add(poolItem.prefab, new List<GameObject>());
+        }
+        pools[poolItem.prefab].Add(poolItem.gameObject);
+    }
+
+    public void DestroyItem(PoolItem poolItem)
+    {
+        if (!pools.ContainsKey(poolItem.prefab))
+        {
+            return;
+        }
+        pools[poolItem.prefab].Remove(poolItem.gameObject);
+    }
+}

+ 11 - 0
ActionTowerDefense/Assets/Scripts/PoolManager.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 089f096f5129267449881ed8ae316682
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 680 - 0
ActionTowerDefense/Assets/Scripts/Util.cs

@@ -0,0 +1,680 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using UnityEngine;
+using UnityEngine.UI;
+using Object = UnityEngine.Object;
+using Random = System.Random;
+using System.Net.Sockets;
+using System.Net.NetworkInformation;
+using UnityEngine.Events;
+using System.Threading.Tasks;
+
+namespace Base.Common
+{
+    public class Util
+    {
+
+        public static int UID = 0;
+        
+        static string[] kSizeSuffix = new string[]
+        {
+            "B",
+            "KB",
+            "MB",
+            "GB",
+            "TB",
+            "PB",
+        };
+
+        private static StringBuilder sTmpStringBuidler = new StringBuilder();
+
+        private static Dictionary<string, Type> sPredefineComponentTypes = new Dictionary<string, Type>()
+        {
+        };
+
+        public static void SetUID(int uid)
+        {
+            UID = uid;
+        }
+        
+        public static string ColorToHex(Color32 color)
+        {
+            string hex = color.r.ToString("X2") + color.g.ToString("X2") + color.b.ToString("X2");
+            return hex;
+        }
+        
+        /// <summary>
+        /// HashToMD5Hex
+        /// </summary>
+        public static string HashToMD5Hex(string sourceStr)
+        {
+            byte[] Bytes = Encoding.UTF8.GetBytes(sourceStr);
+            using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
+            {
+                byte[] result = md5.ComputeHash(Bytes);
+                StringBuilder builder = new StringBuilder();
+                for (int i = 0; i < result.Length; i++)
+                    builder.Append(result[i].ToString("x2"));
+                return builder.ToString();
+            }
+        }
+
+        public static string GetFileMD5(string filePath)
+        {
+            try
+            {
+                using (FileStream fs = new FileStream(filePath, FileMode.Open))
+                {
+                    using (MD5 md5 = new MD5CryptoServiceProvider())
+                    {
+                        byte[] result = md5.ComputeHash(fs);
+                        StringBuilder sb = new StringBuilder();
+                        foreach (byte b in result)
+                        {
+                            sb.Append(b.ToString("x2"));
+                        }
+
+                        return sb.ToString();
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                return string.Empty;
+            }
+        }
+
+        public static string GetMD5(byte[] bytes)
+        {
+            using (MD5 md5 = new MD5CryptoServiceProvider())
+            {
+                byte[] result = md5.ComputeHash(bytes);
+                StringBuilder sb = new StringBuilder();
+                foreach (byte b in result)
+                {
+                    sb.Append(b.ToString("x2"));
+                }
+
+                return sb.ToString();
+            }
+        }
+        
+        public static GameObject LoadGameObject(string path)
+        {
+            GameObject prefab;
+            prefab = Resources.Load<GameObject>(path);
+            if (!prefab)
+            {
+                Debug.LogError("未读取到prefab:" + path);
+                return null;
+            }
+            return prefab;
+        }
+
+        public static GameObject Instantiate(string path, Vector3 pos = default, Quaternion rotation = default, Transform parent = null)
+        {
+            GameObject prefab;
+            
+            prefab = Resources.Load<GameObject>(path);
+            if (!prefab)
+            {
+                Debug.LogError("未读取到prefab:" + path);
+                return null;
+            }
+
+            GameObject gameObject = null;
+            if (prefab != null)
+            {
+                gameObject = PoolManager.Instantiate(prefab, pos, rotation, parent);
+                gameObject.SetActive(true);
+            }
+            else
+            {
+                Debug.LogError("PrefabPathError:" + path);
+            }
+
+            return gameObject;
+        }
+
+        /// <summary>
+        /// 判断物体是否为空,主要给Lua调用
+        /// </summary>
+        /// <param name="o"></param>
+        /// <returns></returns>
+        public static bool IsObjectNull(Object o)
+        {
+            return o == null;
+        }
+
+        private static Type GetComponentType(string _compname)
+        {
+            if (string.IsNullOrEmpty(_compname))
+                return null;
+            Type t = null;
+            if (sPredefineComponentTypes.ContainsKey(_compname))
+                return sPredefineComponentTypes[_compname];
+
+            Assembly assb = Assembly.GetExecutingAssembly(); //.GetExecutingAssembly();
+            t = assb.GetType(_compname);
+            if (null == t)
+            {
+                return null;
+            }
+
+            sPredefineComponentTypes[_compname] = t;
+
+            return t;
+        }
+
+        public static Component GetComponentInChildren(GameObject _go, string _compname)
+        {
+            if (_go == null)
+                return null;
+            Type t = GetComponentType(_compname);
+            if (null == t)
+            {
+                return null;
+            }
+
+            return _go.GetComponentInChildren(t);
+        }
+
+        public static Component GetComponentInChildren(Component _comp, string _compname)
+        {
+            if (_comp == null)
+                return null;
+            Type t = GetComponentType(_compname);
+            if (null == t)
+            {
+                return null;
+            }
+
+            return _comp.GetComponentInChildren(t);
+        }
+
+        public static string GetReadableSize(long size)
+        {
+            long step = 1024;
+            double filesize = size;
+            int size_suffix_index = 0;
+            while (filesize > step)
+            {
+                if (size_suffix_index >= kSizeSuffix.Length - 1)
+                {
+                    size_suffix_index = kSizeSuffix.Length - 1;
+                    break;
+                }
+
+                ++size_suffix_index;
+                filesize /= step;
+            }
+
+            return string.Format("{0:F2}{1}", filesize, kSizeSuffix[size_suffix_index]);
+        }
+
+        /// <summary>
+        /// 打开URL
+        /// </summary>
+        /// <param name="url"></param>
+        public static void OpenUrl(string url)
+        {
+            Application.OpenURL(url);
+        }
+
+        #region PlayerPrefs
+
+        public static int GetInt(string key, int value = default(int))
+        {
+            return PlayerPrefs.GetInt(key, value);
+        }
+
+        public static bool HasKey(string key)
+        {
+            return PlayerPrefs.HasKey(key);
+        }
+
+        public static bool HasString(string key)
+        {
+            return PlayerPrefs.HasKey(key);
+        }
+
+        public static void SetInt(string key, int value = default(int))
+        {
+            PlayerPrefs.DeleteKey(key);
+            PlayerPrefs.SetInt(key, value);
+        }
+
+        public static string GetString(string key, string value = default(string))
+        {
+            return PlayerPrefs.GetString(key, value);
+        }
+
+        /// <summary>
+        /// 保存数据
+        /// </summary>
+        public static void SetString(string key, string value = default(string))
+        {
+            PlayerPrefs.DeleteKey(key);
+            PlayerPrefs.SetString(key, value);
+        }
+
+        /// <summary>
+        /// 删除数据
+        /// </summary>
+        public static void RemoveKey(string key)
+        {
+            PlayerPrefs.DeleteKey(key);
+        }
+
+        public static bool CheckKeyAccord2User(string key)
+        {
+            string k = GetKeyAccord2User(key);
+            return PlayerPrefs.HasKey(k);
+        }
+
+        public static string GetKeyAccord2User(string key)
+        {
+            return $"{key}@{UID}";
+        }
+
+        public static void SetIntAccord2User(string key, int value)
+        {
+            string k = GetKeyAccord2User(key);
+            PlayerPrefs.DeleteKey(k);
+            PlayerPrefs.SetInt(k, value);
+        }
+
+        public static int GetIntAccord2User(string key, int value = default(int))
+        {
+            return PlayerPrefs.GetInt(GetKeyAccord2User(key), value);
+        }
+
+        public static int GetInt4User(string key)
+        {
+            return PlayerPrefs.GetInt(key);
+        }
+
+        public static void SetStringAccord2User(string key, string value)
+        {
+            string k = GetKeyAccord2User(key);
+            PlayerPrefs.DeleteKey(k);
+            PlayerPrefs.SetString(k, value);
+        }
+
+        public static string GetStringAccord2User(string key, string value = default(string))
+        {
+            return PlayerPrefs.GetString(GetKeyAccord2User(key), value);
+        }
+
+        public static void RemoveKeyAccord2User(string key)
+        {
+            string k = GetKeyAccord2User(key);
+            PlayerPrefs.DeleteKey(k);
+        }
+
+        #endregion
+
+        /// <summary>
+        /// 清理内存
+        /// </summary>
+        public static void ClearMemory()
+        {
+            Resources.UnloadUnusedAssets();
+            GC.Collect();
+        }
+
+        public static void SetAllChild(Transform trParent, System.Action<Transform> SetChild)
+        {
+            Queue<Transform> childList = new Queue<Transform>();
+            childList.Enqueue(trParent);
+            while (childList.Count > 0)
+            {
+                Transform t = childList.Dequeue();
+
+                if (null != SetChild) SetChild(t);
+
+                for (int i = 0; i < t.childCount; i++)
+                {
+                    childList.Enqueue(t.GetChild(i));
+                }
+            }
+        }
+        
+        public static bool TryParseEnum<T>(string value, out T result)
+        {
+            bool isSuccess = false;
+            try
+            {
+                result = (T)Enum.Parse(typeof(T), value);
+                isSuccess = true;
+            }
+            catch
+            {
+                result = default(T);
+                isSuccess = false;
+            }
+            
+            return isSuccess;
+        }
+
+        public static bool EnumEquals(Enum m, Enum n)
+        {
+            return Equals(m, n);
+        }
+
+        /// <summary>
+        /// 设置对象层级
+        /// </summary>
+        /// <param name="obj"> 目标对象 </param>
+        /// <param name="layer"> 层级值 </param>
+        /// <param name="includeChildren"> 是否包含子节点标识,默认为true </param>
+        public static void SetLayer(GameObject obj, int layer, bool includeChildren = true)
+        {
+            if (null == obj)
+                return;
+
+            obj.layer = layer;
+            if (!includeChildren)
+                return;
+            
+            var allTransform = obj.GetComponentsInChildren<Transform>(true);
+            foreach (Transform trans in allTransform)
+            {
+                trans.gameObject.layer = layer;
+            }
+        }
+        
+        /// <summary>
+        /// 设置对象层级,通过层级名称
+        /// </summary>
+        /// <param name="obj"> 目标对象 </param>
+        /// <param name="layerName"> 层级名称 </param>
+        /// <param name="includeChildren"> 是否包含子节点标识,默认为true </param>
+        public static void SetLayerByName(GameObject obj, string layerName, bool includeChildren = true)
+        {
+            int layer = LayerMask.NameToLayer(layerName);
+            SetLayer(obj, layer, includeChildren);
+        }
+
+        private static Random rand;
+
+        public static void RandomSeed(int seed)
+        {
+            rand = new Random(seed);
+        }
+        public static float RandomFloat(float m, float n)
+        {
+            if (rand == null)
+                rand = new Random();
+            
+            if (n <= m)
+                return m;
+            
+            return m + ((float)rand.NextDouble()) * (n - m);
+        }
+
+        public static float RandomInt(int m, int n)
+        {
+            if (rand == null)
+                rand = new Random();
+            
+            if (n <= m)
+                return m;
+            
+            return m + rand.Next() % (m - n);
+        }
+
+        /// <summary>
+        /// 设置Renderer的渲染层级
+        /// </summary>
+        /// <param name="renderer"></param>
+        /// <param name="sortingLayer"></param>
+        public static void SetSortingLayer(Renderer renderer, string sortingLayer)
+        {
+            renderer.sortingLayerName = sortingLayer;
+        }
+
+        /// <summary>
+        /// 设置Renderer的渲染顺序
+        /// </summary>
+        /// <param name="renderer"></param>
+        /// <param name="order"></param>
+        public static void SetSortingOrder(Renderer renderer, int order)
+        {
+            renderer.sortingOrder = order;
+        }
+        
+        //世界坐标转化为UI坐标
+        public static Vector2 World2UIPos(Vector3 worldPos, RectTransform uiTrans, Canvas canvas)
+        {
+            if (uiTrans == null || canvas == null)
+                return Vector2.zero;
+            
+            Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
+            screenPos.z = 0;
+            RectTransformUtility.ScreenPointToLocalPointInRectangle(uiTrans, 
+                screenPos, canvas.worldCamera, out var uiPos);
+
+            return uiPos;
+        }
+
+        public static void BinaryWriteInt(BinaryWriter bw, int num)
+        {
+            bw.Write(num);
+        }
+        
+        public static void BinaryWriteLong(BinaryWriter bw, long num)
+        {
+            bw.Write(num);
+        }
+        
+        public static void BinaryWriteDouble(BinaryWriter bw, double num)
+        {
+            bw.Write(num);
+        }
+        
+        public static void BinaryWriteFloat(BinaryWriter bw, float num)
+        {
+            bw.Write(num);
+        }
+        
+        public static SystemLanguage GetSystemLanguage()
+        {
+            return Application.systemLanguage;
+        }
+
+        public static bool CheckStringLegal(string str)
+        {
+            if (str == "null" || str == "nil")
+                return false;
+            
+            string pattern = @"[@+%+""+'+\s+\n+$+#+\^+\*+]";
+            var result = Regex.Match(str, pattern);
+            if (result.Success)
+                return false;
+
+            return true;
+        }
+
+        /// <summary>
+        /// 使用指定字符串连接多组字符串
+        /// </summary>
+        /// <param name="combineStr"> 用于连接的指定字符串 </param>
+        /// <param name="stringParams"> 连接的内容 </param>
+        /// <returns></returns>
+        public static string CombineStrWith(string combineStr, params string[] stringParams)
+        {
+            if (stringParams.Length <= 0)
+                return "";
+
+            StringBuilder sb = new StringBuilder();
+            int count = stringParams.Length;
+            for (int i = 0; i < count; i++)
+            {
+                sb.Append(stringParams[i]);
+                if (i < count - 1)
+                    sb.Append(combineStr);
+            }
+
+            return sb.ToString();
+        }
+
+        public static string GetFileNameFromPath(string filePath, bool excludeSuffix = false)
+        {
+            var name = filePath.Split('/').Last();
+            if (excludeSuffix)
+                name = name.Split('.').First();
+            return name;
+        }
+
+        public static string GenerateLocalDataPath(string fileName, string suffix = ".mp4")
+        {
+            string dataPath = "Data/" + fileName + suffix;
+            return dataPath;
+        }
+
+
+        public static Vector3 RotateRound(Vector3 position, Vector3 center, Vector3 axis, float angle)
+        {
+            Vector3 point = Quaternion.AngleAxis(angle, axis) * (position - center);
+            Vector3 resultVec3 = center + point;
+            return resultVec3;
+        }
+
+        /// <summary>
+        /// 把数据写到本地文件
+        /// 因为 WriteAllBytes 好像会被代码裁剪掉,在这里实现一下调用
+        /// </summary>
+        public static void WriteToLocal(string filePath, byte[] fileBytes, bool cover)
+        {
+            if (!cover && File.Exists(filePath))
+                return;
+            
+            File.WriteAllBytes(filePath, fileBytes);
+        }
+
+        public static void SetImage(Image image, string path)
+        {
+            //var handler = GlobalVariables.ResourceManager.LoadSpriteFromSpriteAtlas(path);
+            //if (!handler.IsDone)
+            //{
+            //    Debug.Log(string.Format("Load Sprite %s Error", path));
+            //    return;
+            //}
+            //
+            //image.sprite = handler.Result;
+            Sprite sprite = Resources.Load<Sprite>(path);
+            if (!sprite)
+            {
+                Debug.LogError("未读取到图片:" + path);
+                return;
+            }
+            image.sprite = sprite;
+        }
+
+        /// <summary>
+        /// 一个点绕另一个点旋转
+        /// </summary>
+        /// <param name="point">要旋转的点</param>
+        /// <param name="pivot">中心点</param>
+        /// <param name="euler">旋转的角度</param>
+        /// <returns></returns>
+        public static Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 euler)
+        {
+            Vector3 direction = point - pivot;
+            Vector3 rotatedDirection = Quaternion.Euler(euler) * direction;
+            Vector3 rotatedPoint = rotatedDirection + pivot;
+            return rotatedPoint;
+        }
+
+        //设置屏幕常亮状态
+        public static void SetScreenNeverSleep(bool neverSleep)
+        {
+            if (neverSleep)
+            {
+                Screen.sleepTimeout = SleepTimeout.NeverSleep;
+            }
+            else
+            {
+                Screen.sleepTimeout = SleepTimeout.SystemSetting;
+            }
+        }
+
+        public static Vector2 GetScreenSize(CanvasScaler canvasScaler = null)
+        {
+            if (canvasScaler)
+            {
+                float realWidth = (Screen.width / ((Screen.height / canvasScaler.referenceResolution.y) * canvasScaler.referenceResolution.x)) * canvasScaler.referenceResolution.x;
+                float realHeight = canvasScaler.referenceResolution.y;
+                return new Vector2(realWidth, realHeight);
+            }
+            else
+            {
+                return new Vector2(Screen.width, Screen.height);
+            }
+            
+        }
+
+        public static Color List2Color(List<int> list)
+        {
+            int a = 255;
+            if (list.Count > 3)
+            {
+                a = list[3];
+            }
+            return new Color(list[0] / 255f, list[1] / 255f, list[2] / 255f, a / 255f);
+        }
+
+        //public static async Task WaitUntil(Func<bool> predicate, int sleep = 50)
+        //{
+        //    while (!predicate())
+        //    {
+        //        await Task.Delay(sleep);
+        //    }
+        //}
+
+        public static async void DelayDoFunc(int delay, UnityAction action)
+        {
+            await Task.Delay(delay);
+            action?.Invoke();
+        }
+
+        public static void ChangeAllLayer(GameObject go, int layer)
+        {
+            go.layer = layer;
+            for (int i = 0; i < go.transform.childCount; i++)
+            {
+                ChangeAllLayer(go.transform.GetChild(i).gameObject, layer);
+            }
+        }
+
+        public static void ShuffleList<T>(List<T> list, int seed = 0)
+        {
+            System.Random rng;
+            if (seed == 0)
+            {
+                rng = new System.Random();
+            }
+            else
+            {
+                rng = new System.Random(seed);
+            }
+            int n = list.Count;
+            while (n > 1)
+            {
+                n--;
+                int k = rng.Next(n + 1);
+                T value = list[k];
+                list[k] = list[n];
+                list[n] = value;
+            }
+        }
+    }
+}

+ 11 - 0
ActionTowerDefense/Assets/Scripts/Util.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 425dfc2242b10784ea41f2c25390ded8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 11 - 7
ActionTowerDefense/GenerateDatas/json/cfgcreateenemy.json

@@ -3,24 +3,28 @@
     "ID": 1,
     "EnemyID": 1,
     "Count": 10,
-    "Time": 10,
-    "TimeInterval": 2,
+    "Time": 5,
+    "TimeInterval": 0.5,
     "Position": [
       0,
       0,
       0
-    ]
+    ],
+    "AttackRatio": 1,
+    "HPRatio": 1
   },
   {
     "ID": 2,
-    "EnemyID": 2,
+    "EnemyID": 1,
     "Count": 20,
-    "Time": 40,
-    "TimeInterval": 2,
+    "Time": 20,
+    "TimeInterval": 0.5,
     "Position": [
       0,
       0,
       0
-    ]
+    ],
+    "AttackRatio": 10,
+    "HPRatio": 10
   }
 ]

+ 6 - 13
ActionTowerDefense/GenerateDatas/json/cfgenemy.json

@@ -3,18 +3,11 @@
     "ID": 1,
     "EnemyPrefab": "Prefab/Enemy_Sword",
     "HP": 100,
-    "Damage": 10
-  },
-  {
-    "ID": 2,
-    "EnemyPrefab": "Prefab/Enemy_Sword",
-    "HP": 200,
-    "Damage": 20
-  },
-  {
-    "ID": 3,
-    "EnemyPrefab": "Prefab/Enemy_Sword",
-    "HP": 300,
-    "Damage": 30
+    "Attack1": [
+      30
+    ],
+    "Attack2": [
+      30
+    ]
   }
 ]

+ 8 - 8
ActionTowerDefense/Luban/.cache.meta

@@ -70,14 +70,14 @@ H:/PartyGame4/PartyGame/Luban/Config/Datas/音效表.xlsx,25C9DF2F7FA43FF1D57461
 H:/PartyGame4/PartyGame/Luban/Config/Datas/游戏表.xlsx,A99E382758A1AFD6FCFCC14B1EC840A0,11849,1710142744357
 H:/PartyGame4/PartyGame/Luban/Config/Datas/装扮表.xlsx,592143E0D76443B3BCA6D2D93C10F693,11472,1697626336686
 H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/CfgCreateEnemy.cs,3A9B470F9A1FA351330EA275C82C399,1404,1711097150748
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/CfgEnemy.cs,1D4F84FB050E32E6A56E53FBA8A3EB9,1362,1711097150748
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/SingleCreateEnemyConfig.cs,4D5FDC97305B817E86DBDE8C9DBCD8C4,3374,1711097150748
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/SingleEnemyConfig.cs,E48CAE7AE7869D1008944702DEEF392,2374,1711097150748
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/CfgEnemy.cs,FDE1CE1B09C67F343EE84B2F88F27A6,1820,1711369259455
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/SingleCreateEnemyConfig.cs,A4477C595E7DF3FF2861CE739014897E,4015,1711367905740
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/SingleEnemyConfig.cs,D5F643F55C5C4765F3F8EFC19A64C96,3343,1711368142177
 H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Assets/Gen/Tables.cs,912B477615DA4F7C6B7D4A0EE96B27A,1296,1711097150748
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/GenerateDatas/json/cfgcreateenemy.json,F79DD2691863C8D58661F7D448633F7E,319,1711097150748
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/GenerateDatas/json/cfgenemy.json,B78BD63D8225146B6B770FD9D41A01,306,1711097150747
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/GenerateDatas/json/cfgcreateenemy.json,BA1AB3C09C414DC3BD273BBB7506B52,408,1711370817664
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/GenerateDatas/json/cfgenemy.json,CFB3629166F97C36B9DEDE5E61F6499B,157,1711368142177
 H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/__beans__.xlsx,98987DA7288FEE34BCD08EA883D425E,9603,1711095806628
 H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/__enums__.xlsx,647733322F7261DFA483C2DBE2D1F9A4,10293,1711095790814
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/__tables__.xlsx,B1AC60D16858DBEBEF16C1BA8C7875,10227,1711097120484
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/出怪表.xlsx,E1B21012B2609CBF46842BA4CEE267,10346,1711097065611
-H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/怪物表.xlsx,636BA611589E0BA44395584C87DBD5,10150,1711097143053
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/__tables__.xlsx,D1BC5823EBCE916872405D2DCDC896D,10232,1711369251824
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/出怪表.xlsx,323867892FF12E522B929E1F5DA976C,10421,1711370811007
+H:/UnityProject/ActionTowerDefense/ActionTowerDefense/Luban/Config/Datas/怪物表.xlsx,14BE9F516EEA490E14F9AF1F47589C,10016,1711368126935

二進制
ActionTowerDefense/Luban/Config/Datas/__tables__.xlsx


二進制
ActionTowerDefense/Luban/Config/Datas/出怪表.xlsx


二進制
ActionTowerDefense/Luban/Config/Datas/怪物表.xlsx