chenxin
2020-11-28 ee2cbc86ed58ae03a092b67690d3869ebe78fd20
解决木精灵在打泡泡和木小怪时索敌问题,只打泡泡不打小怪
11 files modified
1320 ■■■■■ changed files
Assets/Prefabs/UI/Endless/BottomCanvas.prefab 1 ●●●● patch | view | raw | blame | history
Assets/Prefabs/UI/GMPanel.prefab 316 ●●●●● patch | view | raw | blame | history
Assets/Scenes/Levels/Battle/Endless2D.unity 35 ●●●●● patch | view | raw | blame | history
Assets/Scripts/Common/GameConfig.cs 5 ●●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/Affectors/AttackAffector.cs 30 ●●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/Level/AgentInsManager.cs 92 ●●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/Targetting/Targetter.cs 810 ●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/Towers/Placement/TowerPlacementGridEndless.cs 2 ●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/UI/EndlessBossSkill/BossSkillBubbleBomb.cs 7 ●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/UI/EndlessUIStart.cs 12 ●●●●● patch | view | raw | blame | history
Assets/Scripts/TowerDefense/UI/GM.cs 10 ●●●●● patch | view | raw | blame | history
Assets/Prefabs/UI/Endless/BottomCanvas.prefab
@@ -3399,6 +3399,7 @@
  beginDragStep: 0
  guideThirdWaveStep: 0
  darkGroundImg: {fileID: 4623057511338059854}
  GMBtn: {fileID: 0}
--- !u!1 &4623057512730188692
GameObject:
  m_ObjectHideFlags: 0
Assets/Prefabs/UI/GMPanel.prefab
@@ -2051,6 +2051,85 @@
  m_FillOrigin: 0
  m_UseSpriteMesh: 0
  m_PixelsPerUnitMultiplier: 1
--- !u!1 &2322950904165432749
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 390278849146172503}
  - component: {fileID: 6985707116480831674}
  - component: {fileID: 2278244430316628629}
  m_Layer: 5
  m_Name: Label
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &390278849146172503
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 2322950904165432749}
  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_Children: []
  m_Father: {fileID: 7138816553533510817}
  m_RootOrder: 1
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0, y: 0}
  m_AnchorMax: {x: 1, y: 1}
  m_AnchoredPosition: {x: 28.5, y: 3.9499903}
  m_SizeDelta: {x: -28, y: 44.099983}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6985707116480831674
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 2322950904165432749}
  m_CullTransparentMesh: 0
--- !u!114 &2278244430316628629
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 2322950904165432749}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Material: {fileID: 0}
  m_Color: {r: 1, g: 1, b: 1, a: 1}
  m_RaycastTarget: 1
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
  m_Maskable: 1
  m_OnCullStateChanged:
    m_PersistentCalls:
      m_Calls: []
  m_FontData:
    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
    m_FontSize: 35
    m_FontStyle: 0
    m_BestFit: 0
    m_MinSize: 3
    m_MaxSize: 40
    m_Alignment: 3
    m_AlignByGeometry: 0
    m_RichText: 1
    m_HorizontalOverflow: 0
    m_VerticalOverflow: 0
    m_LineSpacing: 1
  m_Text: "\u65E0\u9650\u91CA\u653E\u6280\u80FD"
--- !u!1 &2352833052201251510
GameObject:
  m_ObjectHideFlags: 0
@@ -3378,6 +3457,7 @@
  - {fileID: 4154238555173318587}
  - {fileID: 4194420542261668727}
  - {fileID: 580281433}
  - {fileID: 7138816553533510817}
  m_Father: {fileID: 0}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -6004,6 +6084,91 @@
  m_FillOrigin: 0
  m_UseSpriteMesh: 0
  m_PixelsPerUnitMultiplier: 1
--- !u!1 &6156246034478813876
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 7138816553533510817}
  - component: {fileID: 4090149269951173961}
  m_Layer: 5
  m_Name: 0 (1)
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &7138816553533510817
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 6156246034478813876}
  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
  m_LocalPosition: {x: 0, y: 0, z: -0.00006103517}
  m_LocalScale: {x: 1, y: 1, z: 1}
  m_Children:
  - {fileID: 3816649810714100738}
  - {fileID: 390278849146172503}
  m_Father: {fileID: 3349256846538989983}
  m_RootOrder: 11
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0.5, y: 0.5}
  m_AnchorMax: {x: 0.5, y: 0.5}
  m_AnchoredPosition: {x: -8.17, y: 82}
  m_SizeDelta: {x: 245.45972, y: 20}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!114 &4090149269951173961
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 6156246034478813876}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Navigation:
    m_Mode: 3
    m_SelectOnUp: {fileID: 0}
    m_SelectOnDown: {fileID: 0}
    m_SelectOnLeft: {fileID: 0}
    m_SelectOnRight: {fileID: 0}
  m_Transition: 1
  m_Colors:
    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
    m_ColorMultiplier: 1
    m_FadeDuration: 0.1
  m_SpriteState:
    m_HighlightedSprite: {fileID: 0}
    m_PressedSprite: {fileID: 0}
    m_SelectedSprite: {fileID: 0}
    m_DisabledSprite: {fileID: 0}
  m_AnimationTriggers:
    m_NormalTrigger: Normal
    m_HighlightedTrigger: Highlighted
    m_PressedTrigger: Pressed
    m_SelectedTrigger: Selected
    m_DisabledTrigger: Disabled
  m_Interactable: 1
  m_TargetGraphic: {fileID: 4865835282221787662}
  toggleTransition: 1
  graphic: {fileID: 5368231734457722164}
  m_Group: {fileID: 0}
  onValueChanged:
    m_PersistentCalls:
      m_Calls: []
  m_IsOn: 0
--- !u!1 &6315644736197012094
GameObject:
  m_ObjectHideFlags: 0
@@ -6313,6 +6478,82 @@
    m_VerticalOverflow: 0
    m_LineSpacing: 1
  m_Text: "5\u7EA7"
--- !u!1 &6592600421902923509
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 3816649810714100738}
  - component: {fileID: 3467252156456277279}
  - component: {fileID: 4865835282221787662}
  m_Layer: 5
  m_Name: Background
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &3816649810714100738
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 6592600421902923509}
  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_Children:
  - {fileID: 1243345092286743546}
  m_Father: {fileID: 7138816553533510817}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0, y: 1}
  m_AnchorMax: {x: 0, y: 1}
  m_AnchoredPosition: {x: 10, y: -10}
  m_SizeDelta: {x: 60, y: 60}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3467252156456277279
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 6592600421902923509}
  m_CullTransparentMesh: 0
--- !u!114 &4865835282221787662
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 6592600421902923509}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Material: {fileID: 0}
  m_Color: {r: 1, g: 1, b: 1, a: 1}
  m_RaycastTarget: 1
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
  m_Maskable: 1
  m_OnCullStateChanged:
    m_PersistentCalls:
      m_Calls: []
  m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
  m_Type: 1
  m_PreserveAspect: 0
  m_FillCenter: 1
  m_FillMethod: 4
  m_FillAmount: 1
  m_FillClockwise: 1
  m_FillOrigin: 0
  m_UseSpriteMesh: 0
  m_PixelsPerUnitMultiplier: 1
--- !u!1 &7537806086408887646
GameObject:
  m_ObjectHideFlags: 0
@@ -6909,6 +7150,81 @@
  m_FillOrigin: 0
  m_UseSpriteMesh: 0
  m_PixelsPerUnitMultiplier: 1
--- !u!1 &7890523607911646632
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 1243345092286743546}
  - component: {fileID: 8447133158492106960}
  - component: {fileID: 5368231734457722164}
  m_Layer: 5
  m_Name: Checkmark
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &1243345092286743546
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 7890523607911646632}
  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_Children: []
  m_Father: {fileID: 3816649810714100738}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0.5, y: 0.5}
  m_AnchorMax: {x: 0.5, y: 0.5}
  m_AnchoredPosition: {x: 0, y: 0}
  m_SizeDelta: {x: 60, y: 60}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8447133158492106960
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 7890523607911646632}
  m_CullTransparentMesh: 0
--- !u!114 &5368231734457722164
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 7890523607911646632}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
  m_Name:
  m_EditorClassIdentifier:
  m_Material: {fileID: 0}
  m_Color: {r: 1, g: 1, b: 1, a: 1}
  m_RaycastTarget: 1
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
  m_Maskable: 1
  m_OnCullStateChanged:
    m_PersistentCalls:
      m_Calls: []
  m_Sprite: {fileID: 10901, guid: 0000000000000000f000000000000000, type: 0}
  m_Type: 0
  m_PreserveAspect: 0
  m_FillCenter: 1
  m_FillMethod: 4
  m_FillAmount: 1
  m_FillClockwise: 1
  m_FillOrigin: 0
  m_UseSpriteMesh: 0
  m_PixelsPerUnitMultiplier: 1
--- !u!1 &7940353752494966417
GameObject:
  m_ObjectHideFlags: 0
Assets/Scenes/Levels/Battle/Endless2D.unity
@@ -451,6 +451,12 @@
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 49423732}
  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
--- !u!1 &49762375 stripped
GameObject:
  m_CorrespondingSourceObject: {fileID: 1671498435182872930, guid: dcbcc0c51f9291c498c66e05663360e2,
    type: 3}
  m_PrefabInstance: {fileID: 1671498436155399979}
  m_PrefabAsset: {fileID: 0}
--- !u!1 &96670465
GameObject:
  m_ObjectHideFlags: 0
@@ -4445,6 +4451,11 @@
      propertyPath: NoBuffText
      value: 
      objectReference: {fileID: 213376307}
    - target: {fileID: 7478702450202136628, guid: 29da21b318f42054db30c52123aa4dbf,
        type: 3}
      propertyPath: GMBtn
      value:
      objectReference: {fileID: 49762375}
    m_RemovedComponents: []
  m_SourcePrefab: {fileID: 100100000, guid: 29da21b318f42054db30c52123aa4dbf, type: 3}
--- !u!114 &877647586 stripped
@@ -5921,7 +5932,7 @@
    type: 3}
  m_PrefabInstance: {fileID: 1671498436155399979}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_GameObject: {fileID: 49762375}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: b299ed121c24b1347a4c0dffb392051e, type: 3}
@@ -10909,6 +10920,11 @@
      propertyPath: WaterToggleGroup
      value: 
      objectReference: {fileID: 1832175059}
    - target: {fileID: 1671498435182872928, guid: dcbcc0c51f9291c498c66e05663360e2,
        type: 3}
      propertyPath: InfiniteSkillToggle
      value:
      objectReference: {fileID: 3349256845298627642}
    - target: {fileID: 1671498435182872929, guid: dcbcc0c51f9291c498c66e05663360e2,
        type: 3}
      propertyPath: m_LocalPosition.x
@@ -11097,6 +11113,11 @@
      propertyPath: m_Name
      value: GMPanel
      objectReference: {fileID: 0}
    - target: {fileID: 3349256846538989978, guid: 956e8afbc5c59e6429b9cc31c627d660,
        type: 3}
      propertyPath: m_IsActive
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 3349256846538989983, guid: 956e8afbc5c59e6429b9cc31c627d660,
        type: 3}
      propertyPath: m_LocalPosition.x
@@ -11274,3 +11295,15 @@
      objectReference: {fileID: 1176213067}
    m_RemovedComponents: []
  m_SourcePrefab: {fileID: 100100000, guid: 956e8afbc5c59e6429b9cc31c627d660, type: 3}
--- !u!114 &3349256845298627642 stripped
MonoBehaviour:
  m_CorrespondingSourceObject: {fileID: 4090149269951173961, guid: 956e8afbc5c59e6429b9cc31c627d660,
    type: 3}
  m_PrefabInstance: {fileID: 3349256845298627641}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 9085046f02f69544eb97fd06b6048fe2, type: 3}
  m_Name:
  m_EditorClassIdentifier:
Assets/Scripts/Common/GameConfig.cs
@@ -94,6 +94,11 @@
        /// </summary>
        public static bool IsNewbieStart;
        /// <summary>
        /// 无限释放技能
        /// </summary>
        public static bool InfiniteSkill;
        #region 各个塔的偏移量
        public static Vector3 fireScale = new Vector3(1.92f, 1.92f, 2.208f);
        public static Vector3 woodScale = new Vector3(1.5f, 1.5f, 2.82f);
Assets/Scripts/TowerDefense/Affectors/AttackAffector.cs
@@ -373,7 +373,7 @@
        private void UpdateWoodAim()
        {
            // 离得最近的 Agent
            Agent agent = GetMinDistanceAgent();
            Agent agent = AgentInsManager.instance.GetMinDisAgent(waveLineID, false);
            if (agent != null)
            {
@@ -408,34 +408,6 @@
                    ++agent.WoodAimCount;
                }
            }
        }
        /// <summary>
        /// 获取距离终点最近的Agent
        /// </summary>
        /// <returns></returns>
        private Agent GetMinDistanceAgent()
        {
            Agent ret = null;
            float minDistance = -1f;
            WaveLineAgentInsMgr[] waveLineAgentIns = AgentInsManager.instance.GetWaveLineList();
            WaveLineAgentInsMgr waveLineAgentInsMgr = waveLineAgentIns[waveLineID];
            List<Agent> agents = waveLineAgentInsMgr.listAgent;
            Vector3 endPos = EndlessLevelManager.instance.GetHomeBasePosition(waveLineID + 1);
            for (int i = 0; i < agents.Count; ++i)
            {
                float distance = Mathf.Abs(agents[i].transform.position.z - endPos.z);
                if (minDistance < 0 || distance < minDistance)
                {
                    minDistance = distance;
                    ret = agents[i];
                }
            }
            return ret;
        }
        /// <summary>
Assets/Scripts/TowerDefense/Level/AgentInsManager.cs
@@ -78,44 +78,68 @@
    /// </summary>
    public List<Agent> listAgent { get { return this.agentInsList; } }
    /// <summary>
    /// 获取距目标结点最近的Agent.
    /// </summary>
    /// <returns></returns>
    public Agent getMinDisAgent(bool _noPoison = false)
    // /// <summary>
    // /// 获取距目标结点最近的Agent.
    // /// </summary>
    // /// <returns></returns>
    // public Agent getMinDisAgent(bool _noPoison = false)
    // {
    //     if (agentInsList.Count == 0) return null;
    //     // 排序,然后返回对应的数据
    //     // 直接使用闭包函数来进行排序:
    //     agentInsList.Sort((left, right) =>
    //     {
    //         if (left.distanceToDest > right.distanceToDest)
    //         {
    //             return 1;
    //         }
    //         else if (left.distanceToDest == right.distanceToDest)
    //         {
    //             return 0;
    //         }
    //         else
    //         {
    //             return -1;
    //         }
    //     });
    //     if (_noPoison)
    //     {
    //         for (int ti = 0; ti < agentInsList.Count; ti++)
    //         {
    //             if (!agentInsList[ti].bInPoison)
    //                 return agentInsList[ti];
    //         }
    //         return null;
    //     }
    //     else
    //         return agentInsList[0];
    // }
    public Agent GetMinDistanceAgent()
    {
        if (agentInsList.Count == 0) return null;
        Agent ret = null;
        float minDistance = -1f;
        // 排序,然后返回对应的数据
        // 直接使用闭包函数来进行排序:
        agentInsList.Sort((left, right) =>
        WaveLineAgentInsMgr[] waveLineAgentIns = AgentInsManager.instance.GetWaveLineList();
        WaveLineAgentInsMgr waveLineAgentInsMgr = waveLineAgentIns[waveLineID];
        List<Agent> agents = waveLineAgentInsMgr.listAgent;
        Vector3 endPos = EndlessLevelManager.instance.GetHomeBasePosition(waveLineID + 1);
        for (int i = 0; i < agents.Count; ++i)
        {
            if (left.distanceToDest > right.distanceToDest)
            {
                return 1;
            }
            else if (left.distanceToDest == right.distanceToDest)
            {
                return 0;
            }
            else
            {
                return -1;
            }
        });
            float distance = Mathf.Abs(agents[i].transform.position.z - endPos.z);
        if (_noPoison)
        {
            for (int ti = 0; ti < agentInsList.Count; ti++)
            if (minDistance < 0 || distance < minDistance)
            {
                if (!agentInsList[ti].bInPoison)
                    return agentInsList[ti];
                minDistance = distance;
                ret = agents[i];
            }
            return null;
        }
        else
            return agentInsList[0];
        return ret;
    }
}
@@ -314,9 +338,11 @@
        Agent ag;
        if (oppo)
            ag = oppoAgentWaveLineArray[lineid].getMinDisAgent(noPoison);
            // ag = oppoAgentWaveLineArray[lineid].getMinDisAgent(noPoison);
            ag = oppoAgentWaveLineArray[lineid].GetMinDistanceAgent();
        else
            ag = agentWaveLineArray[lineid].getMinDisAgent(noPoison);
            // ag = agentWaveLineArray[lineid].getMinDisAgent(noPoison);
            ag = agentWaveLineArray[lineid].GetMinDistanceAgent();
        // 这一行防止无限的循环下去。
        if (forceGet) return ag;
Assets/Scripts/TowerDefense/Targetting/Targetter.cs
@@ -10,470 +10,470 @@
namespace TowerDefense.Targetting
{
    /// <summary>
    /// 选择目标Agent的方式
    /// </summary>
    public enum EEnemySelFunc
    {
        NULL,
        Nearest,   // 离终点最近的.
        Random,    // 随机选择.
        MaxHp,     // 血量最高的.
        END
    }
    /// <summary>
    /// Class used to track targets for an affector
    /// 这个类比较古怪的地方是他本身并没有显示出来Collider,但却有Collider Component.是在
    /// void AttachCollider() 这个函数中(TargetterEditor.cs),动态的添加了不同类型的Collider Component,
    /// 然后隐藏了这个Collider Component,但是却可以在游戏内当作Collider Component来使用。
    /// 在TargetterEditor内,设置isTrigger为true
    ///
    /// event 的作用在于声明的对应公有 Action or delegate变量,不能在类的外面执行,只在类的外面 += -= 操作
    ///
    /// </summary>
    public class Targetter : MonoBehaviour
    {
        public static bool bSearchTarget = true;
        /// <summary>
        /// Fires when a targetable enters the target collider
        /// </summary>
        public event Action<Targetable> targetEntersRange;
    /// <summary>
    /// 选择目标Agent的方式
    /// </summary>
    public enum EEnemySelFunc
    {
        NULL,
        Nearest,   // 离终点最近的.
        Random,    // 随机选择.
        MaxHp,     // 血量最高的.
        END
    }
    /// <summary>
    /// Class used to track targets for an affector
    /// 这个类比较古怪的地方是他本身并没有显示出来Collider,但却有Collider Component.是在
    /// void AttachCollider() 这个函数中(TargetterEditor.cs),动态的添加了不同类型的Collider Component,
    /// 然后隐藏了这个Collider Component,但是却可以在游戏内当作Collider Component来使用。
    /// 在TargetterEditor内,设置isTrigger为true
    ///
    /// event 的作用在于声明的对应公有 Action or delegate变量,不能在类的外面执行,只在类的外面 += -= 操作
    ///
    /// </summary>
    public class Targetter : MonoBehaviour
    {
        public static bool bSearchTarget = true;
        /// <summary>
        /// Fires when a targetable enters the target collider
        /// </summary>
        public event Action<Targetable> targetEntersRange;
        /// <summary>
        /// Fires when a targetable exits the target collider
        /// </summary>
        public event Action<Targetable> targetExitsRange;
        /// <summary>
        /// Fires when a targetable exits the target collider
        /// </summary>
        public event Action<Targetable> targetExitsRange;
        /// <summary>
        /// Fires when an appropriate target is found
        /// </summary>
        public event Action<Targetable> acquiredTarget;
        /// <summary>
        /// Fires when an appropriate target is found
        /// </summary>
        public event Action<Targetable> acquiredTarget;
        /// <summary>
        /// Fires when the current target was lost
        /// </summary>
        public event Action lostTarget;
        /// <summary>
        /// Fires when the current target was lost
        /// </summary>
        public event Action lostTarget;
        /// <summary>
        /// The transform to point at the target
        /// </summary>
        public Transform turret;
        /// <summary>
        /// The transform to point at the target
        /// </summary>
        public Transform turret;
        /// <summary>
        /// 搜索敌人的方式:默认是最近,加入随机和血量最多,后期再加其它的模式
        /// </summary>
        public EEnemySelFunc searchEnemyFunc = EEnemySelFunc.Nearest;
        /// <summary>
        /// 搜索敌人的方式:默认是最近,加入随机和血量最多,后期再加其它的模式
        /// </summary>
        public EEnemySelFunc searchEnemyFunc = EEnemySelFunc.Nearest;
        /// <summary>
        /// The range of the turret's x rotation
        /// </summary>
        public Vector2 turretXRotationRange = new Vector2(0, 359);
        /// <summary>
        /// The range of the turret's x rotation
        /// </summary>
        public Vector2 turretXRotationRange = new Vector2(0, 359);
        /// <summary>
        /// If m_Turret rotates freely or only on y;
        /// </summary>
        public bool onlyYTurretRotation;
        /// <summary>
        /// If m_Turret rotates freely or only on y;
        /// </summary>
        public bool onlyYTurretRotation;
        /// <summary>
        /// The search rate in searches per second
        /// </summary>
        public float searchRate;
        /// <summary>
        /// The search rate in searches per second
        /// </summary>
        public float searchRate;
        /// <summary>
        /// Y rotation speed while the turret is idle in degrees per second
        /// </summary>
        public float idleRotationSpeed = 39f;
        /// <summary>
        /// Y rotation speed while the turret is idle in degrees per second
        /// </summary>
        public float idleRotationSpeed = 39f;
        /// <summary>
        /// The time it takes for the tower to correct its x rotation on idle in seconds
        /// </summary>
        public float idleCorrectionTime = 2.0f;
        /// <summary>
        /// The time it takes for the tower to correct its x rotation on idle in seconds
        /// </summary>
        public float idleCorrectionTime = 2.0f;
        /// <summary>
        /// The collider attached to the targetter
        /// </summary>
        public Collider attachedCollider;
        /// <summary>
        /// The collider attached to the targetter
        /// </summary>
        public Collider attachedCollider;
        /// <summary>
        /// How long the turret waits in its idle form before spinning in seconds
        /// </summary>
        public float idleWaitTime = 2.0f;
        /// <summary>
        /// How long the turret waits in its idle form before spinning in seconds
        /// </summary>
        public float idleWaitTime = 2.0f;
        /// <summary>
        /// The current targetables in the collider
        /// </summary>
        protected List<Targetable> m_TargetsInRange = new List<Targetable>();
        /// <summary>
        /// The current targetables in the collider
        /// </summary>
        protected List<Targetable> m_TargetsInRange = new List<Targetable>();
        /// <summary>
        /// The seconds until a search is allowed
        /// </summary>
        protected float m_SearchTimer = 0.0f;
        /// <summary>
        /// The seconds until a search is allowed
        /// </summary>
        protected float m_SearchTimer = 0.0f;
        /// <summary>
        /// The seconds until the tower starts spinning
        /// </summary>
        protected float m_WaitTimer = 0.0f;
        /// <summary>
        /// The seconds until the tower starts spinning
        /// </summary>
        protected float m_WaitTimer = 0.0f;
        /// <summary>
        /// The current targetable
        /// </summary>
        protected Targetable m_CurrrentTargetable;
        /// <summary>
        /// The current targetable
        /// </summary>
        protected Targetable m_CurrrentTargetable;
        /// <summary>
        /// Counter used for x rotation correction
        /// </summary>
        protected float m_XRotationCorrectionTime;
        /// <summary>
        /// Counter used for x rotation correction
        /// </summary>
        protected float m_XRotationCorrectionTime;
        /// <summary>
        /// If there was a targetable in the last frame
        /// </summary>
        protected bool m_HadTarget;
        /// <summary>
        /// If there was a targetable in the last frame
        /// </summary>
        protected bool m_HadTarget;
        /// <summary>
        /// How fast this turret is spinning
        /// </summary>
        protected float m_CurrentRotationSpeed;
        /// <summary>
        /// How fast this turret is spinning
        /// </summary>
        protected float m_CurrentRotationSpeed;
        /// <summary>
        /// returns the radius of the collider whether
        /// its a sphere or capsule
        /// </summary>
        public float effectRadius
        {
            get
            {
                var sphere = attachedCollider as SphereCollider;
                if (sphere != null)
                {
                    return sphere.radius;
                }
                var capsule = attachedCollider as CapsuleCollider;
                if (capsule != null)
                {
                    return capsule.radius;
                }
                return 0;
            }
        }
        /// <summary>
        /// The alignment of the affector
        /// </summary>
        public IAlignmentProvider alignment;
        /// <summary>
        /// Returns the current target
        /// </summary>
        public Targetable GetTarget( int waveline,bool noPoison = false )
        {
            switch( this.searchEnemyFunc)
        /// <summary>
        /// returns the radius of the collider whether
        /// its a sphere or capsule
        /// </summary>
        public float effectRadius
        {
            get
            {
                case EEnemySelFunc.Nearest:
                case EEnemySelFunc.MaxHp:
                case EEnemySelFunc.Random:
                    {
                        if (bOpponent)
                var sphere = attachedCollider as SphereCollider;
                if (sphere != null)
                {
                    return sphere.radius;
                }
                var capsule = attachedCollider as CapsuleCollider;
                if (capsule != null)
                {
                    return capsule.radius;
                }
                return 0;
            }
        }
        /// <summary>
        /// The alignment of the affector
        /// </summary>
        public IAlignmentProvider alignment;
        /// <summary>
        /// Returns the current target
        /// </summary>
        public Targetable GetTarget(int waveline, bool noPoison = false)
        {
            switch (this.searchEnemyFunc)
            {
                case EEnemySelFunc.Nearest:
                case EEnemySelFunc.MaxHp:
                case EEnemySelFunc.Random:
                    {
                        if (bOpponent)
                        {
                            //return AgentInsManager.instance.oppoMinDisAgent;
                            if (waveline >= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline, true,false,noPoison);
                            else
                            {
                                Debug.Log("当前Opponent传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                        else
                        {
                            //return AgentInsManager.instance.MinDisAgent;
                            if(waveline>= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline,false,false, noPoison );
                            //return AgentInsManager.instance.oppoMinDisAgent;
                            if (waveline >= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline, true, false, noPoison);
                            else
                            {
                                Debug.Log("当前传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                    }
/*                case EEnemySelFunc.MaxHp:
                    {
                        return AgentInsManager.instance.getMaxHpAgent(bOpponent);
                    }
                case EEnemySelFunc.Random:
                    {
                        return AgentInsManager.instance.getRandomAgent(bOpponent);
                    }*/
                                Debug.Log("当前Opponent传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                        else
                        {
                            //return AgentInsManager.instance.MinDisAgent;
                            if (waveline >= 0)
                                return AgentInsManager.instance.GetMinDisAgent(waveline, false, false, noPoison);
                            else
                            {
                                Debug.Log("当前传入的参数waveLine is:" + waveline);
                                return AgentInsManager.instance.MinDisAgent;
                            }
                        }
                    }
                    /*                case EEnemySelFunc.MaxHp:
                                        {
                                            return AgentInsManager.instance.getMaxHpAgent(bOpponent);
                                        }
                                    case EEnemySelFunc.Random:
                                        {
                                            return AgentInsManager.instance.getRandomAgent(bOpponent);
                                        }*/
            }
            return null;
        }
            return null;
        }
        /// <summary>
        /// 是否是对手战斗方.
        /// </summary>
        public bool bOpponent { set; get; }
        /// <summary>
        /// 是否是对手战斗方.
        /// </summary>
        public bool bOpponent { set; get; }
        /// <summary>
        /// Clears the list of current targets and clears all events
        /// </summary>
        public void ResetTargetter()
        {
            m_TargetsInRange.Clear();
            m_CurrrentTargetable = null;
        /// <summary>
        /// Clears the list of current targets and clears all events
        /// </summary>
        public void ResetTargetter()
        {
            m_TargetsInRange.Clear();
            m_CurrrentTargetable = null;
            targetEntersRange = null;
            targetExitsRange = null;
            acquiredTarget = null;
            lostTarget = null;
            targetEntersRange = null;
            targetExitsRange = null;
            acquiredTarget = null;
            lostTarget = null;
            // Reset turret facing
            if (turret != null)
            {
                turret.localRotation = Quaternion.identity;
            }
        }
        /// <summary>
        /// Returns all the targets within the collider. This list must not be changed as it is the working
        /// list of the targetter. Changing it could break the targetter
        /// </summary>
        public List<Targetable> GetAllTargets()
        {
            switch( searchEnemyFunc)
            // Reset turret facing
            if (turret != null)
            {
                case EEnemySelFunc.MaxHp:
                turret.localRotation = Quaternion.identity;
            }
        }
        /// <summary>
        /// Returns all the targets within the collider. This list must not be changed as it is the working
        /// list of the targetter. Changing it could break the targetter
        /// </summary>
        public List<Targetable> GetAllTargets()
        {
            switch (searchEnemyFunc)
            {
                case EEnemySelFunc.MaxHp:
                    {
                        return AgentInsManager.instance.getAgentListMaxHp(bOpponent);
                        return AgentInsManager.instance.getAgentListMaxHp(bOpponent);
                    }
                case EEnemySelFunc.Random:
                case EEnemySelFunc.Random:
                    {
                        return AgentInsManager.instance.getAgentListRandom(bOpponent);
                        return AgentInsManager.instance.getAgentListRandom(bOpponent);
                    }
            }
            // 最后返回的就是距离计算的模式:
            if (this.bOpponent)
                return AgentInsManager.instance.OppoAgentInRange;
            else
                return AgentInsManager.instance.AgentInRange;
        }
            // 最后返回的就是距离计算的模式:
            if (this.bOpponent)
                return AgentInsManager.instance.OppoAgentInRange;
            else
                return AgentInsManager.instance.AgentInRange;
        }
        /// <summary>
        /// Checks if the targetable is a valid target
        /// </summary>
        /// <param name="targetable"></param>
        /// <returns>true if targetable is vaild, false if not</returns>
        protected virtual bool IsTargetableValid(Targetable targetable)
        {
            if (targetable == null)
            {
                return false;
            }
            IAlignmentProvider targetAlignment = targetable.configuration.alignmentProvider;
            bool canDamage = alignment == null || targetAlignment == null ||
                             alignment.CanHarm(targetAlignment);
            return canDamage;
        }
        /// <summary>
        /// Checks if the targetable is a valid target
        /// </summary>
        /// <param name="targetable"></param>
        /// <returns>true if targetable is vaild, false if not</returns>
        protected virtual bool IsTargetableValid(Targetable targetable)
        {
            if (targetable == null)
            {
                return false;
            }
        /// <summary>
        /// On exiting the trigger, a valid targetable is removed from the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerExit(Collider other)
        {
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            m_TargetsInRange.Remove(targetable);
            if (targetExitsRange != null)
            {
                targetExitsRange(targetable);
            }
            if (targetable == m_CurrrentTargetable)
            {
                OnTargetRemoved(targetable);
            }
            else
            {
                // Only need to remove if we're not our actual target, otherwise OnTargetRemoved will do the work above
                targetable.removed -= OnTargetRemoved;
            }
        }
        /// <summary>
        /// On entering the trigger, a valid targetable is added to the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerEnter(Collider other)
        {
            GameObject tobj = this.transform.parent.gameObject;
            Collider tcol = GetComponent<Collider>();
            IAlignmentProvider targetAlignment = targetable.configuration.alignmentProvider;
            bool canDamage = alignment == null || targetAlignment == null ||
                             alignment.CanHarm(targetAlignment);
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            targetable.removed += OnTargetRemoved;
            m_TargetsInRange.Add(targetable);
            if (targetEntersRange != null)
            {
                targetEntersRange(targetable);
            }
        }
            return canDamage;
        }
        /// <summary>
        /// Returns the nearest targetable within the currently tracked targetables
        /// </summary>
        /// <returns>The nearest targetable if there is one, null otherwise</returns>
        protected virtual Targetable GetNearestTargetable()
        {
        /// <summary>
        /// On exiting the trigger, a valid targetable is removed from the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerExit(Collider other)
        {
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            m_TargetsInRange.Remove(targetable);
            if (targetExitsRange != null)
            {
                targetExitsRange(targetable);
            }
            if (targetable == m_CurrrentTargetable)
            {
                OnTargetRemoved(targetable);
            }
            else
            {
                // Only need to remove if we're not our actual target, otherwise OnTargetRemoved will do the work above
                targetable.removed -= OnTargetRemoved;
            }
        }
        /// <summary>
        /// On entering the trigger, a valid targetable is added to the tracking list.
        /// </summary>
        /// <param name="other">The other collider in the collision</param>
        protected virtual void OnTriggerEnter(Collider other)
        {
            GameObject tobj = this.transform.parent.gameObject;
            Collider tcol = GetComponent<Collider>();
            var targetable = other.GetComponent<Targetable>();
            if (!IsTargetableValid(targetable))
            {
                return;
            }
            targetable.removed += OnTargetRemoved;
            m_TargetsInRange.Add(targetable);
            if (targetEntersRange != null)
            {
                targetEntersRange(targetable);
            }
        }
        /// <summary>
        /// Returns the nearest targetable within the currently tracked targetables
        /// </summary>
        /// <returns>The nearest targetable if there is one, null otherwise</returns>
        protected virtual Targetable GetNearestTargetable()
        {
            // 使用统一管理的管理器接口.
            if (this.bOpponent)
                return AgentInsManager.instance.oppoMinDisAgent;
            else
                return AgentInsManager.instance.MinDisAgent;
        }
            else
                return AgentInsManager.instance.MinDisAgent;
        }
        /// <summary>
        /// Starts the search timer
        /// </summary>
        protected virtual void Start()
        {
            m_SearchTimer = searchRate;
            m_WaitTimer = idleWaitTime;
        /// <summary>
        /// Starts the search timer
        /// </summary>
        protected virtual void Start()
        {
            m_SearchTimer = searchRate;
            m_WaitTimer = idleWaitTime;
        }
        }
        /// <summary>
        /// Checks if any targets are destroyed and aquires a new targetable if appropriate
        /// </summary>
        protected virtual void Update()
        {
            m_SearchTimer -= Time.deltaTime;
        /// <summary>
        /// Checks if any targets are destroyed and aquires a new targetable if appropriate
        /// </summary>
        protected virtual void Update()
        {
            m_SearchTimer -= Time.deltaTime;
            if (m_SearchTimer <= 0.0f && m_CurrrentTargetable == null && m_TargetsInRange.Count > 0)
            {
                m_CurrrentTargetable = GetNearestTargetable();
                if (m_CurrrentTargetable != null)
                {
                    if (acquiredTarget != null)
                    {
                        acquiredTarget(m_CurrrentTargetable);
                    }
                    m_SearchTimer = searchRate;
                }
            }
            if (m_SearchTimer <= 0.0f && m_CurrrentTargetable == null && m_TargetsInRange.Count > 0)
            {
                m_CurrrentTargetable = GetNearestTargetable();
                if (m_CurrrentTargetable != null)
                {
                    if (acquiredTarget != null)
                    {
                        acquiredTarget(m_CurrrentTargetable);
                    }
                    m_SearchTimer = searchRate;
                }
            }
            AimTurret();
            AimTurret();
            m_HadTarget = m_CurrrentTargetable != null;
        }
            m_HadTarget = m_CurrrentTargetable != null;
        }
        /// <summary>
        /// Fired by the agents died event or when the current target moves out of range,
        /// Fires the lostTarget event.
        /// </summary>
        void OnTargetRemoved(DamageableBehaviour target)
        {
            target.removed -= OnTargetRemoved;
            if (m_CurrrentTargetable != null && target.configuration == m_CurrrentTargetable.configuration)
            {
                if (lostTarget != null)
                {
                    lostTarget();
                }
                m_HadTarget = false;
                m_TargetsInRange.Remove(m_CurrrentTargetable);
                m_CurrrentTargetable = null;
                m_XRotationCorrectionTime = 0.0f;
            }
            else //wasnt the current target, find and remove from targets list
            {
                for (int i = 0; i < m_TargetsInRange.Count; i++)
                {
                    if (m_TargetsInRange[i].configuration == target.configuration)
                    {
                        m_TargetsInRange.RemoveAt(i);
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Fired by the agents died event or when the current target moves out of range,
        /// Fires the lostTarget event.
        /// </summary>
        void OnTargetRemoved(DamageableBehaviour target)
        {
            target.removed -= OnTargetRemoved;
            if (m_CurrrentTargetable != null && target.configuration == m_CurrrentTargetable.configuration)
            {
                if (lostTarget != null)
                {
                    lostTarget();
                }
                m_HadTarget = false;
                m_TargetsInRange.Remove(m_CurrrentTargetable);
                m_CurrrentTargetable = null;
                m_XRotationCorrectionTime = 0.0f;
            }
            else //wasnt the current target, find and remove from targets list
            {
                for (int i = 0; i < m_TargetsInRange.Count; i++)
                {
                    if (m_TargetsInRange[i].configuration == target.configuration)
                    {
                        m_TargetsInRange.RemoveAt(i);
                        break;
                    }
                }
            }
        }
        /// <summary>
        /// Aims the turret at the current target
        /// 将炮塔对准当前目标
        /// </summary>
        protected virtual void AimTurret()
        {
            if (turret == null)
            {
                return;
            }
        {
            if (turret == null)
            {
                return;
            }
            if (m_CurrrentTargetable == null) // do idle rotation
            {
                if (m_WaitTimer > 0)
                {
                    m_WaitTimer -= Time.deltaTime;
                    if (m_WaitTimer <= 0)
                    {
                        m_CurrentRotationSpeed = (Random.value * 2 - 1) * idleRotationSpeed;
                    }
                }
                else
                {
                    Vector3 euler = turret.rotation.eulerAngles;
                    euler.x = Mathf.Lerp(Wrap180(euler.x), 0, m_XRotationCorrectionTime);
                    m_XRotationCorrectionTime = Mathf.Clamp01((m_XRotationCorrectionTime + Time.deltaTime) / idleCorrectionTime);
                    euler.y += m_CurrentRotationSpeed * Time.deltaTime;
            if (m_CurrrentTargetable == null) // do idle rotation
            {
                if (m_WaitTimer > 0)
                {
                    m_WaitTimer -= Time.deltaTime;
                    if (m_WaitTimer <= 0)
                    {
                        m_CurrentRotationSpeed = (Random.value * 2 - 1) * idleRotationSpeed;
                    }
                }
                else
                {
                    Vector3 euler = turret.rotation.eulerAngles;
                    euler.x = Mathf.Lerp(Wrap180(euler.x), 0, m_XRotationCorrectionTime);
                    m_XRotationCorrectionTime = Mathf.Clamp01((m_XRotationCorrectionTime + Time.deltaTime) / idleCorrectionTime);
                    euler.y += m_CurrentRotationSpeed * Time.deltaTime;
                    turret.eulerAngles = euler;
                }
            }
            else
            {
                m_WaitTimer = idleWaitTime;
                    turret.eulerAngles = euler;
                }
            }
            else
            {
                m_WaitTimer = idleWaitTime;
                Vector3 targetPosition = m_CurrrentTargetable.position;
                if (onlyYTurretRotation)
                {
                    targetPosition.y = turret.position.y;
                }
                Vector3 direction = targetPosition - turret.position;
                Quaternion look = Quaternion.LookRotation(direction, Vector3.up);
                Vector3 lookEuler = look.eulerAngles;
                // We need to convert the rotation to a -180/180 wrap so that we can clamp the angle with a min/max
                float x = Wrap180(lookEuler.x);
                lookEuler.x = Mathf.Clamp(x, turretXRotationRange.x, turretXRotationRange.y);
                look.eulerAngles = lookEuler;
                turret.rotation = look;
            }
        }
                Vector3 targetPosition = m_CurrrentTargetable.position;
                if (onlyYTurretRotation)
                {
                    targetPosition.y = turret.position.y;
                }
                Vector3 direction = targetPosition - turret.position;
                Quaternion look = Quaternion.LookRotation(direction, Vector3.up);
                Vector3 lookEuler = look.eulerAngles;
                // We need to convert the rotation to a -180/180 wrap so that we can clamp the angle with a min/max
                float x = Wrap180(lookEuler.x);
                lookEuler.x = Mathf.Clamp(x, turretXRotationRange.x, turretXRotationRange.y);
                look.eulerAngles = lookEuler;
                turret.rotation = look;
            }
        }
        /// <summary>
        /// A simply function to convert an angle to a -180/180 wrap
        /// </summary>
        static float Wrap180(float angle)
        {
            angle %= 360;
            if (angle < -180)
            {
                angle += 360;
            }
            else if (angle > 180)
            {
                angle -= 360;
            }
            return angle;
        }
    }
        /// <summary>
        /// A simply function to convert an angle to a -180/180 wrap
        /// </summary>
        static float Wrap180(float angle)
        {
            angle %= 360;
            if (angle < -180)
            {
                angle += 360;
            }
            else if (angle > 180)
            {
                angle -= 360;
            }
            return angle;
        }
    }
}
Assets/Scripts/TowerDefense/Towers/Placement/TowerPlacementGridEndless.cs
@@ -1107,7 +1107,7 @@
        {
            CloseCanPlace();
            currentCanPlace = m_Tiles[x, y];
            currentCanPlace.SetRender(true, isEmpty ? towerName : "");
            currentCanPlace?.SetRender(true, isEmpty ? towerName : "");
            // if (isEmpty)
            // {
            //     currentCanPlace.SetTowerVirtualshadow(towerName);
Assets/Scripts/TowerDefense/UI/EndlessBossSkill/BossSkillBubbleBomb.cs
@@ -290,7 +290,7 @@
                    if (config.IsAttackDeath || config.IsArrived) return;
                    ++config.AttackCount;
                    Debug.Log($"---- BubbleBombAgent Id: {id}, AttackCount: {config.AttackCount} ----");
                    // Debug.Log($"---- BubbleBombAgent Id: {id}, AttackCount: {config.AttackCount} ----");
                    if (config.AttackCount >= config.NeedAttackCount)
                        AgentDead(config);
@@ -437,5 +437,10 @@
                }
            }
        }
        protected override List<int> GetTunelList()
        {
            return EndlessPortData.GetAllTunelByLevelWave(EndlessLevelManager.instance.CurrentLevel, EndlessLevelManager.instance.WaveManager.CurrentWaveIndex);
        }
    }
}
Assets/Scripts/TowerDefense/UI/EndlessUIStart.cs
@@ -54,6 +54,8 @@
    private CanvasGroup canvasGroup;
    public GameObject GMBtn;
    /// <summary>
    /// Awake is called when the script instance is being loaded.
    /// </summary>
@@ -143,6 +145,7 @@
        transform.Find("Panel/BuffPreviewButton").gameObject.SetActive(false);
        transform.Find("Panel/SwitchSpeed").gameObject.SetActive(false);
        GMBtn.SetActive(false);
        GameConfig.EnergyCount = 0;
        GameConfig.SkillLevel = 1;
@@ -198,6 +201,7 @@
        {
            transform.Find("Panel/BuffPreviewButton").gameObject.SetActive(true);
            transform.Find("Panel/SwitchSpeed").gameObject.SetActive(true);
            GMBtn.SetActive(true);
        }
        fireSkillCost = JsonDataCenter.GetSkillLevelInfo(fireSkillID, GameConfig.SkillLevel).cost;
@@ -459,6 +463,14 @@
    /// </summary>
    private void OnClickFireSkillBtn()
    {
        if (GameConfig.InfiniteSkill)
        {
            GuideEnergyUp(200);
            EndlessWaveLineManager.instance.PlayAllWaveLineEffect();
            AgentInsManager.instance.ExecAllWavelineAttack(fireSkillID, GameConfig.SkillLevel, false);
            return;
        }
        //Debug.Log("释放了火技能:" + GameConfig.EnergyCount + "  fireSkillCost:" + fireSkillCost);
        if (GameConfig.EnergyCount >= fireSkillCost)
        {
Assets/Scripts/TowerDefense/UI/GM.cs
@@ -29,10 +29,20 @@
        private int waterSelectedIndex;
        public Toggle InfiniteSkillToggle;
        // Start is called before the first frame update
        private void Start()
        {
            GMPanel.SetActive(false);
            InfiniteSkillToggle.onValueChanged.AddListener((bool select) =>
             {
                 GameConfig.InfiniteSkill = select;
                 if (select)
                    EndlessUIStart.instance.GuideEnergyUp(200);
             });
            InfiniteSkillToggle.isOn = GameConfig.InfiniteSkill;
        }
        public void OnClickWood(int index)