diff --git a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset index 9edb9e85c..f77790e6c 100644 --- a/Assets/WL/Island/Resources/WL/WLIslandSettings.asset +++ b/Assets/WL/Island/Resources/WL/WLIslandSettings.asset @@ -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 diff --git a/Assets/WL/Island/WLDungeonSceneSetup.cs b/Assets/WL/Island/WLDungeonSceneSetup.cs index 94688c04e..12c22d1f1 100644 --- a/Assets/WL/Island/WLDungeonSceneSetup.cs +++ b/Assets/WL/Island/WLDungeonSceneSetup.cs @@ -48,6 +48,11 @@ namespace WL.Island WLIslandSceneSetup.EnsureDungeonMapNodes(cfg, scene, d); FixDuplicates(cfg, scene); + // ๐Ÿ”ด WL-816i โ€” ๋˜์ „ ์นด๋ฉ”๋ผ๋ฅผ **์„ฌ ๊ตฌ๋„**(๊ฐ 45ยฐ ยท ๊ฑฐ๋ฆฌ 22 m ยท FOV 60)๋กœ ๋งž์ถ˜๋‹ค. + // ์”ฌ์ด ๋‚ด๋ ค๊ฐ€๋ฉด ์ด ์˜ค๋ธŒ์ ํŠธ๋„ ์‚ฌ๋ผ์ง€๊ณ  OnDestroy ๊ฐ€ ์›๋ž˜ ๊ฐ’์„ ๋Œ๋ ค๋†“๋Š”๋‹ค(C8). + if (Object.FindFirstObjectByType() == null) + WLDungeonCameraRig.Create(cfg, scene); + // โ‘  NavMesh if (cfg.bakeDungeonNavMesh != 0) BakeNavMesh(cfg, scene); @@ -173,15 +178,28 @@ namespace WL.Island for (int i = 0; i < walkers.Length; i++) if (walkers[i] != null) { walkers[i].gameObject.SetActive(false); rigs++; } - var allCams = Object.FindObjectsByType(FindObjectsSortMode.None); - for (int i = 0; i < allCams.Length; i++) + // ๐Ÿ”ด WL-816i โ€” ์ „์—๋Š” `c == Camera.main` ์„ ๊ฑด๋„ˆ๋›ฐ์—ˆ๋Š”๋ฐ, ์•„๋ ˆ๋‚˜ ๋ณต์‚ฌ๋ณธ ์”ฌ์˜ `Main Camera`(์ง๊ต ยท orthoSize 10)๊ฐ€ + // ๋ฐ”๋กœ ๊ทธ MainCamera ํƒœ๊ทธ๋ฅผ ๋‹ฌ๊ณ  ์žˆ์–ด์„œ **๋ฃฉ ๊ฐœ๋ฐœ์šฉ ์ง๊ต ์นด๋ฉ”๋ผ๊ฐ€ ๊ทธ๋Œ€๋กœ ์‚ด์•„ ์ „ํˆฌ ํ™”๋ฉด์„ ์ฐจ์ง€**ํ–ˆ๋‹ค. + // ๋˜์ „ ์”ฌ ์•ˆ์˜ ์นด๋ฉ”๋ผ๋Š” ์ „๋ถ€ ๋ฃฉ์šฉ์ด๋‹ค โ†’ ์ „ํˆฌ ์นด๋ฉ”๋ผ(RealCamera ยท Ingame ์”ฌ)๋งŒ ๋‚จ๊ธฐ๊ณ  ๋ชจ๋‘ ๋ˆ๋‹ค. + // ๋‹จ ์ „ํˆฌ ์นด๋ฉ”๋ผ๋ฅผ ๋ชป ์ฐพ์œผ๋ฉด ํ™”๋ฉด์ด ๊นŒ๋งค์ง€๋ฏ€๋กœ ์•„๋ฌด๊ฒƒ๋„ ๋„์ง€ ์•Š๋Š”๋‹ค(์•ˆ์ „). + var combat = Object.FindFirstObjectByType(); + var combatCam = combat != null ? combat.GetComponent() : null; + if (combatCam != null) { - var c = allCams[i]; - if (c == null || c.gameObject.scene != scene) continue; - if (c == Camera.main) continue; - c.enabled = false; cams++; - var al = c.GetComponent(); - if (al != null && al.enabled) { al.enabled = false; listeners++; } + if (!combatCam.enabled) { combatCam.enabled = true; cams++; } + var allCams = Object.FindObjectsByType(FindObjectsSortMode.None); + for (int i = 0; i < allCams.Length; i++) + { + var c = allCams[i]; + if (c == null || c == combatCam || c.gameObject.scene != scene) continue; + if (c.enabled) { c.enabled = false; cams++; } + var al = c.GetComponent(); + if (al != null && al.enabled) { al.enabled = false; listeners++; } + } + } + else + { + WLIslandBridge.Log(cfg, "๐Ÿ”ด ๋˜์ „ ์ „ํˆฌ ์นด๋ฉ”๋ผ(RealCamera)๋ฅผ ๋ชป ์ฐพ์•˜๋‹ค โ€” ์”ฌ ์นด๋ฉ”๋ผ๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘”๋‹ค"); } WLIslandBridge.Log(cfg, "๋˜์ „ ์ค‘๋ณต ์ •๋ฆฌ โ€” ๋ฐ๋ชจ ๋ฆฌ๊ทธ " + rigs + " ยท ์นด๋ฉ”๋ผ " + cams + " ยท ๋ฆฌ์Šค๋„ˆ " + listeners); diff --git a/Assets/WL/Island/WLIslandBridge.cs b/Assets/WL/Island/WLIslandBridge.cs index e3d97b16e..efd871b9d 100644 --- a/Assets/WL/Island/WLIslandBridge.cs +++ b/Assets/WL/Island/WLIslandBridge.cs @@ -199,6 +199,16 @@ namespace WL.Island if (InGameInfo.Ins != null) { + // ๐Ÿ”ด WL-816i โ€” ์”ฌ์„ ๋ฐ”๊พธ๊ธฐ **์ „์—** ๋งต ๋…ธ๋“œ๋ฅผ ๋˜์ „ ๊ฒƒ์œผ๋กœ ๋Œ๋ ค ๋†“๋Š”๋‹ค. + // Load_Map ์€ ์„ฌ ์”ฌ ์–ธ๋กœ๋“œ์™€ ๋˜์ „ ์”ฌ ๋กœ๋“œ๋ฅผ ๊ฐ™์€ ํ”„๋ ˆ์ž„์— ๊ฑด๋‹ค โ†’ ๊ทธ ์‚ฌ์ด์— + // `PCInfo.Make_Actors` ๊ฐ€ `LoadMapMgr.Ins.Get_PortalPos` ๋ฅผ ๋ถ€๋ฅด๋Š”๋ฐ, + // ๋…ธ๋“œ๊ฐ€ ์”ฌ๊ณผ ํ•จ๊ป˜ ํŒŒ๊ดด๋˜๋ฉด MissingReferenceException ์ด ๋‚œ๋‹ค(PD ์‹คํ”Œ๋ ˆ์ด ์—๋Ÿฌ โ‘ ). + var nodes = WLIslandMapNodes.Ensure(cfg); + if (nodes != null) + { + nodes.LoadSceneName = d.sceneName; + nodes.SetStartPos(WLIslandSceneSetup.DungeonStartPos(d), WLDungeonCameraRig.SolvedYaw(cfg)); + } Log(cfg, "๋˜์ „ ์ž…์žฅ โ€” " + d.displayName + " (map " + d.mapId + " ยท stage " + d.stageIndex + ")"); InGameInfo.Ins.Load_Map(d.mapId); } @@ -219,6 +229,10 @@ namespace WL.Island if (InGameInfo.Ins != null) { + // ๐Ÿ”ด WL-816i โ€” ๋‚˜๊ฐˆ ๋•Œ๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋‹ค(๋˜์ „ ์”ฌ์ด ๋‚ด๋ ค๊ฐ€๋ฉฐ ๋…ธ๋“œ๊ฐ€ ์ฃฝ์ง€ ์•Š๊ฒŒ ๋ฏธ๋ฆฌ ์„ฌ ๊ฒƒ์œผ๋กœ). + var nodes = WLIslandMapNodes.Ensure(cfg); + if (nodes != null) nodes.LoadSceneName = cfg.islandSceneName; + Log(cfg, "์„ฌ์œผ๋กœ ๋ณต๊ท€(" + reason + ") โ€” ๋ณด๋ฅ˜ ๋ณด์ƒ " + s_pendingGold); InGameInfo.Ins.Load_Map(cfg.islandMapId); } @@ -366,11 +380,20 @@ namespace WL.Island /// ์ฝ”๋ฃจํ‹ดยท์ž…๋ ฅ์„ ๋„๋งก๋Š” ์ˆจ๊น€ ๋Ÿฌ๋„ˆ(์ƒˆ Manager ๊ฐ€ ์•„๋‹ˆ๋‹ค ยท ์”ฌ์— ๋‚จ์ง€ ์•Š๋Š”๋‹ค). 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); } } } diff --git a/Assets/WL/Island/WLIslandCameraRig.cs b/Assets/WL/Island/WLIslandCameraRig.cs new file mode 100644 index 000000000..a815d9fbc --- /dev/null +++ b/Assets/WL/Island/WLIslandCameraRig.cs @@ -0,0 +1,259 @@ +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +// WLIslandCameraRig.cs โ€” ๋˜์ „(์ „์žฅ)์˜ ์นด๋ฉ”๋ผ ๊ตฌ๋„๋ฅผ **์„ฌ ๊ตฌ๋„ ๊ทธ๋Œ€๋กœ** ๋งŒ๋“ ๋‹ค +// +// PD ์ง€์‹œ(2026-09-13) ใ€Œ์นด๋ฉ”๋ผ ๊ตฌ๋„๋Š” ์„ฌ์—์„œ ์กฐ์ž‘ํ•  ๋•Œ์˜ ๊ฐ๋„์™€ ๋ทฐ๋ฅผ ๋งํ•œ๊ฑฐ์•ผ. +// ์ „์žฅ์— ์ง„์ž…ํ•ด๋„ ๋ฐ”๋€Œ์ง€ ์•Š์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธใ€ +// โ†’ ๊ธฐ์ค€์€ **์„ฌ**. ๋˜์ „ ์นด๋ฉ”๋ผ๋ฅผ ์„ฌ ๊ฐ’์— ๋งž์ถ˜๋‹ค(๋ฐ˜๋Œ€๊ฐ€ ์•„๋‹ˆ๋‹ค). +// +// โ–  ์„ฌ ์นด๋ฉ”๋ผ ์‹ค์ธก(Assets/FarmingIsland/Scenes/Level01.unity ยท ์”ฌ ํŒŒ์ผ ๊ฐ’ ๊ทธ๋Œ€๋กœ) +// PlayerVirtualCamera(Cinemachine) โ€” ํšŒ์ „ ์˜ค์ผ๋Ÿฌ (45, 0, 0) ยท Follow = Player +// Body = CinemachineFramingTransposer ยท m_CameraDistance 22 ยท ScreenX/Y 0.5(๋Œ€์ƒ ํ™”๋ฉด ์ •์ค‘์•™) +// Lens โ€” FieldOfView 60 ยท ์›๊ทผ(orthographic 0) ยท Near 0.1 ยท Far 500 +// = ใ€Œ๋Œ€์ƒ์—์„œ 22 m ยท 45ยฐ ์œ„ ยท ํ™”๊ฐ 60ยฐ ยท ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋”ฐ๋ผ ๋Œ์ง€ ์•Š๋Š” ๊ณ ์ • ๋ฐฉํ–ฅใ€ +// +// โ–  ๋˜์ „ ์นด๋ฉ”๋ผ๋Š” ํž˜๋ฏผ์ง€ `RealCamera`(Assets/Script/Util/RealCamera.cs) ๋‹ค. ์›๋ณธ 0์ค„์ด ์›์น™์ด๋ผ +// **๊ทธ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฐ’๋งŒ** ๋งค ํ”„๋ ˆ์ž„ ์ฑ„์›Œ ๋„ฃ๋Š”๋‹ค(Update โ†’ RealCamera.LateUpdate ์ˆœ์„œ). +// ๊ทธ๋Ÿฌ๋ฉด ํ”๋“ค๋ฆผ(ShakeCamera)ยท์Šคํ‚ฌ ๋ทฐ(CamStatus 2ยท3)ยท๊ฐ€๋ฆผ ์ฒ˜๋ฆฌ๋Š” ์ „๋ถ€ ์›๋ณธ ๊ทธ๋Œ€๋กœ ์‚ฐ๋‹ค. +// +// โ–  RealCamera ์˜ ์œ„์น˜ ์‹(์›๋ณธ Update_Cam) +// rotateVector = Quaternion.Euler(cameraHeight, rotateAround, cameraPan) * Vector3.one +// camPosition = target.position + Vector3.up * DistanceUp - rotateVector * DistanceAway +// Vector3.one(1,1,1) ์„ ๋Œ๋ฆฌ๋Š” ์‹์ด๋ผ ๊ฐ๋„ = ๊ตฌ๋„ ๊ฐ€ ์•„๋‹ˆ๋‹ค โ†’ ์•„๋ž˜์—์„œ **์—ญ์œผ๋กœ ํ’€์–ด** ๊ฐ’์„ ๋งŒ๋“ ๋‹ค. +// ๊ฒฐ๊ณผ ์˜คํ”„์…‹์ด ์ •ํ™•ํžˆ (0, Rยทsinฮธ, -Rยทcosฮธ) ๋ฅผ yaw ฯ† ๋กœ ๋Œ๋ฆฐ ๊ฒƒ์ด ๋˜๊ฒŒ ํ•œ๋‹ค(= ์„ฌ๊ณผ ๊ฐ™์€ ๊ตฌ๋„). +// +// ๐Ÿ”ด Debug.LogError / Debug.LogException ์„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค. +// โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +using UnityEngine; + +namespace WL.Island +{ + /// ๋˜์ „์—์„œ๋งŒ ์‚ด์•„ ์žˆ๋Š” ์นด๋ฉ”๋ผ ๋ณด์ •๊ธฐ. ์”ฌ์ด ๋‚ด๋ ค๊ฐ€๋ฉด ๊ฐ’์„ ์›๋ž˜๋Œ€๋กœ ๋Œ๋ ค๋†“๋Š”๋‹ค(C8). + public sealed class WLDungeonCameraRig : MonoBehaviour + { + public const string RigName = "~WL_DungeonCameraRig"; + + /// ๋ณด๊ณ ยท์ง„๋‹จ์šฉ โ€” ๋งˆ์ง€๋ง‰์œผ๋กœ ์ ์šฉํ•œ ๊ฐ’. + public static string LastApplied = ""; + public static int Applies; + + WLIslandSettings _cfg; + RealCamera _rc; + Camera _cam; + + // ๋˜๋Œ๋ฆฌ๊ธฐ(C8) โ€” ์šฐ๋ฆฌ๊ฐ€ ๋งŒ์ง€๊ธฐ ์ „ ๊ฐ’ + bool _saved; + float _oMin, _oMax, _oHeight, _oUp, _oRot, _oPan, _oFov; + bool _oOrtho; + Vector3 _oLookAt; + float[] _oCamMin, _oCamMax, _oCamHeight, _oCamUp; + bool _leadTouched; float _oLead = 1f; + + public static WLDungeonCameraRig Create(WLIslandSettings cfg, UnityEngine.SceneManagement.Scene scene) + { + if (cfg == null || cfg.dungeonCameraMatchIsland == 0) return null; + var go = new GameObject(RigName); + UnityEngine.SceneManagement.SceneManager.MoveGameObjectToScene(go, scene); + var rig = go.AddComponent(); + rig._cfg = cfg; + return rig; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ์„ฌ ๊ตฌ๋„ โ†’ RealCamera ๊ฐ’ ํ’€์ด + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + /// + /// ์›ํ•˜๋Š” ๊ตฌ๋„(๋‚ด๋ ค๋ณด๋Š” ๊ฐ ฮธ ยท ๊ฑฐ๋ฆฌ R)๋ฅผ RealCamera ์˜ (cameraHeight, DistanceAway, DistanceUp, rotateAround) ๋กœ ํ‘ผ๋‹ค. + /// ๊ฒฐ๊ณผ ์˜คํ”„์…‹ = Ry(ฯ†) ยท (0, R sinฮธ, -R cosฮธ) โ€” ์„ฌ์˜ Cinemachine ๊ตฌ๋„์™€ ๊ฐ™์€ ์‹์ด๋‹ค. + /// + public static bool Solve(float pitchDeg, float distance, float yawDeg, + out float camHeight, out float away, out float up, out float rotateAround) + { + camHeight = pitchDeg; away = 0f; up = 0f; rotateAround = yawDeg; + + float p = pitchDeg * Mathf.Deg2Rad; + float c = Mathf.Cos(p), s = Mathf.Sin(p); + float k = c - s, m = s + c; // Rx(p)ยท(1,1,1) = (1, k, m) + if (Mathf.Abs(m) < 1e-4f) return false; + + float a = -Mathf.Atan(1f / m); // cos a + mยทsin a = 0 โ†’ ์˜คํ”„์…‹์˜ x ์„ฑ๋ถ„ 0 + float z = -Mathf.Sin(a) + m * Mathf.Cos(a); + if (Mathf.Abs(z) < 1e-4f) return false; + + away = distance * c / z; + up = distance * s + away * k; + rotateAround = a * Mathf.Rad2Deg + yawDeg; + return true; + } + + /// ์Šคํ…Œ์ด์ง€ ์ง„์ž… ์‹œ ์›๋ณธ์ด ์“ฐ๋Š” ์ดˆ๊ธฐ ์นด๋ฉ”๋ผ ๋ฐฉํ–ฅ(MapZoneData.CamRot_Portal) ๊ฐ’. + public static float SolvedYaw(WLIslandSettings cfg) + { + if (cfg == null) return 0f; + float h, d, u, r; + if (!Solve(cfg.camPitch, cfg.camDistance, cfg.camYaw, out h, out d, out u, out r)) return cfg.camYaw; + return r; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ์ ์šฉ โ€” Update ๋Š” ๋ชจ๋“  LateUpdate ๋ณด๋‹ค ๋จผ์ € ๋ˆ๋‹ค(= RealCamera ๊ฐ€ ์ด ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์“ด๋‹ค) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + void Update() + { + if (_cfg == null) _cfg = WLIslandSettings.Instance; + if (_cfg == null || _cfg.dungeonCameraMatchIsland == 0) return; + + if (_rc == null) + { + _rc = MyValue.m_RealCamera; + if (_rc == null) return; + _cam = _rc.GetComponent(); + if (_cam == null) _cam = Camera.main; + Save(); + } + + float camHeight, away, up, rot; + if (!Solve(_cfg.camPitch, _cfg.camDistance, _cfg.camYaw, out camHeight, out away, out up, out rot)) return; + + _rc.cameraHeight = camHeight; + _rc.minDistance = away; + _rc.maxDistance = away; + _rc.DistanceUp = up; + _rc.cameraPan = 0f; + _rc.m_LookAt = new Vector3(0f, _cfg.camLookHeight, 0f); + if (_cfg.camLockYaw != 0) _rc.rotateAround = rot; + + // Init_Cam()/Set_DistanceUp() ์ด ์ด ๋ฐฐ์—ด์—์„œ ๊ฐ’์„ ๋˜์ฝ์œผ๋ฏ€๋กœ ๊ฐ™์€ ๊ฐ’์„ ๋„ฃ์–ด ๋‘”๋‹ค(1ํ”„๋ ˆ์ž„ ํА ๋ฐฉ์ง€). + if (_rc.arr_cCamData != null) + for (int i = 0; i < _rc.arr_cCamData.Length; i++) + { + var d = _rc.arr_cCamData[i]; + if (d == null) continue; + d.minDistance = away; d.maxDistance = away; + d.cameraHeight = camHeight; d.DistanceUp = up; + } + + if (_cam != null) + { + if (_cam.orthographic) _cam.orthographic = false; + 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") + + " ยท up " + up.ToString("F4") + " ยท rotateAround " + rot.ToString("F3") + ")"; + } + + void Save() + { + if (_saved || _rc == null) return; + _saved = true; + _oMin = _rc.minDistance; _oMax = _rc.maxDistance; _oHeight = _rc.cameraHeight; + _oUp = _rc.DistanceUp; _oRot = _rc.rotateAround; _oPan = _rc.cameraPan; _oLookAt = _rc.m_LookAt; + if (_cam != null) { _oFov = _cam.fieldOfView; _oOrtho = _cam.orthographic; } + if (_rc.arr_cCamData != null) + { + int n = _rc.arr_cCamData.Length; + _oCamMin = new float[n]; _oCamMax = new float[n]; _oCamHeight = new float[n]; _oCamUp = new float[n]; + for (int i = 0; i < n; i++) + { + var d = _rc.arr_cCamData[i]; + if (d == null) continue; + _oCamMin[i] = d.minDistance; _oCamMax[i] = d.maxDistance; + _oCamHeight[i] = d.cameraHeight; _oCamUp[i] = d.DistanceUp; + } + } + WLIslandBridge.Log(_cfg, "๋˜์ „ ์นด๋ฉ”๋ผ ์›๋ž˜ ๊ฐ’ ๋ณด๊ด€ โ€” ๊ฑฐ๋ฆฌ " + _oMin.ToString("F2") + "~" + _oMax.ToString("F2") + + " ยท cameraHeight " + _oHeight.ToString("F1") + " ยท DistanceUp " + _oUp.ToString("F2") + + " ยท FOV " + _oFov.ToString("F1") + (_oOrtho ? " ยท ์ง๊ต" : " ยท ์›๊ทผ")); + } + + 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; + if (_cam != null) { _cam.fieldOfView = _oFov; _cam.orthographic = _oOrtho; } + if (_rc.arr_cCamData != null && _oCamMin != null) + for (int i = 0; i < _rc.arr_cCamData.Length && i < _oCamMin.Length; i++) + { + var d = _rc.arr_cCamData[i]; + if (d == null) continue; + d.minDistance = _oCamMin[i]; d.maxDistance = _oCamMax[i]; + d.cameraHeight = _oCamHeight[i]; d.DistanceUp = _oCamUp[i]; + } + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // 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 = ""; + + /// ํˆฌ์˜์ด ๊นจ์ง„ ์นด๋ฉ”๋ผ๋ฅผ ์ฐพ์•„ ๋˜์‚ด๋ฆฐ๋‹ค. ๋˜์‚ด๋ฆฐ ์ˆ˜๋ฅผ ๋Œ๋ ค์ค€๋‹ค(์ •์ƒ์ด๋ฉด 0 ยท ๋กœ๊ทธ๋„ ์—†๋‹ค). + public static int RepairDegenerateCameras(WLIslandSettings cfg) + { + if (cfg == null || cfg.fixDegenerateCameras == 0) return 0; + var cams = Object.FindObjectsByType(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; + } + + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + // ์‹ค์ธก ๋ณด๊ณ ์šฉ โ€” ์ง€๊ธˆ ํ™”๋ฉด์˜ ๊ตฌ๋„๋ฅผ ์žฐ๋‹ค(์„ฌยท๋˜์ „ ๊ณตํ†ต ยท ์บก์ฒ˜์™€ ํ•จ๊ป˜ ํ‘œ๋กœ ๋‚จ๊ธด๋‹ค) + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + /// ํ˜„์žฌ ํ™œ์„ฑ ์นด๋ฉ”๋ผ์™€ ์ถ”์  ๋Œ€์ƒ ์‚ฌ์ด์˜ ์‹ค์ œ ๊ตฌ๋„(๊ฐยท๊ฑฐ๋ฆฌยทFOV)๋ฅผ ๋ฌธ์ž์—ด๋กœ ๋Œ๋ ค์ค€๋‹ค. + public static string Measure(Transform target) + { + var cam = Camera.main; + if (cam == null) return "์นด๋ฉ”๋ผ ์—†์Œ"; + if (target == null) return "์นด๋ฉ”๋ผ " + cam.name + " ยท ๋Œ€์ƒ ์—†์Œ ยท FOV " + cam.fieldOfView.ToString("F2") + + (cam.orthographic ? (" ยท ์ง๊ต size " + cam.orthographicSize.ToString("F2")) : " ยท ์›๊ทผ") + + " ยท ์˜ค์ผ๋Ÿฌ " + cam.transform.eulerAngles.ToString("F2"); + Vector3 off = cam.transform.position - target.position; + float horiz = new Vector2(off.x, off.z).magnitude; + float pitch = Mathf.Atan2(off.y, horiz) * Mathf.Rad2Deg; + float yaw = Mathf.Atan2(-off.x, -off.z) * Mathf.Rad2Deg; + return "์นด๋ฉ”๋ผ " + cam.name + " ยท ๊ฑฐ๋ฆฌ " + off.magnitude.ToString("F3") + " m ยท ๋‚ด๋ ค๋ณด๋Š” ๊ฐ " + + pitch.ToString("F2") + "ยฐ ยท ์ˆ˜ํ‰๊ฐ " + yaw.ToString("F2") + "ยฐ ยท ๋†’์ด " + off.y.ToString("F3") + + " m ยท FOV " + cam.fieldOfView.ToString("F2") + + (cam.orthographic ? (" ยท ์ง๊ต size " + cam.orthographicSize.ToString("F2")) : " ยท ์›๊ทผ") + + " ยท near/far " + cam.nearClipPlane.ToString("F2") + "/" + cam.farClipPlane.ToString("F0") + + " ยท ์นด๋ฉ”๋ผ ์˜ค์ผ๋Ÿฌ " + cam.transform.eulerAngles.ToString("F2"); + } + } +} diff --git a/Assets/WL/Island/WLIslandCameraRig.cs.meta b/Assets/WL/Island/WLIslandCameraRig.cs.meta new file mode 100644 index 000000000..aa4f8b75b --- /dev/null +++ b/Assets/WL/Island/WLIslandCameraRig.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4b386eb2f387c634992d62d3591735a9 \ No newline at end of file diff --git a/Assets/WL/Island/WLIslandSceneSetup.cs b/Assets/WL/Island/WLIslandSceneSetup.cs index 893da5995..10ae52b23 100644 --- a/Assets/WL/Island/WLIslandSceneSetup.cs +++ b/Assets/WL/Island/WLIslandSceneSetup.cs @@ -41,32 +41,36 @@ namespace WL.Island public static void EnsureMapNodes(WLIslandSettings cfg, Scene scene) { if (InGameInfo.Ins == null) return; // ์„ฌ ์”ฌ๋งŒ ๋‹จ๋… Play = ์›๋ณธ ํ๋ฆ„์ด ์—†๋‹ค โ†’ ํ•„์š” ์—†๋‹ค - if (LoadMapMgr.isIns && LoadMapMgr.Ins != null) return; - - var go = new GameObject(MapNodesName); - SceneManager.MoveGameObjectToScene(go, scene); - // ๐Ÿ”ด ์›๋ณธ LoadMapMgr ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ถ™์ด๋ฉด Awake ๊ฐ€ ๋นˆ ์ฃผ์†Œ๋กœ Addressables ๋ฅผ ๋•Œ๋ ค ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค. - // ํŒŒ์ƒ ํด๋ž˜์Šค๊ฐ€ Awake ๋งŒ ๊ฐˆ์•„๋ผ์šด๋‹ค = ์›๋ณธ 0์ค„ ยท ์ฃผ์†Œ ๋กœ๋“œ 0 ยท Ins ๋งŒ ์ฑ„์šด๋‹ค. - var mgr = go.AddComponent(); + var mgr = WLIslandMapNodes.Ensure(cfg); + if (mgr == null) return; mgr.LoadSceneName = cfg.islandSceneName; - mgr.LoadMapPrefab = ""; - MapNodesMade++; - WLIslandBridge.Log(cfg, "์„ฌ ๋งต ๋…ธ๋“œ ์ƒ์„ฑ(LoadMapMgr) โ€” " + scene.name); + mgr.SetStartPos(IslandStartPos(), WLDungeonCameraRig.SolvedYaw(cfg)); + WLIslandBridge.Log(cfg, "์„ฌ ๋งต ๋…ธ๋“œ ์ค€๋น„(LoadMapMgr) โ€” " + scene.name + " ยท ์‹œ์ž‘ " + mgr.StartPos.ToString("F1")); + } + + /// ์„ฌ์˜ ์‹œ์ž‘ ์ง€์  = FI ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์„œ ์žˆ๋Š” ์ž๋ฆฌ(์—†์œผ๋ฉด ์›์ ). + static Vector3 IslandStartPos() + { + var fiPlayer = Object.FindFirstObjectByType(); + return fiPlayer != null ? fiPlayer.transform.position : Vector3.zero; } /// ๋˜์ „ ์”ฌ์—๋„ ๊ฐ™์€ ์ด์œ ๋กœ `LoadMapMgr` ์ž๋ฆฌ๋ฅผ ์ฑ„์šด๋‹ค(๋งต ํ”„๋ฆฌํŒน ์—†์Œ = ์ฃผ์†Œ ๋กœ๋“œ 0). public static void EnsureDungeonMapNodes(WLIslandSettings cfg, Scene scene, WLDungeonDef d) { if (InGameInfo.Ins == null) return; - if (LoadMapMgr.isIns && LoadMapMgr.Ins != null && LoadMapMgr.Ins.gameObject.scene == scene) return; - - var go = new GameObject(MapNodesName); - SceneManager.MoveGameObjectToScene(go, scene); - var mgr = go.AddComponent(); + var mgr = WLIslandMapNodes.Ensure(cfg); + if (mgr == null) return; mgr.LoadSceneName = d.sceneName; - mgr.LoadMapPrefab = ""; - MapNodesMade++; - WLIslandBridge.Log(cfg, "๋˜์ „ ๋งต ๋…ธ๋“œ ์ƒ์„ฑ(LoadMapMgr) โ€” " + scene.name); + mgr.SetStartPos(DungeonStartPos(d), WLDungeonCameraRig.SolvedYaw(cfg)); + WLIslandBridge.Log(cfg, "๋˜์ „ ๋งต ๋…ธ๋“œ ์ค€๋น„(LoadMapMgr) โ€” " + scene.name + " ยท ์‹œ์ž‘ " + mgr.StartPos.ToString("F1")); + } + + /// ๋˜์ „(์•„๋ ˆ๋‚˜)์—๋Š” ํฌํ„ธ์ด ์—†๋‹ค โ€” ์Šคํ…Œ์ด์ง€ ํ‘œ์˜ ์•ต์ปค๊ฐ€ ์‹œ์ž‘ ์ง€์ ์ด๋‹ค. + public static Vector3 DungeonStartPos(WLDungeonDef d) + { + var def = WLDungeonSceneSetup.StageDefOf(d); + return def != null ? def.anchor : Vector3.zero; } // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ @@ -263,13 +267,115 @@ namespace WL.Island } /// - /// ์„ฌ ์”ฌ ์ „์šฉ `LoadMapMgr`. ์›๋ณธ์€ Awake ์—์„œ ๋งต ํ”„๋ฆฌํŒน์„ Addressables ๋กœ ๋ฐ›์•„ ์˜ค๋Š”๋ฐ, - /// ์„ฌ์€ ์”ฌ ์ž์ฒด๊ฐ€ ๋งต์ด๋ผ ๋ฐ›์„ ๊ฒƒ์ด ์—†๋‹ค โ†’ Awake ๋งŒ ๊ฐˆ์•„๋ผ์›Œ **์ฃผ์†Œ ๋กœ๋“œ 0**ยท์‹ฑ๊ธ€ํ„ด๋งŒ ์ฑ„์šด๋‹ค. - /// ๐Ÿ”ด ์›๋ณธ `LoadMapMgr.cs` ๋Š” ํ•œ ์ค„๋„ ๊ณ ์น˜์ง€ ์•Š๋Š”๋‹ค(ํŒŒ์ƒ์œผ๋กœ ํ•ด๊ฒฐ). + /// ์„ฌยท๋˜์ „ ๊ณต์šฉ `LoadMapMgr`. ์›๋ณธ์€ Awake ์—์„œ ๋งต ํ”„๋ฆฌํŒน์„ Addressables ๋กœ ๋ฐ›์•„ ์˜ค๋Š”๋ฐ, + /// ์„ฌยท๋˜์ „์€ ์”ฌ ์ž์ฒด๊ฐ€ ๋งต์ด๋ผ ๋ฐ›์„ ๊ฒƒ์ด ์—†๋‹ค โ†’ Awake ๋งŒ ๊ฐˆ์•„๋ผ์›Œ **์ฃผ์†Œ ๋กœ๋“œ 0**ยท์‹ฑ๊ธ€ํ„ด๋งŒ ์ฑ„์šด๋‹ค. + /// ๐Ÿ”ด ์›๋ณธ `LoadMapMgr.cs` ๋Š” ํ•œ ์ค„๋„ ๊ณ ์น˜์ง€ ์•Š๋Š”๋‹ค(ํŒŒ์ƒ + private ํ•„๋“œ ์ฃผ์ž…์œผ๋กœ ํ•ด๊ฒฐ). + /// + /// โ–  WL-816i ๊ฐ€ ๊ณ ์นœ ๊ฒƒ 2๊ฐ€์ง€ (PD ์‹คํ”Œ๋ ˆ์ด ์—๋Ÿฌ โ‘ ) + /// โ‘  `MissingReferenceException โ€ฆ WLIslandMapNodes has been destroyed` โ€” + /// ์”ฌ๋งˆ๋‹ค ์ƒˆ๋กœ ๋งŒ๋“ค๋˜ ๋…ธ๋“œ๊ฐ€ `LoadSceneMode` ์ „ํ™˜์—์„œ ํŒŒ๊ดด๋˜๋Š”๋ฐ ์ •์  `Ins` ๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ์•„ + /// `Get_PortalPos` ์˜ `StartCoroutine` ์ด ์ฃฝ์€ ๊ฐ์ฒด๋ฅผ ๋•Œ๋ ธ๋‹ค. + /// โ†’ **DontDestroyOnLoad ๋‹จ ํ•˜๋‚˜**๋งŒ ๋‘๊ณ  ์”ฌ์ด ๋ฐ”๋€Œ๋ฉด ์‹œ์ž‘ ์ง€์ ๋งŒ ๊ฐˆ์•„๋ผ์šด๋‹ค. + /// โ‘ก `Co_Get_PortalPos` ์˜ `while (CheckNull(m_MapData)) yield return null;` ์ด ๋๋‚˜์ง€ ์•Š๋˜ ๊ฒƒ โ€” + /// ์šฐ๋ฆฌ๋Š” ๋งต ํ”„๋ฆฌํŒน์ด ์—†์–ด `m_MapData` ๊ฐ€ ์˜์›ํžˆ null ์ด๋ผ `Make_Actors` ์˜ ์ฝœ๋ฐฑ(ํŽซยท์ผ๊พผ)๊ณผ + /// `PCActor.Set_Warp`ยท`MobControlMgr` ์˜ ๋„๋‹ฌ์„ฑ ๊ฒ€์‚ฌ๊ฐ€ **์˜์˜ ์•ˆ ๋ถˆ๋ ธ๋‹ค**. + /// โ†’ ๋Ÿฐํƒ€์ž„ `MapData` ๋ฅผ ํ•˜๋‚˜ ๋งŒ๋“ค์–ด ๋„ฃ๋Š”๋‹ค. **๊บผ์ง„ ์˜ค๋ธŒ์ ํŠธ**์— ๋ถ™์—ฌ์„œ + /// `MapData.Awake/Start` ๊ฐ€ ๋Œ์ง€ ์•Š๊ฒŒ ํ•œ๋‹ค(= `MapData.Ins` ๋„, RenderSettings ์•ฐ๋น„์–ธํŠธยท์•ˆ๊ฐœ๋„ ๋ฌด๋ณ€๊ฒฝ). /// public sealed class WLIslandMapNodes : LoadMapMgr { + public const int ZoneSlots = 16; // n_PortalIndex ๊ฐ€ ๋ฌด์—‡์ด ์˜ค๋“  IndexOutOfRange ๊ฐ€ ์•ˆ ๋‚˜๊ฒŒ + public const int ObjSlots = 8; // Get_tfObj(index) ์—ฌ์œ ๋ถ„ + + static WLIslandMapNodes s_ins; + static readonly System.Reflection.FieldInfo F_MapData = + typeof(LoadMapMgr).GetField("m_MapData", System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Instance); + static readonly System.Reflection.FieldInfo F_GoMap = + typeof(LoadMapMgr).GetField("go_map", System.Reflection.BindingFlags.NonPublic | + System.Reflection.BindingFlags.Instance); + + Transform _start; + MapData _data; + GameObject _dummyMap; + + /// ํ˜„์žฌ ์”ฌ์˜ ์‹œ์ž‘ ์ง€์ (๋ณด๊ณ ยท์ง„๋‹จ์šฉ). + public Vector3 StartPos { get { return _start != null ? _start.position : Vector3.zero; } } + protected override void Awake() { Ins = this; isIns = true; } + + /// + /// ์”ฌ์„ ๋„˜์–ด ์‚ฌ๋Š” ๋…ธ๋“œ๋ฅผ **ํ•˜๋‚˜๋งŒ** ๋งŒ๋“ค๊ณ  ์‹ฑ๊ธ€ํ„ด ์ž๋ฆฌ๋ฅผ ๋˜์ฐพ๋Š”๋‹ค. + /// (๋‹ค๋ฅธ ๋งต์˜ ์›๋ณธ `LoadMapMgr` ๊ฐ€ `Ins` ๋ฅผ ๊ฐ€์ ธ๊ฐ”๋‹ค๊ฐ€ ๊ทธ ์”ฌ์ด ์–ธ๋กœ๋“œ๋˜๋ฉด ์ฃฝ์€ ์ฐธ์กฐ๊ฐ€ ๋‚จ๋Š”๋‹ค โ€” ๊ทธ๊ฒƒ๋„ ์—ฌ๊ธฐ์„œ ๋ฎ๋Š”๋‹ค.) + /// + public static WLIslandMapNodes Ensure(WLIslandSettings cfg) + { + if (s_ins == null) + { + var go = new GameObject(WLIslandSceneSetup.MapNodesName); + Object.DontDestroyOnLoad(go); + s_ins = go.AddComponent(); + s_ins.LoadMapPrefab = ""; + s_ins.Build(); + WLIslandSceneSetup.MapNodesMade++; + } + Ins = s_ins; isIns = true; // ์ฃฝ์€ ์ฐธ์กฐ๋กœ ๋‚จ์ง€ ์•Š๊ฒŒ ๋งค๋ฒˆ ๋˜์ฐพ๋Š”๋‹ค + return s_ins; + } + + void Build() + { + // โ‘  ์‹œ์ž‘ ์ง€์  ๋…ธ๋“œ โ€” ํฌํ„ธ/๋ถ€ํ™œ/๋†๋ถ€/์˜ค๋ธŒ์ ํŠธ ์ž๋ฆฌ๋ฅผ ์ „๋ถ€ ์ด ํ•˜๋‚˜๋กœ ๋‹ตํ•œ๋‹ค(๋˜์ „์€ ์•„๋ ˆ๋‚˜๋ผ ํฌํ„ธ์ด ์—†๋‹ค). + var holder = new GameObject("MapNodes"); + holder.transform.SetParent(transform, false); + holder.SetActive(false); // ๐Ÿ”ด ๊บผ์ง„ ์ฑ„๋กœ AddComponent โ†’ MapData.Awake/Start ๊ฐ€ ์•„์˜ˆ ์•ˆ ๋ˆ๋‹ค + + var startGo = new GameObject("start"); + startGo.transform.SetParent(holder.transform, false); + _start = startGo.transform; + + _data = holder.AddComponent(); + var objs = new Transform[ObjSlots]; + for (int i = 0; i < ObjSlots; i++) objs[i] = _start; + + _data.m_MapZoneData = new List(ZoneSlots); + for (int i = 0; i < ZoneSlots; i++) + { + _data.m_MapZoneData.Add(new MapZoneData + { + go_portal = startGo, + go_reincarnation = startGo, + go_mazeNextPortal = null, // Set_MazeNextPortal ์€ null ์ด๋ฉด ๊ทธ๋ƒฅ ๋„˜์–ด๊ฐ„๋‹ค(์›๋ณธ ํ™•์ธ) + tf_Farmer = _start, + tf_Obj = objs, + CamRot_Portal = 0f, + CamRot_Reincarnation = 0f, + }); + } + + // โ‘ก SetActive_Map(bool) ์ด ๋งŒ์ง€๋Š” ์ž๋ฆฌ(๋น„์–ด ์žˆ์œผ๋ฉด NullReference) โ€” ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ๋“  ๋”๋ฏธ. + _dummyMap = new GameObject("map"); + _dummyMap.transform.SetParent(transform, false); + + // โ‘ข ์›๋ณธ์˜ private ํ•„๋“œ์— ๋„ฃ๋Š”๋‹ค โ€” ์›๋ณธ ์ฝ”๋“œ 0์ค„๋กœ `Co_Get_*` ์˜ ๋Œ€๊ธฐ๋ฅผ ์ฆ‰์‹œ ๋๋‚ธ๋‹ค. + if (F_MapData != null) F_MapData.SetValue(this, _data); + if (F_GoMap != null) F_GoMap.SetValue(this, _dummyMap); + } + + /// ์”ฌ์ด ๋ฐ”๋€” ๋•Œ๋งˆ๋‹ค ์‹œ์ž‘ ์ง€์ ๊ณผ ์นด๋ฉ”๋ผ ์ดˆ๊ธฐ ๋ฐฉํ–ฅ๋งŒ ๊ฐˆ์•„๋ผ์šด๋‹ค(๊ฐ์ฒด๋Š” ๊ทธ๋Œ€๋กœ ์‚ฐ๋‹ค). + public void SetStartPos(Vector3 pos, float camYaw) + { + if (_start == null) Build(); + _start.position = pos - Vector3.forward; // ์›๋ณธ์ด +Vector3.forward ๋ฅผ ๋”ํ•ด ๋Œ๋ ค์ค€๋‹ค โ†’ ๊ฒฐ๊ณผ๊ฐ€ pos ๊ฐ€ ๋˜๊ฒŒ + if (_data == null || _data.m_MapZoneData == null) return; + for (int i = 0; i < _data.m_MapZoneData.Count; i++) + { + var z = _data.m_MapZoneData[i]; + if (z == null) continue; + z.CamRot_Portal = camYaw; + z.CamRot_Reincarnation = camYaw; + } + } } /// FI ์• ๋‹ˆ๋ฉ”์ดํ„ฐ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์น˜๋น„ ์• ๋‹ˆ๋ฉ”์ดํ„ฐ๋กœ ๊ทธ๋Œ€๋กœ ํ˜๋ฆฐ๋‹ค(Humanoid ๋ฆฌํƒ€๊นƒ). diff --git a/Assets/WL/Island/WLIslandSettings.cs b/Assets/WL/Island/WLIslandSettings.cs index 65ab83674..c7c9c1f47 100644 --- a/Assets/WL/Island/WLIslandSettings.cs +++ b/Assets/WL/Island/WLIslandSettings.cs @@ -313,6 +313,47 @@ namespace WL.Island "๋น„์šฐ๋ฉด ์•„๋ฌด๊ฒƒ๋„ ์ง€์šฐ์ง€ ์•Š๋Š”๋‹ค. PD ์—๊ฒŒ ์ƒˆ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ ์ค„ ๋•Œ ๊ฐ’์„ ๋ฐ”๊พผ๋‹ค.")] public string saveResetToken = "816g-1"; + // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ WL-816i ยท ์นด๋ฉ”๋ผ ๊ตฌ๋„ ๊ณ ์ • + // PD ใ€Œ์นด๋ฉ”๋ผ ๊ตฌ๋„๋Š” ์„ฌ์—์„œ ์กฐ์ž‘ํ•  ๋•Œ์˜ ๊ฐ๋„์™€ ๋ทฐ๋ฅผ ๋งํ•œ๊ฑฐ์•ผ. ์ „์žฅ์— ์ง„์ž…ํ•ด๋„ ๋ฐ”๋€Œ์ง€ ์•Š์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธใ€ + // โ†’ ๊ธฐ์ค€์€ **์„ฌ**. ์•„๋ž˜ ๊ธฐ๋ณธ๊ฐ’์€ ์„ฌ(FI) ์นด๋ฉ”๋ผ ์‹ค์ธก๊ฐ’ ๊ทธ๋Œ€๋กœ๋‹ค. + // Level01.unity `PlayerVirtualCamera` โ€” ํšŒ์ „ ์˜ค์ผ๋Ÿฌ (45,0,0) ยท FramingTransposer m_CameraDistance 22 + // ยท ๋ Œ์ฆˆ FOV 60 ยท ์›๊ทผ(orthographic 0) ยท Near 0.1 / Far 500 ยท ScreenX/Y 0.5(๋Œ€์ƒ ํ™”๋ฉด ์ค‘์•™) + [Header("์นด๋ฉ”๋ผ ๊ตฌ๋„ (WL-816i ยท ์„ฌ ๊ธฐ์ค€์„ ๋˜์ „์— ๊ทธ๋Œ€๋กœ)")] + [Tooltip("1 ์ด๋ฉด ๋˜์ „(์ „์žฅ)์˜ ์นด๋ฉ”๋ผ๋ฅผ ์•„๋ž˜ **์„ฌ ๊ตฌ๋„** ๊ฐ’์œผ๋กœ ๋งž์ถ˜๋‹ค. 0 ์ด๋ฉด ๋˜์ „์€ ํž˜๋ฏผ์ง€ ๊ธฐ๋ณธ ์ „ํˆฌ ์นด๋ฉ”๋ผ ๊ทธ๋Œ€๋กœ(= 816g ๊นŒ์ง€์˜ ๋™์ž‘).")] + public int dungeonCameraMatchIsland = 1; + + [Tooltip("์„ฌ ์นด๋ฉ”๋ผ ๋‚ด๋ ค๋ณด๋Š” ๊ฐ(๋„). ์„ฌ ์‹ค์ธก = 45.")] + public float camPitch = 45f; + + [Tooltip("์„ฌ ์นด๋ฉ”๋ผ ์ˆ˜ํ‰ ๋ฐฉํ–ฅ(๋„). ์„ฌ์€ ํ”Œ๋ ˆ์ด์–ด๋ฅผ ๋”ฐ๋ผ ๋Œ์ง€ ์•Š๋Š” ๊ณ ์ • ๊ตฌ๋„๋ผ 0.")] + public float camYaw = 0f; + + [Tooltip("์„ฌ ์นด๋ฉ”๋ผ์™€ ๋Œ€์ƒ ์‚ฌ์ด ๊ฑฐ๋ฆฌ(m). ์„ฌ ์‹ค์ธก = 22 (CinemachineFramingTransposer.m_CameraDistance).")] + public float camDistance = 22f; + + [Tooltip("์„ฌ ์นด๋ฉ”๋ผ ํ™”๊ฐ(๋„). ์„ฌ ์‹ค์ธก = 60 ยท ์›๊ทผ ํˆฌ์˜.")] + public float camFov = 60f; + + [Tooltip("์นด๋ฉ”๋ผ๊ฐ€ ๋ฐ”๋ผ๋ณด๋Š” ์ ์˜ ๋†’์ด(๋Œ€์ƒ ๋ฐœ๋ฐ‘ ๊ธฐ์ค€ m). ์„ฌ์€ ๋Œ€์ƒ ์›์ ์„ ํ™”๋ฉด ์ค‘์•™์— ๋‘๋ฏ€๋กœ 0.")] + public float camLookHeight = 0f; + + [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];