using System.Collections.Generic; using UnityEngine; namespace IndianOceanAssets.Engine2_5D { /// /// 黑暗遮罩管理器 v2 —— 乘法混合 + 编辑器开关(不含回声)。 /// 回声描边已独立到 EchoSystem + SpriteEchoOutline shader。 /// /// 单例模式:收集场景中所有 LightSource,每帧把位置/半径/强度 /// 写入 DarknessOverlay 材质的 _LightData 数组。 /// /// 使用方式: /// 1. 在场景中创建一个空物体,挂上此脚本 /// 2. 创建一个大平面(覆盖可视区域),Y 稍高于精灵 /// 3. 给平面分配使用 DarknessOverlay shader 的材质 /// 4. 把该材质拖到此脚本的 darknessMaterial 字段 /// public class LightMaskSystem : MonoBehaviour { public static LightMaskSystem Instance { get; private set; } [Header("遮罩材质(使用 DarknessOverlay shader)")] [SerializeField] private Material darknessMaterial; [Header("编辑器开关")] [Tooltip("勾选后关闭黑暗,所有物件完全可见,方便摆放场景。\n取消勾选恢复黑暗效果。")] [SerializeField] private bool debugShowAll = false; [Header("黑暗设置")] [Tooltip("最小亮度:0=纯黑(精灵完全看不见),0.05=微微看到轮廓")] [SerializeField, Range(0f, 0.3f)] private float minBrightness = 0f; [Tooltip("黑暗底色:夜间微光色调,接近纯黑")] [SerializeField] private Color darknessColor = new Color(0.01f, 0.01f, 0.02f, 1f); [Tooltip("光线边缘柔和度,0 = 硬边,1 = 最柔")] [SerializeField, Range(0.01f, 1f)] private float lightSoftness = 0.35f; [Header("遮罩平面跟随设置")] [Tooltip("如果勾选,遮罩平面会跟随摄像机移动(适合大地图)")] [SerializeField] private bool followCamera = true; [SerializeField] private Transform maskPlane; [SerializeField] private Vector3 maskOffset = new Vector3(0f, 5f, 0f); [SerializeField] private float planeSize = 50f; // Shader Property IDs private static readonly int LightDataID = Shader.PropertyToID("_LightData"); private static readonly int LightCountID = Shader.PropertyToID("_LightCount"); private static readonly int DarknessColorID = Shader.PropertyToID("_DarknessColor"); private static readonly int MinBrightnessID = Shader.PropertyToID("_MinBrightness"); private static readonly int LightSoftnessID = Shader.PropertyToID("_LightSoftness"); private readonly List _lights = new List(); private Vector4[] _lightData = new Vector4[64]; private Camera _mainCamera; private void Awake() { if (Instance != null && Instance != this) { Destroy(this); return; } Instance = this; } private void Start() { _mainCamera = Camera.main; UpdateMaterialProperties(); } private void Update() { UpdateLightData(); UpdateMaskPosition(); } private void UpdateMaterialProperties() { if (darknessMaterial == null) return; darknessMaterial.SetColor(DarknessColorID, darknessColor); darknessMaterial.SetFloat(LightSoftnessID, lightSoftness); if (debugShowAll) darknessMaterial.SetFloat(MinBrightnessID, 1.0f); else darknessMaterial.SetFloat(MinBrightnessID, minBrightness); } private void UpdateLightData() { if (darknessMaterial == null) return; int writeIndex = 0; for (int i = 0; i < _lights.Count && writeIndex < 64; i++) { var src = _lights[i]; if (src == null || !src.gameObject.activeInHierarchy) continue; Vector2 pos = src.GetWorldPositionXZ(); _lightData[writeIndex] = new Vector4(pos.x, pos.y, src.Radius, src.Intensity); writeIndex++; } darknessMaterial.SetVectorArray(LightDataID, _lightData); darknessMaterial.SetInt(LightCountID, writeIndex); } private void UpdateMaskPosition() { if (!followCamera || maskPlane == null || _mainCamera == null) return; Vector3 camPos = _mainCamera.transform.position; maskPlane.position = new Vector3(camPos.x + maskOffset.x, maskOffset.y, camPos.z + maskOffset.z); } // ===== 光源注册/注销 ===== public void RegisterLight(LightSource source) { if (!_lights.Contains(source)) _lights.Add(source); } public void UnregisterLight(LightSource source) { _lights.Remove(source); } // ===== Inspector 同步 ===== private void OnValidate() { if (darknessMaterial == null) return; darknessMaterial.SetColor(DarknessColorID, darknessColor); darknessMaterial.SetFloat(LightSoftnessID, lightSoftness); if (debugShowAll) darknessMaterial.SetFloat(MinBrightnessID, 1.0f); else darknessMaterial.SetFloat(MinBrightnessID, minBrightness); } /// /// 快捷切换黑暗开关(右键组件 > Toggle Darkness) /// [ContextMenu("Toggle Darkness (Debug)")] private void ToggleDarknessDebug() { debugShowAll = !debugShowAll; OnValidate(); Debug.Log($"[LightMaskSystem] Darkness {(debugShowAll ? "OFF (debug)" : "ON")}"); } // ===== 运行时动态调整 ===== /// /// 动态调整最小亮度(白天/黑夜切换、Q键过场点亮等) /// 0 = 纯黑(完全看不见),0.3 = 能模糊看到东西 /// public void SetMinBrightness(float value) { minBrightness = Mathf.Clamp01(value); if (darknessMaterial != null && !debugShowAll) darknessMaterial.SetFloat(MinBrightnessID, minBrightness); } /// /// 动态调整黑暗底色 /// public void SetDarknessColor(Color color) { darknessColor = color; if (darknessMaterial != null) darknessMaterial.SetColor(DarknessColorID, darknessColor); } } }