diff --git a/Assets/Art/FengLei/PaoTai/DaQiao/shot_14053_0_kb_effect.prefab b/Assets/Art/FengLei/PaoTai/DaQiao/shot_14053_0_kb_effect.prefab index 212cf4e15..8c0415b68 100644 --- a/Assets/Art/FengLei/PaoTai/DaQiao/shot_14053_0_kb_effect.prefab +++ b/Assets/Art/FengLei/PaoTai/DaQiao/shot_14053_0_kb_effect.prefab @@ -28292,7 +28292,7 @@ Transform: serializedVersion: 2 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_LocalScale: {x: 1, y: 1, z: 21.722227} m_ConstrainProportionsScale: 0 m_Children: - {fileID: 454631387865839470} @@ -44926,6 +44926,11 @@ PrefabInstance: propertyPath: m_Layer value: 7 objectReference: {fileID: 0} + - target: {fileID: 5897679978818280078, guid: f968506c49fa41842bb684eb1e7016ac, + type: 3} + propertyPath: m_IsActive + value: 1 + objectReference: {fileID: 0} m_RemovedComponents: [] m_RemovedGameObjects: [] m_AddedGameObjects: [] diff --git a/Assets/Art/JABY/Fish/JiGuangControl.cs b/Assets/Art/JABY/Fish/JiGuangControl.cs index e1a4ce8a1..b49f91ef6 100644 --- a/Assets/Art/JABY/Fish/JiGuangControl.cs +++ b/Assets/Art/JABY/Fish/JiGuangControl.cs @@ -1,56 +1,98 @@ using System.Collections.Generic; using UnityEngine; -public class JiGuangControl : MonoBehaviour +/// +/// 激光静态工具类:激光实例的创建/朝向/长度计算,不依赖组件实例。 +/// 调用方自行持有激光实例列表(fxList)、基准缩放(fxBaseScale)和长度校正值(lengthCorrect), +/// 创建后每帧调用 JiGuangDirection/JiGuangLength 刷新。 +/// 起点支持 Transform / Vector3 两种;Z 轴重载: +/// - 不带 z 参数 → 直接读取起点/目标对象自身的 Z +/// - 带 z 参数 → 强制起点和终点都使用指定 Z(如战神激光统一压到 -50 显示层) +/// +public static class JiGuangControl { - [Header("起点")] - public Transform posShanDianYu; + // ==================== 创建 ==================== - [Header("激光预制体")] - public GameObject jiGuangPrefab; - - [Header("目标列表")] - public List posTargets = new List(); - public float length‌Correct=1f;//用于校正激光的长度,不同的激光设置不同的值 - - List _fxList = new List(); - private Vector3 fxBaseScale; - - void Start() + /// 按目标数量创建激光实例(克隆预制体,挂到 parent 下)。生成位置读取起点对象自身的 Z + public static void JiGuangCreate(GameObject prefab, Transform start, List targets, List fxList, Transform parent = null) { - JiGuangCreate(jiGuangPrefab, posShanDianYu, posTargets, _fxList); - fxBaseScale = jiGuangPrefab.transform.localScale; + if (start == null) return; + JiGuangCreateCore(prefab, start.position, targets, fxList, parent, null); } - void Update() + /// 同上,但生成位置强制使用指定 Z + public static void JiGuangCreate(GameObject prefab, Transform start, List targets, List fxList, Transform parent, float z) { - JiGuangDirection(posShanDianYu, posTargets, _fxList); - JiGuangLength(posShanDianYu, posTargets, _fxList, fxBaseScale); + if (start == null) return; + JiGuangCreateCore(prefab, start.position, targets, fxList, parent, z); } - void JiGuangCreate(GameObject prefab, Transform start, List targets, List fxList) + /// 起点直接给坐标(如战神激光的动态炮口位置)。生成位置读取 startPos 自身的 Z + public static void JiGuangCreate(GameObject prefab, Vector3 startPos, List targets, List fxList, Transform parent = null) { - if (prefab == null || start == null || targets == null || fxList == null) + JiGuangCreateCore(prefab, startPos, targets, fxList, parent, null); + } + + /// 同上,但生成位置强制使用指定 Z + public static void JiGuangCreate(GameObject prefab, Vector3 startPos, List targets, List fxList, Transform parent, float z) + { + JiGuangCreateCore(prefab, startPos, targets, fxList, parent, z); + } + + private static void JiGuangCreateCore(GameObject prefab, Vector3 startPos, List targets, List fxList, Transform parent, float? forcedZ) + { + if (prefab == null || targets == null || fxList == null) return; for (int i = 0; i < fxList.Count; i++) if (fxList[i] != null) - Destroy(fxList[i].gameObject); + Object.Destroy(fxList[i].gameObject); fxList.Clear(); for (int i = 0; i < targets.Count; i++) { if (targets[i] == null) continue; - GameObject go = Instantiate(prefab, start.position, Quaternion.identity, transform); + Vector3 spawnPos = startPos; + if (forcedZ.HasValue) + spawnPos.z = forcedZ.Value; + GameObject go = Object.Instantiate(prefab, spawnPos, Quaternion.identity, parent); go.name = prefab.name + "_" + i; fxList.Add(go.transform); } } - void JiGuangDirection(Transform start, List targets, List fxList) + // ==================== 朝向 ==================== + + /// 刷新激光朝向:直接读取起点/目标对象自身的 Z + public static void JiGuangDirection(Transform start, List targets, List fxList) { - if (start == null || targets == null || fxList == null || fxList.Count == 0) + if (start == null) return; + JiGuangDirectionCore(start.position, targets, fxList, null); + } + + /// 刷新激光朝向:起点和终点强制使用指定 Z(同层) + public static void JiGuangDirection(Transform start, List targets, List fxList, float z) + { + if (start == null) return; + JiGuangDirectionCore(start.position, targets, fxList, z); + } + + /// 刷新激光朝向(起点为坐标):直接使用 startPos 自身的 Z + public static void JiGuangDirection(Vector3 startPos, List targets, List fxList) + { + JiGuangDirectionCore(startPos, targets, fxList, null); + } + + /// 刷新激光朝向(起点为坐标):起点和终点强制使用指定 Z(同层) + public static void JiGuangDirection(Vector3 startPos, List targets, List fxList, float z) + { + JiGuangDirectionCore(startPos, targets, fxList, z); + } + + private static void JiGuangDirectionCore(Vector3 startPos, List targets, List fxList, float? forcedZ) + { + if (targets == null || fxList == null || fxList.Count == 0) return; int fxIndex = 0; @@ -59,8 +101,13 @@ public class JiGuangControl : MonoBehaviour if (targets[i] == null) continue; if (fxIndex >= fxList.Count) break; - Vector3 dir = targets[i].position - start.position; - dir.z = 0f; + Vector3 endPos = targets[i].position; + if (forcedZ.HasValue) + { + startPos.z = forcedZ.Value; + endPos.z = forcedZ.Value; + } + Vector3 dir = endPos - startPos; if (dir.sqrMagnitude > 0.0001f) fxList[fxIndex].rotation = Quaternion.LookRotation(dir); @@ -69,9 +116,37 @@ public class JiGuangControl : MonoBehaviour } } - void JiGuangLength(Transform start, List targets, List fxList, Vector3 fxBaseScale) + // ==================== 长度 ==================== + + /// 刷新激光长度(按基准缩放拉伸 Z 轴):直接读取起点/目标对象自身的 Z + public static void JiGuangLength(Transform start, List targets, List fxList, Vector3 fxBaseScale, float lengthCorrect = 1f) { - if (start == null || targets == null || fxList == null || fxList.Count == 0) + if (start == null) return; + JiGuangLengthCore(start.position, targets, fxList, fxBaseScale, lengthCorrect, null); + } + + /// 刷新激光长度:起点和终点强制使用指定 Z(同层) + public static void JiGuangLength(Transform start, List targets, List fxList, Vector3 fxBaseScale, float lengthCorrect, float z) + { + if (start == null) return; + JiGuangLengthCore(start.position, targets, fxList, fxBaseScale, lengthCorrect, z); + } + + /// 刷新激光长度(起点为坐标):直接使用 startPos 自身的 Z + public static void JiGuangLength(Vector3 startPos, List targets, List fxList, Vector3 fxBaseScale, float lengthCorrect = 1f) + { + JiGuangLengthCore(startPos, targets, fxList, fxBaseScale, lengthCorrect, null); + } + + /// 刷新激光长度(起点为坐标):起点和终点强制使用指定 Z(同层) + public static void JiGuangLength(Vector3 startPos, List targets, List fxList, Vector3 fxBaseScale, float lengthCorrect, float z) + { + JiGuangLengthCore(startPos, targets, fxList, fxBaseScale, lengthCorrect, z); + } + + private static void JiGuangLengthCore(Vector3 startPos, List targets, List fxList, Vector3 fxBaseScale, float lengthCorrect, float? forcedZ) + { + if (targets == null || fxList == null || fxList.Count == 0) return; int fxIndex = 0; @@ -80,11 +155,15 @@ public class JiGuangControl : MonoBehaviour if (targets[i] == null) continue; if (fxIndex >= fxList.Count) break; - Vector3 delta = targets[i].position - start.position; - delta.z = 0f; - float distance = delta.magnitude; + Vector3 endPos = targets[i].position; + if (forcedZ.HasValue) + { + startPos.z = forcedZ.Value; + endPos.z = forcedZ.Value; + } + float distance = (endPos - startPos).magnitude; Vector3 scale = fxBaseScale; - scale.z = distance * fxBaseScale.z * length‌Correct; + scale.z = distance * fxBaseScale.z * lengthCorrect; fxList[fxIndex].localScale = scale; fxIndex++; diff --git a/Assets/Art/JABY/ResJABY/assetspackage/fishes/fish_4003/prefab/fx_fish_4003_die_lighting_01_zise.prefab b/Assets/Art/JABY/ResJABY/assetspackage/fishes/fish_4003/prefab/fx_fish_4003_die_lighting_01_zise.prefab index 18ad66ac1..a384e3b8e 100644 --- a/Assets/Art/JABY/ResJABY/assetspackage/fishes/fish_4003/prefab/fx_fish_4003_die_lighting_01_zise.prefab +++ b/Assets/Art/JABY/ResJABY/assetspackage/fishes/fish_4003/prefab/fx_fish_4003_die_lighting_01_zise.prefab @@ -9,7 +9,7 @@ GameObject: serializedVersion: 6 m_Component: - component: {fileID: 4985305603660343} - m_Layer: 13 + m_Layer: 7 m_Name: GameObject m_TagString: Untagged m_Icon: {fileID: 0} @@ -43,7 +43,7 @@ GameObject: - component: {fileID: 198884547391421755} - component: {fileID: 199099118695303065} - component: {fileID: 210061268324235159} - m_Layer: 13 + m_Layer: 7 m_Name: fx_fish_4003_die_lighting_01_zise m_TagString: Untagged m_Icon: {fileID: 0} @@ -2348,7 +2348,7 @@ GameObject: - component: {fileID: 4589667821646929} - component: {fileID: 198372920208702427} - component: {fileID: 199025901758691103} - m_Layer: 13 + m_Layer: 7 m_Name: lighting_01 m_TagString: Untagged m_Icon: {fileID: 0} @@ -4680,7 +4680,7 @@ GameObject: - component: {fileID: 4587765826080250} - component: {fileID: 198709867586078768} - component: {fileID: 199044625135789424} - m_Layer: 13 + m_Layer: 7 m_Name: fx_fish_4003_die_lighting_01 m_TagString: Untagged m_Icon: {fileID: 0} diff --git a/Assets/MyGame/Scripts/Turret/SitPosData.cs b/Assets/MyGame/Scripts/Turret/SitPosData.cs index 6761e7e34..eaae714af 100644 --- a/Assets/MyGame/Scripts/Turret/SitPosData.cs +++ b/Assets/MyGame/Scripts/Turret/SitPosData.cs @@ -88,10 +88,16 @@ public class SitPosData : MonoBehaviour public TurretData currentZhanShenData; // ------------------------- - // 战神激光(锁定时显示) + // 战神激光(锁定时显示):两种形态 + // - laserPrefab 非空 → 预制体形态:JiGuangControl 工具类驱动(克隆激光预制体,方向/长度每帧刷新) + // - laserPrefab 为空 → LineRenderer 兜底形态:直接新建 GameObject + LineRenderer // ------------------------- - /// 战神激光对象列表(世界空间 LineRenderer,起点=子弹创建位置,终点=锁定鱼的位置)。三发锁定=3条 + /// LineRenderer 形态激光列表(起点=子弹创建位置,终点=锁定鱼的位置)。三发锁定=3条 private readonly List _zhanShenLasers = new List(); + /// 预制体形态激光实例列表(世界空间,由 JiGuangControl.JiGuangCreate 按目标数量重建) + private readonly List _laserFx = new List(); + /// 预制体形态激光的基准缩放(重建时取自 laserPrefab.transform.localScale) + private Vector3 _laserFxBaseScale = Vector3.one; // ------------------------- // XJMH 炮台列表(星际迷航专用) @@ -234,8 +240,9 @@ public class SitPosData : MonoBehaviour // ==================== 战神激光(锁定时显示) ==================== /// - /// 战神锁定时显示激光(LineRenderer):起点=创建子弹的位置(GetBulletStartPos),终点=锁定鱼的位置。 - /// ⭐ 一个锁定点一条线:单发锁定=1条,三发锁定=3条(主目标+2个三发目标)。 + /// 战神锁定时显示激光:起点=创建子弹的位置(GetBulletStartPos),终点=锁定鱼的位置,Z 统一压到 -50 显示层。 + /// ⭐ 一个锁定点一条激光:单发锁定=1条,三发锁定=3条(主目标+2个三发目标)。 + /// ⭐ 两种形态:laserPrefab 非空 → 预制体形态(JiGuangControl 工具类);为空 → LineRenderer 兜底形态。 /// ⭐ 只是视觉表现:打中鱼仍按正常攻击频率由子弹完成(HandleAutoFire 以 attackTime 发射锁定子弹)。 /// 普通炮台没有此效果(currentZhanShenData 为 null 时不显示)。 /// @@ -248,7 +255,7 @@ public class SitPosData : MonoBehaviour if (monCotl_Lock_Position != null && monCotl_Lock_Position.gameObject.activeInHierarchy) _laserTargets.Add(monCotl_Lock_Position); - // 三发锁定:每个有效目标一条线(主目标已在上面,这里只补额外的) + // 三发锁定:每个有效目标一条激光(主目标已在上面,这里只补额外的) if (tripleLockTargets.Count > 0) { for (int i = 0; i < tripleLockTargets.Count && i < tripleLockPositions.Count; i++) @@ -266,14 +273,68 @@ public class SitPosData : MonoBehaviour // 没有锁定点:隐藏所有激光 if (_laserTargets.Count == 0) { - for (int i = 0; i < _zhanShenLasers.Count; i++) - { - if (_zhanShenLasers[i] != null && _zhanShenLasers[i].gameObject.activeSelf) - _zhanShenLasers[i].gameObject.SetActive(false); - } + HideZhanShenLasers(); return; } + // 按配置走对应形态:laserPrefab 非空=预制体形态,为空=LineRenderer 兜底 + if (currentZhanShenData.laserPrefab != null) + UpdateZhanShenLaserFx(); + else + UpdateZhanShenLaserLine(); + } + + /// 隐藏全部战神激光(两种形态都处理) + private void HideZhanShenLasers() + { + for (int i = 0; i < _zhanShenLasers.Count; i++) + { + if (_zhanShenLasers[i] != null && _zhanShenLasers[i].gameObject.activeSelf) + _zhanShenLasers[i].gameObject.SetActive(false); + } + for (int i = 0; i < _laserFx.Count; i++) + { + if (_laserFx[i] != null && _laserFx[i].gameObject.activeSelf) + _laserFx[i].gameObject.SetActive(false); + } + } + + /// + /// 预制体形态(laserPrefab 非空):JiGuangControl 工具类驱动。 + /// 目标数量变化时全量重建(JiGuangCreate),每帧刷新位置/朝向/长度(JiGuangDirection/JiGuangLength,Z 强制 -50)。 + /// + private void UpdateZhanShenLaserFx() + { + var zsData = currentZhanShenData; + + // 起点:创建子弹的位置(Z=-50,战神激光显示层,和 FireBullet 的 startPos 同源仅 Z 不同) + Vector3 startPos = GetBulletStartPos(); + startPos.z = -50f; + + // 目标数量变化时全量重建(锁定建立/解除/三发目标增减时触发),并刷新基准缩放 + if (_laserFx.Count != _laserTargets.Count) + { + _laserFxBaseScale = zsData.laserPrefab.transform.localScale; + JiGuangControl.JiGuangCreate(zsData.laserPrefab, startPos, _laserTargets, _laserFx, null, -50f); + } + + // 炮口随炮台旋转会移动,位置需每帧同步;隐藏中的重新激活 + for (int i = 0; i < _laserFx.Count; i++) + { + if (_laserFx[i] == null) continue; + if (!_laserFx[i].gameObject.activeSelf) + _laserFx[i].gameObject.SetActive(true); + _laserFx[i].position = startPos; + } + + // 朝向 + 长度(指定 Z=-50 的重载:起终点同层) + JiGuangControl.JiGuangDirection(startPos, _laserTargets, _laserFx, -50f); + JiGuangControl.JiGuangLength(startPos, _laserTargets, _laserFx, _laserFxBaseScale, zsData.laserLengthCorrect, -50f); + } + + /// LineRenderer 兜底形态(laserPrefab 为空):按需创建、多余的隐藏 + private void UpdateZhanShenLaserLine() + { // 起点:创建子弹的位置(Z=-50,战神激光显示层,和 FireBullet 的 startPos 同源仅 Z 不同) Vector3 start = GetBulletStartPos(); start.z = -50f; @@ -303,53 +364,33 @@ public class SitPosData : MonoBehaviour private readonly List _laserTargets = new List(); /// - /// 创建一条战神激光(第 index 条): - /// - laserPrefab 非空 → 克隆预制体(LineRenderer 配置以预制体为准); - /// - laserPrefab 为空 → 直接创建一个新 GameObject 并加上 LineRenderer(用 TurretData 上的宽度/颜色)。 + /// 创建一条 LineRenderer 形态的战神激光(第 index 条)。 + /// 仅 laserPrefab 为空时使用:直接创建一个新 GameObject 并加上 LineRenderer(用 TurretData 上的宽度/颜色)。 /// private LineRenderer CreateZhanShenLaser(int index) { var zsData = currentZhanShenData; - GameObject laserGo; - LineRenderer line; - if (zsData.laserPrefab != null) - { - laserGo = Instantiate(zsData.laserPrefab); - laserGo.name = $"ZhanShenLaser_{index}"; - line = laserGo.GetComponent(); - if (line == null) - line = laserGo.AddComponent(); - } - else - { - // ⭐ prefab 为空:直接新建对象 + LineRenderer - laserGo = new GameObject($"ZhanShenLaser_{index}"); - line = laserGo.AddComponent(); - } - - // 公共配置:两个端点 + GameObject laserGo = new GameObject($"ZhanShenLaser_{index}"); + LineRenderer line = laserGo.AddComponent(); line.positionCount = Mathf.Max(line.positionCount, 2); laserGo.transform.SetParent(null); // 世界空间,不随座位/炮台旋转 - if (zsData.laserPrefab == null) - { - // 自动创建时应用 TurretData 上的宽度/颜色/材质 - line.startWidth = zsData.laserWidth; - line.endWidth = zsData.laserWidth; - line.startColor = zsData.laserColor; - line.endColor = zsData.laserColor; - var shader = Shader.Find("Sprites/Default"); - if (shader != null) - line.material = new Material(shader) { color = Color.white }; - line.useWorldSpace = true; - } + // 应用 TurretData 上的宽度/颜色/材质 + line.startWidth = zsData.laserWidth; + line.endWidth = zsData.laserWidth; + line.startColor = zsData.laserColor; + line.endColor = zsData.laserColor; + var shader = Shader.Find("Sprites/Default"); + if (shader != null) + line.material = new Material(shader) { color = Color.white }; + line.useWorldSpace = true; _zhanShenLasers.Add(line); return line; } - /// 销毁所有战神激光对象(清战神/座位禁用时调用) + /// 销毁所有战神激光对象(两种形态都清,清战神/座位禁用时调用) private void DestroyZhanShenLaser() { for (int i = 0; i < _zhanShenLasers.Count; i++) @@ -358,6 +399,13 @@ public class SitPosData : MonoBehaviour Destroy(_zhanShenLasers[i].gameObject); } _zhanShenLasers.Clear(); + + for (int i = 0; i < _laserFx.Count; i++) + { + if (_laserFx[i] != null) + Destroy(_laserFx[i].gameObject); + } + _laserFx.Clear(); } /// diff --git a/Assets/MyGame/Scripts/Turret/TurretData.cs b/Assets/MyGame/Scripts/Turret/TurretData.cs index 56a8e62da..dca1f4d17 100644 --- a/Assets/MyGame/Scripts/Turret/TurretData.cs +++ b/Assets/MyGame/Scripts/Turret/TurretData.cs @@ -35,12 +35,14 @@ public class TurretData : MonoBehaviour public GameObject bulletPrefab; [Header("战神激光(锁定时显示,普通炮台不配置即无此效果)")] - [Tooltip("激光预制体(带 LineRenderer)。为空时自动创建一个 GameObject 并加 LineRenderer")] + [Tooltip("激光预制体。非空=预制体形态(JiGuangControl 工具类驱动);为空=LineRenderer 兜底形态(自动创建)")] public GameObject laserPrefab; - [Tooltip("自动创建激光时的线宽")] + [Tooltip("LineRenderer 兜底形态的线宽")] public float laserWidth = 0.3f; - [Tooltip("自动创建激光时的颜色")] + [Tooltip("LineRenderer 兜底形态的颜色")] public Color laserColor = new Color(1f, 0.3f, 0.3f, 0.9f); + [Tooltip("预制体形态激光的长度校正值(不同激光预制体设置不同值)")] + public float laserLengthCorrect = 1f; [Header("UI面板属性")] [Tooltip("炮台编号,对应 TurretResourcesData.turretNumber")] diff --git a/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/ShanDian_DieEvent.cs b/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/ShanDian_DieEvent.cs index 90cbd270e..6484861b3 100644 --- a/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/ShanDian_DieEvent.cs +++ b/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/ShanDian_DieEvent.cs @@ -19,6 +19,10 @@ using UnityEngine; public class ShanDian_DieEvent : DieEffect_TSY { [Header("闪电表现")] + [Tooltip("激光预制体(配了则用 JiGuangControl 锁定鱼:激光跟随游动的鱼;不配则走 LineRenderer 折线闪电兜底)")] + public GameObject laserPrefab; + [Tooltip("激光长度校正值(JiGuangControl.JiGuangLength 用)")] + public float laserLengthCorrect = 1f; [Tooltip("闪电材质(空则自动用 Additive 发光材质,效果更亮更明显)")] public Material lightningMaterial; [Tooltip("闪电颜色")] @@ -54,6 +58,13 @@ public class ShanDian_DieEvent : DieEffect_TSY [Tooltip("杀鱼的物理层(默认 Fish 层)")] public LayerMask fishLayer; + // 激光锁定实例 / 折线闪电实例(收尾统一清理) + private readonly List _laserFx = new List(); + private readonly List _lightningObjects = new List(); + private Vector3 _laserFxBaseScale = Vector3.one; + // 运行时选鱼中心(供 Gizmos 画圈用) + private Vector3 _gizmoCenter; + public override void SetCoinCount() { } @@ -67,6 +78,7 @@ public class ShanDian_DieEvent : DieEffect_TSY { Vector3 startPos = startPoint != Vector3.zero ? startPoint : transform.position; startPos.z = 0; + _gizmoCenter = startPos; if (fishLayer.value == 0) fishLayer = LayerMask.GetMask("Fish"); @@ -100,32 +112,54 @@ public class ShanDian_DieEvent : DieEffect_TSY var fishPositions = new List(); var addedGuids = new HashSet(); - void Collect(float radius) + // nearestFirst=true:按离死亡位置的距离升序收集(身边的鱼优先遭殃);false:按物理检测原始顺序(近似随机,全屏补鱼分布更自然) + void Collect(float radius, bool nearestFirst, string tag) { var hits = Physics2D.OverlapCircleAll(startPos, radius, fishLayer); + var candidates = new List>(); + int noCtrl = 0, skipType = 0, skipDup = 0; foreach (var hit in hits) { - if (fishList.Count >= lightningCount) break; // 收集到目标数量即停 var control = hit.GetComponentInParent(); - if (control == null) continue; - if (control.fishenum != FishTypeEnum.普通鱼) continue; // ⭐ 只杀小鱼 - if (addedGuids.Contains(control.guid)) continue; + if (control == null) { noCtrl++; continue; } + if (control.fishenum != FishTypeEnum.普通鱼) { skipType++; continue; } // ⭐ 只杀小鱼 + if (addedGuids.Contains(control.guid)) { skipDup++; continue; } + candidates.Add(new KeyValuePair( + Vector2.Distance(startPos, control.transform.position), control)); + } + // 圈内模式:距离排序后从最近的开始取,保证"身边的鱼先被电" + if (nearestFirst) candidates.Sort((a, b) => a.Key.CompareTo(b.Key)); + + int before = fishList.Count; + var picked = new System.Text.StringBuilder(); + foreach (var kv in candidates) + { + if (fishList.Count >= lightningCount) break; // 收集到目标数量即停 + var control = kv.Value; addedGuids.Add(control.guid); fishList.Add(control); var pos = control.transform.position; pos.z = 0; fishPositions.Add(pos); + picked.Append($" {control.name}(d={kv.Key:F1})"); } + + Debug.Log($"[闪电][{tag}] 中心={startPos} 半径={radius:F1} 命中={hits.Length} " + + $"候选={candidates.Count}(无脚本={noCtrl} 非小鱼={skipType} 重复={skipDup}) " + + $"选中={fishList.Count - before}{(fishList.Count > before ? ":" + picked : "(无)")}"); } - // 第一段:圈内优先(身边的先遭殃) - if (circleRange > 0f) Collect(circleRange); - // 第二段:圈内不够 → 全屏补足 + Debug.Log($"[闪电] 选鱼开始 startPoint={startPoint} 本体={transform.position} 中心={startPos} " + + $"circleRange={circleRange} fillRange={fillRange} 目标={lightningCount} 层=[{LayerMaskNames(fishLayer)}]"); + + // 第一段:圈内按距离取最近的(身边的先遭殃) + if (circleRange > 0f) Collect(circleRange, true, "圈内"); + // 第二段:圈内不够 → 全屏补足(随机分布) if (fishList.Count < lightningCount) { float fill = fillRange > 0f ? fillRange : ScreenDiagonal(); - Collect(fill); + Collect(fill, false, "全屏补"); } int count = Mathf.Min(lightningCount, fishList.Count); @@ -137,41 +171,71 @@ public class ShanDian_DieEvent : DieEffect_TSY yield break; } - // ====== 2. 随机选 count 条鱼,画闪电 ====== - var lightningObjects = new List(); + // ====== 2. 按选鱼结果锁定(圈内最近优先 / 全屏补足,激光锁定 / 折线闪电两种表现)====== var explodePoints = new List(); - - var indices = new List(fishList.Count); - for (int i = 0; i < fishList.Count; i++) indices.Add(i); - for (int i = 0; i < count; i++) - { - int r = Random.Range(i, indices.Count); - (indices[i], indices[r]) = (indices[r], indices[i]); - } + var lockedFish = new List(); + var frozen = new List(); for (int i = 0; i < count; i++) { - Vector3 endPos = fishPositions[indices[i]]; - explodePoints.Add(endPos); + explodePoints.Add(fishPositions[i]); + lockedFish.Add(fishList[i]); + frozen.Add(false); - var lrObj = CreateLightningBolt(startPos, endPos); - if (lrObj != null) lightningObjects.Add(lrObj); - - // 雷电命中点特效:闪电打到鱼身上时在命中点播,强化打击感(没配跳过) + // 雷电命中点特效:命中鱼身时播,强化打击感(没配跳过) if (hitFxPrefab != null) { - var hit = Instantiate(hitFxPrefab, endPos, Quaternion.identity, CreateFishManager.Instance.bjEffectRoot); + var hit = Instantiate(hitFxPrefab, fishPositions[i], Quaternion.identity, CreateFishManager.Instance.bjEffectRoot); Destroy(hit, lightningWait); } } - // ====== 3. 等待 → 清除闪电 → 释放爆炸特效 ====== - yield return new WaitForSeconds(lightningWait); - - foreach (var obj in lightningObjects) + bool useLaser = laserPrefab != null; + if (useLaser) { - if (obj != null) Destroy(obj); + // 激光锁定形态:JiGuangControl 按目标数量创建激光实例,持续期间跟随游动的鱼 + var laserTargets = new List(); + for (int i = 0; i < lockedFish.Count; i++) laserTargets.Add(lockedFish[i].transform); + _laserFxBaseScale = laserPrefab.transform.localScale; + JiGuangControl.JiGuangCreate(laserPrefab, startPos, laserTargets, _laserFx, + CreateFishManager.Instance.bjEffectRoot); + + float elapsed = 0f; + while (elapsed < lightningWait) + { + elapsed += Time.deltaTime; + // 死鱼冻结爆炸点并隐藏激光;活鱼更新爆炸点 + 刷新激光朝向/长度(跟随移动) + for (int i = 0; i < lockedFish.Count; i++) + { + var ctrl = lockedFish[i]; + if (!frozen[i] && (ctrl == null || ctrl.isDead || !ctrl.gameObject.activeInHierarchy)) + { + frozen[i] = true; + if (i < _laserFx.Count && _laserFx[i] != null) _laserFx[i].gameObject.SetActive(false); + } + if (frozen[i]) continue; + Vector3 p = ctrl.transform.position; + p.z = 0; + explodePoints[i] = p; + } + JiGuangControl.JiGuangDirection(startPos, laserTargets, _laserFx); + JiGuangControl.JiGuangLength(startPos, laserTargets, _laserFx, _laserFxBaseScale, laserLengthCorrect); + yield return null; + } } + else + { + // LineRenderer 折线闪电兜底 + for (int i = 0; i < lockedFish.Count; i++) + { + var lrObj = CreateLightningBolt(startPos, explodePoints[i]); + if (lrObj != null) _lightningObjects.Add(lrObj); + } + yield return new WaitForSeconds(lightningWait); + } + + // ====== 3. 清除闪电/激光 → 释放爆炸特效 ====== + ClearLightningFx(); foreach (var ep in explodePoints) { @@ -210,6 +274,26 @@ public class ShanDian_DieEvent : DieEffect_TSY EffectPool.Instance.Return(gameObject); } + /// + /// 清理激光/闪电表现(两种形态都清),在爆炸杀鱼前调用。 + /// + private void ClearLightningFx() + { + // 激光实例:JiGuangCreate 克隆出来的销毁掉 + for (int i = 0; i < _laserFx.Count; i++) + { + if (_laserFx[i] != null) Destroy(_laserFx[i].gameObject); + } + _laserFx.Clear(); + + // 折线闪电对象 + for (int i = 0; i < _lightningObjects.Count; i++) + { + if (_lightningObjects[i] != null) Destroy(_lightningObjects[i]); + } + _lightningObjects.Clear(); + } + /// /// 创建一根闪电(主线折线 + 持续抖动闪烁)。 /// ⭐ 效果增强:Additive 发光材质、粗主线、细分段随机折线、持续期间高频闪烁。 @@ -276,21 +360,58 @@ public class ShanDian_DieEvent : DieEffect_TSY } } -/// 屏幕对角线长度:选鱼范围 0=全屏时用它,保证电鳗能劈到全场鱼 + /// LayerMask 转层名串:日志用,一眼看出 fishLayer 配没配对 + private static string LayerMaskNames(LayerMask mask) + { + var sb = new System.Text.StringBuilder(); + for (int i = 0; i < 32; i++) + { + if ((mask.value & (1 << i)) == 0) continue; + string n = LayerMask.LayerToName(i); + if (!string.IsNullOrEmpty(n)) sb.Append(n).Append(","); + } + return sb.ToString(); + } + +/// 屏幕(z=0 平面)对角线长度:选鱼范围 0=全屏时用它,保证电鳗能劈到全场鱼。兼容透视相机 private float ScreenDiagonal() { Camera cam = Camera.main; - if (cam == null || !cam.orthographic) return 50f; - float h = cam.orthographicSize * 2f; + if (cam == null) return 50f; + + float h; + if (cam.orthographic) + { + h = cam.orthographicSize * 2f; + } + else + { + // 透视相机:按相机到 z=0 平面的距离换算该平面的可视高度(鱼都在 z=0) + float dist = Mathf.Abs(cam.transform.position.z); + h = 2f * dist * Mathf.Tan(cam.fieldOfView * 0.5f * Mathf.Deg2Rad); + } float w = h * cam.aspect; return Mathf.Sqrt(h * h + w * w); } #if UNITY_EDITOR - private void OnDrawGizmosSelected() + // 改用 OnDrawGizmos:不用选中物体就常显(OnDrawGizmosSelected 只有选中才画,物体隐藏/回池时不调用) + // 黄圈=circleRange 圈内优先范围;青圈=fillRange 全屏补鱼范围;红圈=explosionRadius 爆炸伤害半径 + private void OnDrawGizmos() { - Gizmos.color = new Color(1, 1, 0, 0.3f); - Gizmos.DrawWireSphere(transform.position, circleRange > 0f ? circleRange : 5f); + Vector3 center = Application.isPlaying && _gizmoCenter != Vector3.zero ? _gizmoCenter : transform.position; + + Gizmos.color = new Color(1f, 1f, 0f, 0.35f); + Gizmos.DrawWireSphere(center, circleRange > 0f ? circleRange : 5f); + + if (fillRange > 0f) + { + Gizmos.color = new Color(0f, 1f, 1f, 0.35f); + Gizmos.DrawWireSphere(center, fillRange); + } + + Gizmos.color = new Color(1f, 0f, 0f, 0.35f); + Gizmos.DrawWireSphere(center, explosionRadius); } #endif } diff --git a/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/dianman.prefab b/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/dianman.prefab index e9319c54d..2a6a6fd5e 100644 --- a/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/dianman.prefab +++ b/Assets/MyGame/aa/Game/FLZY/TSY/ShanDian/dianman.prefab @@ -90,6 +90,9 @@ MonoBehaviour: dieEffectTesyNum: 0 minBJCount: 10 maxBJCount: 15 + laserPrefab: {fileID: 1709254077376921, guid: fdbfa7c8291331a4d838237be97893de, + type: 3} + laserLengthCorrect: 1 lightningMaterial: {fileID: 2100000, guid: a023113e8eb8bf245b8ba10c3e9788d1, type: 2} lightningColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1} lightningWidth: 10 @@ -101,9 +104,10 @@ MonoBehaviour: explosionRadius: 2 hitFxPrefab: {fileID: 0} lightningCount: 5 - detectRange: 0 - lightningWait: 0.5 + circleRange: 20 + fillRange: 10 + lightningWait: 5 killDelay: 0.3 fishLayer: serializedVersion: 2 - m_Bits: 0 + m_Bits: 64 diff --git a/Assets/MyGame/aa/Game/ZhanShen/DaQiao/display_wargod_in_14053_0_dev.prefab b/Assets/MyGame/aa/Game/ZhanShen/DaQiao/display_wargod_in_14053_0_dev.prefab index 8d3024fe6..df6c17c5f 100644 --- a/Assets/MyGame/aa/Game/ZhanShen/DaQiao/display_wargod_in_14053_0_dev.prefab +++ b/Assets/MyGame/aa/Game/ZhanShen/DaQiao/display_wargod_in_14053_0_dev.prefab @@ -2868,6 +2868,11 @@ MonoBehaviour: tra_RotationPivot: {fileID: 0} bulletPrefab_Two: {fileID: 0} bulletPrefab: {fileID: 0} + laserPrefab: {fileID: 5307749005136556621, guid: 631875e9f9d12204c92185be2ea23965, + type: 3} + laserWidth: 0.3 + laserColor: {r: 1, g: 0.3, b: 0.3, a: 0.9} + laserLengthCorrect: 1 turretNumber: 0 turretType: 1 displayName: @@ -2886,3 +2891,8 @@ MonoBehaviour: laserStartAnim: laserLoopAnim: laserEndAnim: + playFireParam: PlayFire + fireIndexParam: FireIndex + idleIndexParam: IdleIndex + fireIndex: 0 + idleIndex: 0 diff --git a/Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_in_14053_0_dev.prefab b/Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_in_14053_0_dev.prefab index e56816ba9..ddc645faa 100644 --- a/Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_in_14053_0_dev.prefab +++ b/Assets/MyGame/aa/Game/ZhanShen/Game/display_wargod_in_14053_0_dev.prefab @@ -937,6 +937,7 @@ MonoBehaviour: type: 3} laserWidth: 3 laserColor: {r: 1, g: 0.3, b: 0.3, a: 0.9} + laserLengthCorrect: 1 turretNumber: 0 turretType: 1 displayName: