Project_WL/AgentScripts/WL816j2_Verify.cs

286 lines
16 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// WL-816j2 — ② 🔴 판정은 **PD 카메라 구도**(MainCamera · FOV 60 · 45° · 22 m · 1280×720)에서.
// 무늬 선 3단 · 띠 폭 3단 · 거품 색 중립화 · 사방 확인 · 성능(드로우콜·ms/frame) 전/후.
// 🔴 Play 중 에셋 수정 0 — 물은 런타임 Material 복사본, 띠는 컴포넌트의 런타임 오버라이드.
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
using WL.Look.Farm;
using FIIsland = CryingSnow.FarmingIsland.Island;
public static class WL816j2_Verify
{
public const string D = "Screenshots_WL/WL816j2/";
public const int PW = 1280, PH = 720; // 🔴 PD 화면과 같은 해상도·화각
public const string C_Light = "Color_1ec6cccf8ead489ebb917674f7e8b3b1";
public const string C_Foam = "Color_e1f155248d144786a62fc1585ca21e9a";
public const string R_FoamDistance = "Vector1_101dc4546b684d40bc2ff2fc59ab43d5";
public const string R_FoamEdge = "Vector1_34f757bec6b8422b9aef3b9d97117a6e";
public static void Open()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
}
public static void Start()
{
var go = GameObject.Find("~WL816j2V");
if (go != null) Object.DestroyImmediate(go);
go = new GameObject("~WL816j2V");
go.AddComponent<WL816j2_VerifyRunner>();
}
public static Texture2D Grab(Camera cam, int w, int h)
{
var rt = new RenderTexture(w, h, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var pT = cam.targetTexture; var pA = RenderTexture.active;
cam.targetTexture = rt; cam.Render(); RenderTexture.active = rt;
var t = new Texture2D(w, h, TextureFormat.RGB24, false);
t.ReadPixels(new Rect(0, 0, w, h), 0, 0); t.Apply();
RenderTexture.active = pA; cam.targetTexture = pT; rt.Release(); Object.DestroyImmediate(rt);
return t;
}
/// <summary>PD 구도로 찍고 ⓐ 물 무늬 선 대비 ⓑ 거품(밝은 픽셀) 평균색·비율을 잰다.</summary>
public static string Shot(Camera cam, string path)
{
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var t = Grab(cam, PW, PH);
System.IO.File.WriteAllBytes(path, t.EncodeToPNG());
var px = t.GetPixels32();
// ⓐ 무늬 선 대비 — 🔴 **바다만 있는 왼쪽 아래 구역**(섬·UI 가 절대 안 들어온다)에서만 잰다
double sum = 0, sum2 = 0; int n = 0;
for (int y = 20; y < 320; y += 2)
for (int x = 20; x < 340; x += 2)
{
var c = px[y * PW + x];
float lum = (0.299f * c.r + 0.587f * c.g + 0.114f * c.b) / 255f;
sum += lum; sum2 += lum * lum; n++;
}
double mean = n > 0 ? sum / n : 0, sd = n > 0 ? System.Math.Sqrt(System.Math.Max(0, sum2 / n - mean * mean)) : 0;
// ⓑ 거품(밝은 픽셀) — 화면 아래 55 %(UI 회피)에서 비율과 색
long fr = 0, fg = 0, fb = 0; int fn = 0, tot = 0;
int y1 = (int)(PH * 0.55f);
for (int y = 0; y < y1; y++)
for (int x = 0; x < PW; x += 2)
{
var c = px[y * PW + x]; tot++;
if (c.r > 200 && c.g > 195 && c.b > 195) { fr += c.r; fg += c.g; fb += c.b; fn++; }
}
Object.DestroyImmediate(t);
string foam = fn == 0 ? "거품 0%" :
"거품 " + (100f * fn / tot).ToString("F2") + "% 색 #"
+ ((int)(fr / fn)).ToString("X2") + ((int)(fg / fn)).ToString("X2") + ((int)(fb / fn)).ToString("X2")
+ " (RB " + ((int)(fr / fn) - (int)(fb / fn)) + ")";
return "바다 무늬대비 σ=" + sd.ToString("F4") + " · " + foam + " → " + path;
}
public static float Ms(Camera cam, int frames)
{
var rt = new RenderTexture(PW, PH, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var pT = cam.targetTexture; cam.targetTexture = rt;
for (int i = 0; i < 10; i++) cam.Render();
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < frames; i++) cam.Render();
sw.Stop();
cam.targetTexture = pT; rt.Release(); Object.DestroyImmediate(rt);
return (float)sw.Elapsed.TotalMilliseconds / frames;
}
public static void Strip(string[] paths, string outPath)
{
var ts = new Texture2D[paths.Length]; int w = 0, h = 0;
for (int i = 0; i < paths.Length; i++)
{
if (!System.IO.File.Exists(paths[i])) return;
ts[i] = new Texture2D(2, 2, TextureFormat.RGB24, false);
ts[i].LoadImage(System.IO.File.ReadAllBytes(paths[i]));
w = Mathf.Max(w, ts[i].width); h += ts[i].height;
}
var o = new Texture2D(w, h, TextureFormat.RGB24, false);
var f = new Color32[w * h];
for (int i = 0; i < f.Length; i++) f[i] = new Color32(24, 24, 28, 255);
o.SetPixels32(f);
int y = h;
for (int i = 0; i < ts.Length; i++) { y -= ts[i].height; o.SetPixels32(0, y, ts[i].width, ts[i].height, ts[i].GetPixels32()); }
o.Apply();
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(outPath));
System.IO.File.WriteAllBytes(outPath, o.EncodeToPNG());
Object.DestroyImmediate(o);
for (int i = 0; i < ts.Length; i++) Object.DestroyImmediate(ts[i]);
}
}
public class WL816j2_VerifyRunner : MonoBehaviour
{
static StringBuilder sb;
static void L(string s) { sb.AppendLine(s); }
const string D = WL816j2_Verify.D;
const string MatDir = "Assets/WL/Look/Farm/Materials/";
void Start() { StartCoroutine(Co()); }
IEnumerator Co()
{
sb = new StringBuilder();
L("=== WL-816j2 — PD 구도(FOV 60 · 45° · 22 m · 1280×720)에서 다시 맞춘다 ===");
var op = SceneManager.LoadSceneAsync("Level01", LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
for (int i = 0; i < 8; i++) yield return new WaitForSeconds(1f);
Renderer water = null;
foreach (var r in Object.FindObjectsByType<Renderer>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
if (r != null && r.gameObject.scene.name == "Level01" && r.name == "Water") water = r;
Camera live = null;
foreach (var c in Object.FindObjectsByType<Camera>(FindObjectsInactive.Exclude, FindObjectsSortMode.None))
{
if (!c.gameObject.activeInHierarchy || !c.enabled || c.targetTexture != null) continue;
if (c.name.StartsWith("~WL816")) continue;
if (live == null || c.depth > live.depth) live = c;
}
var foam = Object.FindFirstObjectByType<WLShoreFoam>(FindObjectsInactive.Include);
if (water == null || live == null) { L("🔴 물/카메라 없음"); Done(); yield break; }
L("카메라 " + live.name + " pos=" + live.transform.position.ToString("F2") + " 각=" + live.transform.eulerAngles.ToString("F0")
+ " fov=" + live.fieldOfView + " · 거리 " + live.transform.position.magnitude.ToString("F1") + " m");
L("물 = " + water.sharedMaterial.name + " · 둘레 띠 = " + (foam == null ? "🔴 없음" : WLShoreFoam.LastLog));
var E = water.sharedMaterial;
var Dm = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_Water_WL_D.mat");
// ── 1) 지금(main 상태 = 물 D · 띠 없음) ─────────────────────
if (foam != null) foam.GetComponent<MeshRenderer>().enabled = false;
water.sharedMaterial = Dm; yield return null; yield return new WaitForSeconds(0.3f);
L("");
L("1) 지금(main · 물 D · 띠 없음) " + WL816j2_Verify.Shot(live, D + "a_now_pd.png"));
// ── 2) 무늬 선 3단 (띠 없이 · 물만) ──────────────────────────
L("");
L("2) 무늬 흰 선 3단 — LightColor (물색 0.36/0.69/0.82 에 가까울수록 은은)");
var lc = new[] { new Color(0.80f, 0.90f, 0.96f), new Color(0.60f, 0.82f, 0.91f), new Color(0.45f, 0.74f, 0.86f) };
var ls = new string[3];
for (int i = 0; i < 3; i++)
{
var m = new Material(E); m.SetColor(WL816j2_Verify.C_Light, lc[i]);
water.sharedMaterial = m; yield return null; yield return new WaitForSeconds(0.2f);
ls[i] = D + "b_line" + (i + 1) + ".png";
L(" " + lc[i] + " " + WL816j2_Verify.Shot(live, ls[i]));
}
WL816j2_Verify.Strip(ls, D + "b_line_steps.png");
// 데모(FI 원본) 물의 선 대비 = 기준
var FI = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_SimpleWater_Demo.mat");
water.sharedMaterial = FI; yield return null; yield return new WaitForSeconds(0.2f);
L(" [기준] 데모 물(FI SimpleWater) " + WL816j2_Verify.Shot(live, D + "b_line0_demo.png"));
// ── 3) 띠 폭 3단 (물 = E · 선 중간값) ───────────────────────
var pick = new Material(E); pick.SetColor(WL816j2_Verify.C_Light, lc[1]);
water.sharedMaterial = pick; yield return null;
L("");
L("3) 둘레 띠 폭 3단 (PD 구도)");
if (foam != null)
{
foam.GetComponent<MeshRenderer>().enabled = true;
var ws = new string[3]; float[] wid = { 0.9f, 1.6f, 2.6f };
for (int i = 0; i < 3; i++)
{
foam.widthOverride = wid[i]; foam.Rebuild();
yield return null; yield return new WaitForSeconds(0.2f);
ws[i] = D + "c_band" + (i + 1) + ".png";
L(" 폭 " + wid[i] + " m · " + WLShoreFoam.LastLog);
L(" " + WL816j2_Verify.Shot(live, ws[i]));
}
WL816j2_Verify.Strip(ws, D + "c_band_steps.png");
foam.widthOverride = 0f; foam.Rebuild(); yield return null;
}
else L(" 🔴 띠 컴포넌트가 없다");
// ── 4) 거품 색 중립화 — 화면에서 RB 를 0 에 가깝게 ─────────
L("");
L("4) 거품 색 — PD 캡처가 분홍빛이었다. 화면에서 재서 중립 흰색으로 맞춘다(RB 가 0 에 가까울수록 중립)");
if (foam != null)
{
var mr = foam.GetComponent<MeshRenderer>();
var tints = new[] { new Color(1f,1f,1f,0.92f), new Color(0.93f,0.97f,1f,0.92f), new Color(0.86f,0.95f,1f,0.90f) };
var cs = new string[3];
for (int i = 0; i < 3; i++)
{
var mi = new Material(foam.cfg.shoreFoamMaterial); mi.SetColor("_BaseColor", tints[i]);
mr.sharedMaterial = mi; yield return null; yield return new WaitForSeconds(0.2f);
cs[i] = D + "d_tint" + (i + 1) + ".png";
L(" _BaseColor " + tints[i] + " " + WL816j2_Verify.Shot(live, cs[i]));
}
WL816j2_Verify.Strip(cs, D + "d_tint_steps.png");
}
// ── 5) 사방 확인 + 확장 ─────────────────────────────────────
// ── 4-b) 물가 확대 (띠가 어떻게 보이는지) ───────────────────
if (foam != null)
{
var mr2 = foam.GetComponent<MeshRenderer>();
mr2.sharedMaterial = foam.cfg.shoreFoamMaterial;
var b = mr2.bounds;
var zgo = new GameObject("~WL816j2Zoom"); zgo.hideFlags = HideFlags.DontSave;
var zc = zgo.AddComponent<Camera>();
zc.fieldOfView = 34f; zc.nearClipPlane = 0.1f; zc.farClipPlane = 2000f;
zc.clearFlags = CameraClearFlags.Skybox; zc.cullingMask = ~0; zc.depth = -100f;
zgo.AddComponent<UniversalAdditionalCameraData>().renderPostProcessing = true;
zgo.transform.position = new Vector3(b.center.x, 3.2f, b.min.z - 9f);
zgo.transform.LookAt(new Vector3(b.center.x, -1f, b.min.z + 2f));
mr2.enabled = false; yield return null; yield return null;
L("");
L("4-b) 물가 확대 — 띠 off " + WL816j2_Verify.Shot(zc, D + "f_zoom_off.png"));
mr2.enabled = true; yield return null; yield return null;
L(" 물가 확대 — 띠 on " + WL816j2_Verify.Shot(zc, D + "f_zoom_on.png"));
WL816j2_Verify.Strip(new[] { D + "f_zoom_off.png", D + "f_zoom_on.png" }, D + "f_zoom_pair.png");
Object.DestroyImmediate(zgo);
}
L("");
L("5) 사방 확인(위에서) + 섬 확장");
var top = new GameObject("~WL816j2Top"); top.hideFlags = HideFlags.DontSave;
var tc = top.AddComponent<Camera>();
tc.fieldOfView = 55f; tc.nearClipPlane = 0.1f; tc.farClipPlane = 2000f;
tc.clearFlags = CameraClearFlags.Skybox; tc.cullingMask = ~0; tc.depth = -100f;
top.AddComponent<UniversalAdditionalCameraData>().renderPostProcessing = true;
top.transform.position = new Vector3(0f, 42f, -12f); top.transform.LookAt(Vector3.zero);
L(" 위에서 " + WL816j2_Verify.Shot(tc, D + "e_top_allsides.png"));
var islands = Object.FindObjectsByType<FIIsland>(FindObjectsInactive.Exclude, FindObjectsSortMode.None);
FIIsland target = null;
foreach (var isl in islands) if (isl != null && isl.IsUnlocked && isl.gameObject.activeInHierarchy) target = isl;
if (target != null)
{
target.gameObject.SetActive(false); yield return new WaitForSeconds(2.5f);
L(" 1칸 잠금 → " + WLShoreFoam.LastLog);
L(" " + WL816j2_Verify.Shot(tc, D + "e_top_locked.png"));
target.gameObject.SetActive(true); yield return new WaitForSeconds(3.5f);
L(" 다시 열기 → " + WLShoreFoam.LastLog + " · 띠 재생성 " + WL.Look.Farm.WLIslandLook.ShoreFoamRebuilds + "회");
L(" " + WL816j2_Verify.Shot(tc, D + "e_top_unlocked.png"));
}
WL816j2_Verify.Strip(new[] { D + "e_top_allsides.png", D + "e_top_locked.png", D + "e_top_unlocked.png" }, D + "e_expand_steps.png");
// ── 6) 성능 — 띠 on/off 교대 2회 ────────────────────────────
L("");
L("6) 성능 (PD 구도 1280×720 · 60프레임 오프스크린 · 순서 교대 2회)");
var fr = foam != null ? foam.GetComponent<MeshRenderer>() : null;
for (int round = 0; round < 2; round++)
{
if (fr != null) { fr.enabled = false; yield return null; yield return null; }
L(" " + round + "회차 띠 off = " + WL816j2_Verify.Ms(live, 60).ToString("F3") + " ms/frame");
if (fr != null) { fr.enabled = true; yield return null; yield return null; }
L(" " + round + "회차 띠 on = " + WL816j2_Verify.Ms(live, 60).ToString("F3") + " ms/frame");
}
L(" 띠 = 렌더러 1 · 머티리얼 1 · 드로우콜 +1 · " + WLShoreFoam.LastLog);
L(" 화면 통계(에디터) drawCalls=" + UnityEditor.UnityStats.drawCalls + " batches=" + UnityEditor.UnityStats.batches
+ " tris=" + UnityEditor.UnityStats.triangles);
Object.DestroyImmediate(top);
Done();
}
void Done()
{
System.IO.File.WriteAllText("AgentScripts/WL816j2_VERIFY.txt", sb.ToString());
Debug.Log("[816j2 verify]\n" + sb);
}
}