Merge branch 'wl/gameplay/WL-816f-ground-match'

This commit is contained in:
깃 관리자 2026-09-13 14:27:51 +09:00
commit 840655c06c
16 changed files with 1499 additions and 3 deletions

View File

@ -0,0 +1,58 @@
// WL-816f — 채택값을 에셋에 굽는다(에디트 모드).
// ① 풀 머티리얼 복사본 `Farm_Grass_Demo.mat`(데모 `Instanced_Grass` 0줄 · 복사본만)
// ② SO: 풀 머티리얼 교체 · 밀도 2.5 → 1.8
public static class WL816f_Build
{
const string SoPath = "Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset";
const string MatPath = "Assets/WL/Look/Farm/Materials/Farm_Grass_Demo.mat";
public static void Run()
{
var sb = new System.Text.StringBuilder();
var cfg = UnityEditor.AssetDatabase.LoadAssetAtPath<WL.Look.Farm.WLIslandLookSettings>(SoPath);
if (cfg == null) { UnityEngine.Debug.Log("SO 없음"); return; }
// ① 풀 머티리얼 복사본
UnityEngine.Material grassSrc = null;
for (int i = 0; i < cfg.scatter.Length; i++)
if (cfg.scatter[i] != null && cfg.scatter[i].label.Contains("풀")) grassSrc = cfg.scatter[i].material;
if (grassSrc == null) { UnityEngine.Debug.Log("풀 머티리얼 없음"); return; }
var mat = UnityEditor.AssetDatabase.LoadAssetAtPath<UnityEngine.Material>(MatPath);
if (mat == null)
{
mat = new UnityEngine.Material(grassSrc);
mat.name = "Farm_Grass_Demo";
UnityEditor.AssetDatabase.CreateAsset(mat, MatPath);
sb.AppendLine("새 머티리얼 " + MatPath + " (원본 " + grassSrc.name + " 복사)");
}
else sb.AppendLine("기존 머티리얼 갱신 " + MatPath);
// 816f 실측 채택값 — 풀 화면색 ≈ 바닥 화면색(데모와 같은 「안 튀는 풀」)
mat.SetColor("_DiffuseColor", Hex("#A6E39F"));
mat.SetColor("_ShadowDiffuseColor", Hex("#95CC8F"));
UnityEditor.EditorUtility.SetDirty(mat);
// ② SO
for (int i = 0; i < cfg.scatter.Length; i++)
{
var s = cfg.scatter[i];
if (s == null) continue;
if (s.label.Contains("풀")) { s.material = mat; s.density = 1.8f; sb.AppendLine("풀: mat→Farm_Grass_Demo · density 2.5→1.8"); }
else if (s.label.Contains("꽃")) { s.density = 1.8f; sb.AppendLine("꽃: density 2.5→1.8 (풀과 같은 층)"); }
}
UnityEditor.EditorUtility.SetDirty(cfg);
UnityEditor.AssetDatabase.SaveAssets();
UnityEditor.AssetDatabase.Refresh();
sb.AppendLine("soilToneEnabled=" + cfg.soilToneEnabled + " soilTint=" + cfg.soilTint + " soilCheckerContrast=" + cfg.soilCheckerContrast);
sb.AppendLine("islandTopMaterial=" + (cfg.islandTopMaterial ? cfg.islandTopMaterial.name : "없음")
+ " _BaseMap=" + (cfg.islandTopMaterial && cfg.islandTopMaterial.GetTexture("_BaseMap")
? cfg.islandTopMaterial.GetTexture("_BaseMap").name : "없음"));
System.IO.File.WriteAllText("AgentScripts/WL816f_BUILD.txt", sb.ToString());
UnityEngine.Debug.Log(sb.ToString());
}
static UnityEngine.Color Hex(string h)
{ UnityEngine.Color c; UnityEngine.ColorUtility.TryParseHtmlString(h, out c); return c; }
}

128
AgentScripts/WL816f_Cmp.cs Normal file
View File

@ -0,0 +1,128 @@
// WL-816f §2 — 「같은 화각·같은 거리」 바닥 비교용 렌더.
// 표준 바닥 카메라 = 직교 size 5 · euler(30,60,0) · 1080×1920 (814r/816a/816e 화각과 같은 방향)
// 데모는 에디트 모드에서 인스턴싱을 직접 발행해 그린다(데모 씬 Play 금지 · §9).
public static class WL816f_Cmp
{
public const string Dir = "Screenshots_WL/WL816f/";
public const int W = 1080, H = 1920;
// ── 데모 바닥 ──────────────────────────────────────────────────────
public static void Demo()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/3DPixelArtEnvironment/Demo/Demo.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
var sb = new System.Text.StringBuilder();
// 평평한 풀밭 지점을 찾는다(경사가 작고 1층 스플랫이 지배적인 곳)
var terr = UnityEngine.Object.FindFirstObjectByType<UnityEngine.Terrain>();
UnityEngine.Vector3 spot = new UnityEngine.Vector3(130f, 0f, 90f);
if (terr != null)
{
var td = terr.terrainData;
float best = 999f;
for (float x = 40f; x < 180f; x += 10f)
for (float z = 40f; z < 180f; z += 10f)
{
float u = x / td.size.x, v = z / td.size.z;
var nrm = td.GetInterpolatedNormal(u, v);
float slope = 1f - nrm.y;
var c = td.alphamapTextures[0].GetPixelBilinear(u, v);
if (c.r < 0.9f) continue; // 1층(풀)이 지배적인 곳만
if (slope < best) { best = slope; spot = new UnityEngine.Vector3(x, td.GetInterpolatedHeight(u, v) + terr.transform.position.y, z); }
}
sb.AppendLine("데모 바닥 지점 " + spot.ToString("F2") + " slope=" + best.ToString("F4"));
}
var cam = GroundCam(spot, 6f);
int n = WL816f_Inst.Publish(sb);
Shoot(cam, Dir + "x_demo_ground.png", sb);
// 넓은 화각(ortho 10)도 하나
cam = GroundCam(spot, 10f);
WL816f_Inst.Publish(sb);
Shoot(cam, Dir + "x_demo_wide.png", sb);
sb.AppendLine("데모 인스턴싱 구성 " + n);
System.IO.File.WriteAllText("AgentScripts/WL816f_DEMO.txt", sb.ToString());
UnityEngine.Debug.Log(sb.ToString());
}
/// <summary>표준 바닥 카메라 — 지점 spot 을 데모 화각으로 본다.</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, System.Text.StringBuilder sb)
{
if (cam == null) { if (sb != null) sb.AppendLine("카메라 없음 " + path); return; }
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var rt = new UnityEngine.RenderTexture(W, H, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var prevT = cam.targetTexture; var prevA = UnityEngine.RenderTexture.active;
cam.targetTexture = rt; cam.Render();
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();
System.IO.File.WriteAllBytes(path, UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.RenderTexture.active = prevA; cam.targetTexture = prevT;
UnityEngine.Object.DestroyImmediate(tex);
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
WL816f_Inst.Free();
if (sb != null) sb.AppendLine("캡처 " + path);
}
}
/// <summary>에디트 모드에서 InstancesBehaviour 가 그릴 것을 이번 프레임에 직접 발행한다(Play 없이).</summary>
public static class WL816f_Inst
{
static readonly System.Collections.Generic.List<Environment.Instancing.InstancingConfiguration> s_pending
= new System.Collections.Generic.List<Environment.Instancing.InstancingConfiguration>();
public static int Publish(System.Text.StringBuilder sb)
{
int n = 0; long inst = 0;
var bs = UnityEngine.Object.FindObjectsByType<Environment.Instancing.InstancesBehaviour>(
UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
for (int i = 0; i < bs.Length; i++)
{
var b = bs[i];
System.Collections.Generic.Dictionary<Environment.Instancing.InstancingSettings,
System.Collections.Generic.List<Environment.Instancing.InstanceData>> data = null;
try { data = b.GetInstanceData(); } catch { continue; }
if (data == null) continue;
var bounds = b.CalculateInstancesBounds();
var l2w = b.transform.localToWorldMatrix; l2w.m03 = 0; l2w.m13 = 0; l2w.m23 = 0;
foreach (var kv in data)
{
if (kv.Value == null || kv.Value.Count == 0) continue;
var cfg = new Environment.Instancing.InstancingConfiguration(kv.Key, kv.Value, "_InstanceData");
cfg.MaterialPropertyBlock.SetMatrix("_LocalToWorld", l2w);
UnityEngine.Graphics.DrawMeshInstancedIndirect(cfg.Mesh, 0, cfg.Material, bounds, cfg.CommandBuffer, 0,
cfg.MaterialPropertyBlock, UnityEngine.Rendering.ShadowCastingMode.Off, false, 0);
n++; inst += kv.Value.Count;
s_pending.Add(cfg);
}
}
if (sb != null) sb.AppendLine(" 인스턴스 " + inst + " (구성 " + n + ")");
return n;
}
public static void Free()
{
for (int i = 0; i < s_pending.Count; i++) s_pending[i].FreeMemory();
s_pending.Clear();
}
}

View File

@ -0,0 +1,87 @@
// WL-816f — SO 토글(되돌리기 검증용)
public static class WL816f_Ctl
{
const string kSo = "Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset";
public static void Off() { Set(0); }
public static void On() { Set(1); }
static void Set(int en)
{
var so = UnityEditor.AssetDatabase.LoadAssetAtPath<WL.Look.Farm.WLIslandLookSettings>(kSo);
so.enabled_ = en;
UnityEditor.EditorUtility.SetDirty(so);
UnityEditor.AssetDatabase.SaveAssets();
WL.Look.Farm.WLIslandLookSettings.Invalidate();
UnityEngine.Debug.Log("[816f Ctl] enabled_=" + en);
}
/// <summary>Play 중: 되돌리기 상태 실측 + 캡처</summary>
public static void Rollback()
{
var go = UnityEngine.GameObject.Find("~WL816fRb");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816fRb");
go.AddComponent<WL816f_RbRunner>();
}
}
public class WL816f_RbRunner : UnityEngine.MonoBehaviour
{
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
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 < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
var sb = new System.Text.StringBuilder();
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
sb.AppendLine("enabled_=" + (cfg == null ? -1 : cfg.enabled_));
sb.AppendLine("머티리얼 교체 " + WL.Look.Farm.WLIslandLook.SwappedRenderers + " · 풀 " + WL.Look.Farm.WLIslandGrass.Instances
+ " · refLook " + WL.Look.Arena.WLReferenceLook.IsApplied
+ " · Soil톤 farm " + WL.Look.Farm.WLIslandLook.SoilFarmsToned);
sb.AppendLine("ambient " + UnityEngine.RenderSettings.ambientMode + " " + UnityEngine.RenderSettings.ambientLight
+ " skybox=" + (UnityEngine.RenderSettings.skybox ? UnityEngine.RenderSettings.skybox.name : "없음"));
var soils = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Soil>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
string c0 = "-", c1 = "-";
for (int i = 0; i < soils.Length && i < 2; i++)
{
var r = soils[i].GetComponentInChildren<UnityEngine.Renderer>(true);
if (r == null) continue;
if (i == 0) c0 = UnityEngine.ColorUtility.ToHtmlStringRGB(r.material.color);
else c1 = UnityEngine.ColorUtility.ToHtmlStringRGB(r.material.color);
}
sb.AppendLine("Soil 색 [0]=" + c0 + " [1]=" + c1);
var rends = UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
var shaders = new System.Collections.Generic.Dictionary<string, int>();
for (int i = 0; i < rends.Length; i++)
{
if (rends[i].gameObject.scene.name != "Level01") continue;
var m = rends[i].sharedMaterial; if (m == null) continue;
var k = m.shader.name;
shaders[k] = shaders.ContainsKey(k) ? shaders[k] + 1 : 1;
}
foreach (var kv in shaders) sb.AppendLine("shader " + kv.Key + " ×" + kv.Value);
var cams = UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Camera top = null;
for (int i = 0; i < cams.Length; i++) { var c = cams[i]; if (!c.enabled || c.targetTexture != null) continue; if (top == null || c.depth > top.depth) top = c; }
if (top != null)
{
System.IO.Directory.CreateDirectory("Screenshots_WL/WL816f");
var rt = new UnityEngine.RenderTexture(1080, 1920, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); var pt = top.targetTexture; top.targetTexture = rt; top.Render();
UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(1080, 1920, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, 1080, 1920), 0, 0); tex.Apply();
System.IO.File.WriteAllBytes("Screenshots_WL/WL816f/z_rollback.png", UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.RenderTexture.active = null; top.targetTexture = pt;
UnityEngine.Object.DestroyImmediate(tex); rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
sb.AppendLine("캡처 Screenshots_WL/WL816f/z_rollback.png");
}
System.IO.File.WriteAllText("AgentScripts/WL816f_ROLLBACK.txt", sb.ToString());
UnityEngine.Debug.Log("[816f rollback]\n" + sb.ToString());
}
}

View File

@ -0,0 +1,114 @@
// WL-816f — 마무리 실측: ⓐ성능 전(밀도 2.5)/후(1.8) ⓑ밭 칸 표시 유지 ⓒ확장 시 새 타일에 풀
public static class WL816f_Final
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816fFinal");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816fFinal");
go.AddComponent<WL816f_FinalRunner>();
}
}
public class WL816f_FinalRunner : UnityEngine.MonoBehaviour
{
static System.Text.StringBuilder sb;
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
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 < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
var src = WL.Look.Farm.WLIslandLookSettings.Instance;
var rt = UnityEngine.Object.Instantiate(src); // 에셋 무변경
g.cfg = rt;
var cam = LiveCam();
// ── ⓐ 성능: 816e(2.5) vs 816f(1.8) vs 풀 없음 ───────────────
L("[성능] 같은 카메라(" + (cam ? cam.name : "?") + ") · 1080×1920 오프스크린 60프레임 평균");
float[] dens = { 2.5f, 1.8f };
for (int k = 0; k < dens.Length; k++)
{
SetD(rt, dens[k]); g.Rebuild(); yield return null; yield return null;
float ms = Bench(cam, 60);
L(" 밀도 " + dens[k].ToString("F2") + " : 인스턴스 " + WL.Look.Farm.WLIslandGrass.Instances
+ " · 삼각형 " + WL.Look.Farm.WLIslandGrass.Triangles
+ " · 드로우콜 +" + WL.Look.Farm.WLIslandGrass.DrawnConfigs
+ " · " + ms.ToString("F3") + " ms/frame (" + (1000f / ms).ToString("F0") + " fps 환산)");
}
g.enabled = false; yield return null; yield return null;
float off = Bench(cam, 60);
L(" 풀 없음 : " + off.ToString("F3") + " ms/frame");
g.enabled = true; SetD(rt, 1.8f); g.Rebuild(); yield return null;
// ── ⓑ 밭 칸 표시가 남아 있나(두 색이 서로 달라야 한다) ──────
L("");
var soils = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Soil>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
var seen = new System.Collections.Generic.Dictionary<string, int>();
for (int i = 0; i < soils.Length; i++)
{
var r = soils[i].GetComponentInChildren<UnityEngine.Renderer>(true);
if (r == null) continue;
var h = UnityEngine.ColorUtility.ToHtmlStringRGB(r.material.color);
seen[h] = seen.ContainsKey(h) ? seen[h] + 1 : 1;
}
L("[밭 칸 표시] Soil " + soils.Length + "개의 색 종류 " + seen.Count + " (2 여야 칸 구분이 살아 있다)");
foreach (var kv in seen) L(" " + kv.Key + " ×" + kv.Value);
// ── ⓒ 확장: 타일 하나를 잠갔다 다시 열어 풀이 따라오는지 ────
L("");
var islands = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
CryingSnow.FarmingIsland.Island target = null;
for (int i = 0; i < islands.Length; i++)
if (islands[i].IsUnlocked && islands[i].gameObject.activeInHierarchy && islands[i].transform.position != UnityEngine.Vector3.zero) { target = islands[i]; break; }
int before = WL.Look.Farm.WLIslandGrass.Tiles;
if (target != null)
{
target.gameObject.SetActive(false);
yield return new UnityEngine.WaitForSeconds(2.5f);
int mid = WL.Look.Farm.WLIslandGrass.Tiles;
target.gameObject.SetActive(true);
yield return new UnityEngine.WaitForSeconds(3f);
int after = WL.Look.Farm.WLIslandGrass.Tiles;
L("[확장] 풀 깔린 타일 " + before + " → (타일 1개 잠금) " + mid + " → (다시 열기) " + after
+ (after == before ? " ✔ 새로 열린 타일에 풀이 자동으로 깔린다" : " 🔴 복귀 실패"));
}
else L("[확장] 대상 타일 없음(미확인)");
System.IO.File.WriteAllText("AgentScripts/WL816f_FINAL.txt", sb.ToString());
UnityEngine.Debug.Log("[816f final]\n" + sb.ToString());
}
static void L(string s) { sb.AppendLine(s); }
static void SetD(WL.Look.Farm.WLIslandLookSettings c, float d)
{
for (int i = 0; i < c.scatter.Length; i++)
if (c.scatter[i] != null && (c.scatter[i].label.Contains("풀") || c.scatter[i].label.Contains("꽃"))) c.scatter[i].density = d;
}
static UnityEngine.Camera LiveCam()
{
var cams = UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Camera t = null;
for (int i = 0; i < cams.Length; i++) { var c = cams[i]; if (!c.enabled || c.targetTexture != null) continue; if (t == null || c.depth > t.depth) t = c; }
return t;
}
static float Bench(UnityEngine.Camera cam, int frames)
{
if (cam == null) return -1f;
var rt = new UnityEngine.RenderTexture(1080, 1920, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.Create(); var prev = 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 = prev;
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
return (float)sw.Elapsed.TotalMilliseconds / frames;
}
}

View File

@ -0,0 +1,116 @@
// WL-816f §2 — 데모 바닥 vs 우리 바닥 실측(에디트 모드 · 데모 씬 Play 안 함)
// ① 풀 메시의 월드 크기 ② 격자 간격 ③ 풀 머티리얼 색 ④ 바닥 머티리얼 색
public static class WL816f_Ground
{
public static void Run()
{
var sb = new System.Text.StringBuilder();
// ── 데모 ────────────────────────────────────────────────────
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/3DPixelArtEnvironment/Demo/Demo.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
sb.AppendLine("=== 데모 (Demo.unity) ===");
var tibs = UnityEngine.Object.FindObjectsByType<Environment.Instancing.TerrainInstancesBehaviour>(
UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None);
foreach (var t in tibs)
{
sb.AppendLine("TIB " + t.name + " lossyScale=" + t.transform.lossyScale.ToString("F3")
+ " posVar=" + t.PositionVariance + " scaleVar=" + t.ScaleVariance);
var terr = t.GetComponent<UnityEngine.Terrain>();
if (terr != null) sb.AppendLine(" terrain size=" + terr.terrainData.size.ToString("F1")
+ " alphamapTex=" + terr.terrainData.alphamapTextureCount
+ " mat=" + (terr.materialTemplate ? terr.materialTemplate.name + " (" + terr.materialTemplate.shader.name + ")" : "없음"));
Layer(sb, "1층", t.FirstLayer, t.transform.lossyScale);
Layer(sb, "2층", t.SecondLayer, t.transform.lossyScale);
Layer(sb, "3층", t.ThirdLayer, t.transform.lossyScale);
Layer(sb, "4층", t.FourthLayer, t.transform.lossyScale);
if (terr != null && terr.materialTemplate != null) MatDump(sb, " 지형mat ", terr.materialTemplate);
}
// 데모 지형 스플랫 — 1층(풀)이 차지하는 면적 비율
foreach (var t in tibs)
{
var terr = t.GetComponent<UnityEngine.Terrain>();
if (terr == null || terr.terrainData.alphamapTextureCount == 0) continue;
var tex = terr.terrainData.alphamapTextures[0];
var px = tex.GetPixels();
int[] cnt = new int[4];
for (int i = 0; i < px.Length; i++)
{
float[] v = { px[i].r, px[i].g, px[i].b, px[i].a };
int bi = 0; for (int k = 1; k < 4; k++) if (v[k] > v[bi]) bi = k;
cnt[bi]++;
}
sb.AppendLine(" 스플랫 지배비율(" + tex.width + "x" + tex.height + "): 1층 " + (100f * cnt[0] / px.Length).ToString("F1")
+ "% · 2층 " + (100f * cnt[1] / px.Length).ToString("F1") + "% · 3층 " + (100f * cnt[2] / px.Length).ToString("F1")
+ "% · 4층 " + (100f * cnt[3] / px.Length).ToString("F1") + "%");
}
// ── 우리 ────────────────────────────────────────────────────
sb.AppendLine();
sb.AppendLine("=== 우리 (WLIslandLookSettings) ===");
var cfg = UnityEditor.AssetDatabase.LoadAssetAtPath<WL.Look.Farm.WLIslandLookSettings>(
"Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset");
sb.AppendLine("posVar=" + cfg.positionVariance + " scaleVar=" + cfg.scaleVariance + " edgeInset=" + cfg.edgeInset);
for (int i = 0; i < cfg.scatter.Length; i++)
{
var s = cfg.scatter[i];
var b = s.mesh != null ? s.mesh.bounds.size : UnityEngine.Vector3.zero;
sb.AppendLine(" [" + s.label + "] en=" + s.enabled_ + " mesh=" + (s.mesh ? s.mesh.name : "없음")
+ " bounds=" + b.ToString("F3") + " scale=" + s.scale
+ " → 월드 " + (b * s.scale).ToString("F3")
+ " · prob=" + s.probability + " density=" + s.density + "(=" + (s.density * s.density).ToString("F2") + "개/㎡ · 격자 "
+ (1f / s.density).ToString("F3") + "m)"
+ " mat=" + (s.material ? s.material.name : "없음"));
if (s.material != null) MatDump(sb, " ", s.material);
}
sb.AppendLine("islandTopMaterial = " + (cfg.islandTopMaterial ? cfg.islandTopMaterial.name : "없음"));
if (cfg.islandTopMaterial != null) MatDump(sb, " ", cfg.islandTopMaterial);
for (int i = 0; i < cfg.materialRemap.Length; i++)
{
var e = cfg.materialRemap[i];
sb.AppendLine(" remap en=" + e.enabled_ + " " + (e.from ? e.from.name : "-") + " → " + (e.to ? e.to.name : "-")
+ (e.to != null && e.to.HasProperty("_BaseMap") && e.to.GetTexture("_BaseMap") != null
? " 🔴텍스처 " + e.to.GetTexture("_BaseMap").name : " (텍스처 없음)"));
}
System.IO.File.WriteAllText("AgentScripts/WL816f_GROUND.txt", sb.ToString());
UnityEngine.Debug.Log(sb.ToString());
}
static void Layer(System.Text.StringBuilder sb, string name,
Environment.Instancing.TerrainInstancesBehaviour.TerrainInstancingInput inp, UnityEngine.Vector3 lossy)
{
if (inp == null || inp.Settings == null || inp.Settings.Length == 0) { sb.AppendLine(" " + name + " (없음)"); return; }
sb.AppendLine(" " + name + " Density=" + inp.Density + " (=" + (inp.Density * inp.Density).ToString("F2")
+ "개/㎡ · 격자 " + (1f / inp.Density).ToString("F3") + "m)");
foreach (var s in inp.Settings)
{
var b = s.Mesh != null ? s.Mesh.bounds.size : UnityEngine.Vector3.zero;
var w = new UnityEngine.Vector3(b.x * s.Scale * lossy.x, b.y * s.Scale * lossy.y, b.z * s.Scale * lossy.z);
sb.AppendLine(" mesh=" + (s.Mesh ? s.Mesh.name + "(tri " + (s.Mesh.triangles.Length / 3) + ")" : "없음")
+ " bounds=" + b.ToString("F3") + " Scale=" + s.Scale + " → 월드 " + w.ToString("F3")
+ " prob=" + s.Probability + " nOff=" + s.NormalOffset
+ " mat=" + (s.Material ? s.Material.name : "없음"));
if (s.Material != null) MatDump(sb, " ", s.Material);
}
}
static void MatDump(System.Text.StringBuilder sb, string pad, UnityEngine.Material m)
{
string[] cols = { "_DiffuseColor", "_ShadowDiffuseColor", "_BaseColor", "_Color", "_TopColor", "_BottomColor", "_TipColor" };
string[] flts = { "_Shades", "_Brightness", "_MinimumDarkness", "_AmbientStrength", "_Cull", "_WindStrength", "_WindDensity" };
var s = pad + "shader=" + m.shader.name;
foreach (var c in cols) if (m.HasProperty(c)) s += " " + c + "=" + Hex(m.GetColor(c));
foreach (var f in flts) if (m.HasProperty(f)) s += " " + f + "=" + m.GetFloat(f).ToString("F3");
if (m.HasProperty("_BaseMap")) s += " _BaseMap=" + (m.GetTexture("_BaseMap") ? m.GetTexture("_BaseMap").name : "없음");
if (m.HasProperty("_ShadowBaseMap")) s += " _ShadowBaseMap=" + (m.GetTexture("_ShadowBaseMap") ? m.GetTexture("_ShadowBaseMap").name : "없음");
sb.AppendLine(s);
}
static string Hex(UnityEngine.Color c)
{
return "(" + c.r.ToString("F3") + "," + c.g.ToString("F3") + "," + c.b.ToString("F3") + ")#"
+ UnityEngine.ColorUtility.ToHtmlStringRGB(c);
}
}

View File

@ -0,0 +1,13 @@
# WL-816f 바닥 단계 비교 — 이름 | 바닥초록HEX | 풀Diffuse | 풀Shadow | 밀도 | 꽃확률 | 풀Scale ('-' = 그대로)
# 바닥색 채택 = A0E4A2 (데모 평균 #94BE81 에 화면색을 맞춘 역산값)
# ── 2단계: 풀 색 대비 3단 ──
c1 | A0E4A2 | C6F398 | 40533C | 2.5 | - | -
c2 | A0E4A2 | ABDE93 | 9AC884 | 2.5 | - | -
c3 | A0E4A2 | A6E39F | 95CC8F | 2.5 | - | -
# ── 3단계: 밀도 3단 (대비 = c2) ──
e1 | A0E4A2 | ABDE93 | 9AC884 | 2.50 | - | -
e2 | A0E4A2 | ABDE93 | 9AC884 | 1.80 | - | -
e3 | A0E4A2 | ABDE93 | 9AC884 | 1.25 | - | -
# ── 후보 조합 ──
f1 | A0E4A2 | A6E39F | 95CC8F | 1.80 | - | -
f2 | A0E4A2 | ABDE93 | 9AC884 | 2.20 | - | -

233
AgentScripts/WL816f_Pd.cs Normal file
View File

@ -0,0 +1,233 @@
// WL-816f — ① PD 실행 경로 재현 (로그인 없이): InGame 씬 Play → Level01 을 Additive 로 붙인다.
// = InGameInfo.Load_Map(900) → SceneInfo.Load_AddScene 이 하는 것과 같은 구조(활성 씬 = InGame).
// 배치모드 헤드리스에서 동작. 캡처는 오프스크린 렌더.
public static class WL816f_Pd
{
public const string Dir = "Screenshots_WL/WL816f/";
public const int W = 1080, H = 1920;
public static string Report = "";
// ── 에디트 모드: 씬 준비 ───────────────────────────────────────────
public static void Open()
{
UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
UnityEngine.Debug.Log("[816f] InGame 열림 dirty=" + UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene().isDirty);
}
// ── Play 중: 러너를 띄운다 ─────────────────────────────────────────
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816fPd");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816fPd");
go.AddComponent<WL816f_PdRunner>();
UnityEngine.Debug.Log("[816f] 러너 시작");
}
public static void Fetch()
{
var txt = WL816f_PdRunner.Log;
System.IO.File.WriteAllText("AgentScripts/WL816f_PD.txt", txt);
UnityEngine.Debug.Log("[816f]\n" + txt);
}
}
public class WL816f_PdRunner : UnityEngine.MonoBehaviour
{
public static string Log = "(아직)";
static System.Text.StringBuilder sb;
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
L("=== PD 경로 재현 (InGame 활성 + Level01 Additive) ===");
L("t0 활성씬=" + UnityEngine.SceneManagement.SceneManager.GetActiveScene().name
+ " sceneCount=" + UnityEngine.SceneManagement.SceneManager.sceneCount);
// 훅 설치 상태
var cfg = WL.Look.Farm.WLIslandLookSettings.Instance;
L("SO enabled_=" + (cfg == null ? -1 : cfg.enabled_) + " applyLook=" + (cfg == null ? -1 : cfg.applyLook)
+ " grass=" + (cfg == null ? -1 : cfg.grassEnabled));
// InGameInfo 로 진짜 경로를 태워 본다
bool viaGame = false;
try
{
if (InGameInfo.Ins != null)
{
L("InGameInfo.Ins 있음 → Load_Map(900) 시도");
InGameInfo.Ins.Load_Map(900);
viaGame = true;
}
}
catch (System.Exception e) { L("Load_Map 예외: " + e.GetType().Name + " " + e.Message); }
if (!viaGame)
{
L("→ 로그인 없이는 Load_Map 이 못 돈다. SceneInfo.Load_AddScene 과 **같은 호출**로 대체:");
L(" SceneManager.LoadSceneAsync(\"Level01\", Additive)");
var op = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync("Level01", UnityEngine.SceneManagement.LoadSceneMode.Additive);
while (op != null && !op.isDone) yield return null;
}
for (int i = 0; i < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
L("");
L("t+6s 활성씬=" + UnityEngine.SceneManagement.SceneManager.GetActiveScene().name
+ " sceneCount=" + UnityEngine.SceneManagement.SceneManager.sceneCount);
for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
{
var s = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i);
L(" scene[" + i + "] " + s.name + " loaded=" + s.isLoaded + " roots=" + (s.isLoaded ? s.rootCount : -1));
}
// ── 훅이 돌았나 ───────────────────────────────────────────────
L("");
L("IslandLook : 렌더러 " + WL.Look.Farm.WLIslandLook.SwappedRenderers + " 슬롯 " + WL.Look.Farm.WLIslandLook.SwappedSlots
+ " look=" + WL.Look.Farm.WLIslandLook.LookApplied + " light=" + WL.Look.Farm.WLIslandLook.LightingApplied
+ " refLook=" + WL.Look.Farm.WLIslandLook.ReferenceLookApplied + " grassSpawned=" + WL.Look.Farm.WLIslandLook.GrassSpawned);
L("Grass : " + WL.Look.Farm.WLIslandGrass.LastLog);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
L("GrassObj : " + (g == null ? "없음" : (g.name + " active=" + g.gameObject.activeInHierarchy + "/" + g.enabled
+ " scene=" + g.gameObject.scene.name + " layer=" + g.gameObject.layer)));
L("RefLook : applied=" + WL.Look.Arena.WLReferenceLook.IsApplied + " outlined=" + WL.Look.Arena.WLReferenceLook.OutlinedRenderers);
L("ambient : " + UnityEngine.RenderSettings.ambientMode + " " + UnityEngine.RenderSettings.ambientLight
+ " skybox=" + (UnityEngine.RenderSettings.skybox ? UnityEngine.RenderSettings.skybox.name : "없음")
+ " fog=" + UnityEngine.RenderSettings.fog);
// ── 카메라 — 누가 실제로 화면을 그리나 ────────────────────────
L("");
var cams = UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Camera top = null;
for (int i = 0; i < cams.Length; i++)
{
var c = cams[i];
bool live = c.gameObject.activeInHierarchy && c.enabled && c.targetTexture == null;
L("CAM " + c.name + " scene=" + c.gameObject.scene.name + " live=" + live
+ " depth=" + c.depth + " mask=0x" + c.cullingMask.ToString("X") + " clear=" + c.clearFlags
+ " pos=" + c.transform.position.ToString("F1"));
if (live && (top == null || c.depth > top.depth)) top = c;
}
L("→ 화면에 보이는 카메라 = " + (top == null ? "없음" : top.name + " (scene " + top.gameObject.scene.name + ")"));
if (top != null)
{
L(" 그 카메라의 컬링마스크에 들어가는 레이어:");
string inc = "", exc = "";
for (int i = 0; i < 32; i++)
{
string n = UnityEngine.LayerMask.LayerToName(i);
if (string.IsNullOrEmpty(n)) continue;
if ((top.cullingMask & (1 << i)) != 0) inc += i + ":" + n + " "; else exc += i + ":" + n + " ";
}
L(" 포함 = " + inc);
L(" 🔴제외 = " + exc);
}
// ── 섬 렌더러가 어느 레이어에 있나 ────────────────────────────
L("");
var counts = new System.Collections.Generic.Dictionary<int, int>();
var rends = UnityEngine.Object.FindObjectsByType<UnityEngine.Renderer>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
int nIsland = 0;
for (int i = 0; i < rends.Length; i++)
{
if (rends[i].gameObject.scene.name != "Level01") continue;
nIsland++;
int l = rends[i].gameObject.layer;
counts[l] = counts.ContainsKey(l) ? counts[l] + 1 : 1;
}
L("Level01 렌더러 " + nIsland + " 개의 레이어 분포:");
foreach (var kv in counts)
L(" layer " + kv.Key + " (" + UnityEngine.LayerMask.LayerToName(kv.Key) + ") = " + kv.Value
+ (top != null ? ((top.cullingMask & (1 << kv.Key)) != 0 ? " [보임]" : " 🔴[안 보임]") : ""));
L("풀이 그려지는 레이어 = " + UnityEngine.LayerMask.NameToLayer("Default") + " (InstancesBehaviour.Update 고정)");
L("Soil 톤 : farm " + WL.Look.Farm.WLIslandLook.SoilFarmsToned + " · 타일 " + WL.Look.Farm.WLIslandLook.SoilTilesRepainted);
// ── 캡처 (보이는 카메라 그대로 + 위에서 + 표준 바닥 카메라) ──
Shot(top, WL816f_Pd.Dir + "a_pd_path.png");
Shot(TopCam(), WL816f_Pd.Dir + "a_pd_path_top.png");
Shot(GroundCam(CleanSpot(), 6f), WL816f_Pd.Dir + "x_ours_ground.png");
Shot(GroundCam(CleanSpot(), 10f), WL816f_Pd.Dir + "x_ours_wide.png");
Log = sb.ToString();
System.IO.File.WriteAllText("AgentScripts/WL816f_PD.txt", Log);
UnityEngine.Debug.Log("[816f 완료]\n" + Log);
}
static void L(string s) { sb.AppendLine(s); }
/// <summary>소품이 가장 적은(=풀이 가장 넓은) 섬 타일의 중심.</summary>
UnityEngine.Vector3 CleanSpot()
{
var islands = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(
UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Vector3 best = UnityEngine.Vector3.zero; int bs = int.MaxValue;
for (int i = 0; i < islands.Length; i++)
{
var isl = islands[i];
if (isl == null || !isl.IsUnlocked || !isl.gameObject.activeInHierarchy) continue;
int sc = isl.GetComponentsInChildren<UnityEngine.Collider>(false).Length;
if (sc < bs) { bs = sc; best = isl.transform.position; }
}
return best;
}
/// <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;
}
UnityEngine.Camera TopCam()
{
var go = new UnityEngine.GameObject("~WL816fTop");
var c = go.AddComponent<UnityEngine.Camera>();
c.orthographic = true; c.orthographicSize = 14f;
c.nearClipPlane = 0.3f; c.farClipPlane = 500f;
c.clearFlags = UnityEngine.CameraClearFlags.Skybox;
c.cullingMask = ~0;
c.depth = -100f;
var rot = UnityEngine.Quaternion.Euler(30f, 60f, 0f);
go.transform.rotation = rot;
go.transform.position = new UnityEngine.Vector3(0f, 1f, 0f) - rot * UnityEngine.Vector3.forward * 60f;
if (go.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>() == null)
go.AddComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>();
return c;
}
public static void Shot(UnityEngine.Camera cam, string path)
{
if (cam == null) { L("캡처 실패(카메라 없음) " + path); return; }
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var rt = new UnityEngine.RenderTexture(WL816f_Pd.W, WL816f_Pd.H, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var prevT = cam.targetTexture; var prevA = UnityEngine.RenderTexture.active;
cam.targetTexture = rt;
cam.Render();
UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(WL816f_Pd.W, WL816f_Pd.H, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, WL816f_Pd.W, WL816f_Pd.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);
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
if (sb != null) L("캡처 " + path);
}
}

View File

@ -0,0 +1,113 @@
// WL-816f — ① PD 실행 경로(로그인 → Load_Map(900) → Level01 Additive) 구조 실측
// 에디트 모드 전용. InGame 씬이 섬 위에 무엇을 얹는지(조명·Volume·카메라)를 본다.
public static class WL816f_Probe
{
public static void Run()
{
var sb = new System.Text.StringBuilder();
// 1) InGame 씬(= PD 경로의 활성 씬)을 연다
var sc = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/Scenes/InGame.unity", UnityEditor.SceneManagement.OpenSceneMode.Single);
sb.AppendLine("=== InGame.unity (PD 경로의 ACTIVE 씬) ===");
Dump(sb, sc);
// 2) Level01 을 Additive 로 얹는다 (= Load_Map(900) 과 같은 구조)
var sc2 = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(
"Assets/FarmingIsland/Scenes/Level01.unity", UnityEditor.SceneManagement.OpenSceneMode.Additive);
sb.AppendLine();
sb.AppendLine("=== Level01.unity (Additive) ===");
Dump(sb, sc2);
sb.AppendLine();
sb.AppendLine("active scene = " + UnityEngine.SceneManagement.SceneManager.GetActiveScene().name);
sb.AppendLine("RenderSettings(활성 씬 기준) ambient=" + UnityEngine.RenderSettings.ambientMode
+ " light=" + UnityEngine.RenderSettings.ambientLight
+ " sky=" + UnityEngine.RenderSettings.ambientSkyColor
+ " skyboxMat=" + (UnityEngine.RenderSettings.skybox ? UnityEngine.RenderSettings.skybox.name : "없음")
+ " fog=" + UnityEngine.RenderSettings.fog);
// 3) 레이어 이름 확인 (풀은 Default 레이어에 그려진다)
sb.AppendLine("layer Default=" + UnityEngine.LayerMask.NameToLayer("Default"));
for (int i = 0; i < 32; i++)
{
string n = UnityEngine.LayerMask.LayerToName(i);
if (!string.IsNullOrEmpty(n)) sb.Append(i + ":" + n + " ");
}
sb.AppendLine();
System.IO.File.WriteAllText("AgentScripts/WL816f_PROBE.txt", sb.ToString());
UnityEngine.Debug.Log(sb.ToString());
}
static void Dump(System.Text.StringBuilder sb, UnityEngine.SceneManagement.Scene sc)
{
var roots = sc.GetRootGameObjects();
sb.AppendLine("roots=" + roots.Length);
for (int i = 0; i < roots.Length; i++) sb.Append(roots[i].name + (roots[i].activeSelf ? "" : "(off)") + " · ");
sb.AppendLine();
// 카메라
foreach (var r in roots)
foreach (var c in r.GetComponentsInChildren<UnityEngine.Camera>(true))
sb.AppendLine(" CAM " + Path(c.transform) + " active=" + c.gameObject.activeInHierarchy + "/" + c.enabled
+ " depth=" + c.depth + " mask=0x" + c.cullingMask.ToString("X")
+ " clear=" + c.clearFlags + " ortho=" + c.orthographic + " fov=" + c.fieldOfView
+ " far=" + c.farClipPlane
+ " urp=" + (c.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>() != null
? ("post=" + c.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>().renderPostProcessing
+ " volMask=0x" + c.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>().volumeLayerMask.value.ToString("X")
+ " type=" + c.GetComponent<UnityEngine.Rendering.Universal.UniversalAdditionalCameraData>().renderType)
: "없음"));
// 라이트
foreach (var r in roots)
foreach (var l in r.GetComponentsInChildren<UnityEngine.Light>(true))
sb.AppendLine(" LIGHT " + Path(l.transform) + " active=" + l.gameObject.activeInHierarchy + "/" + l.enabled
+ " " + l.type + " col=" + l.color + " I=" + l.intensity + " sh=" + l.shadows
+ " rot=" + l.transform.eulerAngles);
// Volume (포스트)
foreach (var r in roots)
foreach (var v in r.GetComponentsInChildren<UnityEngine.Rendering.Volume>(true))
sb.AppendLine(" VOL " + Path(v.transform) + " active=" + v.gameObject.activeInHierarchy + "/" + v.enabled
+ " global=" + v.isGlobal + " prio=" + v.priority + " weight=" + v.weight
+ " profile=" + (v.sharedProfile ? v.sharedProfile.name + " [" + Comps(v.sharedProfile) + "]" : "없음")
+ " layer=" + v.gameObject.layer);
// RenderSettings 는 씬별이다 — 씬을 활성으로 만들고 읽는다
var prev = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
UnityEngine.SceneManagement.SceneManager.SetActiveScene(sc);
sb.AppendLine(" RS ambient=" + UnityEngine.RenderSettings.ambientMode
+ " light=" + UnityEngine.RenderSettings.ambientLight + " sky=" + UnityEngine.RenderSettings.ambientSkyColor
+ " I=" + UnityEngine.RenderSettings.ambientIntensity
+ " skyboxMat=" + (UnityEngine.RenderSettings.skybox ? UnityEngine.RenderSettings.skybox.name : "없음")
+ " fog=" + UnityEngine.RenderSettings.fog + " sun=" + (UnityEngine.RenderSettings.sun ? UnityEngine.RenderSettings.sun.name : "없음"));
if (prev.IsValid() && prev != sc) UnityEngine.SceneManagement.SceneManager.SetActiveScene(prev);
// MapData / GlobalVolumeMgr / CheckShadow 같은 룩 관여 컴포넌트
foreach (var r in roots)
foreach (var m in r.GetComponentsInChildren<UnityEngine.MonoBehaviour>(true))
{
if (m == null) continue;
string t = m.GetType().Name;
if (t == "MapData" || t == "GlobalVolumeMgr" || t == "CheckShadow" || t == "SetSkybox"
|| t == "GraphicsManager" || t == "UniversalRenderPipelineAsset")
sb.AppendLine(" COMP " + t + " @ " + Path(m.transform) + " active=" + m.gameObject.activeInHierarchy + "/" + m.enabled);
}
}
static string Comps(UnityEngine.Rendering.VolumeProfile p)
{
var s = "";
for (int i = 0; i < p.components.Count; i++) s += p.components[i].GetType().Name + (p.components[i].active ? "" : "(off)") + ",";
return s;
}
static string Path(UnityEngine.Transform t)
{
string s = t.name;
while (t.parent != null) { t = t.parent; s = t.name + "/" + s; }
return s;
}
}

View File

@ -0,0 +1,227 @@
// 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;
}
}

View File

@ -0,0 +1,155 @@
// WL-816f — PD 경로 Play 에서 ⓓ성능 ⓔ기능(걷기·농사·건설/확장) ⓕ되돌리기를 실측한다.
public static class WL816f_Verify
{
public static void Start()
{
var go = UnityEngine.GameObject.Find("~WL816fVerify");
if (go != null) UnityEngine.Object.DestroyImmediate(go);
go = new UnityEngine.GameObject("~WL816fVerify");
go.AddComponent<WL816f_VerifyRunner>();
}
}
public class WL816f_VerifyRunner : UnityEngine.MonoBehaviour
{
const string Dir = "Screenshots_WL/WL816f/";
static System.Text.StringBuilder sb;
void Start() { StartCoroutine(Co()); }
System.Collections.IEnumerator Co()
{
sb = new System.Text.StringBuilder();
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 < 6; i++) yield return new UnityEngine.WaitForSeconds(1f);
L("=== WL-816f 검증 (PD 경로 · InGame 활성 + Level01 Additive) ===");
L("풀 : " + WL.Look.Farm.WLIslandGrass.LastLog);
L("Soil 톤 : farm " + WL.Look.Farm.WLIslandLook.SoilFarmsToned + " · 타일 " + WL.Look.Farm.WLIslandLook.SoilTilesRepainted);
// ── ⓓ 성능 : 같은 카메라로 60프레임 오프스크린 렌더 ─────────
var cam = FindLiveCam();
L("");
L("[성능] 카메라 " + (cam ? cam.name : "없음"));
float onMs = RenderBench(cam, 60);
var g = UnityEngine.Object.FindFirstObjectByType<WL.Look.Farm.WLIslandGrass>(UnityEngine.FindObjectsInactive.Include);
int instOn = WL.Look.Farm.WLIslandGrass.Instances;
long triOn = WL.Look.Farm.WLIslandGrass.Triangles;
if (g != null) g.enabled = false; // 풀만 끈 상태
yield return null; yield return null;
float offMs = RenderBench(cam, 60);
if (g != null) g.enabled = true;
yield return null; yield return null;
L("풀 켬 " + onMs.ToString("F3") + " ms/frame · 풀 끔 " + offMs.ToString("F3") + " ms/frame · 델타 " + (onMs - offMs).ToString("F3") + " ms");
L("인스턴스 " + instOn + " · 삼각형(풀) " + triOn + " · 드로우콜 +" + WL.Look.Farm.WLIslandGrass.DrawnConfigs);
// ── ⓔ 기능 ─────────────────────────────────────────────────
L("");
L("[기능]");
// 풀 콜라이더 0 · 레이캐스트 · NavMesh
int colliders = 0;
var gobj = g != null ? g.gameObject : null;
if (gobj != null) colliders = gobj.GetComponentsInChildren<UnityEngine.Collider>(true).Length;
L("풀 오브젝트 콜라이더 = " + colliders + " (0 이어야 한다)");
int hit = 0, nav = 0;
for (int i = 0; i < 100; i++)
{
var p = new UnityEngine.Vector3(UnityEngine.Random.Range(-7f, 7f), 3f, UnityEngine.Random.Range(-7f, 7f));
UnityEngine.RaycastHit h;
if (UnityEngine.Physics.Raycast(p, UnityEngine.Vector3.down, out h, 10f)) hit++;
UnityEngine.AI.NavMeshHit nh;
if (UnityEngine.AI.NavMesh.SamplePosition(new UnityEngine.Vector3(p.x, 0f, p.z), out nh, 1.5f, ~0)) nav++;
}
L("풀밭 100점 아래 레이캐스트 적중 " + hit + "/100 · NavMesh 샘플 " + nav + "/100");
// 걷기 — FI PlayerController 의 CharacterController
var cc = UnityEngine.Object.FindFirstObjectByType<UnityEngine.CharacterController>(UnityEngine.FindObjectsInactive.Exclude);
if (cc != null)
{
var p0 = cc.transform.position;
var dir = new UnityEngine.Vector3(1f, 0f, 1f).normalized;
float moved = 0f;
for (int i = 0; i < 120; i++) { cc.Move(dir * 0.05f + UnityEngine.Vector3.down * 0.02f); yield return null; }
moved = UnityEngine.Vector3.Distance(new UnityEngine.Vector3(p0.x, 0f, p0.z), new UnityEngine.Vector3(cc.transform.position.x, 0f, cc.transform.position.z));
L("걷기 : " + p0.ToString("F2") + " → " + cc.transform.position.ToString("F2") + " = " + moved.ToString("F2") + " m · grounded=" + cc.isGrounded);
}
else L("걷기 : CharacterController 없음(미확인)");
// 농사 — Farm/Soil 개수 + 씨뿌리기 상태 전환
var farms = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Farm>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
var soils = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Soil>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
L("농사 : Farm " + farms.Length + " · Soil " + soils.Length);
int painted = 0;
for (int i = 0; i < soils.Length; i++)
{
var r = soils[i].GetComponentInChildren<UnityEngine.Renderer>(true);
if (r != null && r.sharedMaterial != null) painted++;
}
L("Soil 렌더러 살아있음 " + painted + "/" + soils.Length + " · 첫 Soil 색 " +
(soils.Length > 0 && soils[0].GetComponentInChildren<UnityEngine.Renderer>(true) != null
? UnityEngine.ColorUtility.ToHtmlStringRGB(soils[0].GetComponentInChildren<UnityEngine.Renderer>(true).material.color) : "-"));
// 확장 — 섬 타일 수
var islands = UnityEngine.Object.FindObjectsByType<CryingSnow.FarmingIsland.Island>(UnityEngine.FindObjectsInactive.Include, UnityEngine.FindObjectsSortMode.None);
int unlocked = 0; for (int i = 0; i < islands.Length; i++) if (islands[i].IsUnlocked && islands[i].gameObject.activeInHierarchy) unlocked++;
L("확장 : 섬 타일 전체 " + islands.Length + " · 열린 타일 " + unlocked + " · 풀 깔린 타일 " + WL.Look.Farm.WLIslandGrass.Tiles);
// ── 최종 캡처 ───────────────────────────────────────────────
Shot(FindLiveCam(), Dir + "d_final_wide.png");
System.IO.File.WriteAllText("AgentScripts/WL816f_VERIFY.txt", sb.ToString());
UnityEngine.Debug.Log("[816f verify]\n" + sb.ToString());
}
static void L(string s) { sb.AppendLine(s); }
static UnityEngine.Camera FindLiveCam()
{
var cams = UnityEngine.Object.FindObjectsByType<UnityEngine.Camera>(UnityEngine.FindObjectsInactive.Exclude, UnityEngine.FindObjectsSortMode.None);
UnityEngine.Camera top = null;
for (int i = 0; i < cams.Length; i++)
{
var c = cams[i];
if (!c.enabled || c.targetTexture != null) continue;
if (top == null || c.depth > top.depth) top = c;
}
return top;
}
static float RenderBench(UnityEngine.Camera cam, int frames)
{
if (cam == null) return -1f;
var rt = new UnityEngine.RenderTexture(1080, 1920, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var prev = cam.targetTexture; cam.targetTexture = rt;
cam.Render(); // 워밍업
var sw = System.Diagnostics.Stopwatch.StartNew();
for (int i = 0; i < frames; i++) cam.Render();
sw.Stop();
cam.targetTexture = prev;
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
return (float)sw.Elapsed.TotalMilliseconds / frames;
}
static void Shot(UnityEngine.Camera cam, string path)
{
if (cam == null) return;
System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(path));
var rt = new UnityEngine.RenderTexture(1080, 1920, 24, UnityEngine.RenderTextureFormat.ARGB32, UnityEngine.RenderTextureReadWrite.sRGB);
rt.antiAliasing = 1; rt.Create();
var prevT = cam.targetTexture; var prevA = UnityEngine.RenderTexture.active;
cam.targetTexture = rt; cam.Render();
UnityEngine.RenderTexture.active = rt;
var tex = new UnityEngine.Texture2D(1080, 1920, UnityEngine.TextureFormat.RGB24, false);
tex.ReadPixels(new UnityEngine.Rect(0, 0, 1080, 1920), 0, 0); tex.Apply();
System.IO.File.WriteAllBytes(path, UnityEngine.ImageConversion.EncodeToPNG(tex));
UnityEngine.RenderTexture.active = prevA; cam.targetTexture = prevT;
UnityEngine.Object.DestroyImmediate(tex);
rt.Release(); UnityEngine.Object.DestroyImmediate(rt);
L("캡처 " + path);
}
}

View File

@ -0,0 +1,159 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-3938375536428912352
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Farm_Grass_Demo
m_Shader: {fileID: -6465566751694194690, guid: 116bae4840169554085c5aa591d459f5,
type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _MAIN_LIGHT_SHADOWS
- _SHADOWS_SOFT
m_InvalidKeywords:
- _CLOUDSENABLED
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses:
- MOTIONVECTORS
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AlphaClip: 0
- _AlphaToMask: 0
- _AmbientStrength: 0.1
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _Brightness: 0.25
- _BumpScale: 1
- _CLOUDSENABLED: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cloud_Change: 0.005
- _Cloud_Cover: 0.5
- _Cloud_Density: 0.01
- _Cloud_Strength: 1
- _Cull: 2
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _DstBlendAlpha: 0
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _MAIN_LIGHT: 0
- _Metallic: 0
- _MinimumDarkness: 0.2
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueControl: 0
- _QueueOffset: 0
- _ReceiveShadows: 1
- _SHADOWS_SOFT: 1
- _Shades: 7
- _Smoothness: 0.5
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 0
- _VerticalDifference: 1
- _WindDensity: 0.2
- _WindStrength: 0.3
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 1
m_Colors:
- _BaseColor: {r: 1, g: 1, b: 1, a: 1}
- _Cloud_Movement: {r: 1, g: 1, b: 0, a: 0}
- _Cloud_Step: {r: 13, g: 17, b: 0, a: 0}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _DiffuseColor: {r: 0.6509804, g: 0.8901961, b: 0.62352943, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _ShadowDiffuseColor: {r: 0.58431375, g: 0.8, b: 0.56078434, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
- _WindMovement: {r: 6, g: 0, b: 0, a: 0}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: bfa67f7091cb7ff418051fec03e5c28c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -38,6 +38,9 @@ MonoBehaviour:
to: {fileID: 2100000, guid: 1c9ce9b7865f6d643ad9601f6fa81ba3, type: 2}
islandTopMaterial: {fileID: 2100000, guid: 500f1cf3127afb748b316fe28b128edc, type: 2}
skipSoilRenderers: 1
soilToneEnabled: 1
soilTint: {r: 0.84, g: 0.86, b: 0.9, a: 1}
soilCheckerContrast: 0.6
ambientSky: {r: 0.212, g: 0.227, b: 0.259, a: 1}
ambientEquator: {r: 0.114, g: 0.125, b: 0.133, a: 1}
ambientGround: {r: 0.047, g: 0.043, b: 0.035, a: 1}
@ -57,11 +60,11 @@ MonoBehaviour:
- enabled_: 1
label: "\uD480"
mesh: {fileID: -4413095505993930501, guid: 1f298817fdd2a184480e8af5b21278bc, type: 3}
material: {fileID: 2100000, guid: a194cb4ce6027c246af528b83b728a15, type: 2}
material: {fileID: 2100000, guid: bfa67f7091cb7ff418051fec03e5c28c, type: 2}
probability: 100
scale: 0.35
normalOffset: 0.04
density: 2.5
density: 1.8
- enabled_: 1
label: "\uAF43"
mesh: {fileID: -6327915695457451627, guid: dab2c84bf0b5ced4aaf0cffc4c491d55, type: 3}
@ -69,7 +72,7 @@ MonoBehaviour:
probability: 1
scale: 0.35
normalOffset: 0.04
density: 2.5
density: 1.8
- enabled_: 1
label: "\uC790\uAC08"
mesh: {fileID: 2928966540353291585, guid: 7f1f11ac2c783374b876f8dc2bb7c7db, type: 3}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

After

Width:  |  Height:  |  Size: 93 B

View File

@ -25,6 +25,7 @@ using UnityEngine.SceneManagement;
using CryingSnow.FarmingIsland;
using FIIsland = CryingSnow.FarmingIsland.Island;
using FISoil = CryingSnow.FarmingIsland.Soil;
using FIFarm = CryingSnow.FarmingIsland.Farm;
namespace WL.Look.Farm
{
@ -85,6 +86,7 @@ namespace WL.Look.Farm
{
s_done.Clear();
s_hooked.Clear();
s_tonedFarms.Clear();
LookApplied = false; GrassSpawned = false; ReferenceLookApplied = false;
}
@ -127,6 +129,7 @@ namespace WL.Look.Farm
}
if (cfg.applyLighting != 0) ApplyLighting(cfg, scene);
if (cfg.applyLook != 0 && cfg.applyReferenceLook != 0) ApplyReferenceLook(cfg, scene);
if (cfg.soilToneEnabled != 0) ToneSoil(cfg, scene);
if (cfg.grassEnabled != 0) SpawnGrass(cfg, scene);
@ -148,6 +151,11 @@ namespace WL.Look.Farm
if (w > 0f) yield return new WaitForSeconds(w);
if (!scene.isLoaded) yield break;
if (cfg.applyLook != 0) { SwapMaterials(cfg, scene); Rescans++; }
// 🔴 816f 실측 — PD 실행 경로(Additive)에서 `RenderSettings` 는 **활성 씬(InGame)**
// 소유이고, 그 씬에는 조명을 덮어쓰는 코드(`MapData`)가 없다(실측).
// 그래서 조명은 재훑기에서 **다시 걸지 않는다** — 다시 걸면 `WLReferenceLook` 의
// 무드(Flat)를 Skybox 로 되돌려 오히려 룩이 바뀐다(실측으로 확인).
if (cfg.soilToneEnabled != 0) ToneSoil(cfg, scene);
HookIslands(cfg, scene);
}
}
@ -302,6 +310,67 @@ namespace WL.Look.Farm
}
}
// ─────────────────────────────────────────────────────────────────
// ②-b 밭(Soil) 칸 표시 — **기능은 그대로 두고 색만** 데모 톤으로 (816f · PD 지시)
// FI `Farm` 의 4색(마름1·마름2·젖음1·젖음2)을 런타임에 낮추고
// `Soil.Initialize(farm)`(public) 를 다시 불러 칠만 갱신한다.
// → 물주기 피드백(마름↔젖음)·칸 구분은 전부 살아 있다. `Assets/FarmingIsland/**` 0줄.
// ─────────────────────────────────────────────────────────────────
static readonly string[] s_soilFields = { "soilDryColor1", "soilDryColor2", "soilWetColor1", "soilWetColor2" };
static readonly HashSet<FIFarm> s_tonedFarms = new HashSet<FIFarm>();
public static int SoilFarmsToned, SoilTilesRepainted;
public static void ToneSoil(WLIslandLookSettings cfg, Scene scene)
{
var farms = Object.FindObjectsByType<FIFarm>(FindObjectsInactive.Include, FindObjectsSortMode.None);
var t = typeof(FIFarm);
for (int i = 0; i < farms.Length; i++)
{
var farm = farms[i];
if (farm == null || farm.gameObject.scene != scene) continue;
if (s_tonedFarms.Contains(farm)) continue;
// 1) 마름1/마름2 · 젖음1/젖음2 각각의 평균으로 모으고(대비 축소) 틴트를 곱한다
var vals = new Color[4];
var fis = new System.Reflection.FieldInfo[4];
bool ok = true;
for (int k = 0; k < 4; k++)
{
fis[k] = t.GetField(s_soilFields[k],
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
if (fis[k] == null) { ok = false; break; }
vals[k] = (Color)fis[k].GetValue(farm);
}
if (!ok) continue;
Apply(cfg, fis, farm, vals, 0, 1); // 마름 한 쌍
Apply(cfg, fis, farm, vals, 2, 3); // 젖음 한 쌍
s_tonedFarms.Add(farm);
SoilFarmsToned++;
// 2) 이미 칠해진 타일을 다시 칠한다 — FI 의 public 진입점 그대로
var soils = farm.GetComponentsInChildren<FISoil>(true);
for (int s = 0; s < soils.Length; s++)
{
if (soils[s] == null) continue;
try { soils[s].Initialize(farm); SoilTilesRepainted++; } catch { }
}
}
}
static void Apply(WLIslandLookSettings cfg, System.Reflection.FieldInfo[] fis, FIFarm farm, Color[] v, int a, int b)
{
var mid = (v[a] + v[b]) * 0.5f;
float c = cfg.soilCheckerContrast;
var ca = Color.Lerp(mid, v[a], c);
var cb = Color.Lerp(mid, v[b], c);
fis[a].SetValue(farm, Mul(ca, cfg.soilTint));
fis[b].SetValue(farm, Mul(cb, cfg.soilTint));
}
static Color Mul(Color a, Color b) { return new Color(a.r * b.r, a.g * b.g, a.b * b.b, a.a); }
// ─────────────────────────────────────────────────────────────────
// ③ 아레나와 같은 외곽선·대비·무드 (씬 비의존 — 816a 실측)
// ─────────────────────────────────────────────────────────────────

View File

@ -136,6 +136,19 @@ namespace WL.Look.Farm
[Tooltip("1 이면 농사 타일(Soil)은 원본 머티리얼 그대로 둔다(색을 코드가 직접 칠하는 곳이라 건드리면 안 된다).")]
public int skipSoilRenderers = 1;
// ── 816f: 밭(Soil) 체크무늬 **기능은 유지**하고 색만 데모 톤으로 (PD 2026-09-13)
[Header("§0-816f — 밭(Soil) 칸 표시 톤 (기능 유지 · 색만)")]
[Tooltip("1 이면 FI `Farm` 의 마른/젖은 흙 색 4개를 런타임에 데모 톤으로 낮춘다. " +
"칸 구분(마름/젖음 표시)은 그대로 남는다 — 색만 바뀐다. 0 이면 FI 원본 색.")]
public int soilToneEnabled = 1;
[Tooltip("흙 색에 곱하는 값(0~1). 낮출수록 주황이 죽는다.")]
public Color soilTint = new Color(0.84f, 0.86f, 0.90f, 1f);
[Tooltip("체크무늬 두 색의 차이를 이 비율로 줄인다(1 = FI 원본 차이 · 0 = 차이 없음=칸 안 보임). " +
"🔴 0 으로 두지 말 것 — 어디를 갈았는지 안 보인다.")]
[Range(0f, 1f)] public float soilCheckerContrast = 0.6f;
[Header("§0 — 조명 값 (816a 실측 = 데모/아레나와 동일)")]
public Color ambientSky = new Color(0.212f, 0.227f, 0.259f, 1f);
public Color ambientEquator = new Color(0.114f, 0.125f, 0.133f, 1f);