[WL-816zj/816zc] PD 4건 — 지형 굽기 851→62 ms(링 0.5 m · s 격자 재사용 = 상인 설치 시 끊김 해소) · 설치물 스냅이 자기 콜라이더를 맞혀 뜨던 것(상인 y 3.0) 차단(platformSnapMaxRise 0.6 · 설치물 제외) · 상인 프랍 0.5 · 던전 노드 개방 시 원본 확장 가격판 개방(expansionUnlockOnDungeonOpen) · 데모 풀 0.22/꽃 0.26 (#816)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
깃 관리자 2026-09-16 18:33:36 +09:00
parent c40e8a4248
commit b8aaf091aa
4 changed files with 59 additions and 27 deletions

View File

@ -235,6 +235,8 @@ namespace WL.Island
{
var d = cfg.DungeonRow(s.dungeonRow);
if (d != null) WLIslandBridge.SpawnOneGate(cfg, d, scene); // 이미 있으면 안에서 건너뛴다
// 2026-09-16 PD 「던전 개방 이후에는 기존 에셋과 동일한 순서로 확장 노드」 — 던전 노드가 열리면 원본 확장 가격판을 개방한다.
if (d != null && cfg.expansionUnlockOnDungeonOpen != 0) WLIslandExpansionGate.Unlock(cfg);
return d != null;
}
@ -301,10 +303,22 @@ namespace WL.Island
static Vector3 Snap(WLIslandSettings cfg, Vector3 p)
{
if (cfg.platformSnapToGround == 0) return p;
RaycastHit hit;
if (Physics.Raycast(new Vector3(p.x, p.y + 30f, p.z), Vector3.down, out hit, 60f, ~0, QueryTriggerInteraction.Ignore))
return new Vector3(p.x, hit.point.y, p.z);
return p;
// 2026-09-16 PD 「상인이 공중에 떠 있다」 실측: 재훑기마다 다시 스냅하는데, 광선이 **방금 놓은 설치물 자신의 콜라이더**(좌판 3 m 상자) 위를
// 맞혀 한 번 훑을 때마다 3 m 씩 떠올랐다(y 3.00 · 콜라이더 위 6.00 실측). 설치물(`~WL_FlowInstalls` 밑)은 건너뛰고,
// 요청 높이보다 `platformSnapMaxRise` 이상 위로 솟는 맞힘(다른 소품 위)도 무시한다 — 설치물은 타일 윗면에 놓는 것이 목적이다.
var hits = Physics.RaycastAll(new Vector3(p.x, p.y + 30f, p.z), Vector3.down, 60f, ~0, QueryTriggerInteraction.Ignore);
float best = float.NaN;
for (int i = 0; i < hits.Length; i++)
{
var c = hits[i].collider; if (c == null) continue;
var t = c.transform; bool ours = false;
while (t != null) { if (t.name == RootName) { ours = true; break; } t = t.parent; }
if (ours) continue;
float y = hits[i].point.y;
if (y > p.y + Mathf.Max(0f, cfg.platformSnapMaxRise)) continue;
if (float.IsNaN(best) || y > best) best = y;
}
return float.IsNaN(best) ? p : new Vector3(p.x, best, p.z);
}
public static void ClearInstalls()

View File

@ -561,6 +561,7 @@ namespace WL.Island
label = "상인 설치", price = 30,
platformFromDungeonRow = 1, // 기존 발판 #2 좌표
kind = "prefab",
scale = 0.5f, // 2026-09-16 PD 「상인 프랍 크기 50 %」
prefabPath = "Assets/FarmingIsland/Prefabs/Buildings/Stall01.prefab",
extraPrefabPath = "Assets/FarmingIsland/Prefabs/Characters/Merchant.prefab",
extraSkipIfComponent = "Merchant", // 좌판 안에 이미 상인이 있으면 안 덧붙인다
@ -589,6 +590,14 @@ namespace WL.Island
"승리하면 그대로 다시 켜져서 이후는 FI 원본 순서(Island.Constraint)대로 확장된다. 0 = 처음부터 보인다.")]
public int expansionAfterFirstWin = 1;
[Tooltip("1 = 초기 플로우의 **던전 노드가 열리는 순간**(4단계 설치) 원본 확장 가격판을 개방한다(2026-09-16 PD 「던전 개방 이후에는 기존 에셋 순서대로 확장 노드」). " +
"0 = 첫 전투 승리 때만(이전 방식). 둘 다 켜져 있으면 먼저 오는 쪽.")]
public int expansionUnlockOnDungeonOpen = 1;
[Tooltip("설치물·발판 지면 스냅에서 허용하는 최대 상승(m). 요청 높이보다 이만큼 이상 위에서 맞은 콜라이더(다른 소품·설치물)는 무시한다 — " +
"2026-09-16 실측: 재훑기 스냅이 좌판 자신의 콜라이더 위(3 m)를 맞혀 상인이 공중에 떴다.")]
public float platformSnapMaxRise = 0.6f;
[Tooltip("첫 승리 여부를 적는 PlayerPrefs 키(시작마다 초기화 대상).")]
public string expansionUnlockPrefsKey = "WL_Island_ExpansionUnlocked";

View File

@ -412,12 +412,12 @@ MonoBehaviour:
demoGrass:
scatterIndex: 0
probability: 100
scale: 0.35
scale: 0.22
normalOffset: 0.04
demoFlower:
scatterIndex: 1
probability: 1
scale: 0.35
scale: 0.26
normalOffset: 0.04
demoGravel:
scatterIndex: 2

View File

@ -368,9 +368,32 @@ namespace WL.Look.Farm
float round = Mathf.Max(0f, cfg.skirtGrassRound);
float cellA = Mathf.Max(w / aRes, l / aRes);
float far = slope + blend + Mathf.Max(0f, cfg.skirtWaveAmp) + cellA * 2f; // 이보다 먼 바깥은 높이·스플랫 모두 상수
GatherRing(size, half, minX, minZ, maxX, maxZ, Mathf.Min(w / aRes, l / aRes), round);
// 🔴 2026-09-16 Lead 실측: 링 0.125 m 간격(632점) × 스플랫 65k 텍셀 = 굽기 851 ms → 상인 설치 때 화면이 멎어 「지형이 사라졌다 보임」.
// 링은 0.5 m 간격(≈1/4)으로, 경계 거리 s 는 높이맵 격자(0.25 m)에서 한 번만 계산해 스플랫은 겹선형으로 읽는다(blend 1 m 에 충분).
GatherRing(size, half, minX, minZ, maxX, maxZ, Mathf.Max(0.5f, Mathf.Min(w / aRes, l / aRes)), round);
// 4) 스플랫 — 층0 잔디 = 둥글린 발자국 안(경계 blend 에 걸쳐 1→0) · 층1 모래 = 나머지 + 밭 구역(WLSoftDirt 와 같은 둥근+노이즈 경계)
// 4) 높이맵 + 경계 거리 격자 — 발자국 안 윗면 → 경계에서 바깥 slope 동안 smoothstep 으로 바닥 → 그 밖 평평
// terrainRoundHeight 1 = 둥글린·굽이친 경계에서 내려간다(발주서) · 0 = 타일 네모 가장자리에서 내려간다(콜라이더와 일치 · 둥글림은 스플랫만)
bool roundHeight = cfg.terrainRoundHeight != 0;
var heights = new float[hRes, hRes];
var sGrid = new float[hRes * hRes]; // 둥글림+굽이 경계 거리(스플랫이 읽는다)
float stepX = w / (hRes - 1), stepZ = l / (hRes - 1);
for (int j = 0; j < hRes; j++)
{
float z = oz + j * stepZ;
for (int i = 0; i < hRes; i++)
{
float x = ox + i * stepX;
float d = RawDist(x, z, half);
float sb = Boundary(x, z, d, round, far);
sGrid[j * hRes + i] = sb;
float s = roundHeight ? sb : d;
float y = s <= 0f ? topY : s >= slope ? bottom : Mathf.Lerp(topY, bottom, SmoothStep01(s / slope));
heights[j, i] = Mathf.Clamp01((y - bottom) / span);
}
}
// 5) 스플랫 — 층0 잔디 = 둥글린 발자국 안(경계 blend 에 걸쳐 1→0) · 층1 모래 = 나머지 + 밭 구역(WLSoftDirt 와 같은 둥근+노이즈 경계)
float zcx = 0f, zcz = 0f, zhx = 0f, zhz = 0f, zrad = 0f, znoise = 0f, zcell = 1f, feather = 1f;
if (zoneValid)
{
@ -390,7 +413,11 @@ namespace WL.Look.Farm
for (int i = 0; i < aRes; i++)
{
float x = ox + (i + 0.5f) * w / aRes;
float s = Boundary(x, z, RawDist(x, z, half), round, far);
// 경계 거리 = 높이맵 격자의 s 를 겹선형으로(0.25 m 격자 · blend 1 m 에 충분 · 링 재탐색 0)
float gx = Mathf.Clamp((x - ox) / stepX, 0f, hRes - 1.0001f), gz = Mathf.Clamp((z - oz) / stepZ, 0f, hRes - 1.0001f);
int gi = (int)gx, gj = (int)gz; float tx = gx - gi, tz = gz - gj;
float s00 = sGrid[gj * hRes + gi], s10 = sGrid[gj * hRes + gi + 1], s01 = sGrid[(gj + 1) * hRes + gi], s11 = sGrid[(gj + 1) * hRes + gi + 1];
float s = Mathf.Lerp(Mathf.Lerp(s00, s10, tx), Mathf.Lerp(s01, s11, tx), tz);
float grass = 1f - SmoothStep01(s / blend); // s ≤ 0 잔디 1 → s ≥ blend 잔디 0(모래)
float dirt = 0f;
if (zoneValid && Mathf.Abs(x - zcx) <= zhx + znoise + feather && Mathf.Abs(z - zcz) <= zhz + znoise + feather)
@ -404,24 +431,6 @@ namespace WL.Look.Farm
}
}
// 5) 높이맵 — 발자국 안 윗면 → 경계에서 바깥 slope 동안 smoothstep 으로 바닥 → 그 밖 평평
// terrainRoundHeight 1 = 둥글린·굽이친 경계에서 내려간다(발주서) · 0 = 타일 네모 가장자리에서 내려간다(콜라이더와 일치 · 둥글림은 스플랫만)
bool roundHeight = cfg.terrainRoundHeight != 0;
var heights = new float[hRes, hRes];
float stepX = w / (hRes - 1), stepZ = l / (hRes - 1);
for (int j = 0; j < hRes; j++)
{
float z = oz + j * stepZ;
for (int i = 0; i < hRes; i++)
{
float x = ox + i * stepX;
float d = RawDist(x, z, half);
float s = roundHeight ? Boundary(x, z, d, round, far) : d;
float y = s <= 0f ? topY : s >= slope ? bottom : Mathf.Lerp(topY, bottom, SmoothStep01(s / slope));
heights[j, i] = Mathf.Clamp01((y - bottom) / span);
}
}
_data.SetHeights(0, 0, heights);
_data.SetAlphamaps(0, 0, alpha);
Bakes++;