Project_WL/AgentScripts/WL816r_Play.cs

373 lines
20 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-816r — 섬의 「풀 없는 지형」(길 Road · 밭 Soil · 흙띠/모래) 화면색을 데모 흙 톤에 맞춘다.
// 🔴 풀·바닥 초록(816q 확정)은 건드리지 않는다 — 읽기만 하고 대조에만 쓴다.
// 에셋 무변경(전부 런타임 복제본). Debug.LogError 금지.
public static class WL816r_Play
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816rPlay");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816rPlay");
go.AddComponent<WL816r_Runner>();
}
}
public class WL816r_Runner : UnityEngine.MonoBehaviour
{
static System.Text.StringBuilder sb;
const int W = 1080, H = 1920;
const int TS = 768;
static string Dir = "Screenshots_WL/WL816r";
// 섬 윗면 텍스처 후보 (모래 사분면, 흙 사분면) — 데모 흙 화면색 #A4A88B 목표
static readonly string[][] TopCand = {
new[]{"CCCBB5","807057"}, // 0 = 현행 (전)
new[]{"D4D6BE","C6C8B0"}, // 1 = 데모 흙 톤 + 한 단 어두운 흙
new[]{"D9D8C2","CBCAB4"}, // 2
new[]{"CFD1B9","C1C3AB"}, // 3
};
// 밭(Soil) 마름쌍 목표색 후보 (Soil 은 Toon 이 아니라 Simple Lit — 응답이 다르다)
static readonly string[] SoilCand = { "-", "D4D6BE", "BFC1A9", "AAAC94" };
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
System.IO.Directory.CreateDirectory(Dir);
if (UnityEngine.SceneManagement.SceneManager.sceneCount < 2)
{
var op = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Level01", UnityEngine.SceneManagement.LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
}
for (int i = 0; i < 7; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
if (g == null) { L("WLIslandGrass 없음 — 중단"); Flush(); yield break; }
var b = g.CalculateInstancesBounds();
// ── 그룹 수집 ────────────────────────────────────────────────────────
var roadR = new System.Collections.Generic.List<UnityEngine.Renderer>();
var soilR = new System.Collections.Generic.List<UnityEngine.Renderer>();
var topR = new System.Collections.Generic.List<UnityEngine.Renderer>();
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
var topMat = cfg.islandTopMaterial;
foreach (var r in UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsSortMode.None))
{
if (r == null || !r.enabled) continue;
if (r.GetComponent<CryingSnow.FarmingIsland.Soil>() != null) { soilR.Add(r); continue; }
bool isRoad = false;
for (var t = r.transform; t != null; t = t.parent) if (t.name == "Road") { isRoad = true; break; }
if (isRoad) { roadR.Add(r); continue; }
foreach (var m in r.sharedMaterials) if (m != null && m == topMat) { topR.Add(r); break; }
}
L("그룹 — Road 렌더러=" + roadR.Count + " · Soil=" + soilR.Count + " · 섬윗면(=" + (topMat ? topMat.name : "-") + ")=" + topR.Count);
var roadMats = new System.Collections.Generic.HashSet<UnityEngine.Material>();
foreach (var r in roadR) foreach (var m in r.sharedMaterials) if (m != null) roadMats.Add(m);
foreach (var m in roadMats) L(" Road mat=" + m.name + " sh=" + (m.shader ? m.shader.name : "-") + " Dif=" + Cs(m, "_DiffuseColor") + " Shd=" + Cs(m, "_ShadowDiffuseColor"));
var soilMats = new System.Collections.Generic.HashSet<UnityEngine.Material>();
foreach (var r in soilR) foreach (var m in r.sharedMaterials) if (m != null) soilMats.Add(m);
foreach (var m in soilMats) L(" Soil mat=" + m.name + " sh=" + (m.shader ? m.shader.name : "-") + " color=" + (m.HasProperty("_BaseColor") ? Hex32(m.GetColor("_BaseColor").gamma) : (m.HasProperty("_Color") ? Hex32(m.GetColor("_Color").gamma) : "-")));
var camTop = MakeTop(b.center.x, b.center.z, 6f);
var camPd = MakePd();
// ── ID 마스크 (그룹별 화면 픽셀 확정) ────────────────────────────────
var maskTop = IdMask(camTop, TS, TS, roadR, soilR, topR);
var maskPd = IdMask(camPd, W, H, roadR, soilR, topR);
L("마스크(탑다운) road=" + Count(maskTop, 1) + " soil=" + Count(maskTop, 2) + " top=" + Count(maskTop, 3));
L("마스크(PD) road=" + Count(maskPd, 1) + " soil=" + Count(maskPd, 2) + " top=" + Count(maskPd, 3));
// ── 전 ──────────────────────────────────────────────────────────────
Report("[전]", camTop, TS, TS, maskTop, "before_top");
Report("[전PD]", camPd, W, H, maskPd, "before_pd");
// ── 섬 윗면 텍스처 후보 스윕 (초록 행은 원본 바이트 그대로) ──────────
var srcBytes = System.IO.File.ReadAllBytes("Assets/WL/Look/Farm/Textures/WL_IslandTop_Demo.png");
var origTop = topMat;
UnityEngine.Material bestTopMat = null;
for (int ci = 1; ci < TopCand.Length; ci++)
{
var tex = MakeTopTex(srcBytes, TopCand[ci][0], TopCand[ci][1]);
var m = new UnityEngine.Material(origTop);
m.name = origTop.name + "_r" + ci;
m.SetTexture("_BaseMap", tex); m.SetTexture("_ShadowBaseMap", tex);
SwapMat(topR, origTop, m);
yield return null; yield return null;
L("[윗면 후보" + ci + " 모래#" + TopCand[ci][0] + " 흙#" + TopCand[ci][1] + "] " + Sample(camTop, TS, TS, maskTop, 3));
SwapMat(topR, m, origTop);
if (ci == 1) bestTopMat = m; else { UnityEngine.Object.DestroyImmediate(m); UnityEngine.Object.DestroyImmediate(tex); }
}
// ── 길 후보 (데모 흙 하이라이트/그림자비 그대로) ─────────────────────
// 데모 Terrain.mat 흙 = Highlight(0.8396,0.8304,0.6456) · Shadow(0.3019,0.2276,0.1324)
var roadCand = new[] { "D4D6BE", "D9D8C2", "CFD1B9" };
var roadShadowRatio = new UnityEngine.Color(0.3596f, 0.2741f, 0.2051f, 1f);
var roadOrig = new System.Collections.Generic.List<UnityEngine.Material>(roadMats);
for (int ci = 0; ci < roadCand.Length; ci++)
{
var clones = new System.Collections.Generic.List<UnityEngine.Material>();
foreach (var om in roadOrig)
{
var m = new UnityEngine.Material(om);
var lin = Hex(roadCand[ci]).linear;
m.SetColor("_DiffuseColor", lin);
m.SetColor("_ShadowDiffuseColor", new UnityEngine.Color(lin.r * roadShadowRatio.r, lin.g * roadShadowRatio.g, lin.b * roadShadowRatio.b, 1f));
clones.Add(m);
SwapMat(roadR, om, m);
}
yield return null; yield return null;
L("[길 후보 #" + roadCand[ci] + "] " + Sample(camTop, TS, TS, maskTop, 1));
for (int i = 0; i < roadOrig.Count; i++) { SwapMat(roadR, clones[i], roadOrig[i]); UnityEngine.Object.DestroyImmediate(clones[i]); }
}
// ── 밭 후보 ─────────────────────────────────────────────────────────
for (int ci = 1; ci < SoilCand.Length; ci++)
{
RetoneSoil(SoilCand[ci]);
yield return null; yield return null;
L("[밭 후보 #" + SoilCand[ci] + "] " + Sample(camTop, TS, TS, maskTop, 2) + " · 색종류=" + SoilColorKinds(soilR));
}
RetoneSoil("-"); // 원복
Flush();
}
// ───────────────────────────────── 밭 재도색 (WLIslandLook 과 같은 경로)
static UnityEngine.Color[] s_soilOrig;
static readonly string[] SF = { "soilDryColor1", "soilDryColor2", "soilWetColor1", "soilWetColor2" };
static void RetoneSoil(string hex)
{
var farms = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Farm>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None);
var t = typeof(CryingSnow.FarmingIsland.Farm);
foreach (var farm in farms)
{
var fis = new System.Reflection.FieldInfo[4];
var v = new UnityEngine.Color[4];
bool ok = true;
for (int k = 0; k < 4; k++)
{
fis[k] = t.GetField(SF[k], System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (fis[k] == null) { ok = false; break; }
v[k] = (UnityEngine.Color)fis[k].GetValue(farm);
}
if (!ok) continue;
if (s_soilOrig == null) s_soilOrig = new[] { v[0], v[1], v[2], v[3] };
if (hex == "-") { for (int k = 0; k < 4; k++) fis[k].SetValue(farm, s_soilOrig[k]); }
else
{
var tgt = Hex(hex).linear;
// 마름쌍 중점을 목표로 옮기고, 칸 차이·마름/젖음 차이는 밝기비로 보존
var dm = (s_soilOrig[0] + s_soilOrig[1]) * 0.5f;
var wm = (s_soilOrig[2] + s_soilOrig[3]) * 0.5f;
float dl = UnityEngine.Mathf.Max(0.0001f, Lum(dm)), wl = Lum(wm);
var wt = tgt * (wl / dl);
for (int k = 0; k < 4; k++)
{
var mid = k < 2 ? dm : wm;
var nt = k < 2 ? tgt : wt;
var d = s_soilOrig[k] - mid; // 칸 차이 보존
fis[k].SetValue(farm, new UnityEngine.Color(nt.r + d.r, nt.g + d.g, nt.b + d.b, 1f));
}
}
foreach (var s in farm.GetComponentsInChildren<CryingSnow.FarmingIsland.Soil>(true))
if (s != null) { try { s.Initialize(farm); } catch { } }
}
}
static float Lum(UnityEngine.Color c) { return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b; }
static int SoilColorKinds(System.Collections.Generic.List<UnityEngine.Renderer> soilR)
{
var s = new System.Collections.Generic.HashSet<string>();
foreach (var r in soilR)
{
var m = r.material;
if (m == null) continue;
var c = m.HasProperty("_BaseColor") ? m.GetColor("_BaseColor") : (m.HasProperty("_Color") ? m.GetColor("_Color") : UnityEngine.Color.black);
s.Add(Hex32(c.gamma));
}
return s.Count;
}
// ───────────────────────────────── 텍스처 (초록 행 원본 유지)
static UnityEngine.Texture2D MakeTopTex(byte[] srcBytes, string sandHex, string dirtHex)
{
var tex = new UnityEngine.Texture2D(2, 2, UnityEngine.TextureFormat.RGBA32, false);
UnityEngine.ImageConversion.LoadImage(tex, srcBytes);
tex.filterMode = UnityEngine.FilterMode.Point; tex.wrapMode = UnityEngine.TextureWrapMode.Clamp;
int w = tex.width, h = tex.height;
var px = tex.GetPixels32();
var sand = Hex32c(sandHex); var dirt = Hex32c(dirtHex);
// 원본에서 모래/흙 색을 「값으로」 찾아 바꾼다 (좌표 하드코딩 금지 · 초록은 손대지 않는다)
var sandSrc = new UnityEngine.Color32(0xCC, 0xCB, 0xB5, 255);
var dirtSrc = new UnityEngine.Color32(0x80, 0x70, 0x57, 255);
int ns = 0, nd = 0;
for (int i = 0; i < px.Length; i++)
{
if (Same(px[i], sandSrc)) { px[i] = sand; ns++; }
else if (Same(px[i], dirtSrc)) { px[i] = dirt; nd++; }
}
tex.SetPixels32(px); tex.Apply(false);
L(" 텍스처 " + w + "x" + h + " 모래칸=" + ns + " 흙칸=" + nd + " (초록 무변경)");
return tex;
}
static bool Same(UnityEngine.Color32 a, UnityEngine.Color32 b) { return a.r == b.r && a.g == b.g && a.b == b.b; }
static void SwapMat(System.Collections.Generic.List<UnityEngine.Renderer> rs, UnityEngine.Material from, UnityEngine.Material to)
{
foreach (var r in rs)
{
var ms = r.sharedMaterials; bool hit = false;
for (int i = 0; i < ms.Length; i++) if (ms[i] == from) { ms[i] = to; hit = true; }
if (hit) r.sharedMaterials = ms;
}
}
// ───────────────────────────────── ID 마스크
static byte[] IdMask(UnityEngine.Camera cam, int w, int h,
System.Collections.Generic.List<UnityEngine.Renderer> road,
System.Collections.Generic.List<UnityEngine.Renderer> soil,
System.Collections.Generic.List<UnityEngine.Renderer> top)
{
var unlit = UnityEngine.Shader.Find("Universal Render Pipeline/Unlit");
var saved = new System.Collections.Generic.Dictionary<UnityEngine.Renderer, UnityEngine.Material[]>();
var mats = new System.Collections.Generic.List<UnityEngine.Material>();
System.Action<System.Collections.Generic.List<UnityEngine.Renderer>, UnityEngine.Color> paint = (rs, c) =>
{
var m = new UnityEngine.Material(unlit); m.SetColor("_BaseColor", c); mats.Add(m);
foreach (var r in rs)
{
if (!saved.ContainsKey(r)) saved[r] = r.sharedMaterials;
var arr = new UnityEngine.Material[r.sharedMaterials.Length];
for (int i = 0; i < arr.Length; i++) arr[i] = m;
r.sharedMaterials = arr;
}
};
// 그 밖의 렌더러는 검정
var others = new System.Collections.Generic.List<UnityEngine.Renderer>();
foreach (var r in UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsSortMode.None))
if (r != null && r.enabled && !road.Contains(r) && !soil.Contains(r) && !top.Contains(r)) others.Add(r);
paint(others, UnityEngine.Color.black);
paint(road, new UnityEngine.Color(1f, 0f, 0f, 1f));
paint(soil, new UnityEngine.Color(0f, 1f, 0f, 1f));
paint(top, new UnityEngine.Color(0f, 0f, 1f, 1f));
var tex = Grab(cam, w, h);
var px = tex.GetPixels32();
var mask = new byte[w * h];
for (int i = 0; i < px.Length; i++)
{
var p = px[i];
if (p.r > 150 && p.g < 90 && p.b < 90) mask[i] = 1;
else if (p.g > 150 && p.r < 90 && p.b < 90) mask[i] = 2;
else if (p.b > 150 && p.r < 90 && p.g < 90) mask[i] = 3;
}
UnityEngine.Object.DestroyImmediate(tex);
foreach (var kv in saved) kv.Key.sharedMaterials = kv.Value;
foreach (var m in mats) UnityEngine.Object.DestroyImmediate(m);
return mask;
}
static int Count(byte[] m, byte id) { int n = 0; for (int i = 0; i < m.Length; i++) if (m[i] == id) n++; return n; }
// ───────────────────────────────── 측정
static void Report(string tag, UnityEngine.Camera cam, int w, int h, byte[] mask, string name)
{
var tex = Grab(cam, w, h);
System.IO.File.WriteAllBytes(Dir + "/" + name + ".png", UnityEngine.ImageConversion.EncodeToPNG(tex));
var px = tex.GetPixels32();
L(tag + " 길 " + Stat(px, mask, 1) + " | 밭 " + Stat(px, mask, 2) + " | 섬윗면 " + Stat(px, mask, 3));
UnityEngine.Object.DestroyImmediate(tex);
}
static string Sample(UnityEngine.Camera cam, int w, int h, byte[] mask, byte id)
{
var tex = Grab(cam, w, h);
var px = tex.GetPixels32();
string s = Stat(px, mask, id);
UnityEngine.Object.DestroyImmediate(tex);
return s;
}
static string Stat(UnityEngine.Color32[] px, byte[] mask, byte id)
{
long r = 0, g = 0, b2 = 0; int n = 0;
var uniq = new System.Collections.Generic.Dictionary<int, int>();
for (int i = 0; i < mask.Length && i < px.Length; i++)
{
if (mask[i] != id) continue;
var p = px[i]; r += p.r; g += p.g; b2 += p.b; n++;
int k = (p.r << 16) | (p.g << 8) | p.b;
uniq[k] = uniq.ContainsKey(k) ? uniq[k] + 1 : 1;
}
if (n == 0) return "px0";
var list = new System.Collections.Generic.List<System.Collections.Generic.KeyValuePair<int, int>>(uniq);
list.Sort((A, B) => B.Value.CompareTo(A.Value));
var top = new System.Text.StringBuilder();
for (int i = 0; i < System.Math.Min(3, list.Count); i++) top.Append(string.Format(" #{0:X6}×{1}", list[i].Key, list[i].Value));
return string.Format("n={0} 평균=#{1:X2}{2:X2}{3:X2} 고유={4} 상위:{5}", n, r / n, g / n, b2 / n, uniq.Count, top);
}
// ───────────────────────────────── 유틸
static UnityEngine.Color Hex(string h)
{
return new UnityEngine.Color(System.Convert.ToInt32(h.Substring(0, 2), 16) / 255f,
System.Convert.ToInt32(h.Substring(2, 2), 16) / 255f,
System.Convert.ToInt32(h.Substring(4, 2), 16) / 255f, 1f);
}
static UnityEngine.Color32 Hex32c(string h)
{
return new UnityEngine.Color32((byte)System.Convert.ToInt32(h.Substring(0, 2), 16),
(byte)System.Convert.ToInt32(h.Substring(2, 2), 16),
(byte)System.Convert.ToInt32(h.Substring(4, 2), 16), 255);
}
static string Hex32(UnityEngine.Color c)
{
return string.Format("#{0:X2}{1:X2}{2:X2}", (int)(UnityEngine.Mathf.Clamp01(c.r) * 255), (int)(UnityEngine.Mathf.Clamp01(c.g) * 255), (int)(UnityEngine.Mathf.Clamp01(c.b) * 255));
}
static string Cs(UnityEngine.Material m, string n)
{
if (m == null || !m.HasProperty(n)) return "-";
var c = m.GetColor(n);
return "(" + c.r.ToString("F4") + "," + c.g.ToString("F4") + "," + c.b.ToString("F4") + ")=" + Hex32(c.gamma);
}
static UnityEngine.Camera MakeTop(float cx, float cz, float size)
{
var go = UnityEngine.GameObject.Find("~816rTop"); if (go == null) go = new UnityEngine.GameObject("~816rTop");
var c = go.GetComponent<UnityEngine.Camera>(); if (c == null) c = go.AddComponent<UnityEngine.Camera>();
c.orthographic = true; c.orthographicSize = size; c.nearClipPlane = 0.1f; c.farClipPlane = 200f;
c.transform.rotation = UnityEngine.Quaternion.Euler(90f, 0f, 0f);
c.transform.position = new UnityEngine.Vector3(cx, 60f, cz);
c.enabled = false; return c;
}
static UnityEngine.Camera MakePd()
{
UnityEngine.Vector3 tgt = UnityEngine.Vector3.zero;
var pc = UnityEngine.GameObject.FindGameObjectWithTag("Player"); if (pc != null) tgt = pc.transform.position;
var go = UnityEngine.GameObject.Find("~816rPd"); if (go == null) go = new UnityEngine.GameObject("~816rPd");
var c = go.GetComponent<UnityEngine.Camera>(); if (c == null) c = go.AddComponent<UnityEngine.Camera>();
c.orthographic = false; c.fieldOfView = 60f; c.nearClipPlane = 0.1f; c.farClipPlane = 300f;
c.transform.rotation = UnityEngine.Quaternion.Euler(45f, 45f, 0f);
c.transform.position = tgt - c.transform.forward * 12f;
c.enabled = false; return c;
}
static UnityEngine.Texture2D Grab(UnityEngine.Camera cam, int w, int h)
{
var rt = new UnityEngine.RenderTexture(w, h, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); cam.targetTexture = rt; cam.Render();
var prev = UnityEngine.RenderTexture.active; UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(w, h, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, w, h), 0, 0); tex.Apply();
UnityEngine.RenderTexture.active = prev; cam.targetTexture = null;
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
return tex;
}
static void L(string s) { sb.AppendLine(s); Flush(); }
static void Flush()
{
System.IO.File.WriteAllText("AgentScripts/WL816r_PLAY.txt", sb.ToString());
UnityEngine.Debug.Log("[816r play]\n" + sb.ToString());
}
}