using System.Collections; using UnityEngine; [RequireComponent(typeof(LineRenderer))] public class ChaseLaserBeam : MonoBehaviour { [Header("Laser Setup")] public Transform laserOrigin; public float maxDistance = 20f; public LayerMask collisionMask; public Color laserColor = Color.red; public float laserWidth = 0.05f; [Header("Visuals")] public float scrollSpeed = 1f; public float emissionStrength = 5f; [Header("Performance")] public float updateRate = 0.02f; [Header("Pulse (On/Off)")] public bool pulsing = true; public bool startOn = true; public float onDuration = 1.5f; public float offDuration = 1.5f; public float fadeIn = 0.15f; public float fadeOut = 0.15f; public float startDelay = 0f; public float randomStartOffset = 0f; [Header("Lethality")] [Range(0f, 1f)] public float lethalThreshold = 0.9f; private LineRenderer line; private Vector3 laserStart, laserEnd; private Material matInstance; private float currentIntensity = 0f; private bool isActiveVisual = false; private bool isEnabled = false; private Coroutine pulseCo; void Awake() { line = GetComponent(); SetupLaserRenderer(); } void OnEnable() { isEnabled = true; line.enabled = true; InvokeRepeating(nameof(UpdateLaser), 0f, updateRate); if (pulsing) pulseCo = StartCoroutine(PulseRoutine()); else { currentIntensity = 1f; isActiveVisual = true; ApplyIntensity(currentIntensity); } } void OnDisable() { isEnabled = false; CancelInvoke(nameof(UpdateLaser)); if (pulseCo != null) StopCoroutine(pulseCo); if (line != null) line.enabled = false; } void UpdateLaser() { if (!isActiveVisual || currentIntensity <= 0.001f) { if (line.enabled) line.enabled = false; return; } laserStart = laserOrigin ? laserOrigin.position : transform.position; Vector3 direction = transform.forward; if (Physics.Raycast(laserStart, direction, out RaycastHit hit, maxDistance, collisionMask)) { laserEnd = hit.point; if (/*currentIntensity >= lethalThreshold && */hit.collider.CompareTag("Player")) { // Use Shared_DieZibu for laser kill var die = hit.collider.GetComponent() ?? hit.collider.GetComponentInParent(); if (die != null) { // optional: stop the runner’s local movement flags if present var runner = die.GetComponent(); if (runner != null) { runner.StartCoroutine(runner.PlayStateAndGameOver(runner.fallingStateName, runner.fallingShortHash)); } die.KillByLaser(); } else { // Fallback: if no Shared_DieZibu found, keep old behavior (optional) var runner = hit.collider.GetComponent() ?? hit.collider.GetComponentInParent(); if (runner != null /*&& !runner.waitingForGameOver*/) { runner.moveSpeed = 0; runner.waitingForGameOver = true; runner.StartCoroutine(runner.PlayStateAndGameOver(runner.fallingStateName, runner.fallingShortHash)); } } } } else { laserEnd = laserStart + direction * maxDistance; } if (!line.enabled) line.enabled = true; line.SetPosition(0, laserStart); line.SetPosition(1, laserEnd); } void SetupLaserRenderer() { line.useWorldSpace = true; line.positionCount = 2; line.loop = false; line.widthMultiplier = laserWidth; line.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; line.receiveShadows = false; Shader laserShader = Shader.Find("Custom/EmissiveLaser"); if (laserShader != null) { matInstance = new Material(laserShader); matInstance.SetColor("_Color", laserColor); matInstance.SetFloat("_Emission", 0f); matInstance.SetFloat("_ScrollSpeed", scrollSpeed); line.material = matInstance; } else { matInstance = new Material(Shader.Find("Sprites/Default")); matInstance.color = laserColor * 0f; line.material = matInstance; } line.startColor = laserColor; line.endColor = laserColor; } IEnumerator PulseRoutine() { float jitter = (randomStartOffset > 0f) ? Random.Range(-randomStartOffset, randomStartOffset) : 0f; if (startDelay + jitter > 0f) yield return new WaitForSeconds(startDelay + jitter); bool stateOn = startOn; while (isEnabled) { if (stateOn) { if (fadeIn > 0f) yield return FadeIntensity(1f, fadeIn); else { currentIntensity = 1f; ApplyIntensity(currentIntensity); } isActiveVisual = true; if (onDuration > 0f) yield return new WaitForSeconds(onDuration); } else { if (fadeOut > 0f) yield return FadeIntensity(0f, fadeOut); else { currentIntensity = 0f; ApplyIntensity(currentIntensity); } isActiveVisual = false; if (offDuration > 0f) yield return new WaitForSeconds(offDuration); } stateOn = !stateOn; } } IEnumerator FadeIntensity(float target, float time) { float start = currentIntensity; float t = 0f; isActiveVisual = (target > 0f) || (start > 0f); while (t < 1f) { t += Time.deltaTime / Mathf.Max(0.0001f, time); currentIntensity = Mathf.Lerp(start, target, t); ApplyIntensity(currentIntensity); yield return null; } currentIntensity = target; ApplyIntensity(currentIntensity); if (currentIntensity <= 0.001f && line.enabled) line.enabled = false; } void ApplyIntensity(float normalized) { line.widthMultiplier = Mathf.Max(0.0001f, laserWidth * Mathf.Lerp(0.35f, 1f, normalized)); if (matInstance != null) { if (matInstance.HasProperty("_Emission")) { matInstance.SetFloat("_Emission", emissionStrength * normalized); } else { Color c = laserColor; c.a = Mathf.Clamp01(normalized); matInstance.color = c; } } } public void SetPulsing(bool enabled) { pulsing = enabled; if (!isEnabled) return; if (pulseCo != null) { StopCoroutine(pulseCo); pulseCo = null; } if (pulsing) pulseCo = StartCoroutine(PulseRoutine()); else { currentIntensity = 1f; ApplyIntensity(currentIntensity); isActiveVisual = true; } } public void ForceOff() { if (pulseCo != null) { StopCoroutine(pulseCo); pulseCo = null; } pulsing = false; currentIntensity = 0f; ApplyIntensity(0f); isActiveVisual = false; if (line) line.enabled = false; } public void ForceOn() { if (pulseCo != null) { StopCoroutine(pulseCo); pulseCo = null; } pulsing = false; currentIntensity = 1f; ApplyIntensity(1f); isActiveVisual = true; if (line && !line.enabled) line.enabled = true; } }