// ───────────────────────────────────────────────────────────────────────────── // 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 s_buf = new List(16); void Awake() { Apply(gameObject); } /// SO 가 지시하는 모드로 맞춘다. 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); } /// 모드를 직접 지정해 맞춘다(캡처·프로브·A/B 용). 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(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); } /// 머티리얼이 4개 배열 중 어디의 몇 번인지(없으면 -1). 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; } } }