[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:
parent
c40e8a4248
commit
b8aaf091aa
|
|
@ -235,6 +235,8 @@ namespace WL.Island
|
||||||
{
|
{
|
||||||
var d = cfg.DungeonRow(s.dungeonRow);
|
var d = cfg.DungeonRow(s.dungeonRow);
|
||||||
if (d != null) WLIslandBridge.SpawnOneGate(cfg, d, scene); // 이미 있으면 안에서 건너뛴다
|
if (d != null) WLIslandBridge.SpawnOneGate(cfg, d, scene); // 이미 있으면 안에서 건너뛴다
|
||||||
|
// 2026-09-16 PD 「던전 개방 이후에는 기존 에셋과 동일한 순서로 확장 노드」 — 던전 노드가 열리면 원본 확장 가격판을 개방한다.
|
||||||
|
if (d != null && cfg.expansionUnlockOnDungeonOpen != 0) WLIslandExpansionGate.Unlock(cfg);
|
||||||
return d != null;
|
return d != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -301,10 +303,22 @@ namespace WL.Island
|
||||||
static Vector3 Snap(WLIslandSettings cfg, Vector3 p)
|
static Vector3 Snap(WLIslandSettings cfg, Vector3 p)
|
||||||
{
|
{
|
||||||
if (cfg.platformSnapToGround == 0) return p;
|
if (cfg.platformSnapToGround == 0) return p;
|
||||||
RaycastHit hit;
|
// 2026-09-16 PD 「상인이 공중에 떠 있다」 실측: 재훑기마다 다시 스냅하는데, 광선이 **방금 놓은 설치물 자신의 콜라이더**(좌판 3 m 상자) 위를
|
||||||
if (Physics.Raycast(new Vector3(p.x, p.y + 30f, p.z), Vector3.down, out hit, 60f, ~0, QueryTriggerInteraction.Ignore))
|
// 맞혀 한 번 훑을 때마다 3 m 씩 떠올랐다(y 3.00 · 콜라이더 위 6.00 실측). 설치물(`~WL_FlowInstalls` 밑)은 건너뛰고,
|
||||||
return new Vector3(p.x, hit.point.y, p.z);
|
// 요청 높이보다 `platformSnapMaxRise` 이상 위로 솟는 맞힘(다른 소품 위)도 무시한다 — 설치물은 타일 윗면에 놓는 것이 목적이다.
|
||||||
return p;
|
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()
|
public static void ClearInstalls()
|
||||||
|
|
|
||||||
|
|
@ -561,6 +561,7 @@ namespace WL.Island
|
||||||
label = "상인 설치", price = 30,
|
label = "상인 설치", price = 30,
|
||||||
platformFromDungeonRow = 1, // 기존 발판 #2 좌표
|
platformFromDungeonRow = 1, // 기존 발판 #2 좌표
|
||||||
kind = "prefab",
|
kind = "prefab",
|
||||||
|
scale = 0.5f, // 2026-09-16 PD 「상인 프랍 크기 50 %」
|
||||||
prefabPath = "Assets/FarmingIsland/Prefabs/Buildings/Stall01.prefab",
|
prefabPath = "Assets/FarmingIsland/Prefabs/Buildings/Stall01.prefab",
|
||||||
extraPrefabPath = "Assets/FarmingIsland/Prefabs/Characters/Merchant.prefab",
|
extraPrefabPath = "Assets/FarmingIsland/Prefabs/Characters/Merchant.prefab",
|
||||||
extraSkipIfComponent = "Merchant", // 좌판 안에 이미 상인이 있으면 안 덧붙인다
|
extraSkipIfComponent = "Merchant", // 좌판 안에 이미 상인이 있으면 안 덧붙인다
|
||||||
|
|
@ -589,6 +590,14 @@ namespace WL.Island
|
||||||
"승리하면 그대로 다시 켜져서 이후는 FI 원본 순서(Island.Constraint)대로 확장된다. 0 = 처음부터 보인다.")]
|
"승리하면 그대로 다시 켜져서 이후는 FI 원본 순서(Island.Constraint)대로 확장된다. 0 = 처음부터 보인다.")]
|
||||||
public int expansionAfterFirstWin = 1;
|
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 키(시작마다 초기화 대상).")]
|
[Tooltip("첫 승리 여부를 적는 PlayerPrefs 키(시작마다 초기화 대상).")]
|
||||||
public string expansionUnlockPrefsKey = "WL_Island_ExpansionUnlocked";
|
public string expansionUnlockPrefsKey = "WL_Island_ExpansionUnlocked";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -412,12 +412,12 @@ MonoBehaviour:
|
||||||
demoGrass:
|
demoGrass:
|
||||||
scatterIndex: 0
|
scatterIndex: 0
|
||||||
probability: 100
|
probability: 100
|
||||||
scale: 0.35
|
scale: 0.22
|
||||||
normalOffset: 0.04
|
normalOffset: 0.04
|
||||||
demoFlower:
|
demoFlower:
|
||||||
scatterIndex: 1
|
scatterIndex: 1
|
||||||
probability: 1
|
probability: 1
|
||||||
scale: 0.35
|
scale: 0.26
|
||||||
normalOffset: 0.04
|
normalOffset: 0.04
|
||||||
demoGravel:
|
demoGravel:
|
||||||
scatterIndex: 2
|
scatterIndex: 2
|
||||||
|
|
|
||||||
|
|
@ -368,9 +368,32 @@ namespace WL.Look.Farm
|
||||||
float round = Mathf.Max(0f, cfg.skirtGrassRound);
|
float round = Mathf.Max(0f, cfg.skirtGrassRound);
|
||||||
float cellA = Mathf.Max(w / aRes, l / aRes);
|
float cellA = Mathf.Max(w / aRes, l / aRes);
|
||||||
float far = slope + blend + Mathf.Max(0f, cfg.skirtWaveAmp) + cellA * 2f; // 이보다 먼 바깥은 높이·스플랫 모두 상수
|
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;
|
float zcx = 0f, zcz = 0f, zhx = 0f, zhz = 0f, zrad = 0f, znoise = 0f, zcell = 1f, feather = 1f;
|
||||||
if (zoneValid)
|
if (zoneValid)
|
||||||
{
|
{
|
||||||
|
|
@ -390,7 +413,11 @@ namespace WL.Look.Farm
|
||||||
for (int i = 0; i < aRes; i++)
|
for (int i = 0; i < aRes; i++)
|
||||||
{
|
{
|
||||||
float x = ox + (i + 0.5f) * w / aRes;
|
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 grass = 1f - SmoothStep01(s / blend); // s ≤ 0 잔디 1 → s ≥ blend 잔디 0(모래)
|
||||||
float dirt = 0f;
|
float dirt = 0f;
|
||||||
if (zoneValid && Mathf.Abs(x - zcx) <= zhx + znoise + feather && Mathf.Abs(z - zcz) <= zhz + znoise + feather)
|
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.SetHeights(0, 0, heights);
|
||||||
_data.SetAlphamaps(0, 0, alpha);
|
_data.SetAlphamaps(0, 0, alpha);
|
||||||
Bakes++;
|
Bakes++;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue