Merge branch 'wl/systems/WL-816i2-dungeon-cam'

This commit is contained in:
깃 관리자 2026-09-13 18:44:01 +09:00
commit 57b4865c5f
4 changed files with 85 additions and 4 deletions

View File

@ -82,12 +82,14 @@ MonoBehaviour:
camPitch: 45
camYaw: 0
camDistance: 22
dungeonCamDistance: 18
camFov: 60
camLookHeight: 0
camLockYaw: 1
camLeadMultiplier: 1
fixDegenerateCameras: 1
cameraGuardInterval: 0.5
fixCamActionTargets: 1
dungeons:
- enabled_: 1
id: 1

View File

@ -111,7 +111,13 @@ namespace WL.Island
return;
}
var dg = cfg.FindBySceneName(scene.name);
// 🔴 WL-816i2 — 들어가겠다고 **요청한** 던전이 우선이다.
// 표에서 씬 이름으로만 찾으면(FindBySceneName) 같은 씬을 쓰는 던전 1·3(WL_Dungeon01),
// 2·4(WL_Dungeon02) 중 **언제나 앞 줄**이 잡혀, 던전 3 에 들어가도 던전 1 의 스테이지
// (몹 3마리 · 보상 배수 1)가 돌았다. 그래서 「다른 던전에 들어가도 전투가 이상하다」가 된다.
var dg = WLDungeonSceneSetup.Pending;
if (dg == null || dg.enabled_ == 0 || !scene.name.Equals(dg.sceneName))
dg = cfg.FindBySceneName(scene.name);
if (dg != null)
{
DungeonLoads++;
@ -388,6 +394,11 @@ namespace WL.Island
if (cfg == null || cfg.enabled_ == 0) return;
WLDungeonGate.TickAll(cfg);
// 🔴 WL-816i2 — 재입장 시 전투가 시작되지 않던 것: 죽은 카메라 연출 대상을 걷어낸다.
// 매 프레임 · 목록이 비었거나 죽은 항목이 없으면 아무 일도 하지 않는다(근거는 WLIslandCameraRig.cs).
if (cfg.fixCamActionTargets != 0)
WLDungeonCameraRig.PruneCamActionTargets(MyValue._RealCamera);
// WL-816i 에러 ② — 투영이 깨진 카메라 감시(자세한 근거는 WLIslandCameraRig.cs)
if (cfg.fixDegenerateCameras == 0) return;
_guardTimer -= Time.unscaledDeltaTime;

View File

@ -86,6 +86,20 @@ namespace WL.Island
return true;
}
/// <summary>
/// 던전에서 쓸 카메라 거리(m). WL-816i2 — PD 「던전에서는 카메라를 조금만 더 당기자」.
/// 각도·yaw·FOV 는 섬 그대로 두고 **거리만** 던전 전용 값을 쓴다. 0 이하면 섬 값을 그대로 쓴다.
/// </summary>
public static float DungeonDistance(WLIslandSettings cfg)
{
if (cfg == null) return 22f;
if (DistanceOverride > 0f) return DistanceOverride; // 검증용(프로브)
return cfg.dungeonCamDistance > 0f ? cfg.dungeonCamDistance : cfg.camDistance;
}
/// <summary>검증 전용 — 0 보다 크면 그 거리를 쓴다(3단 비교 캡처용 · 실기에는 영향 0).</summary>
public static float DistanceOverride = 0f;
/// <summary>스테이지 진입 시 원본이 쓰는 초기 카메라 방향(MapZoneData.CamRot_Portal) 값.</summary>
public static float SolvedYaw(WLIslandSettings cfg)
{
@ -112,8 +126,9 @@ namespace WL.Island
Save();
}
float dist = DungeonDistance(_cfg); // WL-816i2 — 던전만 조금 더 당긴다(각도·FOV 는 섬 그대로)
float camHeight, away, up, rot;
if (!Solve(_cfg.camPitch, _cfg.camDistance, _cfg.camYaw, out camHeight, out away, out up, out rot)) return;
if (!Solve(_cfg.camPitch, dist, _cfg.camYaw, out camHeight, out away, out up, out rot)) return;
_rc.cameraHeight = camHeight;
_rc.minDistance = away;
@ -148,7 +163,7 @@ namespace WL.Island
}
Applies++;
LastApplied = "pitch " + camHeight.ToString("F2") + "° · 거리 " + _cfg.camDistance.ToString("F2") +
LastApplied = "pitch " + camHeight.ToString("F2") + "° · 거리 " + dist.ToString("F2") +
" m · FOV " + _cfg.camFov.ToString("F1") + " · (RealCamera away " + away.ToString("F4") +
" · up " + up.ToString("F4") + " · rotateAround " + rot.ToString("F3") + ")";
}
@ -211,6 +226,47 @@ namespace WL.Island
public static int Repairs;
public static string LastRepair = "";
// ─────────────────────────────────────────────────────────────────
// WL-816i2 — 「재입장하면 전투가 진행되지 않는다」(PD) 의 원인 지점
//
// 원본 흐름(실측): 전투는 이렇게 **시작**된다.
// InGameInfo.StartCam → PCActor.StartCam(PCActor.cs:245)
// → LoadMapMgr.Get_PortalPos 콜백 → RealCamera.Set_Target(pc, CamRot_Portal)
// → CamStatus = 0 · list_tfCamactionTarget.Add(pc) (RealCamera.cs:84·87)
// → LateUpdate 의 CamStatus==0 가지가 목록을 **다 비우면** Start_Game() (RealCamera.cs:212)
// → ActorInfo.All_Stop(false) + Time.timeScale + DungeonInfo.Start_Dungeon(true)
// 🔴 `list_tfCamactionTarget` 은 원본에 **비우는 곳이 없다**(Add 와 RemoveAt(0) 뿐 · RealCamera.cs:52·122).
// 맵을 바꾸면 그 목록의 Transform(이전 맵 PC)은 파괴되는데 항목은 남는다.
// 그 상태로 다시 입장하면 CamStatus==0 가지의 첫 줄 `camTarget.position`(RealCamera.cs:197·199)이
// **MissingReferenceException** 을 던져 LateUpdate 가 매 프레임 중단된다 →
// 목록이 영영 안 비고 → **Start_Game() 이 안 불려 전투가 멈춘 채로 남는다.**
// → 원본 0줄 유지를 위해 **죽은 항목만 걷어낸다**. 다 걷어내 비면 지금 대상을 한 개 넣어
// 원본이 정상적으로 목록을 소진하고 Start_Game() 에 도달하게 한다(= 첫 입장과 같은 경로).
// ─────────────────────────────────────────────────────────────────
public static int CamTargetsPruned, CamTargetsRefilled;
static readonly System.Reflection.FieldInfo F_CamActionList =
typeof(RealCamera).GetField("list_tfCamactionTarget",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
/// <summary>죽은 카메라 연출 대상만 걷어낸다. 걷어낸 개수를 돌려준다(정상이면 0 · 아무것도 안 한다).</summary>
public static int PruneCamActionTargets(RealCamera rc)
{
if (rc == null || F_CamActionList == null) return 0;
var list = F_CamActionList.GetValue(rc) as System.Collections.Generic.List<Transform>;
if (list == null || list.Count == 0) return 0;
int removed = 0;
for (int i = list.Count - 1; i >= 0; i--)
if (list[i] == null) { list.RemoveAt(i); removed++; }
if (removed == 0) return 0;
CamTargetsPruned += removed;
// 🔴 다 걷어내 비었는데 CamStatus 가 0 이면 원본이 list[0] 에서 터진다 → 지금 대상을 한 개 채워 준다.
if (list.Count == 0 && rc.target != null) { list.Add(rc.target); CamTargetsRefilled++; }
return removed;
}
/// <summary>투영이 깨진 카메라를 찾아 되살린다. 되살린 수를 돌려준다(정상이면 0 · 로그도 없다).</summary>
public static int RepairDegenerateCameras(WLIslandSettings cfg)
{

View File

@ -328,9 +328,15 @@ namespace WL.Island
[Tooltip("섬 카메라 수평 방향(도). 섬은 플레이어를 따라 돌지 않는 고정 구도라 0.")]
public float camYaw = 0f;
[Tooltip("섬 카메라와 대상 사이 거리(m). 섬 실측 = 22 (CinemachineFramingTransposer.m_CameraDistance).")]
[Tooltip("섬 카메라와 대상 사이 거리(m). 섬 실측 = 22 (CinemachineFramingTransposer.m_CameraDistance). " +
"🔴 섬은 이 값을 쓰지 않는다(FI Cinemachine 이 스스로 잡는다) — 기준 기록용이자 아래 던전 값의 기본값이다.")]
public float camDistance = 22f;
[Tooltip("🔴 **던전 전용** 카메라 거리(m) — WL-816i2 · PD 「던전에서는 카메라를 조금만 더 당기자」. " +
"0 이하면 위 섬 값(22)을 그대로 쓴다. 각도 45°·yaw 0°·FOV 60 은 섬과 같게 유지하고 **거리만** 줄인다. " +
"섬은 이 값에 전혀 영향받지 않는다.")]
public float dungeonCamDistance = 18f;
[Tooltip("섬 카메라 화각(도). 섬 실측 = 60 · 원근 투영.")]
public float camFov = 60f;
@ -354,6 +360,12 @@ namespace WL.Island
[Tooltip("위 감시 주기(초).")]
public float cameraGuardInterval = 0.5f;
[Tooltip("1 이면 `RealCamera.list_tfCamactionTarget` 에 남은 **죽은 연출 대상**을 걷어낸다(WL-816i2). " +
"이게 남아 있으면 재입장 때 RealCamera.LateUpdate 가 매 프레임 예외로 끊겨 " +
"`Start_Game()`(= ActorInfo.All_Stop(false) · DungeonInfo.Start_Dungeon)이 불리지 않아 **전투가 시작되지 않는다**. " +
"0 이면 무동작(= 816i 까지의 동작).")]
public int fixCamActionTargets = 1;
// ───────────────────────────────── 던전 표
[Header("던전 표 — 한 줄 = 던전 하나")]
public WLDungeonDef[] dungeons = new WLDungeonDef[0];