Project_WL/Assets/WL/PixelCamera/WLPixelCameraSettings.cs

153 lines
9.0 KiB
C#
Raw Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLPixelCameraSettings.cs — Critter 3D Pixel Camera 리그 값의 SOT (C45)
//
// PD 지시 #814(「도트 게임처럼」) · 발주서 WL-814b §1-3 (2026-09-10)
//
// 값의 SOT = Assets/WL/Combat/Settings/Resources/WL/WLPixelCameraSettings.asset
// → Resources.Load<WLPixelCameraSettings>("WL/WLPixelCameraSettings") 1회 로드 후 캐시(기존 WL 설정 SO 관례).
//
// 🔴 C8 — enabled = 0 (또는 RuntimeDisabled = true) 이면 리그가 아예 조립되지 않고,
// 이미 조립돼 있으면 다음 틱에 원본 카메라 값이 100 % 복원된다(원본 동작과 동일).
//
// 🔴 원본 스크립트 0 · 원본 프리팹/씬 디스크 수정 0. 이 SO 와 PixelCameraRig.cs 만이 WL 신규 파일이다.
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.PixelArt
{
/// <summary>Followed 프록시를 어떻게 원본 카메라에 붙일지.</summary>
public enum PixelProxyMode
{
/// <summary>원본 Main Camera 의 자식(localPos 0 · localRot identity). 지연 0 · 프레임당 비용 0 · GC 0. 권장.</summary>
ChildOfCamera = 0,
/// <summary>독립 오브젝트 + 미러 스크립트([DefaultExecutionOrder(-100)] LateUpdate). 1프레임 지연.</summary>
MirrorScript = 1,
}
/// <summary>Critter 픽셀 카메라 리그 값. 전부 여기서만 바꾼다(코드 상수 0).</summary>
[CreateAssetMenu(fileName = "WLPixelCameraSettings", menuName = "WL/Pixel Camera Settings", order = 814)]
public sealed class WLPixelCameraSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLPixelCameraSettings";
static WLPixelCameraSettings s_cached;
static bool s_lookupDone;
/// <summary>Resources 에서 1회 로드 후 캐시. 없으면 null(= 기능 off).</summary>
public static WLPixelCameraSettings Instance
{
get
{
if (!s_lookupDone)
{
s_lookupDone = true;
s_cached = Resources.Load<WLPixelCameraSettings>(ResourcesPath);
}
return s_cached;
}
}
/// <summary>진단·A/B 전용 런타임 스위치(WLHitFeel.RuntimeDisabled 선례). 에셋을 안 건드리고 이 기능만 끈다.</summary>
public static bool RuntimeDisabled;
/// <summary>이 기능이 살아 있는가(에셋 존재 + enabled + !RuntimeDisabled).</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 이면 조립/복원 로그를 1줄씩 남긴다. LogError 는 쓰지 않는다(813z).")]
public bool verboseLog = false;
// ───────────────────────────────────────── 조립 경계
[Header("조립 경계 (811q EffectWarmup / 811o CameraFramingOverride 와 같은 판정)")]
[Tooltip("맵 진입 경계(메인 PC 교체 또는 맵 ID 변경)에서 자동 조립한다. 0 이면 프로브만 조립할 수 있다.")]
public bool assembleOnMapEnter = true;
[Tooltip("로비에서도 조립할지. 기본 0 = 인게임 맵에서만.")]
public bool assembleInLobby = false;
[Tooltip("경계 폴링 주기(초).")]
public float pollSeconds = 0.25f;
// ───────────────────────────────────────── 리그 원본
[Header("Critter 리그")]
[Tooltip("Assets/3DPixelCamera/URP/PixelCameraSystem.prefab (URP.unitypackage 해제로 생긴 프리팹).")]
public GameObject pixelCameraSystemPrefab;
[Tooltip("리그 전용 레이어 인덱스. 게임 카메라는 이 레이어를 빼고, 뷰 카메라는 이 레이어만 그린다. 프로젝트 미명명 레이어를 쓴다(TagManager 수정 0).")]
[Range(0, 31)] public int rigLayer = 31;
[Tooltip("프록시 방식. ChildOfCamera = 원본 카메라의 자식(지연 0 · 권장).")]
public PixelProxyMode proxyMode = PixelProxyMode.ChildOfCamera;
// ───────────────────────────────────────── 투영·해상도
[Header("직교 투영 · 픽셀 해상도")]
[Tooltip("원본 Main Camera 에 적용할 orthographicSize(= 화면 세로 절반의 월드 단위). "
+ "기본 3.8 = 원근 fov 60 · 카메라 거리 6.58 m 의 반높이(tan30° × 6.58).")]
[Range(1f, 12f)] public float orthoSize = 3.8f;
[Tooltip("저해상도 RT 의 세로 픽셀 수(SetHeight). 가로는 화면 비율로 자동(9:16 · 480 → 270×480).")]
[Range(60, 1080)] public int pixelHeight = 480;
[Tooltip("Critter 뷰 카메라가 캔버스 가장자리를 넘지 않으려고 (1 2/pixelHeight) 만큼 줌아웃하는 것을 보정한다. "
+ "1 이면 GameCameraZoom = orthoSize / (1 2/pixelHeight) 로 올려 화면 매핑을 orthoSize 와 정확히 맞춘다.")]
public bool compensateViewZoom = true;
// ───────────────────────────────────────── Critter 값
[Header("Critter PixelCameraManager 값")]
[Tooltip("픽셀 격자(voxel grid) 스냅. 정지 물체의 픽셀이 떨리지 않게 한다.")]
public bool voxelGridMovement = true;
[Tooltip("서브픽셀 보정. 격자 스냅의 뚝뚝 끊김을 뷰 카메라 오프셋으로 되돌린다.")]
public bool subpixelAdjustments = true;
[Tooltip("followed 트랜스폼의 회전도 따라간다.")]
public bool followRotation = true;
[Tooltip("Critter 가 게임 카메라의 orthographicSize 를 직접 제어한다.")]
public bool controlGameZoom = true;
[Tooltip("뷰 카메라 줌(1~1). 1 = 캔버스를 화면에 꽉 채운다.")]
[Range(-1f, 1f)] public float viewCameraZoom = 1f;
[Tooltip("뷰 카메라를 원본 Main Camera 의 URP 카메라 스택에 Overlay 로 끼운다(권장). "
+ "0 이면 Base 로 두고 depth 를 원본보다 1 높인다.")]
public bool viewCameraInMainStack = true;
// ───────────────────────────────────────── 회전 중 스냅 해제
[Header("회전 중 스냅 해제 (Critter 문서 권고)")]
[Tooltip("카메라가 빠르게 돌 때 격자 스냅·서브픽셀 보정을 잠시 끈다(811o 구도 전환 · 보스 연출).")]
public bool disableSnapWhileRotating = true;
[Tooltip("이 각속도(도/초)를 넘으면 회전 중으로 본다.")]
public float rotationSnapOffDegPerSec = 30f;
[Tooltip("회전이 멎고 이 시간(초)이 지나면 스냅을 다시 켠다.")]
public float rotationSnapResumeSeconds = 0.25f;
// ───────────────────────────────────────── 원본 카메라 처리
[Header("원본 Main Camera 처리 (MainCamera 태그·RealCamera·AudioListener 전부 유지)")]
[Tooltip("원본 카메라가 그릴 레이어. 기본 0 = 월드를 그리지 않는다(인게임 UI 캔버스는 UICamera Overlay 가 그린다).")]
public int mainCameraCullingMask = 0;
[Tooltip("원본 카메라의 포스트프로세싱을 끈다(픽셀 결과를 다시 후처리하지 않게). 게임 카메라 쪽에서 처리한다.")]
public bool mainCameraPostProcessingOff = true;
[Tooltip("원본 카메라의 포스트프로세싱 설정을 저해상도 게임 카메라로 복사한다(블룸 등이 픽셀 해상도로 들어간다).")]
public bool copyPostProcessingToGameCamera = true;
[Tooltip("원본 카메라의 배경을 단색 검정으로 바꾼다(스카이박스 클리어 비용 제거). 복원 시 원값으로 되돌린다.")]
public bool mainCameraSolidBlack = true;
}
}