194 lines
6.6 KiB
C#
194 lines
6.6 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.Rendering;
|
||
using UnityEngine.Rendering.Universal;
|
||
|
||
[RequireComponent(typeof(Collider2D))]
|
||
public class LightDimmer : MonoBehaviour
|
||
{
|
||
[Tooltip("Если не указан, будет использован Collider2D на этом же объекте.")]
|
||
[SerializeField] private Collider2D targetCollider;
|
||
|
||
[Tooltip("Тег игрока (используется для поиска Transform игрока)")]
|
||
[SerializeField] private string playerTag;
|
||
|
||
[Tooltip("Массив со всеми источниками света, которые нужно изменять")]
|
||
[SerializeField] private Light2D[] lights;
|
||
|
||
[Tooltip("Использовать прогресс по оси Y вместо оси X")]
|
||
[SerializeField] private bool useYProgress = false;
|
||
|
||
[Header("Настройки света")]
|
||
[SerializeField] [Min(0)] private float startIntensity;
|
||
[SerializeField] [Min(0)] private float endIntensity;
|
||
[SerializeField] [Min(0)] private float smoothing;
|
||
|
||
[Header("Настройки свечения")]
|
||
[SerializeField] private bool applyBloom = true;
|
||
[SerializeField] [Min(0)] private float startBloomIntensity = 0f;
|
||
[SerializeField] [Min(0)] private float targetBloomIntensity = 1f;
|
||
|
||
private Transform playerTransform;
|
||
|
||
// Храним все коллайдеры игрока, которые в данный момент пересекают триггер
|
||
private readonly HashSet<Collider2D> playerColliders = new HashSet<Collider2D>();
|
||
|
||
private float boundsMin;
|
||
private float boundsMax;
|
||
private Volume[] _volumes;
|
||
|
||
private void OnValidate()
|
||
{
|
||
if (targetCollider != null)
|
||
{
|
||
UpdateBounds();
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(playerTag)) playerTag = "Player";
|
||
}
|
||
|
||
private void OnEnable()
|
||
{
|
||
if (targetCollider == null)
|
||
targetCollider = GetComponent<Collider2D>();
|
||
|
||
if (targetCollider != null)
|
||
{
|
||
UpdateBounds();
|
||
}
|
||
|
||
_volumes = new Volume[lights.Length];
|
||
|
||
if (string.IsNullOrEmpty(playerTag)) playerTag = "Player";
|
||
|
||
var playerObj = GameObject.FindGameObjectWithTag(playerTag);
|
||
if (playerObj != null)
|
||
playerTransform = playerObj.transform;
|
||
|
||
for (int i = 0; i < lights.Length; i++)
|
||
{
|
||
Light2D light2D = lights[i];
|
||
Volume volume = light2D.GetComponentInParent<Volume>();
|
||
if (volume != null)
|
||
{
|
||
volume.enabled = true;
|
||
}
|
||
|
||
_volumes[i] = volume;
|
||
}
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
if (targetCollider == null)
|
||
{
|
||
targetCollider = GetComponent<Collider2D>();
|
||
}
|
||
|
||
UpdateBounds();
|
||
|
||
if (string.IsNullOrEmpty(playerTag)) playerTag = "Player";
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// Нужна ссылка на игрока и хотя бы один свет и collider
|
||
if (!playerTransform || lights == null || lights.Length == 0 || targetCollider == null)
|
||
return;
|
||
|
||
// Проверяем, что игрок действительно внутри триггера (с учётом возможной множественной геометрии)
|
||
bool playerInside = playerColliders.Count > 0;
|
||
if (!playerInside)
|
||
return;
|
||
|
||
Vector3 playerPos = playerTransform.position;
|
||
|
||
// Дополнительная страховка: проверяем AABB bounds
|
||
if (!targetCollider.bounds.Contains(playerPos))
|
||
return;
|
||
|
||
float playerCurrent = useYProgress ? playerPos.y : playerPos.x;
|
||
float t = 0f;
|
||
if (!Mathf.Approximately(boundsMax, boundsMin))
|
||
{
|
||
t = Mathf.InverseLerp(boundsMin, boundsMax, playerCurrent);
|
||
t = Mathf.Clamp01(t);
|
||
}
|
||
|
||
// Интерполируем интенсивность между startXIntensity и endXIntensity
|
||
float desiredForPosition = Mathf.Lerp(startIntensity, endIntensity, t);
|
||
float desiredForBloom = Mathf.Lerp(startBloomIntensity, targetBloomIntensity, t);
|
||
|
||
for (int i = 0; i < lights.Length; i++)
|
||
{
|
||
var light2D = lights[i];
|
||
_volumes[i].profile.TryGet(out Bloom bloom);
|
||
if (!light2D) continue;
|
||
|
||
if (smoothing <= 0f)
|
||
{
|
||
light2D.intensity = desiredForPosition;
|
||
if (applyBloom && bloom != null) bloom.intensity.value = desiredForBloom;
|
||
}
|
||
else
|
||
{
|
||
light2D.intensity = Mathf.MoveTowards(light2D.intensity, desiredForPosition, smoothing * Time.deltaTime);
|
||
if (applyBloom && bloom != null)
|
||
bloom.intensity.value = Mathf.MoveTowards(bloom.intensity.value, desiredForBloom, smoothing * Time.deltaTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void OnTriggerEnter2D(Collider2D other)
|
||
{
|
||
if (!other.CompareTag(playerTag)) return;
|
||
|
||
// Запоминаем коллайдер игрока
|
||
playerColliders.Add(other);
|
||
|
||
// Сохраняем Transform игрока (на случай, если раньше не найден)
|
||
if (playerTransform == null)
|
||
playerTransform = other.transform;
|
||
}
|
||
|
||
private void OnTriggerExit2D(Collider2D other)
|
||
{
|
||
if (!other.CompareTag(playerTag)) return;
|
||
|
||
// Убираем коллайдер из множества; если множество пустое — игрок полностью вышел
|
||
playerColliders.Remove(other);
|
||
}
|
||
|
||
private void UpdateBounds()
|
||
{
|
||
var b = targetCollider.bounds;
|
||
boundsMin = b.min.x;
|
||
boundsMax = b.max.x;
|
||
|
||
if (useYProgress)
|
||
{
|
||
boundsMin = b.min.y;
|
||
boundsMax = b.max.y;
|
||
}
|
||
}
|
||
|
||
void OnDrawGizmosSelected()
|
||
{
|
||
if (targetCollider == null) return;
|
||
|
||
var b = targetCollider.bounds;
|
||
Gizmos.color = Color.yellow;
|
||
Gizmos.DrawLine(new Vector3(b.min.x, b.min.y, 0f), new Vector3(b.max.x, b.min.y, 0f));
|
||
Gizmos.DrawLine(new Vector3(b.max.x, b.min.y, 0f), new Vector3(b.max.x, b.max.y, 0f));
|
||
Gizmos.DrawLine(new Vector3(b.max.x, b.max.y, 0f), new Vector3(b.min.x, b.max.y, 0f));
|
||
Gizmos.DrawLine(new Vector3(b.min.x, b.max.y, 0f), new Vector3(b.min.x, b.min.y, 0f));
|
||
|
||
if (playerTransform != null)
|
||
{
|
||
Gizmos.color = Color.cyan;
|
||
Vector3 p = playerTransform.position;
|
||
Gizmos.DrawSphere(new Vector3(p.x, p.y, 0f), 0.05f);
|
||
}
|
||
}
|
||
}
|