Project_WL/AgentScripts/WL816h_Verify.cs

324 lines
18 KiB
C#
Raw 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-816h — ④ PD 경로에서 적용 확인 · 물 3단 비교 · 성능 전/후 · 🔴 맵별 하늘 격리
// 전부 Play 중 실측. 🔴 Play 중에 에셋(SO·머티리얼)을 고치지 않는다(§9) — 렌더러만 바꾼다.
using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.SceneManagement;
public static class WL816h_Verify
{
public const string Dir = "Screenshots_WL/WL816h/";
public const int W = 1080, H = 1920;
public static void Open()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
Debug.Log("[816h] InGame 열림");
}
public static void Start()
{
var go = GameObject.Find("~WL816hVerify");
if (go != null) Object.DestroyImmediate(go);
go = new GameObject("~WL816hVerify");
go.AddComponent<WL816h_VerifyRunner>();
Debug.Log("[816h] verify 러너 시작");
}
// ── 공용 ────────────────────────────────────────────────────────────
public static string Sky()
{
return "amb=" + RenderSettings.ambientMode + " light=" + F(RenderSettings.ambientLight)
+ " skySrc=" + F(RenderSettings.ambientSkyColor) + " I=" + RenderSettings.ambientIntensity
+ " skybox=" + (RenderSettings.skybox ? RenderSettings.skybox.name : "없음")
+ " fog=" + RenderSettings.fog + " refl=" + RenderSettings.defaultReflectionMode + "/" + RenderSettings.reflectionIntensity
+ " sun=" + (RenderSettings.sun ? RenderSettings.sun.name : "없음");
}
public static string F(Color c) { return "(" + c.r.ToString("F3") + "," + c.g.ToString("F3") + "," + c.b.ToString("F3") + ")"; }
public static Camera Cam(string name, Vector3 pos, Vector3 euler, float fov, CameraClearFlags clear)
{
var go = GameObject.Find(name);
if (go == null) { go = new GameObject(name); go.hideFlags = HideFlags.DontSave; }
var c = go.GetComponent<Camera>(); if (c == null) c = go.AddComponent<Camera>();
c.orthographic = false; c.fieldOfView = fov; c.nearClipPlane = 0.3f; c.farClipPlane = 2000f;
c.clearFlags = clear; c.cullingMask = ~0; c.depth = -100f;
go.transform.position = pos; go.transform.rotation = Quaternion.Euler(euler);
var ac = go.GetComponent<UniversalAdditionalCameraData>();
if (ac == null) ac = go.AddComponent<UniversalAdditionalCameraData>();
ac.renderPostProcessing = true; // 아레나 무드가 걸린 화면 그대로
return c;
}
/// <summary>섬 가장자리 바깥 — 바다가 화면의 절반을 차지한다.</summary>
public static Camera WaterCam()
{ return Cam("~WL816hWaterCam", new Vector3(-16f, 5.5f, -16f), new Vector3(10f, 45f, 0f), 45f, CameraClearFlags.Skybox); }
/// <summary>수평선이 화면에 들어오는 각도 — 하늘과 바다가 함께 보인다.</summary>
public static Camera HorizonCam()
{ return Cam("~WL816hHorizonCam", new Vector3(-14f, 8f, -14f), new Vector3(4f, 45f, 0f), 55f, CameraClearFlags.Skybox); }
public static Camera SkyCam()
{ return Cam("~WL816hSkyCam3", new Vector3(0f, 40f, 0f), new Vector3(-16f, 30f, 0f), 50f, CameraClearFlags.Skybox); }
public static Texture2D Grab(Camera cam)
{
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 tex = new Texture2D(W, H, TextureFormat.RGB24, false);
tex.ReadPixels(new Rect(0, 0, W, H), 0, 0); tex.Apply();
RenderTexture.active = pA; cam.targetTexture = pT;
rt.Release(); Object.DestroyImmediate(rt);
return tex;
}
public static string Shot(Camera cam, string path)
{
if (cam == null) return "카메라 없음";
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var tex = Grab(cam);
System.IO.File.WriteAllBytes(path, tex.EncodeToPNG());
string s = "상10%=" + Band(tex, 0.90f, 1.00f) + " 중=" + Band(tex, 0.45f, 0.55f) + " 하10%=" + Band(tex, 0f, 0.10f);
Object.DestroyImmediate(tex);
return s + " → " + path;
}
public static string Band(Texture2D t, float y0, float y1)
{
int a = Mathf.RoundToInt(y0 * (t.height - 1)), b = Mathf.RoundToInt(y1 * (t.height - 1));
var px = t.GetPixels32();
long r = 0, g = 0, bl = 0; int n = 0;
for (int y = a; y < b; y += 3) for (int x = 0; x < t.width; x += 3)
{ var c = px[y * t.width + x]; r += c.r; g += c.g; bl += c.b; n++; }
if (n == 0) return "-";
return "#" + ((int)(r / n)).ToString("X2") + ((int)(g / n)).ToString("X2") + ((int)(bl / n)).ToString("X2");
}
/// <summary>같은 카메라로 60프레임 오프스크린 렌더 — ms/frame.</summary>
public static float Ms(Camera cam, int frames)
{
var rt = new RenderTexture(W, H, 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;
}
/// <summary>좌/우 두 장을 한 장으로 (PD 판단용).</summary>
public static void Pair(string left, string right, string outPath)
{
var a = Load(left); var b = Load(right);
if (a == null || b == null) return;
int w = a.width + b.width, h = Mathf.Max(a.height, b.height);
var o = new Texture2D(w, h, TextureFormat.RGB24, false);
var fill = new Color32[w * h];
for (int i = 0; i < fill.Length; i++) fill[i] = new Color32(24, 24, 28, 255);
o.SetPixels32(fill);
o.SetPixels32(0, 0, a.width, a.height, a.GetPixels32());
o.SetPixels32(a.width, 0, b.width, b.height, b.GetPixels32());
o.Apply();
System.IO.File.WriteAllBytes(outPath, o.EncodeToPNG());
Object.DestroyImmediate(o); Object.DestroyImmediate(a); Object.DestroyImmediate(b);
}
/// <summary>여러 장을 가로로 이어 붙인다(값 3단 비교용).</summary>
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++)
{ ts[i] = Load(paths[i]); if (ts[i] == null) return; w += ts[i].width; h = Mathf.Max(h, ts[i].height); }
var o = new Texture2D(w, h, TextureFormat.RGB24, false);
var fill = new Color32[w * h];
for (int i = 0; i < fill.Length; i++) fill[i] = new Color32(24, 24, 28, 255);
o.SetPixels32(fill);
int x = 0;
for (int i = 0; i < ts.Length; i++) { o.SetPixels32(x, 0, ts[i].width, ts[i].height, ts[i].GetPixels32()); x += ts[i].width; }
o.Apply();
System.IO.File.WriteAllBytes(outPath, o.EncodeToPNG());
Object.DestroyImmediate(o);
for (int i = 0; i < ts.Length; i++) Object.DestroyImmediate(ts[i]);
}
static Texture2D Load(string p)
{
if (!System.IO.File.Exists(p)) return null;
var t = new Texture2D(2, 2, TextureFormat.RGB24, false);
t.LoadImage(System.IO.File.ReadAllBytes(p));
return t;
}
}
public class WL816h_VerifyRunner : MonoBehaviour
{
static System.Text.StringBuilder sb;
static void L(string s) { sb.AppendLine(s); }
const string MatDir = "Assets/WL/Look/Farm/Materials/";
void Start() { StartCoroutine(Co()); }
IEnumerator Co()
{
sb = new System.Text.StringBuilder();
L("=== WL-816h 적용 확인 (PD 경로 = InGame 활성 + Level01 Additive) ===");
// 활성 씬(InGame)의 **손 안 댄** 하늘 = 나중에 「원래대로」 판정 기준
string pristine = WL816h_Verify.Sky();
L("0) InGame 원래 하늘 : " + pristine);
bool viaGame = false;
try { if (InGameInfo.Ins != null) { InGameInfo.Ins.Load_Map(900); viaGame = true; } }
catch (System.Exception) { }
L(" Load_Map(900) = " + (viaGame ? "성공" : "🔴 로그인 없이는 못 탄다 → LoadSceneAsync(Level01, Additive) 로 대체(816f 와 같음)"));
if (!viaGame)
{
var op = SceneManager.LoadSceneAsync("Level01", LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
}
for (int i = 0; i < 7; i++) yield return new WaitForSeconds(1f);
// ── 1) 훅이 돌았나 ────────────────────────────────────────────
L("");
L("1) 섬 진입 — 활성씬=" + SceneManager.GetActiveScene().name + " sceneCount=" + SceneManager.sceneCount);
L(" IslandLook 렌더러 " + WL.Look.Farm.WLIslandLook.SwappedRenderers
+ " · 물 교체 " + WL.Look.Farm.WLIslandLook.WaterSwapped
+ " · refLook=" + WL.Look.Farm.WLIslandLook.ReferenceLookApplied);
L(" 🔴 SkyScope held=" + WL.Look.Farm.WLSkyScope.Held + " by=" + WL.Look.Farm.WLSkyScope.HeldBy
+ " acquires=" + WL.Look.Farm.WLSkyScope.Acquires + " restores=" + WL.Look.Farm.WLSkyScope.Restores);
L(" 섬 하늘 = " + WL816h_Verify.Sky());
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;
L(" 물 렌더러 = " + (water == null ? "🔴 없음" : water.name + " mat=" + water.sharedMaterial.name
+ " shader=" + water.sharedMaterial.shader.name + " queue=" + water.sharedMaterial.renderQueue));
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("~WL816h")) continue;
if (live == null || c.depth > live.depth) live = c;
}
L(" 보이는 카메라 = " + (live == null ? "없음" : live.name + "(" + live.gameObject.scene.name + ")"));
// ── 2) 물 3단 비교 + 성능 (같은 카메라 · 같은 프레임 구간) ────
L("");
L("2) 물 값 3단 비교 + 성능 (같은 카메라 1080×1920 · 60프레임 오프스크린)");
string[] names = { null, "Farm_Water_WL_A", "Farm_Water_WL_B", "Farm_Water_WL_C" };
string[] labels = { "지금(FI SimpleWater)", "A=WL_Water_Ocean 원본", "B=데모 톤", "C=차분" };
string[] liveShots = new string[4];
string[] horizonShots = new string[4];
var fiMat = water != null ? water.sharedMaterial : null; // 지금 걸려 있는 것(= WL 워터 B)
// 「지금」 = FarmingIsland 물(우리 복사본 Farm_SimpleWater_Demo)
Material now = UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + "Farm_SimpleWater_Demo.mat");
for (int i = 0; i < names.Length; i++)
{
Material m = names[i] == null ? now : UnityEditor.AssetDatabase.LoadAssetAtPath<Material>(MatDir + names[i] + ".mat");
if (m == null) { L(" 🔴 머티리얼 없음 " + (names[i] ?? "Farm_SimpleWater_Demo")); continue; }
if (water != null) water.sharedMaterial = m;
yield return null; yield return null;
yield return new WaitForSeconds(0.4f);
string tag = names[i] == null ? "now" : names[i].Substring(names[i].Length - 1).ToLower();
liveShots[i] = WL816h_Verify.Dir + "w_" + i + "_" + tag + "_live.png";
horizonShots[i] = WL816h_Verify.Dir + "w_" + i + "_" + tag + "_horizon.png";
string s1 = WL816h_Verify.Shot(live, liveShots[i]);
string s2 = WL816h_Verify.Shot(WL816h_Verify.HorizonCam(), horizonShots[i]);
float ms = WL816h_Verify.Ms(live, 60);
L(" [" + labels[i] + "] " + ms.ToString("F3") + " ms/frame");
L(" 게임화면 " + s1);
L(" 수평선 " + s2);
}
// 채택값으로 되돌린다(SO 가 가리키는 것)
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
if (water != null && cfg != null && cfg.waterTo != null) water.sharedMaterial = cfg.waterTo;
yield return null;
L(" → 채택 = " + (cfg != null && cfg.waterTo != null ? cfg.waterTo.name : "?"));
// ── 2-b) 성능 전/후 — 순서 바꿔 두 번씩 (측정 순서 편향 제거) ──
L("");
L("2-b) 성능 전/후 (같은 카메라 · 60프레임 × 2회 · 순서 교대)");
var adopted = cfg != null ? cfg.waterTo : null;
for (int round = 0; round < 2; round++)
{
foreach (var pair in new[] { new object[] { "전(FI SimpleWater)", now }, new object[] { "후(" + (adopted ? adopted.name : "?") + ")", adopted } })
{
var m = pair[1] as Material; if (m == null) continue;
if (water != null) water.sharedMaterial = m;
yield return null; yield return null;
L(" " + round + "회차 " + (string)pair[0] + " = " + WL816h_Verify.Ms(live, 60).ToString("F3") + " ms/frame");
}
}
if (water != null && adopted != null) water.sharedMaterial = adopted;
yield return null;
WL816h_Verify.Strip(liveShots, WL816h_Verify.Dir + "e_water_steps.png");
WL816h_Verify.Strip(horizonShots, WL816h_Verify.Dir + "e_water_steps_horizon.png");
L(" 3단 비교 한 장 = " + WL816h_Verify.Dir + "e_water_steps.png · e_water_steps_horizon.png");
// ── 3) 적용 후 대표 캡처 ──────────────────────────────────────
L("");
L("3) 적용 후");
L(" 섬 게임화면 = " + WL816h_Verify.Shot(live, WL816h_Verify.Dir + "b_after_island.png"));
L(" 수평선 = " + WL816h_Verify.Shot(WL816h_Verify.HorizonCam(), WL816h_Verify.Dir + "b_after_horizon.png"));
L(" 물 근접 = " + WL816h_Verify.Shot(WL816h_Verify.WaterCam(), WL816h_Verify.Dir + "b_after_water.png"));
L(" 하늘만 = " + WL816h_Verify.Shot(WL816h_Verify.SkyCam(), WL816h_Verify.Dir + "b_after_sky.png"));
WL816h_Verify.Pair(WL816h_Verify.Dir + "a_now_island.png", WL816h_Verify.Dir + "b_after_island.png",
WL816h_Verify.Dir + "c_side_by_side.png");
L(" 좌우 비교 = " + WL816h_Verify.Dir + "c_side_by_side.png (좌 지금 / 우 적용 후)");
// ── 4) 🔴 맵별 하늘 격리 ──────────────────────────────────────
L("");
L("4) 🔴 맵별 하늘 격리 — 섬 → 던전 → 전투 맵");
L(" [섬] " + WL816h_Verify.Sky());
string islandSky = WL816h_Verify.Shot(WL816h_Verify.SkyCam(), WL816h_Verify.Dir + "f1_island_sky.png");
L(" [섬] 하늘 = " + islandSky);
// 섬 → 던전 (같은 하늘이어야 한다)
var u1 = SceneManager.UnloadSceneAsync("Level01");
while (u1 != null && !u1.isDone) yield return null;
var d1 = SceneManager.LoadSceneAsync("WL_Dungeon01", LoadSceneMode.Additive);
while (d1 != null && !d1.isDone) yield return null;
yield return new WaitForSeconds(2f);
L(" [던전] SkyScope held=" + WL.Look.Farm.WLSkyScope.Held + " by=" + WL.Look.Farm.WLSkyScope.HeldBy
+ " acquires=" + WL.Look.Farm.WLSkyScope.Acquires + " restores=" + WL.Look.Farm.WLSkyScope.Restores);
L(" [던전] " + WL816h_Verify.Sky());
L(" [던전] 하늘 = " + WL816h_Verify.Shot(WL816h_Verify.SkyCam(), WL816h_Verify.Dir + "f2_dungeon_sky.png"));
// 던전 → 전투 맵 (원래대로여야 한다)
var u2 = SceneManager.UnloadSceneAsync("WL_Dungeon01");
while (u2 != null && !u2.isDone) yield return null;
var b1 = SceneManager.LoadSceneAsync("Map_C01", LoadSceneMode.Additive);
while (b1 != null && !b1.isDone) yield return null;
yield return new WaitForSeconds(2f);
L(" [전투] SkyScope held=" + WL.Look.Farm.WLSkyScope.Held
+ " acquires=" + WL.Look.Farm.WLSkyScope.Acquires + " restores=" + WL.Look.Farm.WLSkyScope.Restores);
string after = WL816h_Verify.Sky();
L(" [전투] " + after);
L(" [전투] 하늘 = " + WL816h_Verify.Shot(WL816h_Verify.SkyCam(), WL816h_Verify.Dir + "f3_battle_sky.png"));
L(" 🔴 원래대로인가 = " + (after == pristine ? "예 — 글자 하나까지 같다(오염 0)" : "🔴 아니오\n 전 : " + pristine + "\n 후 : " + after));
WL816h_Verify.Strip(new string[] {
WL816h_Verify.Dir + "f1_island_sky.png",
WL816h_Verify.Dir + "f2_dungeon_sky.png",
WL816h_Verify.Dir + "f3_battle_sky.png" },
WL816h_Verify.Dir + "f_sky_isolation.png");
L(" 한 장 = " + WL816h_Verify.Dir + "f_sky_isolation.png (좌 섬 / 중 던전 / 우 전투 맵)");
System.IO.File.WriteAllText("AgentScripts/WL816h_VERIFY.txt", sb.ToString());
Debug.Log("[816h verify 완료]\n" + sb);
}
}