Project_WL/Assets/WL/Look/Character/WLCharacterLook.cs

109 lines
4.8 KiB
C#

// ─────────────────────────────────────────────────────────────────────────────
// WLCharacterLook.cs — 캐릭터 머티리얼을 모드에 맞게 갈아끼운다 (WL-814s)
//
// PD 지시 #814 · 발주서 WL-814s §1-5
//
// ■ 무엇을 하나
// 프리팹(LH_M05)은 기본 모드의 머티리얼을 들고 있다. 이 컴포넌트가 Awake 에서
// SO(WLCharacterLookSettings) 를 읽어 「지금 모드」의 머티리얼로 맞춘다.
// enabled_ = 0 · mode = 0 이면 **원본 머티리얼로 되돌린다**(되돌리기 스위치).
//
// ■ 어떻게 맞추나 (프리팹이 무엇을 들고 있든 결과가 같다 = 멱등)
// SO 의 4개 배열(originals · mode1 · mode2 · mode3)은 같은 길이·같은 순서다.
// 렌더러가 들고 있는 머티리얼이 어느 배열의 i번이든, 목표 배열의 i번으로 바꾼다.
// 표에 없는 머티리얼(무기 등)은 건드리지 않는다.
//
// ■ 성능 / GC
// Awake 1회. 렌더러 수만큼만 돈다(LH_M05 = 7). 바뀔 것이 없으면 배열 대입도 하지 않는다.
//
// 🔴 Debug.LogError / Debug.LogException 을 쓰지 않는다(813z 규칙).
// ─────────────────────────────────────────────────────────────────────────────
using System.Collections.Generic;
using UnityEngine;
namespace WL.Look.Character
{
[DisallowMultipleComponent]
public class WLCharacterLook : MonoBehaviour
{
// 진단(프로브가 읽는다)
public static int AppliedRenderers, AppliedSlots, LastMode = -1;
public static string LastLog = "";
static readonly List<Renderer> s_buf = new List<Renderer>(16);
void Awake() { Apply(gameObject); }
/// <summary>SO 가 지시하는 모드로 맞춘다.</summary>
public static void Apply(GameObject root)
{
var cfg = WLCharacterLookSettings.Instance;
if (cfg == null || root == null) return;
ApplyMode(root, WLCharacterLookSettings.ActiveMode, cfg);
// WL-814x — 팔레트 아틀라스 UV. 그쪽 SO 의 enabled_ = 0 이면 아무 일도 일어나지 않는다.
WLCharacterPalette.Apply(root);
}
/// <summary>모드를 직접 지정해 맞춘다(캡처·프로브·A/B 용).</summary>
public static void ApplyMode(GameObject root, int mode, WLCharacterLookSettings cfg)
{
if (root == null) return;
if (cfg == null) cfg = WLCharacterLookSettings.Instance;
if (cfg == null || cfg.originals == null || cfg.originals.Length == 0) return;
Material[] target = cfg.SetForMode(mode);
if (target == null) return;
int n = cfg.originals.Length;
int renderers = 0, slots = 0;
s_buf.Clear();
root.GetComponentsInChildren<Renderer>(true, s_buf);
for (int ri = 0; ri < s_buf.Count; ri++)
{
var r = s_buf[ri];
if (r == null) continue;
var mats = r.sharedMaterials;
if (mats == null || mats.Length == 0) continue;
bool changed = false;
for (int si = 0; si < mats.Length; si++)
{
int idx = IndexOf(cfg, mats[si], n);
if (idx < 0) continue;
var want = target[idx];
if (want == null || mats[si] == want) continue;
mats[si] = want;
changed = true;
slots++;
}
if (changed) { r.sharedMaterials = mats; renderers++; }
}
AppliedRenderers = renderers; AppliedSlots = slots; LastMode = mode;
LastLog = "mode " + mode + " · 렌더러 " + renderers + " · 슬롯 " + slots;
if (cfg.verboseLog != 0) Debug.Log("[WL814s CharacterLook] " + LastLog);
}
/// <summary>머티리얼이 4개 배열 중 어디의 몇 번인지(없으면 -1).</summary>
static int IndexOf(WLCharacterLookSettings c, Material m, int n)
{
if (m == null) return -1;
for (int i = 0; i < n; i++)
{
if (Same(c.originals, i, m)) return i;
if (Same(c.mode1, i, m)) return i;
if (Same(c.mode2, i, m)) return i;
if (Same(c.mode3, i, m)) return i;
}
return -1;
}
static bool Same(Material[] a, int i, Material m)
{
return a != null && i < a.Length && a[i] != null && a[i] == m;
}
}
}