This commit is contained in:
2026-08-30 15:29:02 +08:00
parent ad43a5d40c
commit a1a776d830
9 changed files with 395 additions and 125 deletions
@@ -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<Transform> _laserFx = new List<Transform>();
private readonly List<GameObject> _lightningObjects = new List<GameObject>();
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<Vector3>();
var addedGuids = new HashSet<string>();
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<KeyValuePair<float, MonsterContral>>();
int noCtrl = 0, skipType = 0, skipDup = 0;
foreach (var hit in hits)
{
if (fishList.Count >= lightningCount) break; // 收集到目标数量即停
var control = hit.GetComponentInParent<MonsterContral>();
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<float, MonsterContral>(
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<GameObject>();
// ====== 2. 按选鱼结果锁定(圈内最近优先 / 全屏补足,激光锁定 / 折线闪电两种表现)======
var explodePoints = new List<Vector3>();
var indices = new List<int>(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<MonsterContral>();
var frozen = new List<bool>();
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<Transform>();
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);
}
/// <summary>
/// 清理激光/闪电表现(两种形态都清),在爆炸杀鱼前调用。
/// </summary>
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();
}
/// <summary>
/// 创建一根闪电(主线折线 + 持续抖动闪烁)。
/// ⭐ 效果增强:Additive 发光材质、粗主线、细分段随机折线、持续期间高频闪烁。
@@ -276,21 +360,58 @@ public class ShanDian_DieEvent : DieEffect_TSY
}
}
/// <summary>屏幕对角线长度:选鱼范围 0=全屏时用它,保证电鳗能劈到全场鱼</summary>
/// <summary>LayerMask 转层名串:日志用,一眼看出 fishLayer 配没配对</summary>
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();
}
/// <summary>屏幕(z=0 平面)对角线长度:选鱼范围 0=全屏时用它,保证电鳗能劈到全场鱼。兼容透视相机</summary>
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
}
@@ -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
@@ -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
@@ -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: