266 lines
8.1 KiB
C#
266 lines
8.1 KiB
C#
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; // fully-on time (excludes fade)
|
|
public float offDuration = 1.5f; // fully-off time (excludes fade)
|
|
public float fadeIn = 0.15f; // 0 = snap on
|
|
public float fadeOut = 0.15f; // 0 = snap off
|
|
public float startDelay = 0f;
|
|
public float randomStartOffset = 0f; // +/- random seconds added to startDelay
|
|
|
|
[Header("Lethality")]
|
|
[Tooltip("Beam only damages when intensity >= this (lets you show a non-lethal warmup).")]
|
|
[Range(0f, 1f)] public float lethalThreshold = 0.9f;
|
|
|
|
private LineRenderer line;
|
|
private Vector3 laserStart, laserEnd;
|
|
private Material matInstance;
|
|
|
|
private float currentIntensity = 0f; // 0..1 (drives emission/visibility)
|
|
private bool isActiveVisual = false; // visible & updating
|
|
private bool isEnabled = false; // component enabled
|
|
private Coroutine pulseCo;
|
|
|
|
void Awake()
|
|
{
|
|
line = GetComponent<LineRenderer>();
|
|
SetupLaserRenderer();
|
|
}
|
|
|
|
void OnEnable()
|
|
{
|
|
isEnabled = true;
|
|
line.enabled = true;
|
|
InvokeRepeating(nameof(UpdateLaser), 0f, updateRate);
|
|
|
|
if (pulsing)
|
|
{
|
|
pulseCo = StartCoroutine(PulseRoutine());
|
|
}
|
|
else
|
|
{
|
|
// Non-pulsing: just force ON
|
|
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)
|
|
{
|
|
// Off: keep line hidden, skip raycast
|
|
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;
|
|
|
|
// Damage only when sufficiently "on"
|
|
if (currentIntensity >= lethalThreshold && hit.collider.CompareTag("Player"))
|
|
{
|
|
var player = hit.collider.GetComponent<ChasePlayerController>();
|
|
if (player != null && !player.waitingForGameOver)
|
|
{
|
|
Debug.Log("⚠️ Player hit by chase laser!");
|
|
player.moveSpeed = 0;
|
|
player.waitingForGameOver = true;
|
|
player.StartCoroutine(player.PlayStateAndGameOver(player.fallingStateName, player.fallingShortHash));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
laserEnd = laserStart + direction * maxDistance;
|
|
}
|
|
|
|
// Draw
|
|
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;
|
|
|
|
// Unique material instance
|
|
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; // start off
|
|
line.material = matInstance;
|
|
}
|
|
|
|
line.startColor = laserColor;
|
|
line.endColor = laserColor;
|
|
}
|
|
|
|
// ===== Pulse logic =====
|
|
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)
|
|
{
|
|
// Fade in
|
|
if (fadeIn > 0f) yield return FadeIntensity(1f, fadeIn);
|
|
else { currentIntensity = 1f; ApplyIntensity(currentIntensity); }
|
|
|
|
isActiveVisual = true; // visible & updating
|
|
if (onDuration > 0f) yield return new WaitForSeconds(onDuration);
|
|
}
|
|
else
|
|
{
|
|
// Fade out
|
|
if (fadeOut > 0f) yield return FadeIntensity(0f, fadeOut);
|
|
else { currentIntensity = 0f; ApplyIntensity(currentIntensity); }
|
|
|
|
isActiveVisual = false; // fully off
|
|
if (offDuration > 0f) yield return new WaitForSeconds(offDuration);
|
|
}
|
|
|
|
stateOn = !stateOn;
|
|
}
|
|
}
|
|
|
|
IEnumerator FadeIntensity(float target, float time)
|
|
{
|
|
float start = currentIntensity;
|
|
float t = 0f;
|
|
|
|
// Ensure visuals are considered active during fade (so you can show warning beam)
|
|
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 ended at zero, hide line
|
|
if (currentIntensity <= 0.001f && line.enabled) line.enabled = false;
|
|
}
|
|
|
|
void ApplyIntensity(float normalized)
|
|
{
|
|
// Width & color scaling (optional: keep width fixed)
|
|
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);
|
|
// If your shader uses HDR color for emission, you can also set _Color here if needed.
|
|
}
|
|
else
|
|
{
|
|
// Fallback: scale color alpha
|
|
Color c = laserColor;
|
|
c.a = Mathf.Clamp01(normalized);
|
|
matInstance.color = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ===== Public controls =====
|
|
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;
|
|
}
|
|
}
|