[WL-815c] 스테이지 장벽: 워프백 + 3스타일(마법진/프롭/불꽃) + NavMesh 카브 옵션 + 캡처 (#815)

- 캡처 6장(3스타일 × 원근/도트 근사) · 프로브 43/43 PASS · 콘솔 error 0
- 링 색: Hovl HS_Blend_CG 에서 화면 색이 안 바뀌어 ringTintEnabled 기본 0(보고서 미확인)
- 불꽃: fireScale 3 · 간격 2.5 m → 35기 · 총량 상한 1600 에서 방출률 ×0.37
This commit is contained in:
깃 관리자 2026-09-11 00:59:57 +09:00
parent 89e4d06d99
commit 81ba822413
8 changed files with 374 additions and 31 deletions

View File

@ -0,0 +1,14 @@
WL-815c 장벽 3스타일 캡처 · 2026-09-11 00:59:27
앵커 (115.0, 0.4, -22.0) 반경 14 m · 구도 811o(h 76 · yaw 233)
PASS E0 맵 — WL_Nature
존1 지면 실측 y = 0.19 (앵커 표기 y 0.4)
PASS E1 PC — Ai01 중앙(크기 비교 기준)
PASS E barrier_ring_persp.png — MagicRing · 인스턴스 3 · 렌더러 12 · 머티리얼 1 · tri 0 · PS 12 · 링 스케일 12.27 · 원근 FOV 45 · 거리 64.9 m · 474482 B · 색 837종 · 따뜻한픽셀 3630 · drawCalls 0 · batches 0 · tri 0
PASS E barrier_ring_pixel.png — MagicRing · 인스턴스 3 · 렌더러 12 · 머티리얼 1 · tri 0 · PS 12 · 링 스케일 12.27 · 직교 size 26.1 · 270×480 → Point 확대 · 145042 B · 색 975종 · 따뜻한픽셀 3609 · drawCalls 0 · batches 0 · tri 0
PASS E barrier_props_persp.png — Props · 인스턴스 35 · 렌더러 35 · 머티리얼 2 · tri 4530 · PS 0 · 프롭 35 · 간격 2.51 m · 원근 FOV 45 · 거리 64.9 m · 425850 B · 색 795종 · 따뜻한픽셀 3647 · drawCalls 0 · batches 0 · tri 0
PASS E barrier_props_pixel.png — Props · 인스턴스 35 · 렌더러 35 · 머티리얼 2 · tri 4530 · PS 0 · 프롭 35 · 간격 2.51 m · 직교 size 26.1 · 270×480 → Point 확대 · 138475 B · 색 901종 · 따뜻한픽셀 3628 · drawCalls 0 · batches 0 · tri 0
PASS E barrier_fire_persp.png — Fire · 인스턴스 35 · 렌더러 105 · 머티리얼 3 · tri 0 · PS 105 · 불꽃 35 · 방출률 ×0.37 · 파티클 1600 · 원근 FOV 45 · 거리 64.9 m · 412816 B · 색 759종 · 따뜻한픽셀 3661 · drawCalls 0 · batches 0 · tri 0
PASS E barrier_fire_pixel.png — Fire · 인스턴스 35 · 렌더러 105 · 머티리얼 3 · tri 0 · PS 105 · 불꽃 35 · 방출률 ×0.37 · 파티클 1600 · 직교 size 26.1 · 270×480 → Point 확대 · 135914 B · 색 858종 · 따뜻한픽셀 3639 · drawCalls 0 · batches 0 · tri 0
클리어 연출 중간(t=0.5) 활성 35 · 진행도 0.50
RESULT PASS · 8 PASS · 0 FAIL

View File

@ -0,0 +1,241 @@
// ─────────────────────────────────────────────────────────────────────────────
// WL815c_Capture.cs — PD 대안 비교 캡처 6장 (에디트 모드 · 임시 씬 저장 0 · 커밋 0)
// unity command run_script --file AgentScripts/WL815c_Capture.cs --entry WL815c_Capture.Run
//
// 3스타일(마법진 링 / 프롭 링 / 불꽃 링) × 2구도
// persp : 811o 구도 각도(cameraHeight 76 · MapZoneData yaw 233 실측) · 원근 · 반경 14 m 전체가 보이게 거리만 확장
// pixel : 814c 방식 = 직교 270×480 렌더 → Point 확대 1080×1920 (도트 룩에서 뭉개지는지 본다)
// 앵커 = 기준서 §D-1 존1 (115, 0.4, 22) · 반경 14
// ─────────────────────────────────────────────────────────────────────────────
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using WL.Combat.Stage;
public static class WL815c_Capture
{
const string kOutDir = @"E:\NerdNavis\nn_himminji\Screenshots_WL\WL815c";
const string kTxt = "AgentScripts/WL815c_CAPTURE.txt";
const string kMapPrefab = "Assets/Res_Addr/Map/WL_Nature.prefab";
const string kPcPrefab = "Assets/Res_Addr/PC/Ai01.prefab";
const int kLowW = 270, kLowH = 480, kHiW = 1080, kHiH = 1920;
const float kMapYaw = 233f; // WL_Nature MapZoneData.CamRot_Portal 실측(811o)
const float kCamH = 76f; // WLCameraFramingSettings.cameraHeight 실측(811o)
static readonly Vector3 kAnchor = new Vector3(115f, 0.4f, -22f);
const float kRadius = 14f;
static StringBuilder s_sb;
static int s_pass, s_fail;
static void L(string s) { s_sb.AppendLine(s); }
static void Chk(string n, bool ok, string d) { if (ok) s_pass++; else s_fail++; L((ok ? " PASS " : " FAIL ") + n + " — " + d); }
public static string Run()
{
s_sb = new StringBuilder(); s_pass = 0; s_fail = 0;
L("WL-815c 장벽 3스타일 캡처 · " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
L(" 앵커 " + kAnchor.ToString("F1") + " 반경 " + kRadius + " m · 구도 811o(h " + kCamH + " · yaw " + kMapYaw + ")");
var keepSo = WLStageBarrierSettings.Instance;
try
{
Directory.CreateDirectory(kOutDir);
EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
foreach (var go in SceneManager.GetActiveScene().GetRootGameObjects())
if (go.GetComponent<Camera>() != null) UnityEngine.Object.DestroyImmediate(go);
var map = Inst(kMapPrefab, Vector3.zero);
Chk("E0 맵", map != null, "WL_Nature");
if (map == null) return "FAIL 맵 없음";
// 앵커 지면 실측
Vector3 center = kAnchor;
RaycastHit gh;
if (Physics.Raycast(kAnchor + Vector3.up * 100f, Vector3.down, out gh, 300f)) center.y = gh.point.y;
L(" 존1 지면 실측 y = " + center.y.ToString("F2") + " (앵커 표기 y " + kAnchor.y + ")");
// PC 를 중앙에 세워 크기 비교 기준을 만든다
var pc = Inst(kPcPrefab, center);
if (pc != null)
{
Vector3 f = new Vector3(Mathf.Sin(kMapYaw * Mathf.Deg2Rad), 0f, Mathf.Cos(kMapYaw * Mathf.Deg2Rad));
pc.transform.rotation = Quaternion.LookRotation(-f, Vector3.up);
// 🔴 Animator.Update 는 부르지 않는다 — 에디트 모드는 Awake 가 안 돌아
// Actor.Event_Idle(Actor.cs:2297)이 미할당 m_animation 을 만져 콘솔 예외가 난다(실측).
// 이 캡처에서 PC 는 크기 비교용이라 포즈는 중요하지 않다.
}
Chk("E1 PC", pc != null, "Ai01 중앙(크기 비교 기준)");
StageBarrier.HardResetForProbe();
WLStageBarrierSettings.SetInstanceForProbe(keepSo);
var styles = new StageBarrierStyle[] { StageBarrierStyle.MagicRing, StageBarrierStyle.Props, StageBarrierStyle.Fire };
var names = new string[] { "ring", "props", "fire" };
for (int i = 0; i < styles.Length; i++)
{
StageBarrierVisual.StyleOverride = styles[i];
StageBarrier.Set(center, kRadius);
StageBarrierVisual.ForcePhase(1f); // 입장 연출 끝난 상태
StageBarrierVisual.SimulateForProbe(2.0f); // 파티클을 2 s 진행(에디트 모드)
string info = Info(styles[i]);
ShotPersp(center, kOutDir + @"\barrier_" + names[i] + "_persp.png", styles[i] + " · " + info);
ShotPixel(center, kOutDir + @"\barrier_" + names[i] + "_pixel.png", styles[i] + " · " + info);
}
// 클리어 연출 확인용(파일로 남기지 않고 값만)
StageBarrier.Clear();
StageBarrierVisual.ForcePhase(0.5f);
L(" 클리어 연출 중간(t=0.5) 활성 " + StageBarrierVisual.LiveCount + " · 진행도 " + StageBarrierVisual.Phase.ToString("F2"));
}
catch (Exception ex) { s_fail++; L(" FAIL 예외 — " + ex.GetType().Name + " " + ex.Message + "\n" + ex.StackTrace); }
finally
{
try { StageBarrier.HardResetForProbe(); } catch { }
StageBarrierVisual.StyleOverride = (StageBarrierStyle)(-1);
WLStageBarrierSettings.SetInstanceForProbe(keepSo);
}
L("");
L("RESULT " + (s_fail == 0 ? "PASS" : "FAIL") + " · " + s_pass + " PASS · " + s_fail + " FAIL");
File.WriteAllText(kTxt, s_sb.ToString());
return (s_fail == 0 ? "PASS" : "FAIL") + " " + s_pass + "/" + (s_pass + s_fail);
}
static string Info(StageBarrierStyle st)
{
int rend, mats, ps; long tri;
StageBarrierVisual.MeasureCost(out rend, out mats, out tri, out ps);
string x = "인스턴스 " + StageBarrierVisual.LiveCount + " · 렌더러 " + rend + " · 머티리얼 " + mats + " · tri " + tri + " · PS " + ps;
if (st == StageBarrierStyle.MagicRing) x += " · 링 스케일 " + StageBarrierVisual.RingScale.ToString("F2");
else if (st == StageBarrierStyle.Props) x += " · 프롭 " + StageBarrierVisual.PropCount + " · 간격 " + StageBarrierVisual.PropSpacingUsed.ToString("F2") + " m";
else x += " · 불꽃 " + StageBarrierVisual.FireCount + " · 방출률 ×" + StageBarrierVisual.FireRateMul.ToString("F2") +
" · 파티클 " + StageBarrierVisual.FireParticleEstimate;
return x;
}
// ── 원근 구도(811o 각도 · 링 전체가 들어오게 거리만 확장) ───────────────
static void ShotPersp(Vector3 center, string file, string label)
{
var camGo = new GameObject("[WL815c] PerspCam");
var cam = camGo.AddComponent<Camera>();
var acd = camGo.AddComponent<UniversalAdditionalCameraData>();
acd.renderShadows = true; acd.renderPostProcessing = false; acd.antialiasing = AntialiasingMode.None;
cam.orthographic = false;
cam.fieldOfView = 45f;
cam.nearClipPlane = 0.3f;
cam.farClipPlane = 500f;
cam.clearFlags = CameraClearFlags.Skybox;
// 811o 와 같은 방향 벡터(h = cameraHeight 76 · yaw = 233)
Vector3 dir = (Quaternion.Euler(kCamH, kMapYaw, 0f) * Vector3.one).normalized;
// 반경 14 m 원이 세로 1080×1920 화면에 들어오려면 = r / tan(가로 FOV/2)
float aspect = kHiW / (float)kHiH;
float halfV = cam.fieldOfView * 0.5f * Mathf.Deg2Rad;
float halfH = Mathf.Atan(Mathf.Tan(halfV) * aspect);
float dist = (kRadius * 1.08f) / Mathf.Tan(halfH);
cam.transform.position = center + Vector3.up * 2.4f - dir * dist;
cam.transform.LookAt(center + Vector3.up * 1.0f);
Shot(cam, file, label + " · 원근 FOV 45 · 거리 " + dist.ToString("F1") + " m", false);
UnityEngine.Object.DestroyImmediate(camGo);
}
// ── 도트 룩 근사(814c 방식 · 직교 270×480 → Point 확대) ─────────────────
static void ShotPixel(Vector3 center, string file, string label)
{
var camGo = new GameObject("[WL815c] PixelCam");
var cam = camGo.AddComponent<Camera>();
var acd = camGo.AddComponent<UniversalAdditionalCameraData>();
acd.renderShadows = true; acd.renderPostProcessing = false; acd.antialiasing = AntialiasingMode.None;
Vector3 dir = (Quaternion.Euler(kCamH, kMapYaw, 0f) * Vector3.one).normalized;
cam.orthographic = true;
// 세로 1080×1920 · 가로가 좁다 → 가로에 반경이 들어가야 한다: orthoSize(세로 반) = r×1.2 / aspect
float aspect = kHiW / (float)kHiH;
cam.orthographicSize = kRadius * 1.05f / aspect;
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 300f;
cam.clearFlags = CameraClearFlags.Skybox;
cam.transform.position = center + Vector3.up * 2.4f - dir * 60f;
cam.transform.LookAt(center + Vector3.up * 1.0f);
Shot(cam, file, label + " · 직교 size " + cam.orthographicSize.ToString("F1") + " · " + kLowW + "×" + kLowH + " → Point 확대", true);
UnityEngine.Object.DestroyImmediate(camGo);
}
static void Shot(Camera cam, string file, string label, bool pixel)
{
RenderTexture low = null, hi = null;
Texture2D tex = null;
try
{
if (pixel)
{
low = new RenderTexture(kLowW, kLowH, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
low.filterMode = FilterMode.Point; low.antiAliasing = 1; low.Create();
hi = new RenderTexture(kHiW, kHiH, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
hi.filterMode = FilterMode.Point; hi.Create();
cam.targetTexture = low; cam.Render(); cam.targetTexture = null;
Graphics.Blit(low, hi); // Point 확대 = 픽셀 카메라 근사
}
else
{
hi = new RenderTexture(kHiW, kHiH, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
hi.Create();
cam.targetTexture = hi; cam.Render(); cam.targetTexture = null;
}
int dc = UnityEditor.UnityStats.drawCalls, batches = UnityEditor.UnityStats.batches, tri = UnityEditor.UnityStats.triangles;
var active = RenderTexture.active;
RenderTexture.active = hi;
tex = new Texture2D(kHiW, kHiH, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, kHiW, kHiH), 0, 0);
tex.Apply();
RenderTexture.active = active;
File.WriteAllBytes(file, tex.EncodeToPNG());
var px = tex.GetPixels32();
var seen = new HashSet<int>();
long warm = 0;
for (int i = 0; i < px.Length; i += 331)
{
seen.Add((px[i].r << 16) | (px[i].g << 8) | px[i].b);
if (px[i].r > 120 && px[i].r > px[i].b + 40) warm++; // 주황·붉은 픽셀 = 장벽 표식
}
var fi = new FileInfo(file);
Chk("E " + Path.GetFileName(file), fi.Exists && fi.Length > 4096 && seen.Count > 4,
label + " · " + fi.Length + " B · 색 " + seen.Count + "종 · 따뜻한픽셀 " + warm +
" · drawCalls " + dc + " · batches " + batches + " · tri " + tri);
}
finally
{
RenderTexture.active = null;
if (tex != null) UnityEngine.Object.DestroyImmediate(tex);
if (low != null) { low.Release(); UnityEngine.Object.DestroyImmediate(low); }
if (hi != null) { hi.Release(); UnityEngine.Object.DestroyImmediate(hi); }
}
}
// ── 도구 ────────────────────────────────────────────────────────────────
static GameObject Inst(string path, Vector3 pos)
{
var p = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (p == null) { L(" [없음] " + path); return null; }
var go = UnityEngine.Object.Instantiate(p);
go.transform.position = pos;
return go;
}
}

View File

@ -1,4 +1,4 @@
WL-815c 스테이지 장벽 프로브 · 2026-09-11 00:51:58
WL-815c 스테이지 장벽 프로브 · 2026-09-11 00:59:28
앵커 = 기준서 §D-1 존1 (115.0, 0.4, -22.0) 반경 14 m
① 설정 에셋(C45 — 값은 전부 SO)
@ -7,21 +7,21 @@ WL-815c 스테이지 장벽 프로브 · 2026-09-11 00:51:58
PASS 1-3 몹 값 — 몹여유 3 · 유예 1 s · 쿨 2 s
PASS 1-4 카브 기본 off — carveNavMesh 0 · arc 4 m · 상한 32 · 두께 1.5 · 높이 3
PASS 1-5 스타일 — style=MagicRing · 입장 0.6 s · 클리어 0.6 s
PASS 1-6 ⓐ 링 프리팹 — Marker 3 Zone Loop · 실측 시각반경 1.1 m · 겹 2
PASS 1-6 ⓐ 링 프리팹 — Marker 3 Zone Loop · 실측 시각반경 1.1 m · 겹 3
PASS 1-7 ⓑ 프롭 프리팹 — Fence_01, Fence_01, Rock_Block_crk_m_01 · 간격 2.5 m · 상한 64
PASS 1-8 ⓒ 불꽃 프리팹 — Fire_Particles · 간격 3 m · 상한 48 · 파티클 총량 1200
PASS 1-8 ⓒ 불꽃 프리팹 — Fire_Particles · 간격 2.5 m · 상한 48 · 파티클 총량 1600
② 임시 NavMesh 베이크(에디트 모드에서 실 워프 지점을 재기 위해)
PASS 2-1 NavMesh 베이크 — 정점 64 · 앵커 스냅 (115.00, 0.48, -22.00)
③ Set / Clear 왕복 (오브젝트 0 · 풀 반납)
PASS 3-1 초기 — Active=false · IsInside(어디든)=true → True
PASS 3-2 Set — Active=true · 중심 (115.0, 0.4, -22.0) 반경 14 · 그림 인스턴스 2 · Instantiate 누계 2
PASS 3-2 Set — Active=true · 중심 (115.0, 0.4, -22.0) 반경 14 · 그림 인스턴스 3 · Instantiate 누계 3
PASS 3-3 IsInside — 중심=안 · 13.9 m=안 · 14.1 m=밖 · 높이 무관(y+50 → True)
PASS 3-4a Clear 직후 = 페이드 중 — Active=false · 페이드 중 활성 2 · 진행도 1.00
PASS 3-4 페이드 끝 → 활성 0 · 풀 반납 — 활성 0 · 풀 2(= Instantiate 누계 2) · Destroy 0
PASS 3-5 재Set → 풀 재사용 — Instantiate 2 → 2(증가 0) · 활성 2
PASS 3-6 입장/클리어 연출 — 스케일 t=0 0.00 → t=.5 6.23 → t=1 12.45 (appear 0.6 s / fade 0.6 s)
PASS 3-4a Clear 직후 = 페이드 중 — Active=false · 페이드 중 활성 3 · 진행도 1.00
PASS 3-4 페이드 끝 → 활성 0 · 풀 반납 — 활성 0 · 풀 3(= Instantiate 누계 3) · Destroy 0
PASS 3-5 재Set → 풀 재사용 — Instantiate 3 → 3(증가 0) · 활성 3
PASS 3-6 입장/클리어 연출 — 스케일 t=0 0.00 → t=.5 6.14 → t=1 12.27 (appear 0.6 s / fade 0.6 s)
PASS 3-7 반경 0 → Clear — Set(r=0) → Active=False
④ 되돌릴 지점 계산(순수 함수 · NearestInsidePoint)
@ -41,15 +41,15 @@ WL-815c 스테이지 장벽 프로브 · 2026-09-11 00:51:58
PASS 5-6 유예 안에서는 안 되돌린다 — grace 999 s · 틱 ×2 → 워프 0 (OutsideSeen 0)
⑥ 스타일 전환 3회 (PD 가 값 하나로 바꾼다)
PASS 6-1 MagicRing — 활성 2 · 렌더러 8 · 머티리얼 1 · tri 0 · PS 8 · 빌드 1.4 ms · 링 스케일 12.45(반경 14 ÷ 실측 1.1)
PASS 6-2 Props — 활성 35 · 렌더러 35 · 머티리얼 2 · tri 4530 · PS 0 · 빌드 1.3 ms · 프롭 35개 · 실간격 2.51 m
PASS 6-3 Fire — 활성 29 · 렌더러 87 · 머티리얼 3 · tri 0 · PS 87 · 빌드 7.6 ms · 불꽃 29개 · 방출률 ×0.33 · 파티클 추정 1200(개당 125)
PASS 6-4 전환 누계 — StyleSwitches 3 · Instantiate 누계 66
PASS 6-1 MagicRing — 활성 3 · 렌더러 12 · 머티리얼 1 · tri 0 · PS 12 · 빌드 1.8 ms · 링 스케일 12.27(반경 14 ÷ 실측 1.1)
PASS 6-2 Props — 활성 35 · 렌더러 35 · 머티리얼 2 · tri 4530 · PS 0 · 빌드 1.4 ms · 프롭 35개 · 실간격 2.51 m
PASS 6-3 Fire — 활성 35 · 렌더러 105 · 머티리얼 3 · tri 0 · PS 105 · 빌드 11.2 ms · 불꽃 35개 · 방출률 ×0.37 · 파티클 추정 1600(개당 125)
PASS 6-4 전환 누계 — StyleSwitches 3 · Instantiate 누계 73
PASS 6-5 None → 그림 0 — 활성 그림 0 · 판정은 그대로 Active=True
⑦ NavMesh 카브 on/off (옵션 · 기준서 §D-2 ②)
PASS 7-1 off(기본) → 카브 0 — carveNavMesh 0 → 오브젝트 0
PASS 7-2 on → 세그먼트 생성 — 둘레 88.0 m ÷ arc 4 m = 22(상한 32) · 오브젝트 22 · 생성 0.41 ms · Set 전체 2.32 ms
PASS 7-2 on → 세그먼트 생성 — 둘레 88.0 m ÷ arc 4 m = 22(상한 32) · 오브젝트 22 · 생성 0.44 ms · Set 전체 3.31 ms
PASS 7-3 세그먼트 기하 — carving=true 22 · Box 22 · 상자 4.60 × 두께 1.50 m > 이웃 간격 3.98 m(이론 4.00 · 겹침 15 %) · 원 이탈 최대 0.0000 m
NavMesh 삼각형 32 → 32 (에디트 모드는 carve 가 즉시 반영되지 않을 수 있다 — 실 반영·재베이크 ms 는 Play 확인)
PASS 7-4 Clear → 카브 회수 — 오브젝트 0
@ -64,15 +64,15 @@ WL-815c 스테이지 장벽 프로브 · 2026-09-11 00:51:58
PASS 9-1 enabled=0 → Set 무동작 — Active=False · Instantiate 0 (오브젝트 0)
PASS 9-2 RuntimeDisabled=1 → Set 무동작 — Active=False · Instantiate 0
PASS 9-3 에셋 없음 → Set 무동작 — Active=False · FeatureEnabled=False
PASS 9-4 blockEnabled=0 → 판정 0 · 그림은 남는다 — Checks 0 · 그림 2
PASS 9-4 blockEnabled=0 → 판정 0 · 그림은 남는다 — Checks 0 · 그림 3
PASS 9-5 showVisual=0 → 그림 0 · 판정은 산다 — 그림 0 · Active=True
⑩ 성능 표 (반경 14 m · 스타일별)
| 스타일 | 인스턴스 | 렌더러 | 머티리얼 | 삼각형 | 파티클시스템 | 정상상태 파티클 | 빌드 ms |
|---|---|---|---|---|---|---|---|
| MagicRing | 2 | 8 | 1 | 0 | 8 | 8 | 0.9 |
| Props | 35 | 35 | 2 | 4530 | 0 | 0 | 1.2 |
| Fire | 29 | 87 | 3 | 0 | 87 | 1200 | 8.2 |
| MagicRing | 3 | 12 | 1 | 0 | 12 | 12 | 1.7 |
| Props | 35 | 35 | 2 | 4530 | 0 | 0 | 1.3 |
| Fire | 35 | 105 | 3 | 0 | 105 | 1600 | 10.4 |
PASS 10-1 성능 표 — 위 표 참조(에디트 모드 측정 · 실 드로우콜은 캡처의 UnityStats)
반경별 프롭 개수(간격 2.5 m · 상한 64)
r 8 m(둘레 50.3 m) → 프롭 20 · 실간격 2.51 m · 카브 세그(on 일 때) 13

View File

@ -0,0 +1,9 @@
링 PSR Marker 3 Zone Loop · shader Shader Graphs/HS_Blend_CG · _TintColor 없음 · _Color RGBA(1.000, 1.000, 1.000, 1.000) · renderMode Mesh · 정렬 0
링 PSR Flash · shader Shader Graphs/HS_Blend_CG · _TintColor 없음 · _Color RGBA(1.000, 1.000, 1.000, 1.000) · renderMode Billboard · 정렬 0
링 PSR Runes · shader Shader Graphs/HS_Blend_CG · _TintColor 없음 · _Color RGBA(1.000, 1.000, 1.000, 1.000) · renderMode Mesh · 정렬 0
링 PSR ShockWave · shader Shader Graphs/HS_Blend_CG · _TintColor 없음 · _Color RGBA(1.000, 1.000, 1.000, 1.000) · renderMode Mesh · 정렬 0
링 PS Marker 3 Zone Loop · startColor RGBA(0.027, 0.056, 0.104, 1.000) · colorOverLifetime ON · size 1.00
링 PS Flash · startColor RGBA(0.288, 0.573, 1.000, 0.125) · colorOverLifetime ON · size 2.00
링 PS Runes · startColor RGBA(0.031, 0.076, 0.142, 0.784) · colorOverLifetime ON · size 2.20
링 PS ShockWave · startColor RGBA(0.047, 0.097, 0.170, 1.000) · colorOverLifetime ON · size 3.00
적용: fireScale 3 · fireSpacing 2.5 · maxTotalParticles 1600 · ringExtraRings 2

View File

@ -0,0 +1,65 @@
// WL-815c 튜닝 — 캡처 1차 실측 뒤 값 조정 + 링 머티리얼 진단
// unity command run_script --file AgentScripts/WL815c_Tune.cs --entry WL815c_Tune.Run
using System.Text;
using UnityEditor;
using UnityEngine;
using WL.Combat.Stage;
public static class WL815c_Tune
{
const string kPath = "Assets/WL/Combat/Stage/Barrier/Resources/WL/WLStageBarrierSettings.asset";
public static string Run()
{
var sb = new StringBuilder();
var so = AssetDatabase.LoadAssetAtPath<WLStageBarrierSettings>(kPath);
if (so == null) return "에셋 없음";
// ── 링 머티리얼 진단(왜 색이 안 먹는가)
if (so.ringPrefab != null)
{
var rs = so.ringPrefab.GetComponentsInChildren<ParticleSystemRenderer>(true);
for (int i = 0; i < rs.Length; i++)
{
var m = rs[i].sharedMaterial;
sb.Append(" 링 PSR ").Append(rs[i].name)
.Append(" · shader ").Append(m != null ? m.shader.name : "null")
.Append(" · _TintColor ").Append(m != null && m.HasProperty("_TintColor") ? m.GetColor("_TintColor").ToString() : "없음")
.Append(" · _Color ").Append(m != null && m.HasProperty("_Color") ? m.GetColor("_Color").ToString() : "없음")
.Append(" · renderMode ").Append(rs[i].renderMode)
.Append(" · 정렬 ").Append(rs[i].sortingOrder).AppendLine();
}
var ps = so.ringPrefab.GetComponentsInChildren<ParticleSystem>(true);
for (int i = 0; i < ps.Length; i++)
sb.Append(" 링 PS ").Append(ps[i].name)
.Append(" · startColor ").Append(ps[i].main.startColor.color.ToString())
.Append(" · colorOverLifetime ").Append(ps[i].colorOverLifetime.enabled ? "ON" : "off")
.Append(" · size ").Append(ps[i].main.startSize.constantMax.ToString("F2")).AppendLine();
}
// ── ⓒ 불꽃: 1차 캡처에서 「흩어진 불티」로 보였다 → 크기·밀도를 올린다(실측 기반)
so.fireScale = 3f; // Fire_Particles 는 모닥불 크기(shape r0.46) → 3배면 약 1.4 m
so.fireSpacing = 2.5f; // 둘레 88 m → 35기
so.maxTotalParticles = 1600; // 35 × 125 = 4375 → 방출률 ×0.37
so.fireGroundSnap = true;
// ── ⓐ 링: 겹으로 두께를 낸다(도트 룩 대비) + 색은 「곱」이라 1 보다 큰 값이 증폭이다
so.ringExtraRings = 2;
so.ringExtraSpacing = 0.5f;
so.ringColor = new Color(6f, 1.2f, 0.4f, 1f); // 원색(어두운 청 기저 0.027,0.056,0.104)에 곱
so.ringTintEnabled = false; // 🔴 실측 3회 — 이 셰이더에서는 화면 색이 안 바뀐다(보고서 「미확인」)
EditorUtility.SetDirty(so);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
sb.Append(" 적용: fireScale ").Append(so.fireScale)
.Append(" · fireSpacing ").Append(so.fireSpacing)
.Append(" · maxTotalParticles ").Append(so.maxTotalParticles)
.Append(" · ringExtraRings ").Append(so.ringExtraRings).AppendLine();
System.IO.File.WriteAllText("AgentScripts/WL815c_TUNE.txt", sb.ToString());
return "tune done → AgentScripts/WL815c_TUNE.txt";
}
}

View File

@ -45,11 +45,11 @@ MonoBehaviour:
visualRadiusScale: 1
ringPrefab: {fileID: 1370640339480484, guid: a1b9488b1b1338d4585b154f4b86703e, type: 3}
ringPrefabVisualRadius: 1.1
ringColor: {r: 1, g: 0.35, b: 0.15, a: 1}
ringTintEnabled: 1
ringColor: {r: 6, g: 1.2, b: 0.4, a: 1}
ringTintEnabled: 0
ringYScale: 1
ringExtraRings: 1
ringExtraSpacing: 0.6
ringExtraRings: 2
ringExtraSpacing: 0.5
propPrefabs:
- {fileID: 3287405711304420922, guid: 84026706d57fb9e43b9716c8b1510e98, type: 3}
- {fileID: 3287405711304420922, guid: 84026706d57fb9e43b9716c8b1510e98, type: 3}
@ -67,10 +67,10 @@ MonoBehaviour:
propSeed: 815
firePrefabs:
- {fileID: 131402, guid: 5608cd25f71830e4aaa770d88ff9ebe2, type: 3}
fireSpacing: 3
fireSpacing: 2.5
maxFires: 48
fireScale: 1
maxTotalParticles: 1200
fireScale: 3
maxTotalParticles: 1600
fireBudgetMode: 0
fireTintEnabled: 0
fireColor: {r: 1, g: 0.45, b: 0.1, a: 1}

View File

@ -399,11 +399,21 @@ namespace WL.Combat.Stage
}
}
/// <summary>SO 색으로 「원래 색」을 갈아 끼운다(다음 SetAlpha 부터 이 색이 쓰인다).</summary>
/// <summary>
/// SO 색을 「원래 색」에 **곱한다**(대입이 아니다).
/// 🔴 실측(WL815c_TUNE) — Hovl Marker 3 Zone Loop 은 4개 파티클시스템 전부 `colorOverLifetime` 이 ON 이고
/// startColor 가 (0.027, 0.056, 0.104) 같은 **어두운 기저색**이다(발광 셰이더가 이것을 증폭한다).
/// 여기에 (1, 0.35, 0.15) 를 그냥 대입하면 원래보다 훨씬 밝아져 **흰색으로 뭉개진다**(1차 캡처에서 실측).
/// 곱하면 아티스트가 잡은 밝기를 유지한 채 색조만 돈다.
/// </summary>
static void Tint(Slot s, Color color)
{
if (s.psColor == null) return;
for (int i = 0; i < s.psColor.Length; i++) s.psColor[i] = color;
if (s.psColor == null || s.psOrig == null) return;
for (int i = 0; i < s.psColor.Length && i < s.psOrig.Length; i++)
{
Color o = s.psOrig[i];
s.psColor[i] = new Color(o.r * color.r, o.g * color.g, o.b * color.b, o.a * color.a);
}
}
static void Restart(Slot s)

View File

@ -197,11 +197,15 @@ namespace WL.Combat.Stage
"Marker 3 Zone Loop 실측 1.10 m(Simulate 2 s · 파티클 위치+반크기 최대).")]
public float ringPrefabVisualRadius = 1.1f;
[Tooltip("링 색(파티클 startColor 에 인스턴스 단위로 대입 · 원본 머티리얼·프리팹 무수정). a 는 밝기.")]
public Color ringColor = new Color(1f, 0.35f, 0.15f, 1f);
[Tooltip("링 색 — 프리팹 원색에 **곱해진다**(대입이 아니다). 1,1,1,1 = 원본 그대로. " +
"🔴 실측: Hovl 마커는 startColor 가 어두운 기저색 + colorOverLifetime ON 이라 대입하면 흰색으로 뭉개진다.")]
public Color ringColor = new Color(6f, 1.2f, 0.4f, 1f);
[Tooltip("0 이면 색을 건드리지 않고 프리팹 원색 그대로 쓴다.")]
public bool ringTintEnabled = true;
[Tooltip("1 이면 ringColor 를 프리팹 원색에 곱한다. " +
"🔴 기본 0 — 실측(캡처 3회): Hovl `HS_Blend_CG`(Shader Graph) 는 VertexColorNode 가 그래프에 있는데도 " +
"Marker 3 Zone Loop 의 화면 색이 눈에 띄게 바뀌지 않았다(대입·×1.6·×6 전부). " +
"색을 실제로 바꾸려면 머티리얼 사본이 필요한데 그건 원본 에셋 접촉이라 이 태스크 범위 밖이다.")]
public bool ringTintEnabled;
[Tooltip("도트 룩(814b/814c)에서 링이 뭉개지지 않게 세로로 늘리는 배율. 1 = 프리팹 그대로. " +
"🔴 링은 평면 쿼드라 Y 스케일은 두께가 아니라 「기울기」를 만든다 — 두께는 ringExtraRings 로 낸다.")]