771 lines
53 KiB
C#
771 lines
53 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
||
// 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;
|
||
[Tooltip("1 = 이 정의는 경계(섬 윤곽·흙 경계·소품) 근처 `grassEdgeBand` 띠에만 깐다 — 들판 한가운데의 얼룩덜룩함 제거(2026-09-15 PD). 0 = 어디나.")]
|
||
public int edgeOnly_ = 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 816zj — 데모 인스턴서(`TerrainInstancesBehaviour.InstancingSettings`) 한 줄의 값. Demo.unity 1284~1301 과 같은 구성.
|
||
/// 메시는 `scatter[scatterIndex]` 가 이미 참조하는 데모 메시를 쓰고, 재질은 §0-d 의 데모 원본 재질 필드를 쓴다.
|
||
/// </summary>
|
||
[Serializable]
|
||
public sealed class WLDemoScatter
|
||
{
|
||
[Tooltip("메시를 가져올 `scatter[]` 줄 번호(0부터). 기본 = 풀 0 · 꽃 1 · 자갈 2.")] public int scatterIndex = 0;
|
||
[Tooltip("같은 층 안에서의 가중치. 데모 = 풀 100 : 꽃 1 · 자갈 1.")] public float probability = 1f;
|
||
[Tooltip("메시 배율. 데모 = 풀·꽃 0.35 · 자갈 0.7.")] public float scale = 0.35f;
|
||
[Tooltip("법선 방향으로 띄우는 거리(m). 데모 = 풀·꽃 0.04 · 자갈 0.1.")] public float normalOffset = 0.04f;
|
||
|
||
public WLDemoScatter() { }
|
||
public WLDemoScatter(int index, float prob, float scl, float offset) { scatterIndex = index; probability = prob; scale = scl; normalOffset = offset; }
|
||
}
|
||
|
||
[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;
|
||
[Tooltip("태양(디렉셔널) 색. 2026-09-16 PD 「광원이 부족 — 자연스러운 태양으로 화사하게」 → 살짝 따뜻한 흰색.")]
|
||
public Color dirLightColor = new Color(1f, 0.97f, 0.9f, 1f);
|
||
[Tooltip("태양 세기. 데모 1.0 → PD 「화사하게」 1.15(SSAO 를 끈 뒤 기준 · 잔디 하이라이트가 흰색으로 날리지 않는 상한 근처).")]
|
||
public float dirLightIntensity = 1.15f;
|
||
[Tooltip("1 = Soft(데모) · 0 = 원본(Hard) 유지.")]
|
||
public int dirLightSoftShadows = 1;
|
||
[Tooltip("태양 그림자 진하기(0~1). 음수 = 씬 값 그대로(FI 원본 0.5). 2026-09-16 PD 「화사하게」 → 0.45.")]
|
||
public float dirLightShadowStrength = 0.45f;
|
||
[Tooltip("섬 씬에 켜진 디렉셔널 라이트가 하나도 없으면 `~WL_Sun` 을 만들어 위 값으로 둔다(회전 = 데모 50/330/0). 0 = 만들지 않음.")]
|
||
public int dirLightEnsure = 1;
|
||
|
||
// ── 2026-09-16 PD 「풀이 얼룩덜룩 — 원인을 더 찾아라」 → A/B 실측: SSAO 가 풀잎마다 발밑 그늘을 만든 것(국소 질감 0.70 → 0.07)
|
||
[Header("§0-e — 섬에서는 SSAO 를 끈다 (렌더러 에셋 0줄 · 나갈 때 복원)")]
|
||
[Tooltip("1 = 아래 씬이 떠 있는 동안 URP 렌더러의 SSAO 피처를 끈다(전투 맵은 그대로). 0 = 손대지 않음.")]
|
||
public int islandSsaoOff = 1;
|
||
[Tooltip("SSAO 를 끌 씬 이름(섬만 · 던전은 아레나 룩 그대로).")]
|
||
public string[] ssaoOffScenes = new[] { "Level01", "WL_FarmLook" };
|
||
[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). 크면 싸고 거칠다. 비용이 문제면 이것부터 키운다. 2026-09-16 물가선 모드: 0.5 → 0.25(경계 계단 완화 · 삼각형 ≈ 3천).")]
|
||
public float shoreFoamCell = 0.25f;
|
||
|
||
[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;
|
||
|
||
// ── 2026-09-16 PD 「물이 섬 경계에 닿을 때 자연스러운 물보라가 안 보인다」 — 데모 터레인 모드에서는 타일 발자국 대신 **터레인 물가선**(지형 높이 = 물 높이)을 따른다
|
||
[Tooltip("데모 터레인 모드의 물가 띠 — 물가선에서 **모래 쪽**으로 이만큼(m · 수평 거리)까지 거품이 올라온다. " +
|
||
"2026-09-16 PD 「가장자리가 각졌다」: 높이 차로 정하던 띠가 평평한 곳에서 흰 계단 덩어리가 됐다 → 수평 거리(어디서나 같은 폭)로 바꿈.")]
|
||
public float shoreFoamUp = 0.3f;
|
||
|
||
[Tooltip("데모 터레인 모드의 물가 띠 — 물가선에서 **바다 쪽**으로 이만큼(m · 수평 거리)까지 메시가 뻗는다. 보이는 거품은 그 60 % 안(알파 제곱 감쇠)이고 끝은 투명 → 메시 경계가 안 보인다. " +
|
||
"2026-09-16 실측: 1.2 m 에 알파가 끝까지 남아 격자 칸 경계가 네모 계단으로 보였다 → 2 m.")]
|
||
public float shoreFoamDown = 2f;
|
||
|
||
// ───────────────────────────────── §816zf 부드러운 흙 데칼
|
||
[Header("§816zf — 흙 자리를 데모처럼 「둥글게 번지게」 (PD 2026-09-15)")]
|
||
[Tooltip("0 = 지금(각진 1×1 흙 타일 · 100 % 복귀) · 1 = 밭 구역을 덮는 **부드러운 흙 데칼 1장**" +
|
||
"(드로우콜 +1)으로 바꾸고 흙 타일 렌더러는 숨긴다(`WLIslandSettings.showFarmSoil` 과 배타). " +
|
||
"816za 풀 제외도 이 데칼과 **같은 경계**를 쓰게 된다.")]
|
||
public int softDirtEnabled = 1;
|
||
|
||
[Tooltip("데칼에 쓸 머티리얼. **비워 두는 것이 기본** — 비우면 816j2 거품 띠와 같은 URP Unlit 셰이더로 " +
|
||
"런타임에 반투명 머티리얼을 만든다(에셋 0줄). 채우면 그 머티리얼의 복사본을 쓴다.")]
|
||
public Material softDirtMaterial;
|
||
|
||
[Tooltip("흙 색(리니어) — 816r 데모 흙 톤 `#A4A88B` 를 리니어로 옮긴 값.")]
|
||
public Color softDirtColor = new Color(0.371f, 0.392f, 0.258f, 1f);
|
||
|
||
[Tooltip("데칼 전체의 불투명도. 1 = 흙색 그대로 · 낮추면 밑의 잔디가 비쳐 흙이 옅어진다.")]
|
||
[Range(0f, 1f)] public float softDirtOpacity = 1f;
|
||
|
||
[Tooltip("흙칸 윗면보다 얼마나 위에 깔 것인가(m). 너무 작으면 z-fighting, 너무 크면 떠 보인다.")]
|
||
public float softDirtY = 0.02f;
|
||
|
||
[Tooltip("밭 구역 바운즈를 사방으로 이만큼(m) 넓혀 흙으로 친다. 데칼 가장자리가 페이드로 " +
|
||
"물러나는 만큼을 메워, 흙 자리가 지금보다 좁아 보이지 않게 한다.")]
|
||
public float softDirtExpand = 0.2f;
|
||
|
||
[Tooltip("흙 사각형의 네 모서리를 깎는 반지름(m). 클수록 둥글게 번진 모양으로 읽힌다.")]
|
||
public float softDirtCornerRadius = 1.4f;
|
||
|
||
[Tooltip("흙 경계를 흔드는 진폭(m). 이만큼 흙이 풀 쪽으로 밀려나거나 물러나 경계가 들쭉날쭉해진다.")]
|
||
public float softDirtNoise = 0.45f;
|
||
|
||
[Tooltip("경계 노이즈의 셀 크기(m). 작을수록 잘게 들쭉날쭉하고, 클수록 크게 굽이친다.")]
|
||
public float softDirtNoiseCell = 0.9f;
|
||
|
||
[Tooltip("가장자리가 흙 → 풀로 사라지는 폭(m). 데모의 스플랫 번짐에 해당한다(0.8~1.2 권장).")]
|
||
public float softDirtFeather = 1.0f;
|
||
|
||
[Tooltip("풀을 데칼 가장자리보다 이만큼(m) **안쪽까지** 들여보낸다 — 흙 가장자리에 풀 톱니가 " +
|
||
"파고든 것처럼 보이게 하는 값. 0 이면 풀이 흙 경계에서 딱 끊긴다.")]
|
||
public float softDirtGrassBite = 0.3f;
|
||
|
||
[Tooltip("데칼 사각형을 구역 바깥으로 더 두는 여유 폭(m). 페이드+노이즈가 잘리지 않을 만큼 " +
|
||
"자동으로 커진다 — 보통 손댈 일이 없다.")]
|
||
public float softDirtPad = 1.5f;
|
||
|
||
[Tooltip("마스크 텍스처 한 변의 픽셀 수. 256 = 256 KB · 굽는 데 한 번 몇 ms. " +
|
||
"경계가 뭉개져 보이면 512 로 올린다.")]
|
||
public int softDirtTexRes = 256;
|
||
|
||
[Tooltip("흙 얼룩의 세기(0~1). 한 덩어리 단색으로 보이지 않게 밝기를 이만큼 흔든다.")]
|
||
[Range(0f, 1f)] public float softDirtTexNoise = 0.08f;
|
||
|
||
// ───────────────────────────────── §0-b 바다 바닥 (2026-09-15 Lead 실측)
|
||
[Header("§0-b — 물 밑 바닥판 (데모 연못은 바닥이 얕아 물이 청록 · 우리는 바닥이 없어 배경색이 비쳐 진파랑)")]
|
||
[Tooltip("1 = 물 표면 아래에 모래색 바닥판 1장을 깐다. 데모 물 셰이더는 깊이로 색·거품을 정하므로 바닥이 있어야 데모처럼 청록 물 + 흰 물가가 나온다. 0 = 없음(배경색이 비침).")]
|
||
public int seabedEnabled = 1;
|
||
|
||
[Tooltip("물 표면에서 바닥판까지 깊이(m). 얕을수록 청록·거품이 진해진다.")]
|
||
public float seabedDepth = 0.8f;
|
||
|
||
[Tooltip("바닥판 한 변 길이(m). 카메라가 보는 범위만 덮으면 된다.")]
|
||
public float seabedSize = 400f;
|
||
|
||
[Tooltip("바닥판 색(언릿 · sRGB). 기본 = 데모 모래색.")]
|
||
public Color seabedColor = new Color(0.71f, 0.706f, 0.549f, 1f);
|
||
|
||
// ───────────────────────────────── §0-c 물가 스커트 (WL-816zg · PD 「물은 진행」 2026-09-15)
|
||
[Header("§0-c — 섬 둘레 완만한 모래 경사(물가 스커트) — 데모 연못처럼 가장자리가 모래 경사로 물에 잠긴다")]
|
||
[Tooltip("1 = 열린 타일 발자국 바깥에 거리장 기반 모래 경사 메시 1장(드로우콜 +1)을 깐다 — 둥근 윤곽 · 타일 수직 옆면 가림 · " +
|
||
"흰 물가 띠는 물 셰이더가 깊이로 만든다. 0 = 없음(지금 상태 100 % 복귀).")]
|
||
public int shoreSkirtEnabled = 1;
|
||
|
||
[Tooltip("경사에 쓸 머티리얼(선택). 비우면 `WL/Shore Skirt` 셰이더로 런타임 머티리얼을 만든다(에셋 0줄). " +
|
||
"🔴 모바일 빌드에 이 셰이더가 들어가게 하려면 그 셰이더를 쓰는 머티리얼을 여기에 넣어 두는 것이 가장 확실하다(Always Included 대신).")]
|
||
public Material shoreSkirtMaterial;
|
||
|
||
[Tooltip("타일 가장자리에서 바깥(물 쪽)으로 뻗는 경사 폭(m).")]
|
||
public float skirtWidth = 3.0f;
|
||
|
||
[Tooltip("경사 바깥 끝의 높이(월드 y · m). 물(−1.0) 아래 · 바닥판(물 − seabedDepth = −1.8) 위 또는 같게.")]
|
||
public float skirtBottomY = -1.8f;
|
||
|
||
[Tooltip("타일 가장자리에서 이 거리(m)까지는 풀색(islandTopMaterial `_DiffuseColor`), 그 뒤 모래색(softDirtColor)으로 넘어간다.")]
|
||
public float skirtGrassBand = 0.6f;
|
||
|
||
[Tooltip("풀색 → 모래색으로 넘어가는 폭(m).")]
|
||
public float skirtSandBlend = 0.6f;
|
||
|
||
[Tooltip("메시 격자 칸(m). 🔴 타일 한 변(8 m)을 나누는 값이어야 정점이 타일 가장자리 선 위에 정확히 놓인다(0.5 · 0.25 · 1).")]
|
||
public float skirtCell = 0.5f;
|
||
|
||
[Tooltip("타일 안쪽으로 이 거리(m)까지만 정점을 둔다(가장자리 밑으로 살짝 들어가 틈을 막는다). 그 안쪽은 만들지 않는다(윗면 z-fight 방지).")]
|
||
public float skirtInnerKeep = 0.3f;
|
||
|
||
[Tooltip("타일 윗면보다 이만큼(m) 낮은 곳에서 경사를 시작한다 — 윗면과 같은 높이면 z-fight. 타일 옆면이 그만큼만 보인다.")]
|
||
public float skirtTopDrop = 0.04f;
|
||
|
||
[Tooltip("1 = 타일 윗면 높이를 아래 `skirtTopY` 고정값으로(Lead 실측 0 · 확장 애니메이션 중에도 흔들리지 않는다) · " +
|
||
"0 = 열린 타일 렌더러 bounds 윗면을 실측해 쓴다(로그에 두 값이 같이 찍힌다).")]
|
||
public int skirtTopMode = 1;
|
||
|
||
[Tooltip("타일 윗면 높이 고정값(월드 y · m). 실측(2026-09-15) = 0. 실측 모드에서 렌더러를 못 찾을 때의 대체값이기도 하다.")]
|
||
public float skirtTopY = 0f;
|
||
|
||
[Tooltip("물에 잠긴 정점(y < 물 높이)의 색 배율 — 젖은 모래·물속 어둡기.")]
|
||
public float skirtUnderwaterMul = 0.85f;
|
||
|
||
[Tooltip("풀색 대체값(sRGB). 기본은 `islandTopMaterial`(Farm_IslandTop_Demo3)의 `_DiffuseColor` 를 읽고, 없을 때만 이 값을 쓴다. " +
|
||
"기본 = 816ze 들판 실측 0.62/0.82/0.485.")]
|
||
public Color skirtGrassFallback = new Color(0.62f, 0.82f, 0.485f, 1f);
|
||
|
||
[Tooltip("거리장·메시 격자 정점 수 상한. 넘으면 칸을 두 배씩 키운다(섬이 아무리 커져도 비용이 묶인다).")]
|
||
public int skirtMaxGridNodes = 400000;
|
||
|
||
[Tooltip("1 = 스커트를 타일과 **같은 Toon 재질**로 그린다(풀 = islandTopMaterial 그대로 · 모래 = 그 복사본에 모래색) → " +
|
||
"구름 그림자·조명이 타일과 똑같이 떨어져 이음새가 뜨지 않는다(2026-09-16 PD 「가장자리 경계가 붕 뜬다」 실측: 언릿 스커트가 구름 아래 타일보다 최대 15 % 밝았다). " +
|
||
"0 = 전용 언릿 정점색 셰이더(그라데이션은 부드럽지만 구름을 못 받는다).")]
|
||
public int skirtToonMode = 1;
|
||
|
||
[Tooltip("Toon 모드 모래 재질의 그늘색 배율(모래색 × 이 값) — 경사의 그늘진 면.")]
|
||
public float skirtSandShadowMul = 0.72f;
|
||
|
||
[Tooltip("Toon 모드에서 풀/모래 경계선을 칸마다 흔드는 폭(m) — 직선이 아니라 들쭉날쭉한 물가. 0 = 직선.")]
|
||
public float skirtEdgeJitter = 0.6f;
|
||
|
||
[Tooltip("풀 구역 모서리 둥글림 반지름(m). 모래가 타일 모서리를 이 반지름으로 파고들어(열기 연산) 각진 타일 윤곽을 감춘다 — " +
|
||
"타일 윗면 위(+1 cm)에 모래를 얹는다. 풀 인스턴서 `outlineRoundRadius`(3)와 맞추면 풀잎도 같은 윤곽 안에만 깔린다. 0 = 각진 그대로(2026-09-16 PD 「각진 지형이 남는다」).")]
|
||
public float skirtGrassRound = 3f;
|
||
|
||
[Tooltip("풀/모래 경계선을 굽이치게 하는 진폭(m). 직선 변을 파도처럼 들쭉날쭉하게(값 노이즈 · 결정적). 0 = 직선.")]
|
||
public float skirtWaveAmp = 0.8f;
|
||
|
||
[Tooltip("굽이의 크기(m) — 노이즈 격자 한 칸. 클수록 완만한 큰 굽이.")]
|
||
public float skirtWaveCell = 2.5f;
|
||
|
||
// ───────────────────────────────── §0-d 데모 터레인 조립 (WL-816zj · PD 「데모와 똑같은 결과」 2026-09-16)
|
||
[Header("§0-d — 섬 바닥 = 데모와 같은 부품(Unity Terrain + 데모 Terrain.mat + 데모 TerrainInstancesBehaviour)")]
|
||
[Tooltip("1 = 섬 씬 로드 시 런타임 Terrain(높이맵 = 열린 타일 발자국 · 스플랫 = 잔디/모래 · 콜라이더 없음)을 만들어 데모 `Terrain.mat` 과 " +
|
||
"데모 풀 인스턴서를 **그대로** 얹는다. 이때 §816zf 흙 데칼 · §0-b 바닥판 · §0-c 스커트 · §1 풀(WLIslandGrass)은 만들지 않고(값으로 상호 배타) " +
|
||
"타일 윗면 렌더러는 숨긴다(콜라이더 유지 = 걷기·밭·수확 그대로). 0 = 지금 구조 100 % 복귀.")]
|
||
public int demoTerrainEnabled = 1;
|
||
|
||
[Tooltip("데모 지형·풀·꽃 재질의 구름 그림자 강도를 이 값으로 덮어쓴 런타임 복사본을 쓴다(원본 무수정). " +
|
||
"2026-09-16 PD 「풀이 얼룩덜룩」 A/B 실측: 데모 원값 1.0 은 200 m 지형용이라 32 m 섬이 통째로 어두워졌다 → 0.3. 0 이하 = 원본 그대로.")]
|
||
public float demoCloudStrength = 0.5f; // 2026-09-16 PD 「데모 같은 그라데이션」 A/B: 0.25 는 안 보이고 0.5 가 데모의 구름 그늘 무늬(0.7 은 과함)
|
||
|
||
[Tooltip("데모 지형·풀·꽃 재질의 구름 무늬 밀도(월드 m 당 UV). 데모 원값 0.01 = 구름 한 덩이 100 m 라 32 m 섬은 절반이 통째로 덮인다 → 0.025(한 덩이 40 m · 섬 위에 밝음/그늘이 한 번씩). " +
|
||
"2026-09-16 PD 「섬 크기를 고려해 구름 그림자 조절」. 0 이하 = 원본 그대로.")]
|
||
public float demoCloudDensity = 0.025f;
|
||
|
||
[Tooltip("높이맵 해상도(2ⁿ+1 · 33~4097). 129 = 한 변 128 m 일 때 1 m 간격, 작은 섬이면 더 촘촘해진다.")]
|
||
public int terrainHeightRes = 129;
|
||
|
||
[Tooltip("스플랫(잔디/모래 가중치) 해상도(2ⁿ · 16~4096). 256 = 한 변 128 m 일 때 0.5 m 텍셀.")]
|
||
public int terrainAlphaRes = 256;
|
||
|
||
[Tooltip("열린 타일 발자국 바깥으로 두는 여백(m). 경사(terrainSlope) 밖은 바닥 높이의 평평한 바다 바닥이 된다(이 모드에서는 §0-b 바닥판을 만들지 않는다).")]
|
||
public float terrainMargin = 8f;
|
||
|
||
[Tooltip("터레인 한 변 상한(m). 발자국 + 여백이 이보다 크면 발자국 중심 기준으로 자른다.")]
|
||
public float terrainMaxSize = 128f;
|
||
|
||
[Tooltip("스플랫 잔디→모래 페이드 폭(m). 잔디 경계(둥글림+굽이)에서 이만큼에 걸쳐 1→0. " +
|
||
"2026-09-16 PD 「흙·풀 경계가 너무 드로잉된 느낌」 → 1 → 2(데모의 붓으로 칠한 듯한 넓은 경계).")]
|
||
public float terrainBlend = 2.0f;
|
||
|
||
[Tooltip("페이드 폭 중 잔디 경계 **안쪽**으로 들어오는 비율(0~0.5). 0.3 = 안쪽 0.6 m 부터 모래색이 스미기 시작 · 풀잎이 멈추는 선(잔디 50 %)은 경계 바깥 0.4 m 로 거의 그대로. " +
|
||
"0 = 예전처럼 경계 바깥으로만 페이드(풀잎 선이 폭/2 만큼 밖으로 밀린다).")]
|
||
[Range(0f, 0.5f)] public float terrainBlendInward = 0.3f;
|
||
|
||
[Tooltip("잔디 경계에 얹는 **잔 굽이**(m · 값 노이즈 2번째 옥타브). 큰 굽이(skirtWaveAmp/Cell)만 있으면 매끈한 곡선이라 「그려 넣은 선」처럼 보인다 → 붓 자국처럼 잘게 흔든다. 0 = 없음.")]
|
||
public float terrainEdgeNoiseAmp = 0.35f;
|
||
|
||
[Tooltip("잔 굽이 노이즈 격자(m).")]
|
||
public float terrainEdgeNoiseCell = 1.0f;
|
||
|
||
[Tooltip("지형 렌더 LOD 허용 오차(픽셀 · Unity Terrain heightmapPixelError · 기본 5). 2026-09-16 PD 「가장자리가 각졌다」: 5 면 12 m 카메라에서 해변 경사가 큰 삼각형으로 뭉개져 토온 음영이 각진 면으로 보였다 → 1.")]
|
||
public float terrainPixelError = 1f;
|
||
|
||
[Tooltip("경사면 굴곡(terrainBumpAmp)을 **물가선 근처에서는 0** 으로 죽이는 높이 폭(m). 물 높이 ±0.05 m 에서 0 → 이 값에서 100 %. " +
|
||
"굴곡이 물가선에 걸치면 물가선이 지그재그·계단이 되고 거품 띠가 덩어리진다(2026-09-16 실측). 0 = 안 죽임.")]
|
||
public float terrainBumpWaterNotch = 0.4f;
|
||
|
||
[Tooltip("경사 바깥 끝 = 바다 바닥 높이(월드 y · m). 물(−1.0) 아래.")]
|
||
public float terrainBottomY = -1.8f;
|
||
|
||
[Tooltip("잔디 경계에서 바닥까지 내려가는 경사의 폭(m). smoothstep — 윗면에서도 바닥에서도 접선이 수평. " +
|
||
"2026-09-16 PD 「가장자리 → 바다를 자연스러운 경사면으로」 → 3 → 5(물(−1)까지 약 2.6 m 의 완만한 해변).")]
|
||
public float terrainSlope = 5f;
|
||
|
||
// ── 2026-09-16 PD 「섬 가장자리와 바다로 이어지는 부분을 자연스러운 경사면으로 · 굴곡이 보이게」
|
||
[Tooltip("경사 폭을 자리마다 이 비율(±)로 흔든다(값 노이즈 · 격자 terrainSlopeVarCell). 0.35 = 폭 ×0.65~1.35 → 해안이 넓어졌다 좁아졌다 한다. 0 = 균일.")]
|
||
[Range(0f, 0.8f)] public float terrainSlopeVar = 0.25f; // 2026-09-16 「각짐」 완화: 0.35/5 m → 0.25/7 m(옆으로 꺾이는 주름이 완만)
|
||
|
||
[Tooltip("경사 폭 흔들기 노이즈 격자(m).")]
|
||
public float terrainSlopeVarCell = 7f;
|
||
|
||
[Tooltip("경사면 위에 얹는 능선·골 굴곡 진폭(m · 경사 양 끝·물가선에서 0). 데모 지형처럼 법선 음영이 굴곡을 드러낸다. 0 = 매끈한 smoothstep.")]
|
||
public float terrainBumpAmp = 0.18f;
|
||
|
||
[Tooltip("굴곡 노이즈 격자(m).")]
|
||
public float terrainBumpCell = 3f;
|
||
|
||
[Tooltip("경사 밖 바다 바닥의 완만한 굴곡 진폭(m · 경사 끝에서 2 m 에 걸쳐 서서히 커진다). " +
|
||
"🔴 2026-09-16 실측: 물 셰이더가 깊이 색을 계단으로 양자화해 바닥 굴곡의 등고선이 흰 계단 덩어리로 보였다 → 0(평평). 켜려면 0.05 이하 권장.")]
|
||
public float terrainSeabedBumpAmp = 0f;
|
||
|
||
[Tooltip("바다 바닥 굴곡 격자(m).")]
|
||
public float terrainSeabedBumpCell = 3.5f;
|
||
|
||
// ── 2026-09-16 PD 「잔디 얼룩은 사라졌지만 데모 같은 그라데이션이 안 보이고 그림자가 1단계뿐」 → 섬 윗면 둔덕(데모 언덕 음영)
|
||
[Tooltip("섬 윗면에 얹는 완만한 둔덕의 최대 높이(m · **위로만** 0~값). 데모 지형의 언덕처럼 토온 램프가 밝음/그늘 단계를 만든다. " +
|
||
"잔디 경계 근처(terrainReliefEdgeFade) · 밭 구역 · 설치 자리(플로우 단계 · 던전 입구 · 캠프 천막) 주변은 0(평평). 0 = 평지(지금 구조).")]
|
||
public float terrainReliefAmp = 0.5f;
|
||
|
||
[Tooltip("둔덕 노이즈 격자(m). 작을수록 잔둔덕이 많아지고 경사가 가팔라진다.")]
|
||
public float terrainReliefCell = 4.5f;
|
||
|
||
[Tooltip("잔디 경계에서 안쪽으로 이만큼(m)에 걸쳐 둔덕이 0 에서 자란다(해변 경사와 매끈하게 이어진다).")]
|
||
public float terrainReliefEdgeFade = 2.5f;
|
||
|
||
[Tooltip("설치 자리(플로우 단계 위치 · 던전 입구) 둘레 이 반지름(m) 안은 평평. 캠프파이어 자리는 천막 반지름 + 1.5 m 로 넓힌다.")]
|
||
public float terrainReliefFlatRadius = 3f;
|
||
|
||
[Tooltip("평평한 곳 → 둔덕으로 넘어가는 완충 폭(m).")]
|
||
public float terrainReliefFeather = 1.5f;
|
||
|
||
[Tooltip("1 = 발자국 안(타일 네모)에만 둔덕 높이를 따르는 MeshCollider 를 깐다 → 플레이어(CharacterController)가 둔덕 위를 걷는다. " +
|
||
"FI 타일 콜라이더는 그대로(가장자리 동작 불변). 0 = 콜라이더 없음(둔덕에 발이 묻힌다).")]
|
||
public int terrainReliefCollider = 1;
|
||
|
||
[Tooltip("데모 지형·풀 재질의 토온 램프 밝기 오프셋(`_Brightness`)을 이 값으로 덮어쓴 런타임 복사본을 쓴다(원본 0.25). " +
|
||
"0 이하 = 원본 그대로. 낮출수록 평지가 램프 단계 경계 가까이 내려와 작은 경사에도 단계가 갈린다(데모 언덕 음영이 잘 보인다).")]
|
||
public float demoTerrainBrightness = 0.12f; // 2026-09-16 A/B: 0.25(원본) 는 구름 그늘이 1단계 · 0.12 면 그늘 안에 2~3단계 띠 + 둔덕 경사면 음영이 드러난다
|
||
|
||
[Tooltip("높이맵 size.y(m) — 바닥(terrainBottomY)에서 위로 이만큼까지 표현한다(높이값 1.0 = 바닥 + 이 값). 타일 윗면(0)이 들어가면 된다.")]
|
||
public float terrainHeightSpan = 10f;
|
||
|
||
[Tooltip("1 = 높이도 둥글린·굽이친 잔디 경계(skirtGrassRound · skirtWaveAmp)에서 내려간다(발주서 값). " +
|
||
"🔴 FI 타일 콜라이더는 8 m 네모 그대로라 볼록 모서리(최대 약 0.9 m)·굽이가 안쪽인 곳(최대 0.8 m)에서는 발이 경사 위 허공에 뜬다(최대 약 0.3~0.4 m). " +
|
||
"0 = 높이는 타일 네모 가장자리에서 내려가고(콜라이더와 일치) 둥글림·굽이는 스플랫(잔디/모래 색 경계)만 따른다.")]
|
||
public int terrainRoundHeight = 1;
|
||
|
||
[Tooltip("데모 `Assets/3DPixelArtEnvironment/Materials/Terrain.mat` — 값 무수정 · 참조만. Terrain.materialTemplate.")]
|
||
public Material demoTerrainMaterial;
|
||
|
||
[Tooltip("층0 잔디 = 데모 `Demo/Demo_Grass.terrainlayer`(참조만).")]
|
||
public TerrainLayer demoGrassLayer;
|
||
|
||
[Tooltip("층1 모래 = 데모 `Demo/Demo_Dirt.terrainlayer`(참조만).")]
|
||
public TerrainLayer demoDirtLayer;
|
||
|
||
[Tooltip("풀 재질 = 데모 원본 `Instanced_Grass.mat`(guid a194cb4c…). scatter[] 의 WL 복사본이 아니라 원본을 가리킨다.")]
|
||
public Material demoGrassMaterial;
|
||
|
||
[Tooltip("꽃 재질 = 데모 원본 `Instanced_Flower.mat`(guid d50a40cb…).")]
|
||
public Material demoFlowerMaterial;
|
||
|
||
[Tooltip("자갈 재질 = 데모 원본 `Instanced_Gravel.mat`(guid 77d84437…).")]
|
||
public Material demoGravelMaterial;
|
||
|
||
[Tooltip("1층(잔디 위) 풀 — Demo.unity 1284~1288: 확률 100 · 크기 0.35 · 오프셋 0.04 · 메시 = scatter[0](Grass_Leaf).")]
|
||
public WLDemoScatter demoGrass = new WLDemoScatter(0, 100f, 0.35f, 0.04f);
|
||
|
||
[Tooltip("1층(잔디 위) 꽃 — Demo.unity 1289~1293: 확률 1 · 크기 0.35 · 오프셋 0.04 · 메시 = scatter[1](Flower_Leaf).")]
|
||
public WLDemoScatter demoFlower = new WLDemoScatter(1, 1f, 0.35f, 0.04f);
|
||
|
||
[Tooltip("2층(모래 위) 자갈 — Demo.unity 1297~1301: 확률 1 · 크기 0.7 · 오프셋 0.1 · 메시 = scatter[2](Stone_1).")]
|
||
public WLDemoScatter demoGravel = new WLDemoScatter(2, 1f, 0.7f, 0.1f);
|
||
|
||
[Tooltip("1층 밀도(개/m 한 변 · 실제 = 값²). Demo.unity 1282 = 2.5(6.25개/㎡).")]
|
||
public float demoFirstLayerDensity = 2.5f;
|
||
|
||
[Tooltip("2층 밀도. Demo.unity 1295 = 0.05(20 m 격자).")]
|
||
public float demoSecondLayerDensity = 0.05f;
|
||
|
||
[Tooltip("격자 흔들기(칸 단위). Demo.unity 1308 = 0.5.")]
|
||
[Range(0f, 0.5f)] public float demoPositionVariance = 0.5f;
|
||
|
||
[Tooltip("크기 흔들기. Demo.unity 1309 = 0.2 → ×0.8~1.2.")]
|
||
[Range(0f, 0.9f)] public float demoScaleVariance = 0.2f;
|
||
|
||
// ───────────────────────────────── §1 풀밭
|
||
[Header("§1 — 섬 타일 위 풀밭 (데모와 같은 인스턴싱)")]
|
||
[Tooltip("`edgeOnly_ 1` 정의를 경계에서 이만큼(m) 안쪽까지만 깐다. 0 = 띠 제한 없음. " +
|
||
"2026-09-15 PD 「그라데이션과 무관하게 풀이 얼룩덜룩」 — 들판 안쪽 풀잎은 같은 색이라도 미세한 음영 차로 얼룩이 되므로 경계 실루엣만 남긴다. " +
|
||
"816zh — 구름 경계 띠(`cloudBandEnabled 1`)가 켜지면 들판은 구름 그림자 경계가 맡으므로 이 물리 띠는 0.6 으로 낮춰 병행한다(띠 안은 항상 배율 1).")]
|
||
public float grassEdgeBand = 0.6f;
|
||
|
||
// ── 816zh: 풀잎을 「구름 그림자(그라데이션) 경계」에 — 크기로 부드럽게 자라고 줄어든다 (PD 2026-09-16 「그림자를 기준으로」)
|
||
[Header("§1-816zh — 풀잎 = 구름 그림자 경계 띠 (부드러운 성장 · 팝핑 0)")]
|
||
[Tooltip("🔴 되돌리기 — 0 = 지금처럼 `edgeOnly_` 풀잎을 물리 경계 띠(grassEdgeBand)에만. " +
|
||
"1 = 들판 전체에 후보를 두고, 구름 그림자 경계(구름 높이가 바닥 재질 `_Cloud_Cover` 를 지나는 선) 근처만 배율을 키워 그린다. " +
|
||
"816o(하드 컷 = 구름이 흐르면 순간이동)와 달리 껐다 켜는 게 아니라 **자라고 줄어든다**. 켜지면 816o `cloudEdgeEnabled` 는 무시된다. " +
|
||
"구름 식은 `cloudEdgeSourceMaterial`(비우면 `islandTopMaterial`)에서 읽는다 — 바닥과 같은 식.")]
|
||
public int cloudBandEnabled = 1;
|
||
|
||
[Tooltip("띠 반폭(구름 높이 단위 · m 아님) — 강도를 걷어 낸 구름 높이(cover + 0.5·noise)가 cover 에서 이만큼 벗어나면 배율 0, 경계선 위는 1(smoothstep). " +
|
||
"🔴 현 바닥 재질(_Shades 7 · _Brightness 0.25 · 강도 0.5)의 보이는 색 계단은 cover ± 0.143 에 있다 — 0.12 는 그 사이 그라데이션 중심 띠. 계단선까지 덮으려면 0.15~0.18.")]
|
||
public float cloudBandWidth = 0.12f;
|
||
|
||
[Tooltip("띠를 어디에 둘 것인가. 1 = **화면에 보이는 색 계단선**(ToonRamp 띠가 바뀌는 문턱 · 데모처럼 그림자 가장자리에 풀 · 2026-09-16 PD) · " +
|
||
"0 = 노이즈 0선(cover · 계단 사이 그라데이션 중심 — 그림자에서 떨어져 보인다).")]
|
||
public int cloudBandMode = 1;
|
||
|
||
[Tooltip("띠 목표 배율을 다시 계산하고 인스턴스 버퍼를 다시 채우는 주기(초). 구름은 초당 약 1.4 m 흐른다(_Cloud_Movement 1,1). " +
|
||
"비용 = 노이즈 표본(cloudEdgeSampleCell 간격 · 6 옥타브) + 후보 전수 겹선형 읽기 + 버퍼 재생성, 이 주기마다 1회.")]
|
||
public float cloudBandRefreshSeconds = 0.2f;
|
||
|
||
[Tooltip("갱신 때마다 현재 배율이 목표로 다가가는 비율(0~1). 작을수록 천천히 자라고 줄어든다 — 0.15 × 0.2 s ≈ 시간상수 1.2 초(구름 이동 약 1.7 m 뒤따름). 1 = 즉시(=팝핑).")]
|
||
[Range(0.01f, 1f)] public float cloudBandLerp = 0.15f;
|
||
|
||
[Tooltip("이 배율 미만인 들판 풀잎은 버퍼에서 뺀다(정점 0 · 압축). 너무 크면 사라질 때 톡 끊긴다.")]
|
||
public float cloudBandMinScale = 0.02f;
|
||
|
||
[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;
|
||
|
||
// ── 2026-09-16 PD 「새 맵을 확장할 때 원본 네모 판이 안 보이고 자연스럽게 확장되게」
|
||
[Header("§0-f — 섬 확장 연출 (데모 터레인 모드)")]
|
||
[Tooltip("1 = 잠긴 칸이 열리는 순간(FI 점프·스케일 애니 첫 프레임)부터 그 칸의 원본 타일 렌더러를 숨기고 우리 지형을 그 칸까지 굽는다(다리 칸 제외). 0 = 원본 애니(네모 판이 튀어오름) 그대로.")]
|
||
public int expandHideOriginal = 1;
|
||
|
||
[Tooltip("새 땅이 바다에서 솟아오르는 시간(초) — 새 칸 둘레 높이맵을 이전 높이 → 새 높이로 보간(SetHeightsDelayLOD · 부분 사각형만). 풀·거품은 다 솟은 뒤 깐다. 0 = 즉시.")]
|
||
public float expandRiseSeconds = 1.2f;
|
||
|
||
// ───────────────────────────────── §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 면 충분하다. " +
|
||
"사이 값은 겹선형 보간으로 채운다. 816zh 구름 경계 띠도 이 간격으로 표본을 뜬다(0.2 s 마다 · 키우면 비용이 제곱으로 준다).")]
|
||
public float cloudEdgeSampleCell = 1f;
|
||
|
||
[Tooltip("노이즈를 계산하는 점의 개수 상한. 섬이 커지면 간격을 자동으로 늘려 비용을 묶어 둔다(816zh 띠도 같은 상한).")]
|
||
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); }
|
||
}
|
||
}
|