Files
bansonic_beta_main/Assets/scripts/gamePlay_gameplay/effectEventController.cs
T
2026-07-22 05:35:53 +08:00

404 lines
13 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using SmoothShakeFree;
using UnityEngine;
public class effectEventController : MonoBehaviour
{
public enum CameraOffsetAxis
{
X,
Y,
Z
}
public static effectEventController Instance { get; private set; }
[Header("Multi Note Screen Shake")]
public bool enableMultiNoteScreenShake = true;
public float multiNoteScreenShakeAmplitude = 0.08f;
[Header("Camera Drift")]
public bool enableCameraDrift = false;
public Vector2 cameraDriftRange = new Vector2(0.15f, 0.08f);
public float cameraDriftSpeed = 0.3f;
[Header("Player Skill 10 Camera Offset")]
public float playerSkillCameraOffsetAmount = 0.6f;
public CameraOffsetAxis playerSkillCameraOffsetAxis = CameraOffsetAxis.Y;
[Header("Track Crash")]
[SerializeField] private TrackCrashController trackCrashController;
private Transform cachedShakeTarget;
private Transform cachedCameraDriftPivot;
private Transform cachedCameraSkillPivot;
private Vector3 cameraDriftBaseLocalPosition;
private Vector3 cameraSkillBaseLocalPosition;
private float cameraDriftSeedX;
private float cameraDriftSeedY;
// 分辨率相机微调:改的是"漂移 pivot"(顶层节点,承载 -40°/-8 基准),不碰相机自身,
// 因此不与 SmoothShake(写相机 local)、技能偏移(写技能 pivot)、冲刺(写相机 z)冲突。
// 改 pivot 的 X 旋转无任何系统覆盖;改 pivot 的 Z 必须同步 cameraDriftBaseLocalPosition.z
// 否则 UpdateCameraDrift/ResetCameraDriftIfNeeded 会用旧基准把 z 拉回。
private bool _cameraBaseCaptured;
private float _designPivotRotX; // pivot 初始 X 旋转(如 -40)
private float _designPivotZ; // pivot 初始 Z(如 -8)
private void Awake()
{
if (Instance == null)
{
Instance = this;
cameraDriftSeedX = Random.Range(0f, 1000f);
cameraDriftSeedY = Random.Range(1000f, 2000f);
CacheShakeTarget();
return;
}
if (Instance != this)
{
Destroy(gameObject);
}
}
private void OnEnable()
{
CacheShakeTarget();
ResetCameraDriftIfNeeded();
CacheTrackCrashController();
}
// 门控:漂移关闭且已归位后,跳过每帧的 Perlin/缓存查找;开关切换时才归位一次。
private bool _driftResetDone;
private void Update()
{
if (enableCameraDrift)
{
_driftResetDone = false;
UpdateCameraDrift();
}
else if (!_driftResetDone)
{
// 关闭瞬间归位一次,之后不再每帧空跑。
UpdateCameraDrift();
_driftResetDone = true;
}
UpdatePlayerSkillCameraOffset();
}
private void CacheShakeTarget()
{
if (Camera.main != null)
{
cachedShakeTarget = Camera.main.transform;
PrewarmShakeComponent();
EnsureCameraDriftPivot();
EnsureCameraSkillPivot();
}
}
private void CacheTrackCrashController()
{
if (trackCrashController == null)
{
trackCrashController = SceneObjectLookupCache.FindAny<TrackCrashController>();
}
}
// 捕获漂移 pivot 的设计基准(X旋转/Z)一次。pivot 建好后调用即安全。
private void CaptureCameraBaseIfNeeded()
{
if (_cameraBaseCaptured) return;
if (cachedCameraDriftPivot == null)
{
CacheShakeTarget(); // 确保 pivot 已建立
if (cachedCameraDriftPivot == null) return;
}
_designPivotRotX = cachedCameraDriftPivot.localEulerAngles.x;
_designPivotZ = cachedCameraDriftPivot.localPosition.z;
_cameraBaseCaptured = true;
}
/// <summary>
/// 按分辨率微调相机:在设计基准上叠加 X 旋转增量与 Z 位置增量。
/// 改的是漂移 pivot(不动相机本身),且同步 cameraDriftBaseLocalPosition.z
/// 与漂移/抖动/技能偏移/冲刺全部不冲突。可重复调用(总是相对设计基准,不累积)。
/// </summary>
public void SetResolutionCameraAdjust(float extraRotX, float extraZ)
{
CaptureCameraBaseIfNeeded();
if (cachedCameraDriftPivot == null) return;
// X 旋转:设计值 + 增量。pivot 的 rotation 无系统写入,直接设安全。
Vector3 euler = cachedCameraDriftPivot.localEulerAngles;
euler.x = _designPivotRotX + extraRotX;
cachedCameraDriftPivot.localEulerAngles = euler;
// Z:设计值 + 增量,并同步漂移基准 z(漂移只改 x,y,z 恒取基准)。
float targetZ = _designPivotZ + extraZ;
Vector3 lp = cachedCameraDriftPivot.localPosition;
lp.z = targetZ;
cachedCameraDriftPivot.localPosition = lp;
cameraDriftBaseLocalPosition.z = targetZ; // 关键:否则会被 UpdateCameraDrift/Reset 拉回
}
private void PrewarmShakeComponent()
{
if (cachedShakeTarget == null)
return;
SmoothShake shake = cachedShakeTarget.GetComponent<SmoothShake>();
if (shake == null)
{
shake = cachedShakeTarget.gameObject.AddComponent<SmoothShake>();
}
if (shake.positionShake == null) shake.positionShake = new Shaker();
if (shake.rotationShake == null) shake.rotationShake = new Shaker();
if (shake.shakers == null || shake.shakers.Length != 2)
shake.shakers = new[] { shake.positionShake, shake.rotationShake };
if (shake.sum == null || shake.sum.Length != 2)
shake.sum = new Vector3[2];
}
private void EnsureCameraDriftPivot()
{
if (cachedShakeTarget == null)
return;
if (cachedCameraDriftPivot != null)
return;
Transform currentParent = cachedShakeTarget.parent;
if (currentParent != null && currentParent.name == "__effect_event_camera_drift_pivot")
{
cachedCameraDriftPivot = currentParent;
cameraDriftBaseLocalPosition = cachedCameraDriftPivot.localPosition;
return;
}
GameObject pivotObject = new GameObject("__effect_event_camera_drift_pivot");
Transform pivot = pivotObject.transform;
if (currentParent != null)
{
pivot.SetParent(currentParent, false);
pivot.localPosition = cachedShakeTarget.localPosition;
pivot.localRotation = cachedShakeTarget.localRotation;
pivot.localScale = Vector3.one;
}
else
{
pivot.position = cachedShakeTarget.position;
pivot.rotation = cachedShakeTarget.rotation;
}
cachedShakeTarget.SetParent(pivot, true);
cachedShakeTarget.localRotation = Quaternion.identity;
cachedCameraDriftPivot = pivot;
cameraDriftBaseLocalPosition = cachedCameraDriftPivot.localPosition;
}
private void EnsureCameraSkillPivot()
{
if (cachedShakeTarget == null)
return;
if (cachedCameraSkillPivot != null)
return;
Transform currentParent = cachedShakeTarget.parent;
if (currentParent != null && currentParent.name == "__effect_event_camera_skill_pivot")
{
cachedCameraSkillPivot = currentParent;
cameraSkillBaseLocalPosition = cachedCameraSkillPivot.localPosition;
return;
}
GameObject pivotObject = new GameObject("__effect_event_camera_skill_pivot");
Transform pivot = pivotObject.transform;
if (currentParent != null)
{
pivot.SetParent(currentParent, false);
pivot.localPosition = cachedShakeTarget.localPosition;
pivot.localRotation = cachedShakeTarget.localRotation;
pivot.localScale = Vector3.one;
}
else
{
pivot.position = cachedShakeTarget.position;
pivot.rotation = cachedShakeTarget.rotation;
pivot.localScale = Vector3.one;
}
cachedShakeTarget.SetParent(pivot, true);
cachedShakeTarget.localPosition = Vector3.zero;
cachedShakeTarget.localRotation = Quaternion.identity;
cachedCameraSkillPivot = pivot;
cameraSkillBaseLocalPosition = cachedCameraSkillPivot.localPosition;
}
private void UpdateCameraDrift()
{
if (cachedShakeTarget == null)
{
CacheShakeTarget();
}
if (cachedShakeTarget == null)
return;
EnsureCameraDriftPivot();
if (cachedCameraDriftPivot == null)
return;
if (!enableCameraDrift)
{
ResetCameraDriftIfNeeded();
return;
}
float speed = Mathf.Max(0f, cameraDriftSpeed);
float time = GameplayClock.NowSongTime * speed;
float offsetX = (Mathf.PerlinNoise(cameraDriftSeedX, time) - 0.5f) * 2f * Mathf.Abs(cameraDriftRange.x);
float offsetY = (Mathf.PerlinNoise(cameraDriftSeedY, time) - 0.5f) * 2f * Mathf.Abs(cameraDriftRange.y);
cachedCameraDriftPivot.localPosition = cameraDriftBaseLocalPosition + new Vector3(offsetX, offsetY, 0f);
}
private void ResetCameraDriftIfNeeded()
{
if (cachedCameraDriftPivot == null)
return;
cachedCameraDriftPivot.localPosition = cameraDriftBaseLocalPosition;
}
private void UpdatePlayerSkillCameraOffset()
{
if (cachedShakeTarget == null)
{
CacheShakeTarget();
}
if (cachedShakeTarget == null)
return;
EnsureCameraSkillPivot();
if (cachedCameraSkillPivot == null)
return;
Vector3 targetLocalPosition = cameraSkillBaseLocalPosition;
if (PlayerSkillService.IsSkillEnabled(10))
{
float offset = playerSkillCameraOffsetAmount;
switch (playerSkillCameraOffsetAxis)
{
case CameraOffsetAxis.X:
targetLocalPosition.x += offset;
break;
case CameraOffsetAxis.Y:
targetLocalPosition.y += offset;
break;
case CameraOffsetAxis.Z:
targetLocalPosition.z += offset;
break;
}
}
if (cachedCameraSkillPivot.localPosition != targetLocalPosition)
{
cachedCameraSkillPivot.localPosition = targetLocalPosition;
}
}
public static void RefreshPlayerSkillCameraOffset()
{
effectEventController controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<effectEventController>();
if (controller == null)
return;
controller.CacheShakeTarget();
controller.UpdatePlayerSkillCameraOffset();
}
public static void TryTriggerMultiNoteShake()
{
effectEventController controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<effectEventController>();
if (controller == null)
return;
controller.TriggerMultiNoteShakeInternal();
}
public static void TryTriggerTrackCrash(int trackIndex)
{
effectEventController controller = Instance != null ? Instance : SceneObjectLookupCache.FindAny<effectEventController>();
if (controller == null)
return;
controller.TriggerTrackCrashInternal(trackIndex);
}
private void TriggerMultiNoteShakeInternal()
{
if (!enableMultiNoteScreenShake)
return;
if (cachedShakeTarget == null)
{
CacheShakeTarget();
}
if (cachedShakeTarget == null)
return;
float amplitudeValue = Mathf.Max(0f, multiNoteScreenShakeAmplitude);
if (amplitudeValue <= 0f)
return;
SmoothShake shake = cachedShakeTarget.GetComponent<SmoothShake>();
if (shake == null)
{
shake = cachedShakeTarget.gameObject.AddComponent<SmoothShake>();
}
if (shake.positionShake == null) shake.positionShake = new Shaker();
if (shake.rotationShake == null) shake.rotationShake = new Shaker();
shake.positionShake.noiseType = Shaker.NoiseType.SineWave;
shake.positionShake.amplitude = new Vector3(amplitudeValue, amplitudeValue, 0f);
shake.positionShake.frequency = new Vector3(20f, 20f, 0f);
shake.rotationShake.amplitude = Vector3.zero;
shake.rotationShake.frequency = Vector3.zero;
shake.timeSettings.constantShake = false;
shake.timeSettings.fadeInDuration = 0.03f;
shake.timeSettings.holdDuration = 0.04f;
shake.timeSettings.fadeOutDuration = 0.05f;
if (shake.timeSettings.fadeInCurve == null || shake.timeSettings.fadeInCurve.length == 0)
shake.timeSettings.fadeInCurve = AnimationCurve.Linear(0f, 0f, 1f, 1f);
if (shake.timeSettings.fadeOutCurve == null || shake.timeSettings.fadeOutCurve.length == 0)
shake.timeSettings.fadeOutCurve = AnimationCurve.Linear(0f, 1f, 1f, 0f);
shake.shakers = new[] { shake.positionShake, shake.rotationShake };
shake.sum = new Vector3[2];
shake.StartShake();
}
private void TriggerTrackCrashInternal(int trackIndex)
{
CacheTrackCrashController();
if (trackCrashController == null)
return;
trackCrashController.TriggerTrackCrash(trackIndex);
}
}