[WL-816d] 섬 카메라 소유권 + WL PC 비동기 생성 대응 + NavMesh 기본값 사용 (#816)
- NavMeshSurface 기본값(collectObjects=All · useGeometry=RenderMeshes)을 그대로 쓴다(게이트 PASS) - 섬 카메라 소유권 SO(islandCameraOwner 기본 1 = FI 카메라) · 떠날 때 되살림 - WL 전투 PC 는 Addressables 콜백으로 늦게 생긴다 → hidePcWatchSeconds 동안 감시해 숨김 - 프로브 전용 DebugSceneOverride(던전 복사본 씬 미등록 구간 검증용) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c23fdeb91a
commit
6fa7a421d3
|
|
@ -19,6 +19,8 @@ MonoBehaviour:
|
|||
islandSceneName: Level01
|
||||
hideWlPcOnIsland: 1
|
||||
fixIslandDuplicates: 1
|
||||
islandCameraOwner: 1
|
||||
hidePcWatchSeconds: 6
|
||||
islandSaveKey:
|
||||
swapIslandPlayer: 1
|
||||
pcPrefabPath: Assets/Res_Addr/PC/LH_M05.prefab
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ using System.Collections.Generic;
|
|||
using TMPro;
|
||||
using Unity.AI.Navigation;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UnityEngine.SceneManagement;
|
||||
using WL.Combat.Stage;
|
||||
|
||||
|
|
@ -100,8 +99,8 @@ namespace WL.Island
|
|||
var go = new GameObject("~WL_DungeonNavMesh");
|
||||
SceneManager.MoveGameObjectToScene(go, scene);
|
||||
var surf = go.AddComponent<NavMeshSurface>();
|
||||
surf.collectObjects = CollectObjects.All;
|
||||
surf.useGeometry = NavMeshCollectGeometry.RenderMeshes; // 아레나 바닥에 콜라이더가 없을 수 있다
|
||||
// collectObjects = All · useGeometry = RenderMeshes 는 NavMeshSurface 기본값 그대로 쓴다
|
||||
// (아레나 바닥에 콜라이더가 없어도 렌더 메시로 베이크된다 · 패키지 실측 NavMeshSurface.cs:54·66)
|
||||
surf.BuildNavMesh();
|
||||
NavBakes++;
|
||||
WLIslandBridge.Log(cfg, "던전 NavMesh 런타임 베이크 완료");
|
||||
|
|
|
|||
|
|
@ -35,6 +35,13 @@ namespace WL.Island
|
|||
public static int IslandLoads, DungeonLoads, ClearGrants, FailGrants, CoinGranted, GatesSpawned;
|
||||
public static string LastLog = "";
|
||||
|
||||
/// <summary>
|
||||
/// 프로브 전용 — 비어 있지 않으면 단독 Play 경로에서 이 씬 이름으로 대신 들어간다.
|
||||
/// 🔴 던전 복사본 씬이 아직 빌드 목록에 없어(등록은 Lead·PD 승인 사항) 로그인 없는 검증에 쓴다.
|
||||
/// 실기(InGameInfo 가 있는 흐름)에는 영향이 없다 — 그쪽은 Load_Map(mapId) 를 탄다.
|
||||
/// </summary>
|
||||
public static string DebugSceneOverride = "";
|
||||
|
||||
static bool s_installed;
|
||||
static int s_pendingGold;
|
||||
static WLIslandRunner s_runner;
|
||||
|
|
@ -123,6 +130,15 @@ namespace WL.Island
|
|||
float t = 0f;
|
||||
while (t < 5f && !WLIslandCoin.Ready) { t += Time.unscaledDeltaTime; yield return null; }
|
||||
GrantPending(cfg);
|
||||
|
||||
// 🔴 WL 전투 PC 는 Addressables 콜백으로 **나중에** 생긴다(PCInfo.cs:16) — 나타나면 그때 숨긴다.
|
||||
t = 0f;
|
||||
while (t < cfg.hidePcWatchSeconds)
|
||||
{
|
||||
if (WLIslandSceneSetup.HideWlPc(cfg)) break;
|
||||
t += Time.unscaledDeltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
|
@ -153,6 +169,7 @@ namespace WL.Island
|
|||
|
||||
EnsureRunner();
|
||||
WLDungeonSceneSetup.Pending = d;
|
||||
WLIslandSceneSetup.RestoreDuplicates();
|
||||
|
||||
if (InGameInfo.Ins != null)
|
||||
{
|
||||
|
|
@ -162,8 +179,9 @@ namespace WL.Island
|
|||
else
|
||||
{
|
||||
// 에디터에서 섬 씬만 열고 Play 한 경우(로그인 없는 검증 경로).
|
||||
Log(cfg, "던전 입장(단독) — " + d.sceneName);
|
||||
SceneManager.LoadScene(d.sceneName, LoadSceneMode.Single);
|
||||
string scn = string.IsNullOrEmpty(DebugSceneOverride) ? d.sceneName : DebugSceneOverride;
|
||||
Log(cfg, "던전 입장(단독) — " + scn);
|
||||
SceneManager.LoadScene(scn, LoadSceneMode.Single);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -268,6 +286,33 @@ namespace WL.Island
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 검증용: 클리어 1회를 **실제 계산식 그대로** 태운다(전투만 건너뛴다).
|
||||
/// `ComputeGold`(표의 goldMultiplier 소비) → 보류 설정까지 실기와 같은 코드가 돈다.
|
||||
/// </summary>
|
||||
public static int DebugSimulateClear(int stageIndex, int killed, bool isBoss)
|
||||
{
|
||||
var cfg = WLIslandSettings.Instance;
|
||||
if (cfg == null) return 0;
|
||||
var t = WLStageTable.Instance;
|
||||
var def = t != null ? t.Get(stageIndex) : null;
|
||||
var r = new StageClearResult
|
||||
{
|
||||
index = stageIndex,
|
||||
stageId = def != null ? def.id : 0,
|
||||
win = true,
|
||||
killed = killed,
|
||||
total = def != null ? def.TotalCount : killed,
|
||||
isBoss = isBoss
|
||||
};
|
||||
int gold = ComputeGold(cfg, r);
|
||||
SetPending(cfg, gold);
|
||||
ClearGrants++;
|
||||
Log(cfg, "검증 클리어 — 스테이지 " + stageIndex + " · 처치 " + killed +
|
||||
" · 배수 " + MultiplierOf(stageIndex).ToString("F2") + " → 보류 " + gold + " 코인");
|
||||
return gold;
|
||||
}
|
||||
|
||||
/// <summary>검증용: 보상분을 직접 주입한다(전투 없이 ④⑤⑥ 경로만 태운다).</summary>
|
||||
public static void DebugSetPending(int gold)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -76,26 +76,63 @@ namespace WL.Island
|
|||
{
|
||||
if (cfg.fixIslandDuplicates == 0) return;
|
||||
|
||||
// WL 전투 PC 는 섬에서 숨긴다(플레이어 2명 방지) — 던전에 가면 다시 만들어진다.
|
||||
if (cfg.hideWlPcOnIsland != 0 && MyValue.bMyPC && MyValue.MyPC != null)
|
||||
{
|
||||
MyValue.MyPC.gameObject.SetActive(false);
|
||||
PcHidden++;
|
||||
}
|
||||
HideWlPc(cfg);
|
||||
|
||||
// FI 카메라·AudioListener 중복 — Additive 로 붙었을 때만 정리한다.
|
||||
// · 섬 씬만 단독으로 열려 있으면(검증 Play) FI 카메라가 유일한 카메라라 끄면 안 된다.
|
||||
if (SceneManager.sceneCount <= 1) return;
|
||||
|
||||
// 카메라 소유권 — 섬은 FarmingIsland 처럼 보여야 한다(PD) → 기본은 FI 카메라가 이긴다.
|
||||
if (cfg.islandCameraOwner != 0)
|
||||
{
|
||||
bool fiWins = cfg.islandCameraOwner == 1;
|
||||
var cams = Object.FindObjectsByType<Camera>(FindObjectsSortMode.None);
|
||||
for (int i = 0; i < cams.Length; i++)
|
||||
{
|
||||
var c = cams[i];
|
||||
if (c == null) continue;
|
||||
bool inIsland = c.gameObject.scene == scene;
|
||||
bool want = fiWins ? inIsland : !inIsland;
|
||||
if (c.enabled == want) continue;
|
||||
if (!want) s_disabledCams.Add(c); // 섬을 떠날 때 되살린다
|
||||
c.enabled = want;
|
||||
CamerasFixed++;
|
||||
}
|
||||
}
|
||||
|
||||
var listeners = Object.FindObjectsByType<AudioListener>(FindObjectsSortMode.None);
|
||||
int alive = 0;
|
||||
for (int i = 0; i < listeners.Length; i++)
|
||||
{
|
||||
if (listeners[i] == null || !listeners[i].enabled) continue;
|
||||
alive++;
|
||||
if (alive > 1 && listeners[i].gameObject.scene == scene) { listeners[i].enabled = false; ListenersFixed++; }
|
||||
if (alive > 1) { listeners[i].enabled = false; s_disabledListeners.Add(listeners[i]); ListenersFixed++; }
|
||||
}
|
||||
WLIslandBridge.Log(cfg, "중복 정리 — PC 숨김 " + PcHidden + " · AudioListener 해제 " + ListenersFixed);
|
||||
WLIslandBridge.Log(cfg, "중복 정리 — PC 숨김 " + PcHidden + " · 카메라 " + CamerasFixed +
|
||||
" · AudioListener 해제 " + ListenersFixed);
|
||||
}
|
||||
|
||||
static readonly List<Camera> s_disabledCams = new List<Camera>(4);
|
||||
static readonly List<AudioListener> s_disabledListeners = new List<AudioListener>(4);
|
||||
|
||||
/// <summary>섬을 떠날 때 우리가 끈 것만 되살린다(C8 — 원래대로).</summary>
|
||||
public static void RestoreDuplicates()
|
||||
{
|
||||
for (int i = 0; i < s_disabledCams.Count; i++) if (s_disabledCams[i] != null) s_disabledCams[i].enabled = true;
|
||||
for (int i = 0; i < s_disabledListeners.Count; i++) if (s_disabledListeners[i] != null) s_disabledListeners[i].enabled = true;
|
||||
s_disabledCams.Clear();
|
||||
s_disabledListeners.Clear();
|
||||
}
|
||||
|
||||
/// <summary>WL 전투 PC 를 섬에서 숨긴다(플레이어 2명 방지). 던전에 가면 다시 만들어진다.</summary>
|
||||
public static bool HideWlPc(WLIslandSettings cfg)
|
||||
{
|
||||
if (cfg.hideWlPcOnIsland == 0) return true;
|
||||
if (!MyValue.bMyPC || MyValue.MyPC == null) return false;
|
||||
if (!MyValue.MyPC.gameObject.activeSelf) return true;
|
||||
MyValue.MyPC.gameObject.SetActive(false);
|
||||
PcHidden++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────
|
||||
|
|
|
|||
|
|
@ -109,6 +109,14 @@ namespace WL.Island
|
|||
[Tooltip("섬 씬의 FI 카메라/AudioListener 중복을 정리한다(Additive 로 붙었을 때만).")]
|
||||
public int fixIslandDuplicates = 1;
|
||||
|
||||
[Tooltip("섬에서 어느 카메라가 화면을 그릴지. 0 = 아무것도 안 건드림(카메라 2대 = 겹쳐 그려짐) · " +
|
||||
"1 = FI 카메라가 이긴다(섬을 FarmingIsland 처럼 · 기본) · 2 = WL 카메라가 이긴다. " +
|
||||
"🔴 Additive 경로는 로그인이 필요해 실측하지 못했다 — 실기 첫 실행에서 확인할 값.")]
|
||||
public int islandCameraOwner = 1;
|
||||
|
||||
[Tooltip("WL 전투 PC 는 Addressables 로 비동기 생성된다 — 이 초 동안 지켜보며 나타나면 숨긴다.")]
|
||||
public float hidePcWatchSeconds = 6f;
|
||||
|
||||
[Tooltip("FI 세이브 파일 이름을 이 값으로 고정한다(빈 문자열 = 원본 그대로 = 활성 씬 이름). " +
|
||||
"816c §A-3: 섹터마다 세이브가 갈리는 문제의 최소 수정 — 다만 원본 0줄 원칙 때문에 이번엔 기본 빈칸(미적용).")]
|
||||
public string islandSaveKey = "";
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ namespace WL.Island
|
|||
if (LitShader == null) return null;
|
||||
|
||||
m = new Material(LitShader) { name = "WL_GateDummy", hideFlags = HideFlags.DontSave };
|
||||
m.SetColor("_BaseColor", c);
|
||||
m.SetColor("_Color", c);
|
||||
if (m.HasProperty("_BaseColor")) m.SetColor("_BaseColor", c);
|
||||
if (m.HasProperty("_Color")) m.SetColor("_Color", c);
|
||||
if (m.HasProperty("_Smoothness")) m.SetFloat("_Smoothness", 0.15f);
|
||||
s_cache[key] = m;
|
||||
return m;
|
||||
|
|
|
|||
Loading…
Reference in New Issue