[WL-816i] 던전 진입 에러 제거 + 카메라 구도 고정 (#816)
- "Screen position out of view frustum" 의 진짜 원인을 실측으로 확정: 좌표가 화면 밖이라서가 아니라 **카메라 투영이 깨졌을 때**만 난다(near==far · 직교 size ≤ 0 · aspect ≤ 0). 꺼진 카메라는 무관. → 깨진 투영을 찾아 되살리는 감시(0.5초 주기 · SO 로 끔) 추가. - 섬 카메라 실측값(각 45° · 거리 22 m · FOV 60 · 원근 · 고정 방향)을 SO 에 담아 던전에 적용. RealCamera 의 (cameraHeight, DistanceAway, DistanceUp, rotateAround) 로 역산해 넣는다. - 이동 방향 리드(#755) 배율을 SO 로 노출(기본 1 = 지금 그대로 · 0 이면 섬과 완전히 같은 고정 구도). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
f2e89653ed
commit
84ade4b619
|
|
@ -78,6 +78,16 @@ MonoBehaviour:
|
|||
applyStartCoin: 1
|
||||
startCoin: 10
|
||||
saveResetToken: 816g-1
|
||||
dungeonCameraMatchIsland: 1
|
||||
camPitch: 45
|
||||
camYaw: 0
|
||||
camDistance: 22
|
||||
camFov: 60
|
||||
camLookHeight: 0
|
||||
camLockYaw: 1
|
||||
camLeadMultiplier: 1
|
||||
fixDegenerateCameras: 1
|
||||
cameraGuardInterval: 0.5
|
||||
dungeons:
|
||||
- enabled_: 1
|
||||
id: 1
|
||||
|
|
|
|||
|
|
@ -380,11 +380,20 @@ namespace WL.Island
|
|||
/// <summary>코루틴·입력을 도맡는 숨김 러너(새 Manager 가 아니다 · 씬에 남지 않는다).</summary>
|
||||
public sealed class WLIslandRunner : MonoBehaviour
|
||||
{
|
||||
float _guardTimer;
|
||||
|
||||
void Update()
|
||||
{
|
||||
var cfg = WLIslandSettings.Instance;
|
||||
if (cfg == null || cfg.enabled_ == 0) return;
|
||||
WLDungeonGate.TickAll(cfg);
|
||||
|
||||
// WL-816i 에러 ② — 투영이 깨진 카메라 감시(자세한 근거는 WLIslandCameraRig.cs)
|
||||
if (cfg.fixDegenerateCameras == 0) return;
|
||||
_guardTimer -= Time.unscaledDeltaTime;
|
||||
if (_guardTimer > 0f) return;
|
||||
_guardTimer = cfg.cameraGuardInterval > 0f ? cfg.cameraGuardInterval : 0.5f;
|
||||
WLDungeonCameraRig.RepairDegenerateCameras(cfg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ namespace WL.Island
|
|||
bool _oOrtho;
|
||||
Vector3 _oLookAt;
|
||||
float[] _oCamMin, _oCamMax, _oCamHeight, _oCamUp;
|
||||
bool _leadTouched; float _oLead = 1f;
|
||||
|
||||
public static WLDungeonCameraRig Create(WLIslandSettings cfg, UnityEngine.SceneManagement.Scene scene)
|
||||
{
|
||||
|
|
@ -138,6 +139,14 @@ namespace WL.Island
|
|||
if (!Mathf.Approximately(_cam.fieldOfView, _cfg.camFov)) _cam.fieldOfView = _cfg.camFov;
|
||||
}
|
||||
|
||||
// 이동 방향 리드(#755) 배율 — 기본 1 = 아무것도 만지지 않는다(다른 팀 기능 그대로).
|
||||
// 0 으로 두면 던전에서 화면 중심이 흔들리지 않아 섬과 완전히 같은 고정 구도가 된다.
|
||||
if (_cfg.camLeadMultiplier >= 0f && !Mathf.Approximately(_cfg.camLeadMultiplier, 1f))
|
||||
{
|
||||
if (!_leadTouched) { _leadTouched = true; _oLead = CameraMoveLead.LeadMultiplier; }
|
||||
CameraMoveLead.LeadMultiplier = _cfg.camLeadMultiplier;
|
||||
}
|
||||
|
||||
Applies++;
|
||||
LastApplied = "pitch " + camHeight.ToString("F2") + "° · 거리 " + _cfg.camDistance.ToString("F2") +
|
||||
" m · FOV " + _cfg.camFov.ToString("F1") + " · (RealCamera away " + away.ToString("F4") +
|
||||
|
|
@ -170,6 +179,7 @@ namespace WL.Island
|
|||
|
||||
void OnDestroy()
|
||||
{
|
||||
if (_leadTouched) { CameraMoveLead.LeadMultiplier = _oLead; _leadTouched = false; }
|
||||
if (!_saved || _rc == null) return;
|
||||
_rc.minDistance = _oMin; _rc.maxDistance = _oMax; _rc.cameraHeight = _oHeight;
|
||||
_rc.DistanceUp = _oUp; _rc.rotateAround = _oRot; _rc.cameraPan = _oPan; _rc.m_LookAt = _oLookAt;
|
||||
|
|
@ -184,6 +194,45 @@ namespace WL.Island
|
|||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// WL-816i 에러 ② — "Screen position out of view frustum" 감시·수리
|
||||
//
|
||||
// 🔴 실측(이 프로젝트 에디터 · 같은 문구 그대로 재현):
|
||||
// 이 에러는 **좌표가 화면 밖이라서** 나는 것이 아니다. 화면 밖·클립 밖 좌표(뷰포트 1,0 · z 1000)를
|
||||
// 넣어도 정상 카메라에서는 에러가 **나지 않는다**. 에러가 나는 유일한 조건은 **카메라의 투영이 깨진 것**이다:
|
||||
// · near == far → 에러
|
||||
// · 직교인데 orthographicSize ≤ 0 → 에러
|
||||
// · aspect ≤ 0 → 에러
|
||||
// · 카메라를 꺼 두거나 오브젝트를 꺼 둔 것 → 에러 아님(정상 동작)
|
||||
// PD 콘솔의 「screen pos 1080, 0, 1000」 = (pixelRect.xMax, pixelRect.yMin, 대상 깊이) —
|
||||
// NGUI `UIAnchor.Update`(UIAnchor.cs:206-207)가 화면 우하단 앵커를 계산할 때 넣는 값 모양 그대로다.
|
||||
// → 그래서 값을 넣는 쪽이 아니라 **깨진 카메라**를 찾아 되살린다. 깨진 카메라는 어차피 아무것도 못 그린다.
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
public static int Repairs;
|
||||
public static string LastRepair = "";
|
||||
|
||||
/// <summary>투영이 깨진 카메라를 찾아 되살린다. 되살린 수를 돌려준다(정상이면 0 · 로그도 없다).</summary>
|
||||
public static int RepairDegenerateCameras(WLIslandSettings cfg)
|
||||
{
|
||||
if (cfg == null || cfg.fixDegenerateCameras == 0) return 0;
|
||||
var cams = Object.FindObjectsByType<Camera>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||||
int n = 0;
|
||||
for (int i = 0; i < cams.Length; i++)
|
||||
{
|
||||
var c = cams[i];
|
||||
if (c == null) continue;
|
||||
string why = null;
|
||||
if (c.nearClipPlane >= c.farClipPlane) { why = "near " + c.nearClipPlane + " ≥ far " + c.farClipPlane; c.nearClipPlane = 0.3f; c.farClipPlane = 500f; }
|
||||
if (c.orthographic && c.orthographicSize <= 0f) { why = (why == null ? "" : why + " · ") + "직교 size " + c.orthographicSize; c.orthographicSize = 5f; }
|
||||
if (c.aspect <= 0f || float.IsNaN(c.aspect)) { why = (why == null ? "" : why + " · ") + "aspect " + c.aspect; c.ResetAspect(); }
|
||||
if (why == null) continue;
|
||||
n++; Repairs++;
|
||||
LastRepair = c.name + "(" + (c.gameObject.scene.IsValid() ? c.gameObject.scene.name : "-") + ") — " + why;
|
||||
WLIslandBridge.Log(cfg, "🔴 투영이 깨진 카메라를 되살렸다 — " + LastRepair);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
// 실측 보고용 — 지금 화면의 구도를 잰다(섬·던전 공통 · 캡처와 함께 표로 남긴다)
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -340,6 +340,20 @@ namespace WL.Island
|
|||
[Tooltip("1 이면 던전에서 카메라를 드래그로 돌릴 수 없다(섬과 같은 고정 구도). 0 이면 회전 허용.")]
|
||||
public int camLockYaw = 1;
|
||||
|
||||
[Tooltip("던전에서 「이동 방향 리드」(#755 · CameraLeadSettings)를 얼마나 쓸지. " +
|
||||
"1 = 지금 그대로(리드 살아 있음 · 움직이면 화면 중심이 최대 1.5 m 흔들린다) · " +
|
||||
"0 = 리드 끔(섬과 완전히 같은 고정 구도). 음수면 만지지 않는다. " +
|
||||
"🔴 후진 피치(backwardPitchAdd 6°)는 이 값으로 꺼지지 않는다 — 그건 CameraLeadSettings 소관.")]
|
||||
public float camLeadMultiplier = 1f;
|
||||
|
||||
[Tooltip("1 이면 투영이 깨진 카메라(near==far · 직교 size ≤ 0 · aspect ≤ 0)를 찾아 되살린다. " +
|
||||
"이 상태의 카메라에 ScreenToWorldPoint 를 쓰면 유니티가 " +
|
||||
"\"Screen position out of view frustum\" 에러를 낸다(WL-816i 실측). 0 이면 감시도 수리도 안 한다.")]
|
||||
public int fixDegenerateCameras = 1;
|
||||
|
||||
[Tooltip("위 감시 주기(초).")]
|
||||
public float cameraGuardInterval = 0.5f;
|
||||
|
||||
// ───────────────────────────────── 던전 표
|
||||
[Header("던전 표 — 한 줄 = 던전 하나")]
|
||||
public WLDungeonDef[] dungeons = new WLDungeonDef[0];
|
||||
|
|
|
|||
Loading…
Reference in New Issue