diff --git a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
index c057992d5..9edb9e85c 100644
--- a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
+++ b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset
@@ -48,6 +48,7 @@ MonoBehaviour:
gateFlowEnabled: 1
hideFarms: 1
farmReHideDelaySeconds: 3
+ matchNewTileMaterials: 1
dungeonUnlockPrefsKey: WL_Island_DungeonsUnlocked
dungeonUnlockPrices:
- 10
diff --git a/Assets/WL/Island/WLIslandGateFlow.cs b/Assets/WL/Island/WLIslandGateFlow.cs
index 8bf037f0a..b238950e8 100644
--- a/Assets/WL/Island/WLIslandGateFlow.cs
+++ b/Assets/WL/Island/WLIslandGateFlow.cs
@@ -292,10 +292,10 @@ namespace WL.Island
return haveCenter;
}
- /// 섬이 나중에 열리면 그 섬의 밭도 다시 숨긴다(확장해도 밭이 안 보이게).
+ /// 섬이 나중에 열리면 ① 그 섬의 밭을 다시 숨기고 ② 타일 재질을 기존 타일과 맞춘다(§1).
static void HookIslandActivation(WLIslandSettings cfg, Scene scene)
{
- if (cfg.hideFarms == 0) return;
+ if (cfg.hideFarms == 0 && cfg.matchNewTileMaterials == 0) return;
var islands = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None);
for (int i = 0; i < islands.Length; i++)
{
@@ -311,7 +311,8 @@ namespace WL.Island
static void OnIslandActivated(WLIslandSettings cfg, FIIsland isl)
{
- if (cfg == null || cfg.gateFlowEnabled == 0 || cfg.hideFarms == 0) return;
+ if (cfg == null || cfg.gateFlowEnabled == 0) return;
+ if (cfg.hideFarms == 0 && cfg.matchNewTileMaterials == 0) return;
var runner = WLIslandBridge.Runner;
if (runner == null || isl == null) return;
runner.StartCoroutine(Co_ReHide(cfg, isl));
@@ -319,10 +320,14 @@ namespace WL.Island
static IEnumerator Co_ReHide(WLIslandSettings cfg, FIIsland isl)
{
- // 확장 애니메이션(DOScale 1초) + 소품 배치(AnimateSoils 0.05초 × 30칸)가 끝나기를 기다린다
+ // 확장 애니메이션(DOScale 1초) + 소품 배치(AnimateSoils 0.05초 × 30칸) + 룩 재적용
+ // (`WLIslandLookSettings.rebuildDelaySeconds` = 1.4 실측)이 끝나기를 기다린다.
yield return new WaitForSeconds(cfg.farmReHideDelaySeconds);
if (isl == null) yield break;
+ MatchTileMaterials(cfg, isl);
+
+ if (cfg.hideFarms == 0) yield break;
var farms = isl.GetComponentsInChildren(true);
for (int i = 0; i < farms.Length; i++)
{
@@ -336,6 +341,82 @@ namespace WL.Island
}
}
+ // ─────────────────────────────────────────────────────────────────
+ // §1 새 타일 경계 색차 — 남은 몫(외곽선 패스)
+ //
+ // 실측(2026-09-13 · Play):
+ // · **바닥 색 자체는 Lead 의 `b1e58d795`(대비 복제 제외)로 해결됐다** — 새로 연 타일도
+ // 기존 타일과 **같은 머티리얼 인스턴스** `Farm_IslandTop_Demo#83780` 을 쓴다.
+ // · 다만 새로 연 타일은 슬롯이 **1개**뿐이다(기존 타일은 2개 = 바닥 + `WL/HullOutline`).
+ // `WLReferenceLook.Apply` 가 씬 로드 때 **활성 렌더러만** 훑는데 잠긴 타일은 꺼져 있어
+ // 외곽선 패스를 못 받는다. 그래서 **경계에서 테두리 유무가 갈린다.**
+ // 조치: 열린 직후, 이미 열려 있는 타일의 머티리얼 배열을 **그대로** 물려준다(새 머티리얼 0개).
+ // ─────────────────────────────────────────────────────────────────
+ public static int TilesMatched, SlotsMatched;
+
+ static void MatchTileMaterials(WLIslandSettings cfg, FIIsland isl)
+ {
+ if (cfg.matchNewTileMaterials == 0 || isl == null) return;
+
+ var mine = isl.GetComponentsInChildren(true);
+ if (mine.Length == 0) return;
+
+ // 기준 = 이미 열려 있고 **슬롯이 더 많은** 타일(= 외곽선을 받은 타일)
+ FIIsland refIsl = null;
+ int bestSlots = 0;
+ var all = Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None);
+ for (int i = 0; i < all.Length; i++)
+ {
+ var o = all[i];
+ if (o == null || o == isl || !o.IsUnlocked || !o.gameObject.activeInHierarchy) continue;
+ if (o.gameObject.scene != isl.gameObject.scene) continue;
+ var r = o.GetComponent();
+ if (r == null) continue;
+ int n = r.sharedMaterials != null ? r.sharedMaterials.Length : 0;
+ if (n > bestSlots) { bestSlots = n; refIsl = o; }
+ }
+ if (refIsl == null) return;
+
+ // 🔴 이름으로 짝을 지으면 **타일 본체**(GameObject 이름이 `Island (3)` / `Island (6)` 처럼 다 다르다)가
+ // 안 맞는다(실측 — 첫 시도에서 본체만 슬롯 1개로 남았다). 루트 기준 **상대 경로**로 짝 짓는다.
+ var refs = refIsl.GetComponentsInChildren(true);
+ var byPath = new Dictionary(refs.Length);
+ for (int j = 0; j < refs.Length; j++)
+ if (refs[j] != null) byPath[RelPath(refIsl.transform, refs[j].transform)] = refs[j];
+
+ int changed = 0;
+ for (int i = 0; i < mine.Length; i++)
+ {
+ var m = mine[i];
+ if (m == null || m is ParticleSystemRenderer) continue;
+ var mm = m.sharedMaterials;
+ if (mm == null || mm.Length == 0 || mm[0] == null) continue;
+
+ Renderer r;
+ if (!byPath.TryGetValue(RelPath(isl.transform, m.transform), out r) || r == null) continue;
+ var rm = r.sharedMaterials;
+ if (rm == null || rm.Length <= mm.Length || rm[0] != mm[0]) continue;
+ m.sharedMaterials = rm; // 같은 인스턴스 배열 = 픽셀이 같아진다
+ changed++; SlotsMatched += rm.Length - mm.Length;
+ }
+ if (changed > 0)
+ {
+ TilesMatched++;
+ WLIslandBridge.Log(cfg, "새 타일 재질 맞춤 — " + isl.name + " 렌더러 " + changed +
+ "개를 기존 타일(" + refIsl.name + ")과 같은 배열로(외곽선 포함)");
+ }
+ }
+
+ /// 타일 루트 기준 상대 경로(프리팹 인스턴스끼리 짝 짓는 열쇠 · 루트 자신은 빈 문자열).
+ static string RelPath(Transform root, Transform t)
+ {
+ if (t == root) return "";
+ string s = t.name;
+ var p = t.parent;
+ while (p != null && p != root) { s = p.name + "/" + s; p = p.parent; }
+ return s;
+ }
+
/// 우리가 끈 것만 되살린다(C8).
public static void RestoreFarms()
{
diff --git a/Assets/WL/Island/WLIslandSettings.cs b/Assets/WL/Island/WLIslandSettings.cs
index 5cf18f2e6..65ab83674 100644
--- a/Assets/WL/Island/WLIslandSettings.cs
+++ b/Assets/WL/Island/WLIslandSettings.cs
@@ -220,9 +220,15 @@ namespace WL.Island
"꺼진 오브젝트에서 코루틴을 시작해 콘솔 에러가 난다.")]
public int hideFarms = 1;
- [Tooltip("섬이 나중에 열렸을 때 그 밭을 다시 숨기기까지 기다리는 초(확장 연출 + 흙칸 연출이 끝날 시간).")]
+ [Tooltip("섬이 나중에 열렸을 때 그 밭을 다시 숨기고 타일 재질을 맞추기까지 기다리는 초 " +
+ "(확장 연출 1초 + 흙칸 연출 + 룩 재적용 1.4초가 끝날 시간).")]
public float farmReHideDelaySeconds = 3.0f;
+ [Tooltip("🔴 §1 — 1 이면 **새로 열린 타일**의 머티리얼 배열을 이미 열려 있는 타일과 **같게** 만든다. " +
+ "실측: 바닥 색 자체는 이미 같지만(main b1e58d795), 새 타일만 외곽선 패스(WL/HullOutline)를 " +
+ "못 받아 경계에서 테두리 유무가 갈린다. 새 머티리얼을 만들지 않고 기존 배열을 물려준다.")]
+ public int matchNewTileMaterials = 1;
+
[Tooltip("해금된 던전 수를 적어 두는 PlayerPrefs 키(FI 세이브 파일은 건드리지 않는다).")]
public string dungeonUnlockPrefsKey = "WL_Island_DungeonsUnlocked";