using UnityEngine;
using UnityEngine.UI;
namespace Heroes.Battle
{
///
/// Isolates a camera's post-processing to just what that camera renders
/// (Built-in Render Pipeline).
///
/// In the Built-in pipeline, stacked cameras that render to the screen share one
/// framebuffer, so a second camera's OnRenderImage receives the whole
/// accumulated frame (background included) as its source — the effect ends up
/// applied to everything. The fix is to render this camera into its own
/// RenderTexture instead of the screen, run the effect there, and composite the
/// result back over the screen with a full-screen on an
/// overlay canvas.
///
/// Attach this to the units camera. It sizes the RenderTexture to the screen,
/// forces a transparent clear so the background shows through, and keeps
/// everything in sync when the resolution changes. Put
/// on the same camera; the effect now touches only the units.
///
[ExecuteAlways]
[RequireComponent(typeof(Camera))]
[AddComponentMenu("Heroes/Unit Camera Compositor")]
public sealed class UnitCameraCompositor : MonoBehaviour
{
[Tooltip("Overlay canvas the composite image is drawn on. Left empty -> one is created automatically.")]
public Canvas overlayCanvas;
[Tooltip("Sorting order of the auto-created overlay canvas (higher draws on top).")]
public int canvasSortingOrder = 100;
[Tooltip("Downscale factor for the render texture. 1 = full screen resolution. " +
"RetroImageEffect already pixelates, so leave at 1 unless you want extra headroom.")]
[Range(1, 4)]
public int resolutionDivider = 1;
private Camera _camera;
private RenderTexture _rt;
private RawImage _image;
private int _rtWidth;
private int _rtHeight;
private void OnEnable()
{
_camera = GetComponent();
EnsureComposite();
}
private void OnDisable()
{
if (_camera != null && _camera.targetTexture == _rt)
_camera.targetTexture = null;
if (_image != null) _image.enabled = false;
ReleaseTexture();
}
private void Update()
{
EnsureComposite();
}
private void EnsureComposite()
{
if (_camera == null) _camera = GetComponent();
int div = Mathf.Max(1, resolutionDivider);
int w = Mathf.Max(16, Screen.width / div);
int h = Mathf.Max(16, Screen.height / div);
if (_rt == null || w != _rtWidth || h != _rtHeight)
{
ReleaseTexture();
_rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32)
{
name = "UnitCameraRT",
hideFlags = HideFlags.HideAndDontSave,
};
_rt.Create();
_rtWidth = w;
_rtHeight = h;
}
// Force the camera to render only its own layers into the RT, cleared to
// transparent so the background camera shows through the empty areas.
_camera.targetTexture = _rt;
_camera.clearFlags = CameraClearFlags.SolidColor;
_camera.backgroundColor = new Color(0f, 0f, 0f, 0f);
EnsureImage();
if (_image != null && _image.texture != _rt) _image.texture = _rt;
}
private void EnsureImage()
{
if (_image != null) { _image.enabled = true; return; }
if (overlayCanvas == null)
{
var canvasGo = new GameObject("UnitCompositeCanvas");
canvasGo.hideFlags = HideFlags.DontSave;
overlayCanvas = canvasGo.AddComponent