Project_WL/Assets/WL/Look/Farm/WLIslandLookSettings.cs

411 lines
24 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.

// ─────────────────────────────────────────────────────────────────────────────
// WLIslandLookSettings.cs — 실제 게임 섬(FarmingIsland Level01)에 **런타임으로** 얹는
// ① 데모 룩(머티리얼·조명) ② 데모 풀밭의 단일 출처 (WL-816e · #816)
//
// PD 지시(2026-09-13) 「섬 배경이 단순 타일로만 되어있어. 기본 타일의 디자인을
// 데모씬에서 사용한 풀밭처럼 꾸미고, 전체적인 비주얼을 맞춰줘」
//
// ■ 왜 런타임인가 — 816a 는 **복사본 씬**(WL_FarmLook)에만 룩을 저장했다.
// 게임이 실제로 로드하는 것은 `Assets/FarmingIsland/Scenes/Level01.unity` 라
// 그 씬은 아직 원본 색이다. 원본 씬을 고치지 않고(FI 0줄) 로드 직후 얹는다.
//
// ■ C8 되돌리기 — `enabled_ = 0` 이면 아무것도 얹지 않는다 = 지금 상태 100 %.
// 씬 파일·FI 에셋을 한 글자도 쓰지 않으므로 되돌리기에 복원 작업이 필요 없다.
//
// ■ C45 — 밀도·크기·제외 여백 같은 값은 전부 이 에셋에 있다(코드 상수 0).
//
// 🔴 Debug.LogError / Debug.LogException 을 쓰지 않는다(ErrorLogHookManager 팝업).
// 🔴 어셈블리 주의: Assets/WL/Look/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
// ─────────────────────────────────────────────────────────────────────────────
using System;
using UnityEngine;
namespace WL.Look.Farm
{
/// <summary>원본 머티리얼 → 데모 톤 복사본. 한 줄 = 머티리얼 하나.</summary>
[Serializable]
public sealed class WLMatRemap
{
[Tooltip("0 이면 이 줄은 없는 것으로 친다.")]
public int enabled_ = 1;
[Tooltip("FarmingIsland 원본 머티리얼(한 글자도 안 고친다 · 참조만).")]
public Material from;
[Tooltip("816e/816a 가 만든 데모 톤 복사본.")]
public Material to;
}
/// <summary>
/// 메시 하나의 **윗면(걸어다니는 면)** 을 XZ 격자 비트맵으로 구운 것.
/// FI 메시는 `isReadable = 0` 이라 런타임에 정점을 못 읽는다 → 에디터에서 구워 둔다.
/// (FI 에셋의 임포트 설정을 고치지 않기 위한 선택 — `Assets/FarmingIsland/**` 0줄)
/// </summary>
[Serializable]
public sealed class WLTileMask
{
public string meshName = "";
[Tooltip("격자 해상도(res × res).")] public int res = 64;
[Tooltip("마스크가 덮는 반폭(m). 섬 타일 8 m → 4.")] public float half = 4f;
[Tooltip("res*res 비트를 담은 패킹 배열.")] public byte[] bits;
public bool At(float lx, float lz)
{
if (bits == null || res <= 0) return false;
int cx = (int)((lx + half) / (half * 2f) * res);
int cz = (int)((lz + half) / (half * 2f) * res);
if (cx < 0 || cz < 0 || cx >= res || cz >= res) return false;
int i = cz * res + cx;
int b = i >> 3;
if (b >= bits.Length) return false;
return (bits[b] & (1 << (i & 7))) != 0;
}
public void Set(int cx, int cz, bool v)
{
if (bits == null) bits = new byte[(res * res + 7) / 8];
int i = cz * res + cx;
int b = i >> 3;
if (b >= bits.Length) return;
if (v) bits[b] |= (byte)(1 << (i & 7));
else bits[b] &= (byte)~(1 << (i & 7));
}
}
/// <summary>흩뿌릴 것 하나(풀 · 꽃 · 자갈). 데모 `InstancingSettings` 와 같은 값 구성.</summary>
[Serializable]
public sealed class WLScatterDef
{
public int enabled_ = 1;
[Tooltip("이름표(로그용).")] public string label = "풀";
public Mesh mesh;
public Material material;
[Tooltip("같은 층 안에서의 가중치. 데모 = 풀 100 : 꽃 1.")] public float probability = 100f;
[Tooltip("메시 배율. 데모 = 0.35.")] public float scale = 0.35f;
[Tooltip("법선 방향으로 띄우는 거리(m). 데모 = 0.04.")] public float normalOffset = 0.04f;
[Tooltip("이 층의 밀도(개/m 한 변). 실제 밀도 = 값². 데모 1층 = 2.5(6.25개/㎡).")]
public float density = 2.5f;
}
[CreateAssetMenu(fileName = "WLIslandLookSettings", menuName = "WL/Island Look Settings", order = 39)]
public sealed class WLIslandLookSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLIslandLookSettings";
static WLIslandLookSettings s_inst;
static bool s_tried;
public static WLIslandLookSettings Instance
{
get
{
if (s_inst == null && !s_tried) { s_tried = true; s_inst = Resources.Load<WLIslandLookSettings>(ResourcesPath); }
return s_inst;
}
}
public static void Invalidate() { s_inst = null; s_tried = false; }
// ───────────────────────────────── 마스터
[Header("되돌리기 (C8)")]
[Tooltip("0 이면 섬에 아무것도 얹지 않는다 = 지금(원본) 상태 100 %.")]
public int enabled_ = 1;
[Tooltip("1 이면 적용 결과를 Console 에 남긴다.")]
public int verboseLog = 1;
// ───────────────────────────────── §0 룩
[Header("§0 — 실게임 섬(Level01)에 데모 룩 적용")]
[Tooltip("1 이면 섬 씬 로드 직후 머티리얼을 데모 톤 복사본으로 바꾼다(씬 파일 무변경 · 런타임만).")]
public int applyLook = 1;
[Tooltip("1 이면 조명·앰비언트·스카이박스도 데모/아레나 값으로 맞춘다.")]
public int applyLighting = 1;
[Tooltip("1 이면 아레나와 같은 `WL_ReferenceLook`(외곽선·대비·무드)을 섬에도 얹는다. " +
"🔴 816ze — 데모 씬에는 이 보정이 없다. 데모와 같은 룩을 원하면 0.")]
public int applyReferenceLook = 0;
[Tooltip("섬 타일 셰이더 자체의 외곽선 패스(`_OUTLINESENABLED`). " +
"🔴 816ze — 데모 `Terrain.mat` 에는 외곽선 프로퍼티가 아예 없다 → 기본 0(= 전사본 에셋 상태 그대로). " +
"1 로 두면 `islandTopMaterial` 의 외곽선 키워드를 런타임에 **다시 켠다**(되돌리기용).")]
public int islandOutlineEnabled = 0;
[Tooltip("머티리얼 교체 표. 한 줄 = 원본 → 복사본.")]
public WLMatRemap[] materialRemap = new WLMatRemap[0];
[Tooltip("섬 타일 윗면(걸어다니는 면)에 쓸 머티리얼. 비우면 위 표의 Island01 줄을 쓴다. " +
"체크무늬 두 초록을 데모 잔디색 한 가지로 합친 것.")]
public Material islandTopMaterial;
[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;
// ── 816r: 밭 흙색을 데모 흙 톤으로 「옮긴다」 (곱셈 틴트로는 못 가는 색)
[Tooltip("0 = 816f 상태(곱셈 틴트만) · 1 이면 아래 목표색으로 밭 바닥색을 옮긴다. " +
"칸 차이(어디를 갈았나)와 마름↔젖음 밝기비는 그대로 보존한다.")]
public int soilTargetEnabled = 1;
[Tooltip("밭 「마름」 바닥의 목표색(리니어). 816r 실측 — 데모 흙 화면색에 맞춘 값. " +
"젖은 흙은 이 색에 원본의 젖음/마름 밝기비를 곱해 자동으로 어두워진다.")]
public Color soilTargetColor = new Color(0.3049f, 0.4033f, 0.2961f, 1f);
[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);
public Color ambientGround = new Color(0.047f, 0.043f, 0.035f, 1f);
public float ambientIntensity = 1f;
[Tooltip("빌트인 Default-Skybox. 비우면 스카이박스는 안 건드린다.")]
public Material skybox;
public Color dirLightColor = Color.white;
public float dirLightIntensity = 1f;
[Tooltip("1 = Soft(데모) · 0 = 원본(Hard) 유지.")]
public int dirLightSoftShadows = 1;
[Tooltip("1 이면 안개를 끈다(데모 = off).")]
public int fogOff = 1;
[Tooltip("1 이면 FI `GraphicsManager` 를 끈다. 이 컴포넌트는 Play 중 FI 의 URP 에셋 파일에 " +
"renderScale 을 **직접 써 넣어** `Assets/FarmingIsland/**` 를 더럽힌다(816a 실측). " +
"우리 프로젝트의 활성 URP 에셋이 아니라 화면에는 영향 0.")]
public int disableFiGraphicsManager = 1;
[Tooltip("런타임에 뒤늦게 생기는 오브젝트(상인·작물·아이템)까지 잡기 위해 다시 훑는 시각(초).")]
public float[] rescanAtSeconds = new float[] { 0.5f, 2f, 5f };
// ───────────────────────────────── §816h-1 하늘 범위
[Header("§816h — 하늘(RenderSettings)은 「활성 씬」 소유다")]
[Tooltip("🔴 816f/816h 실측 — 섬·던전·전투 맵은 전부 `InGame` 위에 **Additive** 로 붙는다. " +
"그래서 앰비언트·스카이박스·안개는 붙는 씬이 아니라 **활성 씬(InGame)** 것이 쓰인다. " +
"1 이면 아래 `skyScenes` 에 들어간 맵에 들어갈 때만 활성 씬의 하늘을 **스냅샷 뜨고** 우리 값으로 바꾸고, " +
"그 맵에서 나가면 **스냅샷 그대로 되돌린다**(= 다른 맵 오염 0).")]
public int skyScopeEnabled = 1;
[Tooltip("하늘을 통일할 맵. 섬과 던전이 같은 하늘을 쓰게 한다. 전투 맵은 여기 넣지 않는다(원래대로).")]
public string[] skyScenes = new string[] { "Level01", "WL_FarmLook", "WL_Dungeon01", "WL_Dungeon02" };
[Tooltip("맵을 바꿀 때 `SceneInfo` 는 내리기와 올리기를 같은 프레임에 시작한다(실측). " +
"이만큼 기다린 뒤에도 범위 맵이 하나도 없을 때만 하늘을 반납한다 — 섬↔던전 이동에서 한 번 튀는 것을 막는다.")]
public float skyReleaseDelaySeconds = 1.5f;
// ───────────────────────────────── §816h-2 물
[Header("§816h — 섬의 물을 WL 워터 셰이더로 (PD 지시)")]
[Tooltip("0 = 지금(FarmingIsland 물 · 100 % 복귀) · 1 = WL 워터(`ToonWaterU`).")]
public int waterMode = 1;
[Tooltip("교체 대상 판별 — FarmingIsland 원본 물 머티리얼(참조만 · 0줄 수정).")]
public Material waterFrom;
[Tooltip("WL 워터 복사본(`Assets/WL/Materials/WL_Water_Ocean.mat` 기준). 비우면 교체하지 않는다.")]
public Material waterTo;
// ───────────────────────────────── §816j2 섬 둘레 거품 띠
[Header("§816j2 — 섬 둘레 흰 거품 띠 (PD 「둘레에 띠를 둘러줘」)")]
[Tooltip("0 = 띠 없음(816j 상태로 100 % 복귀) · 1 = 섬 둘레를 따라 메시 띠 1장(드로우콜 +1).")]
public int shoreFoamEnabled = 1;
[Tooltip("띠에 쓰는 머티리얼(URP Unlit · 반투명). 비우면 띠를 만들지 않는다.")]
public Material shoreFoamMaterial;
[Tooltip("띠가 물 쪽으로 뻗는 폭(m). 캐릭터 키 1.19 m · 섬 타일 8 m 기준.")]
public float shoreFoamWidth = 1.6f;
[Tooltip("섬 안쪽으로 겹쳐 들어가는 길이(m) — 물가에 틈이 생기지 않게. 섬 벽이 가려 준다.")]
public float shoreFoamInner = 0.5f;
[Tooltip("띠 메시의 격자 간격(m). 크면 싸고 거칠다. 비용이 문제면 이것부터 키운다.")]
public float shoreFoamCell = 0.5f;
[Tooltip("물 표면보다 얼마나 위에 깔 것인가(m). 너무 작으면 z-fighting.")]
public float shoreFoamY = 0.02f;
[Tooltip("씬에서 `Water` 를 못 찾았을 때 쓸 물 높이(m).")]
public float shoreFoamFallbackY = -1f;
[Tooltip("거품 무늬가 해안을 따라 반복되는 길이(m).")]
public float shoreFoamTileLength = 6f;
[Tooltip("거품이 흐르는 속도(1 = 초당 무늬 1칸). 0 이면 정지.")]
public float shoreFoamScroll = 0.06f;
// ───────────────────────────────── §1 풀밭
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
[Tooltip("0 이면 풀을 깔지 않는다.")]
public int grassEnabled = 1;
[Tooltip("흩뿌릴 것들. 같은 density 를 가진 줄끼리 한 층으로 묶여 확률 분배된다(데모와 같은 방식).")]
public WLScatterDef[] scatter = new WLScatterDef[0];
[Tooltip("격자 흔들기(격자 칸 단위). 데모 = 0.5.")]
[Range(0f, 0.5f)] public float positionVariance = 0.5f;
[Tooltip("크기 흔들기. 데모 = 0.2 → ×0.8~1.2.")]
[Range(0f, 0.9f)] public float scaleVariance = 0.2f;
[Tooltip("1 이면 풀 하나하나를 Y 축으로 랜덤 회전한다(데모는 회전 0 = 빌보드라 불필요). " +
"빌보드 셰이더에는 영향이 없다.")]
public int randomYaw = 0;
[Header("§1 — 풀을 깔지 않는 곳 (제외 규칙)")]
[Tooltip("섬 타일 가장자리에서 이만큼(m) 안쪽까지만 깐다. 물 위로 삐져나오지 않게.")]
public float edgeInset = 0.15f;
[Tooltip("길(Road 메시)에서 이만큼(m) 떨어뜨린다.")]
public float roadMargin = 0.15f;
[Tooltip("농사 타일(Farm 트리거 · Soil 1×1)에서 이만큼(m) 떨어뜨린다.")]
public float farmMargin = 0.35f;
[Tooltip("1 이면 밭 자리 제외 사각형의 네 모서리를 깎고 경계를 노이즈로 흔들어 " +
"풀이 흙 가장자리로 파고들게 한다. 0 이면 예전처럼 반듯한 사각형.")]
public int farmEdgeRoundEnabled = 1;
[Tooltip("밭 자리 모서리를 깎는 반지름(m). 클수록 둥글게 읽힌다.")]
public float farmEdgeRadius = 1.0f;
[Tooltip("밭 자리 경계를 흔드는 진폭(m). 이만큼 풀이 파고들거나 물러난다.")]
public float farmEdgeNoise = 0.4f;
[Tooltip("경계 노이즈의 셀 크기(m). 작을수록 잘게 들쭉날쭉해진다.")]
public float farmEdgeNoiseCell = 0.5f;
// ── 816zd: 땅은 각진 그대로 두고 **풀 카펫의 바깥 윤곽만** 둥글게 (PD 2026-09-15)
[Header("§1-816zd — 섬 바깥 윤곽 둥근 풀 배치 (땅은 무변경)")]
[Tooltip("1 이면 풀을 깔 넓이를 「섬 윗면 footprint 를 반지름만큼 **열고**(침식 후 팽창) " +
"바깥으로 넘침폭만큼 넓힌」 모양으로 정한다. 볼록 모서리가 반지름만큼 둥글어져 " +
"각진 타일 위에서도 풀 카펫의 바깥선만 둥글게 읽힌다. 0 이면 예전처럼 타일 마스크 + edgeInset.")]
public int outlineRoundEnabled = 1;
[Tooltip("모서리를 둥글게 깎는 반지름(m). 클수록 모서리에 풀이 더 비고 윤곽이 더 둥글다.")]
public float outlineRoundRadius = 1.5f;
[Tooltip("둥글린 윤곽을 바깥으로 넓히는 폭(m). 기준선은 위 edgeInset 이다 — 같은 값이면 바깥선이 " +
"지금과 똑같고(모서리만 둥글어짐), 그보다 크면 차이만큼 가장자리 풀이 절벽 위로 넘친다.")]
public float outlineOverhang = 0.3f;
[Tooltip("윤곽 계산 격자 칸 크기(m). 작을수록 윤곽선이 매끄럽지만 다시 깔 때 더 오래 걸린다.")]
public float outlineRoundCell = 0.25f;
[Tooltip("건물·소품 콜라이더에서 이만큼(m) 떨어뜨린다.")]
public float propMargin = 0.05f;
[Tooltip("이 크기(m) 이상인 콜라이더는 섬 본체로 보고 제외 대상에서 뺀다.")]
public float propMaxSize = 7.5f;
[Header("§1 — 모바일 예산")]
[Tooltip("인스턴스 총개수 상한. 넘으면 밀도를 자동으로 낮춘다(0 = 무제한).")]
public int maxInstances = 40000;
[Tooltip("섬이 확장되면 다시 깐다 — 이 주기(초)로 잠긴 섬이 열렸는지 본다. 0 이면 감시 안 함.")]
public float rebuildPollSeconds = 1f;
[Tooltip("확장 애니메이션(DOTween 1초)이 끝나기를 기다리는 초.")]
public float rebuildDelaySeconds = 1.4f;
// ───────────────────────────────── §2 구름 경계 배치 (816o)
[Header("§2 — 구름 그림자 경계에만 풀을 심는다 (816o · PD 「모바일 부하 없게」)")]
[Tooltip("🔴 되돌리기 스위치 — 0 이면 816n 상태 100 % 복귀(섬 전체에 균일 격자). " +
"1 이면 바닥 색 띠가 바뀌는 곳(구름 그림자 경계) 근처에만 심는다. " +
"816n 실측: 보이는 풀의 88 % 가 이 띠에 몰려 있다 = 나머지는 삼각형만 먹는다.")]
public int cloudEdgeEnabled = 1;
[Tooltip("경계 판정 격자 칸(m). 띠 폭의 최소 단위 = 이 값. 여기는 노이즈를 계산하지 않는다(보간).")]
public float cloudEdgeCell = 0.3f;
[Tooltip("판정 격자 칸 수 상한. 섬이 커지면 칸을 자동으로 키운다(0 = 무제한).")]
public int cloudEdgeMaxCells = 120000;
[Tooltip("🔴 비용이 나는 유일한 곳 — 구름 노이즈(6 옥타브)를 실제로 계산하는 간격(m). " +
"구름의 가장 작은 무늬가 1/(32×_Cloud_Density) ≈ 3 m 라 1 m 면 충분하다. " +
"사이 값은 겹선형 보간으로 채운다.")]
public float cloudEdgeSampleCell = 1f;
[Tooltip("노이즈를 계산하는 점의 개수 상한. 섬이 커지면 간격을 자동으로 늘려 비용을 묶어 둔다.")]
public int cloudEdgeMaxSamples = 4096;
[Tooltip("경계에서 몇 칸 더 넓힐 것인가. 띠 폭 ≈ (1 + 2 × 이 값) × 칸(m). " +
"구름이 흐르는 만큼(`cloudEdgeDriftCover`)은 여기에 자동으로 더해진다.")]
public int cloudEdgeDilate = 1;
[Tooltip("1 이면 갱신 주기 동안 구름이 흐르는 거리만큼 띠를 자동으로 더 넓힌다 — " +
"띠가 움직여도 풀이 뒤따라오지 못해 생기는 빈틈을 막는다. 0 이면 위 값만 쓴다.")]
public int cloudEdgeDriftCover = 1;
[Tooltip("구름이 흐르므로 이 주기(초)마다 띠를 다시 고른다. 0 이면 한 번 굽고 고정. " +
"🔴 실제 주기는 `rebuildPollSeconds` 단위로 반올림된다.")]
public float cloudEdgeRefreshSeconds = 1f;
[Tooltip("띠 안의 밀도 배율. 면적이 줄어든 만큼 띠를 더 촘촘히 해 데모 느낌을 살린다. " +
"1 = 816n 과 같은 밀도(= 인스턴스가 띠 면적 비율만큼 그대로 줄어든다).")]
public float cloudEdgeDensityScale = 1.25f;
[Tooltip("구름 값을 읽어 올 머티리얼. 비우면 `islandTopMaterial`(바닥)을 쓴다 — " +
"바닥과 **같은 식**이어야 띠가 어긋나지 않는다.")]
public Material cloudEdgeSourceMaterial;
// ───────────────────────────────── §3 가시 범위 배치 (816q)
[Header("§3 — 카메라가 보는 범위에만 인스턴스를 둔다 (816q · 배치가 아니라 범위로 최적화)")]
[Tooltip("🔴 되돌리기 스위치 — 0 이면 섬 전체에 인스턴스를 둔다(816n 상태). " +
"1 이면 카메라 시야 사각형 + 여유 폭 안에만 둔다. " +
"격자는 월드 고정이라 같은 자리의 풀은 항상 같은 자리다(순간이동 0) — " +
"늘고 주는 것은 **화면 밖 여유 폭 안에서만** 일어난다.")]
public int viewCullEnabled = 1;
[Tooltip("시야 사각형 바깥으로 더 두는 여유 폭(m). " +
"🔴 `viewRebuildStep` 보다 커야 한다 — 차이만큼이 「화면 밖에서만 바뀐다」는 보장 폭이다.")]
public float viewMargin = 4f;
[Tooltip("카메라가 이만큼(m) 움직였을 때만 다시 고른다. " +
"작을수록 자주 고르고, 클수록 여유 폭을 많이 먹는다.")]
public float viewRebuildStep = 1.5f;
[Tooltip("카메라가 이만큼(°) 돌았을 때도 다시 고른다. 회전은 시야를 통째로 돌리므로 위치보다 민감하다.")]
public float viewRebuildAngle = 3f;
[Tooltip("시야 사다리꼴의 먼 쪽을 이 거리(m)에서 끊는다. 하늘을 향하는 모서리도 여기로 자른다. " +
"PD 구도(12 m·45°)에서 화면 위끝의 바닥은 약 32 m 라 40 이면 남는다.")]
public float viewMaxDistance = 40f;
[Tooltip("시야 사각형을 다시 재는 주기(초). 0 이면 감시 안 함(한 번 고르고 고정).")]
public float viewPollSeconds = 0.25f;
// ───────────────────────────────── 구운 마스크
[Header("§1 — 구운 윗면 마스크 (에디터에서 생성 · 손대지 말 것)")]
public WLTileMask[] tileMasks = new WLTileMask[0];
public WLTileMask[] roadMasks = new WLTileMask[0];
// ───────────────────────────────── 섬 씬 판별
[Header("어느 씬에 적용하는가")]
[Tooltip("이 이름의 씬에 적용한다(비워도 IslandManager 가 있으면 섬으로 본다).")]
public string[] islandSceneNames = new string[] { "Level01", "WL_FarmLook" };
public WLTileMask FindTileMask(string meshName) { return Find(tileMasks, meshName); }
public WLTileMask FindRoadMask(string meshName) { return Find(roadMasks, meshName); }
static WLTileMask Find(WLTileMask[] a, string n)
{
if (a == null || string.IsNullOrEmpty(n)) return null;
// `MeshFilter.mesh` 로 읽으면 이름 뒤에 " Instance" 가 붙는다 — 떼고 비교한다.
int k = n.IndexOf(" Instance");
if (k > 0) n = n.Substring(0, k);
for (int i = 0; i < a.Length; i++) if (a[i] != null && a[i].meshName == n) return a[i];
return null;
}
public void Log(string msg) { if (verboseLog != 0) Debug.Log("[WL-816e] " + msg); }
}
}