فهرست منبع

对蝌蚪进行了性能优化,在对象池管理器中添加了一个异步加载的方法,记得关卡切换的时候调用刷新一下蝌蚪的管理器(PolliwogManager)

Callum 3 ماه پیش
والد
کامیت
5a2304f2f7

+ 9 - 206
ActionTowerDefense/Assets/Resources/Prefab/Enemy/Enemy_Polliwog.prefab

@@ -33,7 +33,7 @@ Transform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 3248298059236281109}
-  m_RootOrder: 4
+  m_RootOrder: 2
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!65 &7650605579828095582
 BoxCollider:
@@ -242,7 +242,7 @@ RectTransform:
   m_Children:
   - {fileID: 3610356161953035234}
   m_Father: {fileID: 8023685214597940640}
-  m_RootOrder: 2
+  m_RootOrder: 1
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 0, y: 0}
@@ -340,8 +340,6 @@ Transform:
   m_ConstrainProportionsScale: 0
   m_Children:
   - {fileID: 4044399378262385515}
-  - {fileID: 6584563588054802980}
-  - {fileID: 8023685214372659182}
   - {fileID: 816689562064130092}
   - {fileID: 3372769376507897098}
   m_Father: {fileID: 646884720114117071}
@@ -367,134 +365,6 @@ Animator:
   m_HasTransformHierarchy: 1
   m_AllowConstantClipSamplingOptimization: 1
   m_KeepAnimatorControllerStateOnDisable: 0
---- !u!1 &1947342349314136388
-GameObject:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 7351404853661460315}
-  - component: {fileID: 5438523972308844832}
-  - component: {fileID: 4842403444785962645}
-  - component: {fileID: 838686186165657518}
-  m_Layer: 17
-  m_Name: SearchTrigger
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 1
---- !u!4 &7351404853661460315
-Transform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1947342349314136388}
-  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
-  m_LocalPosition: {x: 0, y: 0, z: 0}
-  m_LocalScale: {x: 1, y: 0.8, z: 1}
-  m_ConstrainProportionsScale: 0
-  m_Children: []
-  m_Father: {fileID: 8023685214597940640}
-  m_RootOrder: 1
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &5438523972308844832
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1947342349314136388}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 21f006d2c845e2e43bce70ea7a2d8dcc, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  owner: {fileID: 0}
-  trigDemonicList: []
-  trigTowerList: []
-  trigPlayerList: []
-  trigEnemyList: []
-  trigEnemyTowerList: []
-  trigBossList: []
-  needToChange: 0
-  maxLen: 0
-  minLen: 0
---- !u!65 &4842403444785962645
-BoxCollider:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1947342349314136388}
-  m_Material: {fileID: 0}
-  m_IsTrigger: 1
-  m_Enabled: 1
-  serializedVersion: 2
-  m_Size: {x: 15.717739, y: 8.111751, z: 20}
-  m_Center: {x: -0.07770944, y: 0.11173636, z: 0}
---- !u!114 &838686186165657518
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 1947342349314136388}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: 940ff74252fd4324fad0d9e6b8e59c5b, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  tanekEnemy: []
-  enemyTower: []
---- !u!1 &2312462261567289556
-GameObject:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 6584563588054802980}
-  - component: {fileID: 1222060622420681600}
-  m_Layer: 8
-  m_Name: BodyCollider
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 1
---- !u!4 &6584563588054802980
-Transform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 2312462261567289556}
-  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: 3248298059236281109}
-  m_RootOrder: 1
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!65 &1222060622420681600
-BoxCollider:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 2312462261567289556}
-  m_Material: {fileID: 13400000, guid: ae11ca82bc783194e8fdded3f8828a76, type: 2}
-  m_IsTrigger: 1
-  m_Enabled: 1
-  serializedVersion: 2
-  m_Size: {x: 0.9065783, y: 0.5057149, z: 2}
-  m_Center: {x: 0.06542587, y: 0.43024182, z: 0}
 --- !u!1 &2658578937632965927
 GameObject:
   m_ObjectHideFlags: 0
@@ -903,70 +773,6 @@ Transform:
   m_Father: {fileID: 8023685214597940640}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!1 &8023685214372659169
-GameObject:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  serializedVersion: 6
-  m_Component:
-  - component: {fileID: 8023685214372659182}
-  - component: {fileID: 8023685214372659183}
-  - component: {fileID: 8023685214372659168}
-  m_Layer: 22
-  m_Name: Foot
-  m_TagString: Untagged
-  m_Icon: {fileID: 0}
-  m_NavMeshLayer: 0
-  m_StaticEditorFlags: 0
-  m_IsActive: 0
---- !u!4 &8023685214372659182
-Transform:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 8023685214372659169}
-  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: 3248298059236281109}
-  m_RootOrder: 2
-  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!65 &8023685214372659183
-BoxCollider:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 8023685214372659169}
-  m_Material: {fileID: 0}
-  m_IsTrigger: 1
-  m_Enabled: 1
-  serializedVersion: 2
-  m_Size: {x: 0.8188312, y: 0.25791693, z: 2}
-  m_Center: {x: 0.009530544, y: 0.12104154, z: 0}
---- !u!114 &8023685214372659168
-MonoBehaviour:
-  m_ObjectHideFlags: 0
-  m_CorrespondingSourceObject: {fileID: 0}
-  m_PrefabInstance: {fileID: 0}
-  m_PrefabAsset: {fileID: 0}
-  m_GameObject: {fileID: 8023685214372659169}
-  m_Enabled: 1
-  m_EditorHideFlags: 0
-  m_Script: {fileID: 11500000, guid: c42213c575288a148b692aad7ab56bab, type: 3}
-  m_Name: 
-  m_EditorClassIdentifier: 
-  rb: {fileID: 0}
-  moveCharacter: {fileID: 0}
-  canOnGiant: 0
-  haveGravity: 1
-  trigGroundList: []
-  platform: {fileID: 0}
 --- !u!1 &8023685214597940645
 GameObject:
   m_ObjectHideFlags: 0
@@ -998,7 +804,6 @@ Transform:
   m_ConstrainProportionsScale: 0
   m_Children:
   - {fileID: 646884720114117071}
-  - {fileID: 7351404853661460315}
   - {fileID: 1133560725009165570}
   m_Father: {fileID: 0}
   m_RootOrder: 0
@@ -1018,8 +823,8 @@ MonoBehaviour:
   rb: {fileID: 0}
   bodyTrans: {fileID: 646884720114117071}
   beSearchTrigger: {fileID: 1461416353662333947}
-  searchTrigger: {fileID: 5438523972308844832}
-  bodyCollider: {fileID: 2312462261567289556}
+  searchTrigger: {fileID: 0}
+  bodyCollider: {fileID: 0}
   uiHp: {fileID: 99864860648192471}
   beHitTrigger: {fileID: 1709563751208538466}
   attackController: {fileID: 4087616115681235363}
@@ -1043,9 +848,10 @@ MonoBehaviour:
   targetCharacter: {fileID: 0}
   attackTarget: {fileID: 0}
   state: 0
-  totalHp: 1
+  totalHp: 30
   hp: 0
   canFly: 1
+  canControlFly: 0
   nowCanFly: 0
   canNotShotDown: 1
   canNotAddForce: 0
@@ -1063,7 +869,7 @@ MonoBehaviour:
   Attack_summonShootCanTransmit: 0
   debugAttackFrom: 0
   hitResistance: 0
-  foot: {fileID: 8023685214372659168}
+  foot: {fileID: 0}
   attributeStatus: {fileID: 0}
   hitFeedbackSystem: {fileID: 0}
   spineEvent: {fileID: 0}
@@ -1105,9 +911,9 @@ MonoBehaviour:
   sustainedInjury_damage: 0
   heavyDamage: 0
   killer: {fileID: 0}
+  exp: 0
   tag: 0
   dieEffect: {fileID: 417478504669294145, guid: 2a4e7e280b9d19042ba0f57ae5edf736, type: 3}
-  exp: 0
   name: 
   baseSortingOrder: 3000
   isBack: 0
@@ -1139,7 +945,6 @@ MonoBehaviour:
   pathCreator: {fileID: 0}
   climbSpeed: 0
   playerTarget: {fileID: 0}
-  shooter: {fileID: 0}
   damage: 0
   separationWeight: 2
   cohesionWeight: 0.8
@@ -1150,9 +955,7 @@ MonoBehaviour:
   baseRotationSpeed: 300
   speedDeviation: 2
   rotationSpeedDeviation: 15
-  squareAvoidanceRadius: 5
   rotationSmoothing: 5
-  isInGroup: 1
   updateInterval: 2
 --- !u!114 &4087616115681235363
 MonoBehaviour:
@@ -1406,7 +1209,7 @@ Transform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 3248298059236281109}
-  m_RootOrder: 3
+  m_RootOrder: 1
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
 --- !u!65 &4470273568402756359
 BoxCollider:

+ 2 - 2
ActionTowerDefense/Assets/Scripts/Characters/Character.cs

@@ -161,12 +161,12 @@ public class Character : MonoBehaviour
         //ѪÁ¿ÖØÖÃ
         hp = totalHp;
         nowCanFly = canFly;
-        if (canFly && !canControlFly)
+        if (canFly && !canControlFly && rb != null)
         {
             rb.useGravity = false;
             rb.constraints = RigidbodyConstraints.FreezeRotation | RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezePositionZ;
         }
-        else if (canFly && canControlFly)
+        else if (canFly && canControlFly && rb != null)
         {
             rb.useGravity = false;
             rb.constraints = RigidbodyConstraints.FreezeRotation | RigidbodyConstraints.FreezePositionZ;

+ 83 - 138
ActionTowerDefense/Assets/Scripts/Characters/Polliwog.cs

@@ -1,22 +1,18 @@
 using System.Collections;
 using System.Collections.Generic;
-using Unity.VisualScripting;
+using Sirenix.OdinInspector;
 using UnityEngine;
-using Spine;
-using Spine.Unity;
 
 public class Polliwog : Enemy
 {
     public Transform playerTarget;
-    public PolliwogShot shooter;
-    private BoxCollider _bodyCollider;
     public int damage;
     [Header("鱼群行为参数")]
-    public float separationWeight = 1.5f; //分离权重
-    public float cohesionWeight = 1.0f; //聚集权重
-    public float alignmentWeight = 1.0f; //对齐权重
-    public float flockInfluence = 0.5f; // 群体行为对最终方向的影响程度
-    public int correctionProbability;
+    [LabelText("分离权重")] public float separationWeight = 1.5f;
+    [LabelText("聚集权重")] public float cohesionWeight = 1.0f;
+    [LabelText("对齐权重")] public float alignmentWeight = 1.0f;
+    [LabelText("群体行为对最终方向的影响程度")] public float flockInfluence = 0.5f;
+    [LabelText("每次循环应用群体行为的概率")] public int correctionProbability;
 
     [Header("移动参数")]
     public float baseMoveSpeed = 5f;
@@ -25,41 +21,38 @@ public class Polliwog : Enemy
     private float rotationSpeed;
     public float speedDeviation;
     public float rotationSpeedDeviation;
-
-    public float squareAvoidanceRadius;
     [Header("旋转平滑参数")]
-    public float rotationSmoothing = 5f;  // 旋转平滑系数(越大越平滑)
-    private float currentAngleVelocity;   // 当前角度变化速度(用于SmoothDamp)
-    public bool isInGroup = true;
+    [LabelText("旋转平滑系数(越大越平滑)")] public float rotationSmoothing = 5f;
+    private float currentAngleVelocity;
 
     public Vector2 currentVelocity { get; private set; }
     private int updateCounter = 0;
-    public int updateInterval = 2;
+    [LabelText("多少次循环更新一次")] public int updateInterval = 2;
+
+    private List<Polliwog> nearbyFish = new List<Polliwog>();
+    private float lastUpdateNearbyFishTime;
 
     public override void Awake()
     {
         base.Awake();
-        _bodyCollider = bodyCollider.GetComponent<BoxCollider>();
     }
     protected override void OnEnable()
     {
         base.OnEnable();
         isDie = false;
-        _bodyCollider.isTrigger = true;
-        //rb.useGravity = false;
         currentVelocity = Vector2.zero;
         moveSpeed = baseMoveSpeed + Random.Range(-speedDeviation, speedDeviation);
         rotationSpeed = baseRotationSpeed + Random.Range(-rotationSpeedDeviation, rotationSpeedDeviation);
         ChangeState(CharacterState.Run);
+        if (PlayersInput.instance.Length > 0) playerTarget = PlayersInput.instance[0].transform;
     }
     private void Start()
     {
         attackController.attackTrigger.attackInfo.damage = damage;
     }
 
-    public void Init(PolliwogShot _shooter, int _damage)
+    public void Init(int _damage)
     {
-        shooter = _shooter;
         damage = _damage;
     }
 
@@ -79,36 +72,14 @@ public class Polliwog : Enemy
         {
             return;
         }
-        switch (state)
-        {
-            case CharacterState.Run:
-                if (attackController != null)
-                {
-                    attackController.isAttackTriggerOn = false;
-                    attackController.attackTrigger.gameObject.SetActive(false);
-                }
-                //rb.useGravity = true;
-                isInGroup = false;
-                _bodyCollider.isTrigger = false;
-                if (shooter != null)
-                {
-                    if (shooter.list.Contains(this)) shooter.list.Remove(this);
-                }
-                break;
-        }
-        Debug.Log("从" + state + "切换到" + newState);
         state = newState;
         switch (newState)
         {
             case CharacterState.Idle:
                 ani.Play("idle", 0, 0);
-                //rb.velocity = Vector3.zero;
                 currentVelocity = Vector3.zero;
                 break;
             case CharacterState.Run:
-                //rb.useGravity = false;
-                _bodyCollider.isTrigger = true;
-                attackController.isAttackTriggerOn = true;
                 ani.Play("walk", 0, 0);
                 break;
             case CharacterState.Die:
@@ -117,23 +88,15 @@ public class Polliwog : Enemy
                 dieKeepTime = totalDieKeepTime;
                 DropSouls();
                 break;
-            case CharacterState.HitStun:
-                canNotShotDown = true;
-                break;
             default:
                 break;
         }
     }
     public override void FixedUpdate()
     {
-        //updateCounter++;
-        //if (updateCounter > updateInterval) return;
-        //updateCounter = 0;
-        if (PlayersInput.instance.Length > 0)
-        {
-            playerTarget = PlayersInput.instance[0].transform;
-        }
-        else return;
+        updateCounter++;
+        if (updateCounter > updateInterval) return;
+        updateCounter = 0;
         OnState();
     }
 
@@ -160,7 +123,6 @@ public class Polliwog : Enemy
         pastAttackTime += Time.deltaTime;
         Vector3 leftDir = GetMoveDir();
         bool isAttack = GetAttack();
-        //Vector3 velocity = rb.velocity;
         Quaternion targetQt = Quaternion.Euler(Vector3.zero);
         switch (state)
         {
@@ -168,63 +130,41 @@ public class Polliwog : Enemy
                 ChangeState(CharacterState.Run);
                 break;
             case CharacterState.Run:
-                if (isInGroup)
-                {
-                    Vector2 toPlayer = playerTarget.position - transform.position;
-                    float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer);;
-                    float desiredAngle = targetAngle;
-                    bool applyFlock = Random.Range(0, 100) <= correctionProbability;
-                    if (applyFlock)
-                    {
-                        Vector2 separation = CalculateMove1();
-                        Vector2 alignment = CalculateMove3();
-                        Vector2 cohesion = CalculateMove2();
-
-                        Vector2 combinedMove = (separation * separationWeight +
-                                              cohesion * cohesionWeight +
-                                              alignment * alignmentWeight);
-
-                        float flockAngle = combinedMove != Vector2.zero ?
-                            Vector2.SignedAngle(Vector2.left, combinedMove) : targetAngle;
-                        desiredAngle = Mathf.LerpAngle(targetAngle, flockAngle, flockInfluence);
-                    }
-                    float newAngle = Mathf.SmoothDampAngle(
-                        transform.eulerAngles.z,
-                        desiredAngle,
-                        ref currentAngleVelocity,
-                        1f / rotationSmoothing,
-                        rotationSpeed * 2f,
-                        Time.fixedDeltaTime
-                    );
-
-                    transform.rotation = Quaternion.Euler(0, 0, newAngle);
-                    currentVelocity = -transform.right * moveSpeed;
-                    //rb.velocity = -transform.right * moveSpeed;
-
-                }
-                else
+                Vector2 toPlayer = playerTarget.position - transform.position;
+                float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer); ;
+                float desiredAngle = targetAngle;
+                bool applyFlock = Random.Range(0, 100) <= correctionProbability;
+                if (applyFlock)
                 {
-                    if (!gameObject.activeSelf) return;
-                    Vector2 toPlayer = shooter.UpdateGroupCenter() - transform.position;
-                    float targetAngle = Vector2.SignedAngle(Vector2.left, toPlayer);
-                    transform.rotation = Quaternion.Euler(0, 0, targetAngle + 360);
-                    currentVelocity = -transform.right * moveSpeed;
-                    //rb.velocity = -transform.right * moveSpeed;
-                    if (toPlayer.magnitude < squareAvoidanceRadius && shooter != null)
-                    {
-                        shooter.list.Add(this);
-                        isInGroup = true;
-                    }
+                    UpdateNearbyFish();
+                    Vector2 separation = CalculateMove1();
+                    Vector2 alignment = CalculateMove3();
+                    Vector2 cohesion = CalculateMove2();
+
+                    Vector2 combinedMove = (separation * separationWeight +
+                                          cohesion * cohesionWeight +
+                                          alignment * alignmentWeight);
+
+                    float flockAngle = combinedMove != Vector2.zero ?
+                        Vector2.SignedAngle(Vector2.left, combinedMove) : targetAngle;
+                    desiredAngle = Mathf.LerpAngle(targetAngle, flockAngle, flockInfluence);
                 }
+                float newAngle = Mathf.SmoothDampAngle(
+                    transform.eulerAngles.z,
+                    desiredAngle,
+                    ref currentAngleVelocity,
+                    1f / rotationSmoothing,
+                    rotationSpeed * 2f,
+                    Time.fixedDeltaTime
+                );
+
+                transform.rotation = Quaternion.Euler(0, 0, newAngle);
+                currentVelocity = -transform.right * moveSpeed;
                 if (!attackController.attackTrigger.gameObject.activeSelf)
                 {
                     attackController.attackTrigger.gameObject.SetActive(true);
                 }
                 break;
-            case CharacterState.HitStun:
-                currentVelocity = Vector2.zero;
-                hitFeedbackSystem.HitStunUpdate();
-                break;
             case CharacterState.Die:
                 currentVelocity = Vector2.zero;
                 if (dieKeepTime <= 0)
@@ -237,63 +177,68 @@ public class Polliwog : Enemy
                     break;
                 }
                 break;
-            case CharacterState.SpecialStatus_Float:
-                attributeStatus.SpecialStateEffect(SpecialState.FloatState);
-                break;
-            case CharacterState.SpecialStatus_BlowUp:
-                attributeStatus.SpecialStateEffect(SpecialState.BlownUp);
-                break;
-            case CharacterState.SpecialStatus_ShotDown:
-                currentVelocity = Vector2.zero;
-                attributeStatus.SpecialStateEffect(SpecialState.ShotDown);
-                break;
-            case CharacterState.SpecialStatus_Weak:
-                attributeStatus.SpecialStateEffect(SpecialState.Weak);
-                break;
         }
         transform.position += (Vector3)currentVelocity * Time.fixedDeltaTime;
     }
+
     public Vector2 CalculateMove1()
     {
-        if (shooter == null || shooter.list.Count == 0)
+        if (nearbyFish.Count == 0)
         {
             return Vector2.zero;
         }
+
         Vector2 avoidDanceMove = Vector2.zero;
-        int nAvoid = 0;
-        foreach (Polliwog fish in shooter.list)
-        {
-            if ((fish.transform.position - transform.position).sqrMagnitude < squareAvoidanceRadius)
-            {
-                nAvoid++;
-                avoidDanceMove += (Vector2)(transform.transform.position - fish.transform.position);
-            }
-        }
-        if (nAvoid > 0)
+
+        foreach (Polliwog fish in nearbyFish)
         {
-            avoidDanceMove /= nAvoid;
+            avoidDanceMove += (Vector2)(transform.transform.position - fish.transform.position) / Vector2.Distance(transform.transform.position, fish.transform.position);
         }
-        return avoidDanceMove;
+
+        return avoidDanceMove.normalized;
     }
+
     public Vector2 CalculateMove2()
     {
-        if (shooter == null || shooter.list.Count == 0)
+        if (nearbyFish.Count == 0)
         {
             return Vector2.zero;
         }
-        Vector2 cohesionMove = shooter.UpdateGroupCenter();
-
+        Vector2 cohesionMove = Vector2.zero;
+        foreach (Polliwog fish in nearbyFish)
+        {
+            cohesionMove += (Vector2)(fish.transform.transform.position);
+        }
+        cohesionMove /= nearbyFish.Count;
         cohesionMove -= (Vector2)transform.position;
-        return cohesionMove;
+        return cohesionMove.normalized;
     }
+
     public Vector2 CalculateMove3()
     {
-        if (shooter == null || shooter.list.Count == 0)
+        if (nearbyFish.Count == 0)
         {
             return Vector2.zero;
         }
-        Vector2 alignmentMove = Vector2.zero;
+        Vector2 groupVelocity = Vector2.zero;
+        foreach (Polliwog fish in nearbyFish)
+        {
+            groupVelocity += fish.currentVelocity;
+        }
+        groupVelocity /= nearbyFish.Count;
+        return groupVelocity.normalized;
+    }
+
+    public void OnDisable()
+    {
+        EnemyCreater.instance.OnEnemyRecycle(this);
+        PolliwogManager.GetInstance().RemovePolliwog(this);
+    }
 
-        return shooter.UpdateGroupVelocity();
+    private void UpdateNearbyFish()
+    {
+        if(Time.time - lastUpdateNearbyFishTime < 0.1) return;
+        lastUpdateNearbyFishTime = Time.time;
+        nearbyFish = PolliwogManager.GetInstance().GetNearbyFish(transform.position, 6);
     }
 }

+ 222 - 0
ActionTowerDefense/Assets/Scripts/PolliwogManager.cs

@@ -0,0 +1,222 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class PolliwogManager : MonoBehaviour
+{
+    private static PolliwogManager instance;
+
+    public List<Polliwog> list = new List<Polliwog>();
+
+    private QuadTree quadTree;
+    private float quadTreeUpdateInterval = 0.1f;
+    private float lastQuadTreeUpdateTime;
+    private Rect quadTreeBounds;
+
+    public const int maxNum = 150;
+
+    public static PolliwogManager GetInstance()
+    {
+        if( instance == null )
+        {
+            GameObject obj = new GameObject();
+            obj.name = typeof(PolliwogManager).ToString();
+            DontDestroyOnLoad(obj);
+            instance = obj.AddComponent<PolliwogManager>();
+        }
+        return instance;
+    }
+
+    private void Start()
+    {
+        quadTreeBounds = new Rect(-20, -20, 200, 200);
+        quadTree = new QuadTree(0, quadTreeBounds);
+    }
+
+    private void UpdateQuadTree()
+    {
+        if (Time.time - lastQuadTreeUpdateTime < quadTreeUpdateInterval) return;
+        lastQuadTreeUpdateTime = Time.time;
+
+        quadTree.Clear();
+        foreach (var fish in list)
+        {
+            quadTree.Insert(fish);
+        }
+    }
+
+    public List<Polliwog> GetNearbyFish(Vector2 position, float radius)
+    {
+        UpdateQuadTree();
+        return quadTree.Retrieve(position, radius);
+    }
+
+    public void AddPolliwog(Polliwog polliwog)
+    {
+        if(list.Count >= maxNum) return;
+        if(!list.Contains(polliwog)) list.Add(polliwog);
+    }
+
+    public void RemovePolliwog(Polliwog polliwog)
+    {
+        if (list.Contains(polliwog)) list.Remove(polliwog);
+    }
+
+    public bool IsFull()
+    {
+        return list.Count >= maxNum;
+    }
+
+    public void Refresh()
+    {
+        list.Clear();
+        quadTree.Clear();
+    }
+}
+
+public class QuadTree
+{
+    private const int MAX_OBJECTS = 20;
+    private const int MAX_LEVELS = 5;
+
+    private int level;
+    private List<Polliwog> objects;
+    private Rect bounds;
+    private QuadTree[] nodes;
+
+    public QuadTree(int level, Rect bounds)
+    {
+        this.level = level;
+        this.bounds = bounds;
+        objects = new List<Polliwog>();
+        nodes = new QuadTree[4];
+    }
+
+    public void Clear()
+    {
+        objects.Clear();
+
+        for (int i = 0; i < nodes.Length; i++)
+        {
+            if (nodes[i] != null)
+            {
+                nodes[i].Clear();
+                nodes[i] = null;
+            }
+        }
+    }
+
+    private void Split()
+    {
+        float subWidth = bounds.width / 2f;
+        float subHeight = bounds.height / 2f;
+        float x = bounds.x;
+        float y = bounds.y;
+
+        nodes[0] = new QuadTree(level + 1, new Rect(x + subWidth, y, subWidth, subHeight));
+        nodes[1] = new QuadTree(level + 1, new Rect(x, y, subWidth, subHeight));
+        nodes[2] = new QuadTree(level + 1, new Rect(x, y + subHeight, subWidth, subHeight));
+        nodes[3] = new QuadTree(level + 1, new Rect(x + subWidth, y + subHeight, subWidth, subHeight));
+    }
+
+    private int GetIndex(Vector2 position)
+    {
+        int index = -1;
+        float verticalMidpoint = bounds.x + (bounds.width / 2);
+        float horizontalMidpoint = bounds.y + (bounds.height / 2);
+
+        bool topQuadrant = position.y > horizontalMidpoint;
+        bool bottomQuadrant = position.y < horizontalMidpoint;
+
+        if (position.x < verticalMidpoint)
+        {
+            if (topQuadrant) index = 2;
+            else if (bottomQuadrant) index = 1;
+        }
+        else if (position.x > verticalMidpoint)
+        {
+            if (topQuadrant) index = 3;
+            else if (bottomQuadrant) index = 0;
+        }
+
+        return index;
+    }
+
+    public void Insert(Polliwog obj)
+    {
+        if (nodes[0] != null)
+        {
+            int index = GetIndex(obj.transform.position);
+
+            if (index != -1)
+            {
+                nodes[index].Insert(obj);
+                return;
+            }
+        }
+
+        objects.Add(obj);
+
+        if (objects.Count > MAX_OBJECTS && level < MAX_LEVELS)
+        {
+            if (nodes[0] == null)
+            {
+                Split();
+            }
+
+            int i = 0;
+            while (i < objects.Count)
+            {
+                int index = GetIndex(objects[i].transform.position);
+                if (index != -1)
+                {
+                    nodes[index].Insert(objects[i]);
+                    objects.RemoveAt(i);
+                }
+                else
+                {
+                    i++;
+                }
+            }
+        }
+    }
+
+    public List<Polliwog> Retrieve(Vector2 position, float radius)
+    {
+        List<Polliwog> returnObjects = new List<Polliwog>();
+        returnObjects.AddRange(objects);
+
+        if (nodes[0] != null)
+        {
+            int index = GetIndex(position);
+            if (index != -1)
+            {
+                returnObjects.AddRange(nodes[index].Retrieve(position, radius));
+            }
+            else
+            {
+                // 检查所有子节点是否与搜索区域相交
+                for (int i = 0; i < nodes.Length; i++)
+                {
+                    if (RectOverlapCircle(nodes[i].bounds, position, radius))
+                    {
+                        returnObjects.AddRange(nodes[i].Retrieve(position, radius));
+                    }
+                }
+            }
+        }
+
+        return returnObjects;
+    }
+
+    private bool RectOverlapCircle(Rect rect, Vector2 circleCenter, float radius)
+    {
+        float closestX = Mathf.Clamp(circleCenter.x, rect.x, rect.x + rect.width);
+        float closestY = Mathf.Clamp(circleCenter.y, rect.y, rect.y + rect.height);
+
+        float distanceX = circleCenter.x - closestX;
+        float distanceY = circleCenter.y - closestY;
+
+        return (distanceX * distanceX + distanceY * distanceY) < (radius * radius);
+    }
+}

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

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

+ 12 - 9
ActionTowerDefense/Assets/Scripts/PolliwogShot.cs

@@ -14,17 +14,20 @@ public class PolliwogShot : SpecialSkills
     private Vector2 groupVelocity;
     private float lastUpdateVelocityTime;
 
-    public List<Polliwog> list = new List<Polliwog>();
     public int num;
 
     public override void Attack()
     {
         for (int i = 0; i < num; i++)
         {
-            GameObject obj = PoolManager.Instantiate(fishPrefab, transform.position + new Vector3(Random.Range(-5,5), Random.Range(-5, 5),0), new Quaternion(0, 0, 0, 0));
-            Polliwog pol = obj.GetComponent<Polliwog>();
-            pol.Init(this, 6);
-            list.Add(pol);
+            if(PolliwogManager.GetInstance().IsFull())  continue;
+            //GameObject obj = PoolManager.Instantiate(fishPrefab, transform.position + new Vector3(Random.Range(-5,5), Random.Range(-5, 5),0), new Quaternion(0, 0, 0, 0));
+            PoolManager.InstantiateAsync("Prefab/Enemy/Enemy_Polliwog", transform.position + new Vector3(Random.Range(-5, 5), Random.Range(-5, 5), 0), new Quaternion(0, 0, 0, 0),null, obj =>
+            {
+                Polliwog pol = obj.GetComponent<Polliwog>();
+                pol.Init(6);
+                PolliwogManager.GetInstance().AddPolliwog(pol);
+            });
         }
     }
     public Vector3 UpdateGroupCenter()
@@ -32,11 +35,11 @@ public class PolliwogShot : SpecialSkills
         if (Time.time - lastUpdateCenterTime < 0.1f) return groupCenter;
         lastUpdateCenterTime = Time.time;
         groupCenter = Vector2.zero;
-        foreach (Polliwog pol in list)
+        foreach (Polliwog pol in PolliwogManager.GetInstance().list)
         {
             groupCenter += (Vector2)transform.position;
         }
-        groupCenter /= list.Count;
+        groupCenter /= PolliwogManager.GetInstance().list.Count;
         return groupCenter;
     }
 
@@ -45,11 +48,11 @@ public class PolliwogShot : SpecialSkills
         if (Time.time - lastUpdateVelocityTime < 0.1f) return groupVelocity;
         lastUpdateVelocityTime = Time.time;
         groupVelocity = Vector2.zero;
-        foreach (Polliwog pol in list)
+        foreach (Polliwog pol in PolliwogManager.GetInstance().list)
         {
             groupVelocity += pol.currentVelocity;
         }
-        groupVelocity /= list.Count;
+        groupVelocity /= PolliwogManager.GetInstance().list.Count;
         return groupVelocity;
     }
 }

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

@@ -1,6 +1,7 @@
 using System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
+using UnityEngine.Events;
 
 public class PoolManager : MonoBehaviour
 {
@@ -47,6 +48,63 @@ public class PoolManager : MonoBehaviour
         return go;
     }
 
+    public static void InstantiateAsync(string name, Vector3 pos = default, Quaternion rotation = default, Transform parent = null, UnityAction<GameObject> callback = null)
+    {
+        if (instance == null)
+        {
+            Debug.LogError("PoolManager instance not initialized!");
+            callback(null);
+            return;
+        }
+        instance.StartCoroutine(instance.ReallyLoadGameObjectAsync(name,pos,rotation,parent, callback));
+    }
+
+    private IEnumerator ReallyLoadGameObjectAsync(string name, Vector3 pos = default, Quaternion rotation = default, Transform parent = null, UnityAction<GameObject> callback = null)
+    {
+        ResourceRequest r = Resources.LoadAsync<GameObject>(name);
+        yield return r;
+
+        if (r.asset == null)
+        {
+            Debug.LogError($"Failed to load GameObject: {name}");
+            callback(null);
+            yield break;
+        }
+
+        GameObject prefab = r.asset as GameObject;
+
+        if (instance.pools.ContainsKey(prefab) && instance.pools[prefab].Count > 0)
+        {
+            GameObject go = instance.pools[prefab][0];
+            go.transform.parent = parent;
+            go.transform.position = pos;
+            go.transform.rotation = rotation;
+            go.SetActive(true);
+            instance.pools[prefab].RemoveAt(0);
+
+            if (!instance.activeObjs.ContainsKey(prefab))
+                instance.activeObjs.Add(prefab, new List<GameObject>());
+
+            instance.activeObjs[prefab].Add(go);
+            callback(go);
+        }
+        else
+        {
+            GameObject go = Object.Instantiate(prefab);
+            go.transform.parent = parent;
+            go.transform.position = pos;
+            go.transform.rotation = rotation;
+            PoolItem poolItem = go.AddComponent<PoolItem>();
+            poolItem.prefab = prefab;
+
+            if (!instance.activeObjs.ContainsKey(prefab))
+                instance.activeObjs.Add(prefab, new List<GameObject>());
+
+            instance.activeObjs[prefab].Add(go);
+            callback(go);
+        }
+    }
+
     public void Recycle(PoolItem poolItem)
     {
         if (!pools.ContainsKey(poolItem.prefab))