feat: SongCreator 씬 완성 — Beat Sage URL 지원, info.dat 메타데이터 자동 추출
- BeatSageUploader: audio_url 지원(UploadFromUrl), PollAndDownload 공통화, ZIP 500 오류 3회 재시도 - BeatSageConverter: info.dat 파싱(SongMetadata), BPM 자동 감지 → 노트 타이밍 변환에 적용 - SongCreatorManager: title/BPM 필수 입력 제거, 난이도 4개 자동 선택, GenerateFlowFromUrl 버그 수정 - NasPublisher: audioPath null 허용(URL 흐름에서 로컬 파일 없는 경우 스킵) - .gitignore/.gitattributes 초기 설정 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
|
||||
// Editor/PC-only helper — auto-injects at runtime, no need to place in scene.
|
||||
// On Quest builds this entire class is stripped.
|
||||
//
|
||||
// Features:
|
||||
// 1. Replaces TrackedDeviceGraphicRaycaster → GraphicRaycaster (enables mouse clicks)
|
||||
// 2. Keeps worldCamera up to date on all World Space canvases
|
||||
// 3. ESC key navigates back
|
||||
public class DesktopUIMode : MonoBehaviour
|
||||
{
|
||||
#if !UNITY_ANDROID || UNITY_EDITOR
|
||||
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
|
||||
private static void AutoCreate()
|
||||
{
|
||||
if (FindObjectOfType<DesktopUIMode>() != null) return;
|
||||
new GameObject("[DesktopUIMode]").AddComponent<DesktopUIMode>();
|
||||
}
|
||||
|
||||
private static readonly System.Collections.Generic.Dictionary<string, string> BackMap =
|
||||
new()
|
||||
{
|
||||
{ "SongSelect", "Menu" },
|
||||
{ "SongCreator", "Menu" },
|
||||
{ "MapEditorScene", "SongCreator" },
|
||||
{ "Game", "SongSelect" },
|
||||
};
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (FindObjectsByType<DesktopUIMode>(FindObjectsSortMode.None).Length > 1)
|
||||
{ Destroy(gameObject); return; }
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
PatchCanvases();
|
||||
}
|
||||
|
||||
private void OnDestroy() => SceneManager.sceneLoaded -= OnSceneLoaded;
|
||||
|
||||
private void OnSceneLoaded(Scene s, LoadSceneMode m) => StartCoroutine(PatchAfterFrame());
|
||||
|
||||
private System.Collections.IEnumerator PatchAfterFrame()
|
||||
{ yield return null; PatchCanvases(); }
|
||||
|
||||
private void Update()
|
||||
{
|
||||
RefreshCanvasCameras();
|
||||
if (Keyboard.current?.escapeKey.wasPressedThisFrame == true) GoBack();
|
||||
}
|
||||
|
||||
private static void PatchCanvases()
|
||||
{
|
||||
foreach (var canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
||||
{
|
||||
if (canvas.renderMode != RenderMode.WorldSpace) continue;
|
||||
|
||||
var tracked = canvas.GetComponent("TrackedDeviceGraphicRaycaster");
|
||||
if (tracked != null)
|
||||
{
|
||||
DestroyImmediate(tracked);
|
||||
if (canvas.GetComponent<GraphicRaycaster>() == null)
|
||||
canvas.gameObject.AddComponent<GraphicRaycaster>();
|
||||
}
|
||||
}
|
||||
|
||||
RemoveDuplicateAudioListeners();
|
||||
RefreshCanvasCameras();
|
||||
}
|
||||
|
||||
private static void RemoveDuplicateAudioListeners()
|
||||
{
|
||||
var listeners = FindObjectsByType<AudioListener>(FindObjectsSortMode.None);
|
||||
if (listeners.Length <= 1) return;
|
||||
|
||||
AudioListener keep = null;
|
||||
foreach (var al in listeners)
|
||||
if (al.gameObject.scene.name != "DontDestroyOnLoad") { keep = al; break; }
|
||||
keep ??= listeners[0];
|
||||
|
||||
foreach (var al in listeners)
|
||||
if (al != keep) DestroyImmediate(al);
|
||||
}
|
||||
|
||||
private static void RefreshCanvasCameras()
|
||||
{
|
||||
Camera cam = Camera.main;
|
||||
if (cam == null)
|
||||
foreach (var c in FindObjectsByType<Camera>(FindObjectsSortMode.None))
|
||||
if (c.enabled && c.gameObject.scene.name != "DontDestroyOnLoad") { cam = c; break; }
|
||||
cam ??= FindObjectOfType<Camera>();
|
||||
if (cam == null) return;
|
||||
|
||||
foreach (var canvas in FindObjectsByType<Canvas>(FindObjectsSortMode.None))
|
||||
if (canvas.renderMode == RenderMode.WorldSpace && canvas.worldCamera != cam)
|
||||
canvas.worldCamera = cam;
|
||||
}
|
||||
|
||||
private static void GoBack()
|
||||
{
|
||||
if (BackMap.TryGetValue(SceneManager.GetActiveScene().name, out string target))
|
||||
SceneManager.LoadScene(target);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
Reference in New Issue
Block a user