Project_WL/AgentScripts/WL816f_Sweep.cs

228 lines
11 KiB
C#
Raw Permalink Normal View History

[WL-816f] 바닥을 데모와 일치 — 잔디 단색 + 풀 대비↓ + 밀도 2.5→1.8 + 밭 칸 표시 톤↓ (#816) PD 「내가 실행할 때는 전혀 풀밭이 아니야 · 바닥이 완전히 같은 느낌이 나도록」 ■ ① PD 실행 경로 실측 = 훅은 실제로 돈다(버그 아님) InGame 활성 + Level01 Additive 재현: 머티리얼 1,575 렌더러 교체 · 풀 생성 · ReferenceLook on · 콘솔 error 0. 「안 나온다」가 아니라 「데모와 전혀 다르게 보인다」였다. 다만 이 경로에서만 생기는 실측 차이 2가지를 찾아 보고에 적었다 (세이브 파일이 활성 씬 이름으로 갈린다 · RenderSettings 가 InGame 소유). ■ ② 바닥 3요소 (같은 화각·같은 거리 캡처로 판정) 체크무늬 : 잔디 타일 텍스처는 단색(#A0E4A2) — 격자 0. 밭(Soil)의 칸 표시는 게임 기능이라 남기고 **색만** 데모 톤으로(soilTint·soilCheckerContrast). 바닥 색 : #AFEDAE → #A0E4A2 (화면색 평균 데모 #94BE81 ↔ 우리 #9AC37E) 풀 색 : 새 복사본 Farm_Grass_Demo.mat (_DiffuseColor #A6E39F · _ShadowDiffuseColor #95CC8F) → 풀이 바닥과 같은 색으로 깔려 「카펫」이 사라진다(에지% 8.09 → 1.96 · 데모 1.79) 밀도 : 2.5 → 1.8 (6.25 → 3.24 개/㎡) 🔴 Assets/FarmingIsland/** 0줄 · Assets/Script/** 0줄 · 3DPixelArtEnvironment 0줄 · ProjectSettings 0줄 · 전역 URP/품질 0. 되돌리기 = enabled_ = 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-13 05:19:39 +00:00
// WL-816f §2 — PD 경로(InGame 활성 + Level01 Additive) Play 에서 바닥 3요소를 단계별로 바꿔 캡처한다.
// 변형 목록은 AgentScripts/WL816f_PLAN.txt (한 줄 = 한 단계)
// 이름 | 바닥초록HEX | 풀Diffuse | 풀Shadow | 밀도 | 꽃확률 | 풀Scale
// '-' = 그대로.
// 🔴 에셋을 고치지 않는다 — 전부 런타임 인스턴스(Material/Texture2D/SO 복사본).
public static class WL816f_Sweep
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816fSweep");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816fSweep");
go.AddComponent<WL816f_SweepRunner>();
UnityEngine.Debug.Log("[816f sweep] 시작");
}
}
public class WL816f_SweepRunner : UnityEngine.MonoBehaviour
{
const string Dir = "Screenshots_WL/WL816f/";
const int W = 1080, H = 1920;
static System.Text.StringBuilder sb;
UnityEngine.Material groundMat, grassMat, flowerMat;
UnityEngine.Texture2D groundTex;
UnityEngine.Color32 sand = new UnityEngine.Color32(0xCC, 0xCB, 0xB5, 255);
UnityEngine.Color32 dirt = new UnityEngine.Color32(0x80, 0x70, 0x57, 255);
WL.Look.Farm.WLIslandLookSettings rt;
UnityEngine.Vector3 aim;
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
// ── PD 경로 부팅 ────────────────────────────────────────────
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 < 5; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
if (g == null) { L("🔴 WLIslandGrass 없음 — 중단"); Done(); yield break; }
var src = WL.Look.Farm.WLIslandLookSettings.Instance;
rt = UnityEngine.Object.Instantiate(src); // 에셋을 고치지 않기 위한 런타임 복사본
g.cfg = rt;
// 런타임 머티리얼·텍스처
groundTex = new UnityEngine.Texture2D(8, 8, UnityEngine.TextureFormat.RGBA32, false);
groundTex.filterMode = UnityEngine.FilterMode.Point;
groundTex.wrapMode = UnityEngine.TextureWrapMode.Clamp;
SetGround(new UnityEngine.Color32(0xAF, 0xED, 0xAE, 255));
groundMat = new UnityEngine.Material(rt.islandTopMaterial);
groundMat.SetTexture("_BaseMap", groundTex);
if (groundMat.HasProperty("_ShadowBaseMap")) groundMat.SetTexture("_ShadowBaseMap", groundTex);
rt.islandTopMaterial = groundMat;
for (int i = 0; i < rt.scatter.Length; i++)
{
var s = rt.scatter[i];
if (s == null || s.material == null) continue;
if (s.label.Contains("풀")) { grassMat = new UnityEngine.Material(s.material); s.material = grassMat; }
else if (s.label.Contains("꽃")) { flowerMat = new UnityEngine.Material(s.material); s.material = flowerMat; }
}
// 섬 본체 렌더러에 런타임 바닥 머티리얼을 얹는다
int nb = 0;
var rends = UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None);
for (int i = 0; i < rends.Length; i++)
{
var r = rends[i];
if (r == null || r.gameObject.scene.name != "Level01") continue;
if (r.GetComponent<CryingSnow.FarmingIsland.Island>() == null) continue;
var ms = r.sharedMaterials;
bool ch = false;
for (int m = 0; m < ms.Length; m++) if (ms[m] != null && ms[m].name.StartsWith("Farm_IslandTop_Demo")) { ms[m] = groundMat; ch = true; }
if (ch) { r.sharedMaterials = ms; nb++; }
}
L("바닥 머티리얼 런타임 교체 렌더러 " + nb);
// 조준점 = 깨끗한 잔디 타일 중심(막힌 것이 가장 적은 타일)
aim = PickCleanSpot();
L("조준점 " + aim.ToString("F2"));
// ── 변형 실행 ───────────────────────────────────────────────
string planPath = "AgentScripts/WL816f_PLAN.txt";
if (!System.IO.File.Exists(planPath)) { L("PLAN 없음"); Done(); yield break; }
var lines = System.IO.File.ReadAllLines(planPath);
foreach (var raw in lines)
{
var line = raw.Trim();
if (line.Length == 0 || line.StartsWith("#")) continue;
var p = line.Split('|');
for (int i = 0; i < p.Length; i++) p[i] = p[i].Trim();
string name = p[0];
if (p.Length > 1 && p[1] != "-") SetGround(Hex(p[1]));
if (p.Length > 2 && p[2] != "-" && grassMat != null) grassMat.SetColor("_DiffuseColor", HexC(p[2]));
if (p.Length > 3 && p[3] != "-" && grassMat != null) grassMat.SetColor("_ShadowDiffuseColor", HexC(p[3]));
if (p.Length > 4 && p[4] != "-") SetDensity(float.Parse(p[4]));
if (p.Length > 5 && p[5] != "-") SetFlowerProb(float.Parse(p[5]));
if (p.Length > 6 && p[6] != "-") SetGrassScale(float.Parse(p[6]));
g.Rebuild();
yield return null; yield return null;
var cam = GroundCam(aim, 6f);
Shoot(cam, Dir + name + ".png");
L(name + " : 타일 " + WL.Look.Farm.WLIslandGrass.Tiles + " · 인스턴스 " + WL.Look.Farm.WLIslandGrass.Instances
+ " · 밀도 " + WL.Look.Farm.WLIslandGrass.UsedDensity.ToString("F2")
+ " · 삼각형 " + WL.Look.Farm.WLIslandGrass.Triangles
+ " · 그라운드 " + UnityEngine.ColorUtility.ToHtmlStringRGB(groundTex.GetPixel(0, 7))
+ " · 풀D " + (grassMat ? UnityEngine.ColorUtility.ToHtmlStringRGB(grassMat.GetColor("_DiffuseColor")) : "-")
+ " · 풀S " + (grassMat ? UnityEngine.ColorUtility.ToHtmlStringRGB(grassMat.GetColor("_ShadowDiffuseColor")) : "-"));
yield return null;
}
Done();
}
void Done()
{
System.IO.File.WriteAllText("AgentScripts/WL816f_SWEEP.txt", sb.ToString());
UnityEngine.Debug.Log("[816f sweep 완료]\n" + sb.ToString());
}
static void L(string s) { sb.AppendLine(s); }
void SetGround(UnityEngine.Color32 green)
{
// Island01 UV 배치: 위 절반 = 모래/흙, 아래 절반(v<0.5) = 잔디
var px = new UnityEngine.Color32[64];
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++)
px[y * 8 + x] = y < 4 ? green : (x < 4 ? sand : dirt);
groundTex.SetPixels32(px); groundTex.Apply();
}
void SetDensity(float d)
{
for (int i = 0; i < rt.scatter.Length; i++)
{
var s = rt.scatter[i];
if (s == null) continue;
if (s.label.Contains("풀") || s.label.Contains("꽃")) s.density = d;
}
}
void SetFlowerProb(float p)
{
for (int i = 0; i < rt.scatter.Length; i++)
if (rt.scatter[i] != null && rt.scatter[i].label.Contains("꽃")) rt.scatter[i].probability = p;
}
void SetGrassScale(float v)
{
for (int i = 0; i < rt.scatter.Length; i++)
if (rt.scatter[i] != null && (rt.scatter[i].label.Contains("풀") || rt.scatter[i].label.Contains("꽃"))) rt.scatter[i].scale = v;
}
/// <summary>표준 바닥 카메라 — 데모와 같은 화각(직교 · euler 30,60,0).</summary>
public static UnityEngine.Camera GroundCam(UnityEngine.Vector3 spot, float size)
{
var go = UnityEngine.GameObject.Find("~WL816fGroundCam");
if (go == null) { go = new UnityEngine.GameObject("~WL816fGroundCam"); go.hideFlags = UnityEngine.HideFlags.DontSave; }
var c = go.GetComponent<UnityEngine.Camera>();
if (c == null) c = go.AddComponent<UnityEngine.Camera>();
c.orthographic = true; c.orthographicSize = size;
c.nearClipPlane = 0.3f; c.farClipPlane = 500f;
c.clearFlags = UnityEngine.CameraClearFlags.SolidColor;
c.backgroundColor = new UnityEngine.Color(0.192f, 0.302f, 0.475f, 1f);
c.cullingMask = ~0; c.depth = -100f;
var rot = UnityEngine.Quaternion.Euler(30f, 60f, 0f);
go.transform.rotation = rot;
go.transform.position = spot - rot * UnityEngine.Vector3.forward * 60f;
if (go.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>() == null)
go.AddComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>();
return c;
}
public static void Shoot(UnityEngine.Camera cam, string path)
{
if (cam == null) return;
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var rt2 = new UnityEngine.RenderTexture(W, H, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt2.antiAliasing = 1; rt2.Create();
var prevT = cam.targetTexture; var prevA = UnityEngine.RenderTexture.active;
cam.targetTexture = rt2; cam.Render();
UnityEngine.RenderTexture.active = rt2;
var tex = new UnityEngine.Texture2D(W, H, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, W, H), 0, 0); tex.Apply();
System.IO.File.WriteAllBytes(path, UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.RenderTexture.active = prevA; cam.targetTexture = prevT;
UnityEngine.Object.DestroyImmediate(tex);
rt2.Release(); UnityEngine.Object.DestroyImmediate(rt2);
}
static UnityEngine.Color32 Hex(string h)
{
UnityEngine.Color c; UnityEngine.ColorUtility.TryParseHtmlString(h.StartsWith("#") ? h : "#" + h, out c);
return (UnityEngine.Color32)c;
}
static UnityEngine.Color HexC(string h)
{
UnityEngine.Color c; UnityEngine.ColorUtility.TryParseHtmlString(h.StartsWith("#") ? h : "#" + h, out c);
return c;
}
/// <summary>풀이 가장 넓게 깔린 타일의 중심을 고른다(소품·밭이 적은 곳).</summary>
UnityEngine.Vector3 PickCleanSpot()
{
var islands = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(
UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Vector3 best = UnityEngine.Vector3.zero; int bestScore = int.MaxValue;
for (int i = 0; i < islands.Length; i++)
{
var isl = islands[i];
if (isl == null || !isl.IsUnlocked || !isl.gameObject.activeInHierarchy) continue;
int score = isl.GetComponentsInChildren<UnityEngine.Collider>(false).Length;
if (score < bestScore) { bestScore = score; best = isl.transform.position; }
}
return best;
}
}