[WL-816za] 밭 자리 풀 경계 라운드 (#816)
밭(흙) 제외 사각형을 반듯한 사각형 대신 「모서리 반지름 + 경계 노이즈」로 판정해 풀이 흙 가장자리로 불규칙하게 파고들게 한다. 길·건물·소품 제외 규칙은 무변경. - WLIslandLookSettings: farmEdgeRoundEnabled 1 / farmEdgeRadius 1.0 / farmEdgeNoise 0.4 / farmEdgeNoiseCell 0.5 - WLIslandGrass: 밭 rect 를 _farmRounds 로 분리 + InRoundedFarm(둥근 모서리 + 값 노이즈) 판정 - 라운드 모드에서 밭 안에 든 Soil 1x1 사각형은 빼서 깎아낸 모서리를 도로 메우지 않게 함 - farmEdgeRoundEnabled 0 이면 기존 동작 그대로 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2799b311b6
commit
dd5757bc4b
|
|
@ -109,6 +109,10 @@ MonoBehaviour:
|
||||||
edgeInset: 0.15
|
edgeInset: 0.15
|
||||||
roadMargin: 0.15
|
roadMargin: 0.15
|
||||||
farmMargin: 0.35
|
farmMargin: 0.35
|
||||||
|
farmEdgeRoundEnabled: 1
|
||||||
|
farmEdgeRadius: 1
|
||||||
|
farmEdgeNoise: 0.4
|
||||||
|
farmEdgeNoiseCell: 0.5
|
||||||
propMargin: 0.05
|
propMargin: 0.05
|
||||||
propMaxSize: 7.5
|
propMaxSize: 7.5
|
||||||
maxInstances: 40000
|
maxInstances: 40000
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ namespace WL.Look.Farm
|
||||||
|
|
||||||
Bounds _bounds = new Bounds(Vector3.zero, Vector3.one * 16f);
|
Bounds _bounds = new Bounds(Vector3.zero, Vector3.one * 16f);
|
||||||
readonly List<Rect> _blockers = new List<Rect>(128);
|
readonly List<Rect> _blockers = new List<Rect>(128);
|
||||||
|
// 816za — 밭 자리만 따로. 반듯한 사각형이 아니라 「모서리 둥글게 + 경계 노이즈」로 판정한다.
|
||||||
|
readonly List<Rect> _farmRounds = new List<Rect>(8);
|
||||||
|
|
||||||
// ── 816o 캐시 — 한 번 뽑은 후보를 들고 있다가, 구름이 흐르면 「고르기」만 다시 한다 ──
|
// ── 816o 캐시 — 한 번 뽑은 후보를 들고 있다가, 구름이 흐르면 「고르기」만 다시 한다 ──
|
||||||
struct Cand { public float x, z, y; public int def; public float sc; public float yaw; }
|
struct Cand { public float x, z, y; public int def; public float sc; public float yaw; }
|
||||||
|
|
@ -570,6 +572,8 @@ namespace WL.Look.Farm
|
||||||
void BuildBlockers(List<TileRef> tiles)
|
void BuildBlockers(List<TileRef> tiles)
|
||||||
{
|
{
|
||||||
_blockers.Clear();
|
_blockers.Clear();
|
||||||
|
_farmRounds.Clear();
|
||||||
|
bool round = cfg.farmEdgeRoundEnabled != 0 && cfg.farmEdgeRadius > 0f;
|
||||||
var scene = tiles.Count > 0 ? tiles[0].tf.gameObject.scene : default(UnityEngine.SceneManagement.Scene);
|
var scene = tiles.Count > 0 ? tiles[0].tf.gameObject.scene : default(UnityEngine.SceneManagement.Scene);
|
||||||
|
|
||||||
// ① 농사 — Farm 이 Awake 에서 만드는 BoxCollider(size = (length,2,width))가 밭의 정확한 넓이다.
|
// ① 농사 — Farm 이 Awake 에서 만드는 BoxCollider(size = (length,2,width))가 밭의 정확한 넓이다.
|
||||||
|
|
@ -579,17 +583,21 @@ namespace WL.Look.Farm
|
||||||
var f = farms[i];
|
var f = farms[i];
|
||||||
if (f == null || f.gameObject.scene != scene) continue;
|
if (f == null || f.gameObject.scene != scene) continue;
|
||||||
var bc = f.GetComponent<BoxCollider>();
|
var bc = f.GetComponent<BoxCollider>();
|
||||||
if (bc != null) AddBox(f.transform, bc.center, bc.size, cfg.farmMargin);
|
Rect r = bc != null
|
||||||
else AddRect(f.transform.position.x, f.transform.position.z, 6f, 5f, cfg.farmMargin);
|
? BoxRect(f.transform, bc.center, bc.size, cfg.farmMargin)
|
||||||
|
: MakeRect(f.transform.position.x, f.transform.position.z, 6f, 5f, cfg.farmMargin);
|
||||||
|
if (round) _farmRounds.Add(r); else _blockers.Add(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ② 농사 타일 하나하나(1×1) — Farm 이 런타임에 만든다. 이중 안전.
|
// ② 농사 타일 하나하나(1×1) — Farm 이 런타임에 만든다. 이중 안전.
|
||||||
|
// 라운드 모드에서는 ① 안에 든 타일을 빼야 한다. 안 그러면 반듯한 1×1 사각형들이 깎아낸 모서리를 도로 메운다.
|
||||||
var soils = Object.FindObjectsByType<FISoil>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
var soils = Object.FindObjectsByType<FISoil>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||||
for (int i = 0; i < soils.Length; i++)
|
for (int i = 0; i < soils.Length; i++)
|
||||||
{
|
{
|
||||||
var s = soils[i];
|
var s = soils[i];
|
||||||
if (s == null || s.gameObject.scene != scene) continue;
|
if (s == null || s.gameObject.scene != scene) continue;
|
||||||
var p = s.transform.position;
|
var p = s.transform.position;
|
||||||
|
if (round && InAnyFarmRect(p.x, p.z)) continue;
|
||||||
AddRect(p.x, p.z, 1f, 1f, cfg.farmMargin);
|
AddRect(p.x, p.z, 1f, 1f, cfg.farmMargin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -626,10 +634,47 @@ namespace WL.Look.Farm
|
||||||
var r = _blockers[i];
|
var r = _blockers[i];
|
||||||
if (x >= r.xMin && x <= r.xMax && z >= r.yMin && z <= r.yMax) return true;
|
if (x >= r.xMin && x <= r.xMax && z >= r.yMin && z <= r.yMax) return true;
|
||||||
}
|
}
|
||||||
|
for (int i = 0; i < _farmRounds.Count; i++)
|
||||||
|
if (InRoundedFarm(_farmRounds[i], x, z)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddBox(Transform tf, Vector3 center, Vector3 size, float margin)
|
// 밭 사각형 안인가 — 타일을 어느 밭에 딸린 것으로 볼지 가르는 데만 쓴다(모서리·노이즈 무시).
|
||||||
|
bool InAnyFarmRect(float x, float z)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _farmRounds.Count; i++)
|
||||||
|
{
|
||||||
|
var r = _farmRounds[i];
|
||||||
|
if (x >= r.xMin && x <= r.xMax && z >= r.yMin && z <= r.yMax) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 816za — 밭 자리 판정. 네 모서리를 반지름만큼 깎고, 경계 전체를 노이즈로 흔든다.
|
||||||
|
// 노이즈가 (−) 면 흙이 물러나 풀이 파고들고, (+) 면 흙이 조금 넓어진다. 평균 0 이라 총량은 그대로다.
|
||||||
|
bool InRoundedFarm(Rect r, float x, float z)
|
||||||
|
{
|
||||||
|
float hx = r.width * 0.5f, hz = r.height * 0.5f;
|
||||||
|
float cx = r.xMin + hx, cz = r.yMin + hz;
|
||||||
|
|
||||||
|
if (cfg.farmEdgeNoise > 0f && cfg.farmEdgeNoiseCell > 0.0001f)
|
||||||
|
{
|
||||||
|
float n = (CellNoise(x / cfg.farmEdgeNoiseCell, z / cfg.farmEdgeNoiseCell) * 2f - 1f) * cfg.farmEdgeNoise;
|
||||||
|
hx += n; hz += n;
|
||||||
|
}
|
||||||
|
if (hx <= 0f || hz <= 0f) return false;
|
||||||
|
|
||||||
|
float dx = Mathf.Abs(x - cx), dz = Mathf.Abs(z - cz);
|
||||||
|
if (dx > hx || dz > hz) return false;
|
||||||
|
|
||||||
|
float rad = Mathf.Min(cfg.farmEdgeRadius, Mathf.Min(hx, hz));
|
||||||
|
if (rad <= 0f) return true;
|
||||||
|
float ox = dx - (hx - rad), oz = dz - (hz - rad);
|
||||||
|
if (ox <= 0f || oz <= 0f) return true; // 모서리 원호 밖 = 십자 몸통 = 안쪽
|
||||||
|
return ox * ox + oz * oz <= rad * rad;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect BoxRect(Transform tf, Vector3 center, Vector3 size, float margin)
|
||||||
{
|
{
|
||||||
// 콜라이더가 disabled 여도 안전하게 — bounds 대신 직접 계산한다.
|
// 콜라이더가 disabled 여도 안전하게 — bounds 대신 직접 계산한다.
|
||||||
var c = tf.TransformPoint(center);
|
var c = tf.TransformPoint(center);
|
||||||
|
|
@ -637,12 +682,17 @@ namespace WL.Look.Farm
|
||||||
var ez = tf.TransformVector(new Vector3(0f, 0f, size.z * 0.5f));
|
var ez = tf.TransformVector(new Vector3(0f, 0f, size.z * 0.5f));
|
||||||
float hx = Mathf.Abs(ex.x) + Mathf.Abs(ez.x);
|
float hx = Mathf.Abs(ex.x) + Mathf.Abs(ez.x);
|
||||||
float hz = Mathf.Abs(ex.z) + Mathf.Abs(ez.z);
|
float hz = Mathf.Abs(ex.z) + Mathf.Abs(ez.z);
|
||||||
AddRect(c.x, c.z, hx * 2f, hz * 2f, margin);
|
return MakeRect(c.x, c.z, hx * 2f, hz * 2f, margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Rect MakeRect(float cx, float cz, float w, float h, float margin)
|
||||||
|
{
|
||||||
|
return new Rect(cx - w * 0.5f - margin, cz - h * 0.5f - margin, w + margin * 2f, h + margin * 2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddRect(float cx, float cz, float w, float h, float margin)
|
void AddRect(float cx, float cz, float w, float h, float margin)
|
||||||
{
|
{
|
||||||
_blockers.Add(new Rect(cx - w * 0.5f - margin, cz - h * 0.5f - margin, w + margin * 2f, h + margin * 2f));
|
_blockers.Add(MakeRect(cx, cz, w, h, margin));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Rect Inflate(Rect r, float m)
|
static Rect Inflate(Rect r, float m)
|
||||||
|
|
@ -700,5 +750,23 @@ namespace WL.Look.Farm
|
||||||
uint v = Hash(h + (uint)(k * 0x9E3779B9U));
|
uint v = Hash(h + (uint)(k * 0x9E3779B9U));
|
||||||
return (v & 0xFFFFFF) / 16777215f;
|
return (v & 0xFFFFFF) / 16777215f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 816za — 셀 격자 값 노이즈 [0,1]. 셀 네 귀퉁이 난수를 부드럽게 섞어 경계를 들쭉날쭉하게 만든다.
|
||||||
|
static float CellNoise(float fx, float fz)
|
||||||
|
{
|
||||||
|
int x0 = Mathf.FloorToInt(fx), z0 = Mathf.FloorToInt(fz);
|
||||||
|
float tx = fx - x0, tz = fz - z0;
|
||||||
|
tx = tx * tx * (3f - 2f * tx);
|
||||||
|
tz = tz * tz * (3f - 2f * tz);
|
||||||
|
float a = CellRand(x0, z0), b = CellRand(x0 + 1, z0);
|
||||||
|
float c = CellRand(x0, z0 + 1), d = CellRand(x0 + 1, z0 + 1);
|
||||||
|
return Mathf.Lerp(Mathf.Lerp(a, b, tx), Mathf.Lerp(c, d, tx), tz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float CellRand(int x, int z)
|
||||||
|
{
|
||||||
|
uint h = Hash(unchecked((uint)x * 73856093U) ^ unchecked((uint)z * 19349663U) ^ 0x9E3779B9U);
|
||||||
|
return (h & 0xFFFFFF) / 16777215f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -263,6 +263,19 @@ namespace WL.Look.Farm
|
||||||
[Tooltip("농사 타일(Farm 트리거 · Soil 1×1)에서 이만큼(m) 떨어뜨린다.")]
|
[Tooltip("농사 타일(Farm 트리거 · Soil 1×1)에서 이만큼(m) 떨어뜨린다.")]
|
||||||
public float farmMargin = 0.35f;
|
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;
|
||||||
|
|
||||||
[Tooltip("건물·소품 콜라이더에서 이만큼(m) 떨어뜨린다.")]
|
[Tooltip("건물·소품 콜라이더에서 이만큼(m) 떨어뜨린다.")]
|
||||||
public float propMargin = 0.05f;
|
public float propMargin = 0.05f;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue