Project_WL/Assets/WL/Look/WLPixelLookSettings.cs

127 lines
7.3 KiB
C#
Raw Permalink Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLPixelLookSettings.cs — 도트(픽셀아트) 룩 스위치 값의 단일 출처 (SOT · C45)
//
// PD 지시 #814(「Critter 3D Pixel Art Toolkit 으로 우리 게임을 도트 게임처럼 보이게」)
// 발주서 WL-814a §1-2 (2026-09-10)
//
// ■ 왜 URP 기본 기능인가 (실측)
// 임포트된 것은 `Assets/Critter 3D Pixel Art Toolkit/README.pdf` **1개뿐**이다
// (번들 3종 = Critter 3D Pixel Camera · Environment · Volumetric Lighting 은 Asset Store 에서 별도 다운로드).
// → 에셋이 도착하기 전까지 URP 17.3 이 이미 가진 두 값만으로 「도트 룩」 1차 시안을 만든다:
// UniversalRenderPipelineAsset.renderScale … 낮은 해상도로 그린다(= 픽셀이 커진다)
// UniversalRenderPipelineAsset.upscalingFilter … Point(Nearest-Neighbor) 로 확대한다(= 뭉개지지 않는다)
// 이 둘은 **파이프라인 에셋의 런타임 프로퍼티**라 코드에서 켜고 끌 수 있다(셰이더·머티리얼·포스트 0).
//
// ■ 🔴 절대 규칙 — 디스크에 쓰지 않는다
// 파이프라인 에셋은 프로젝트 에셋이다. 에디터에서 그 오브젝트의 값을 바꾸면 **메모리에 남고**,
// PD 가 「Save Project」 를 누르면 `Assets/Settings/URP-*.asset` 에 그대로 기록된다.
// 그래서 이 기능은
// · `EditorUtility.SetDirty` 를 **부르지 않는다**(호출 0)
// · 맵 이탈 · `OnApplicationQuit` · 에디터 플레이모드 종료에서 **원래 값으로 되돌린다**
// · 되돌릴 값(에셋 경로 + renderScale + filter)을 에디터 `SessionState` 에도 적어 둬서
// 도메인 리로드로 static 이 날아가도 다음 로드에서 복원한다(§PixelLook.EditorSafetyNet)
//
// ■ C8 롤백
// `enabled = 0` → 러너 오브젝트가 아예 뜨지 않고 파이프라인 에셋을 한 번도 건드리지 않는다(원본 100 %).
//
// 에셋 경로(고정): Assets/WL/Combat/Settings/Resources/WL/WLPixelLookSettings.asset
// → Resources.Load<WLPixelLookSettings>("WL/WLPixelLookSettings") 1회 로드 후 캐시(기존 WL 설정 SO 관례).
//
// 🔴 어셈블리 주의: Assets/WL/Look/ 에 .asmdef 를 만들지 말 것(MyValue·InGameInfo 가 Assembly-CSharp 에 있다).
// 🔴 이 파일은 Debug.LogError / Debug.LogException 을 쓰지 않는다(813z 규칙).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Look
{
/// <summary>낮은 해상도를 화면 크기로 확대할 때 쓰는 필터. URP UpscalingFilterSelection 으로 매핑된다.</summary>
public enum PixelUpscaleFilter
{
/// <summary>Nearest-Neighbor — 픽셀이 네모로 남는다(도트 룩의 핵심).</summary>
Point = 0,
/// <summary>Bilinear — 뭉개진다(비교용).</summary>
Linear = 1,
/// <summary>URP 자동 선택(렌더 스케일·해상도에 따라 Point 또는 Linear).</summary>
Auto = 2,
/// <summary>AMD FSR 1.0(선명하게 복원 = 도트 룩과 반대 방향 · 비교용).</summary>
FSR = 3,
}
[CreateAssetMenu(fileName = "WLPixelLookSettings", menuName = "WL/Pixel Look Settings", order = 324)]
public sealed class WLPixelLookSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLPixelLookSettings";
static WLPixelLookSettings s_cached;
static bool s_lookupDone;
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null = 기능 전체 무동작.</summary>
public static WLPixelLookSettings Instance
{
get
{
if (!s_lookupDone)
{
s_cached = Resources.Load<WLPixelLookSettings>(ResourcesPath);
s_lookupDone = true;
}
return s_cached;
}
}
/// <summary>진단·A/B 전용 런타임 스위치(WLCameraFramingSettings.RuntimeDisabled 선례).</summary>
public static bool RuntimeDisabled;
/// <summary>기능이 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off.</summary>
public static bool Enabled
{
get { var i = Instance; return i != null && i.enabled && !RuntimeDisabled; }
}
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다.</summary>
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
// ─────────────────────────────────────────── 전체 스위치
[Header("전체 스위치 (C8)")]
[Tooltip("0 이면 러너 오브젝트도 뜨지 않고 파이프라인 에셋을 한 번도 건드리지 않는다 = 원본 100 %.")]
public bool enabled = true;
[Tooltip("적용/복원 1줄 로그. 프레임당 로그는 없다(경계에서만).")]
public bool verboseLog = false;
// ─────────────────────────────────────────── 픽셀 크기
[Header("픽셀 크기")]
[Tooltip("세로로 몇 픽셀을 그릴 것인가. renderScale = targetHeightPx ÷ Screen.height.\n" +
"1080×1920 기준: 1920=원본 · 640≈0.333 · 480=0.25 · 360≈0.1875")]
[Range(64, 4096)]
public int targetHeightPx = 480;
[Tooltip("확대 필터. 도트 룩은 Point(Nearest-Neighbor).")]
public PixelUpscaleFilter filter = PixelUpscaleFilter.Point;
[Tooltip("renderScale 하한(URP 자체 하한도 0.1). 0.1 미만은 URP 가 어차피 0.1 로 잘라낸다.")]
[Range(0.05f, 1f)]
public float minRenderScale = 0.1f;
[Tooltip("renderScale 상한. 1 이면 「원본보다 크게 그리는 것」을 막는다(=슈퍼샘플링 방지).")]
[Range(0.5f, 3f)]
public float maxRenderScale = 1f;
// ─────────────────────────────────────────── 적용 범위
[Header("적용 범위 (인게임 한정)")]
[Tooltip("1 이면 전투 맵(Stage/Dungeon)에서만 켠다. 로비·타이틀·농장은 선명하게 둔다.")]
public bool applyOnlyInBattleMap = true;
[Tooltip("1 이면 맵 이탈(타이틀/로비 복귀) · 종료 · 플레이모드 종료에서 원래 값으로 되돌린다. 🔴 0 으로 두지 말 것.")]
public bool restoreOnExit = true;
[Tooltip("맵 진입/이탈 경계를 보는 주기(초). 프레임당 비용 0(시간 비교 1회).")]
[Range(0.05f, 2f)]
public float pollSeconds = 0.25f;
[Tooltip("1 이면 해상도가 바뀌었을 때(회전·창 크기) renderScale 을 다시 계산한다.")]
public bool trackScreenSize = true;
}
}