feat: Game scene — SongController bridges custom map to VRBeatsKit
- SongController: loads MP3 + Beat Saber JSON map, runs countdown (3→2→1→GO), spawns cubes via VR_BeatManager.Spawn() synced to audioSource.time - NoteData → SpawnEventInfo mapping: position/lineLayer → x/y, colorType → ColorSide, cutDirection → Direction enum - travelTimeOverride on SpawnEventInfo: each cube's travel time is back-calculated from remaining time at spawn moment, so simultaneous notes arrive at hit zone together regardless of frame-level spawn delay - AudioManager: add PlayClip(AudioClip) and CurrentTime property - VR_BeatManager: respect travelTimeOverride when non-zero - Settings.asset: targetTravelTime 0.5 → 1.8 for natural Beat Saber approach feel - SceneBuilder ④: auto-builds Game.unity from SaberStyle, wires SongController refs, registers in Build Settings - LiberationSans SDF fallback updated with NanumGothic for Korean text support - Remove unused SampleScene Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,11 +3,98 @@ using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using TMPro;
|
||||
using VRBeats;
|
||||
using VRBeats.ScriptableEvents;
|
||||
|
||||
public static class VRBeatSaberSceneBuilder
|
||||
{
|
||||
private const string MenuScene = "Assets/VRBeatsKit/Scenes/Menu.unity";
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// ④ Build Game Scene
|
||||
// SaberStyle 복제 → Game.unity 생성
|
||||
// PlayableManager 제거, SongController + 카운트다운 캔버스 추가
|
||||
// ─────────────────────────────────────────────
|
||||
[MenuItem("Tools/VRBeatSaber/④ Build Game Scene")]
|
||||
public static void BuildGameScene()
|
||||
{
|
||||
const string saberStylePath = "Assets/VRBeatsKit/Scenes/SaberStyle.unity";
|
||||
const string gamePath = "Assets/Scenes/Game.unity";
|
||||
|
||||
// SaberStyle → Game.unity 복제 (이미 있으면 그냥 열기)
|
||||
if (!AssetDatabase.LoadAssetAtPath<Object>(gamePath))
|
||||
{
|
||||
if (!AssetDatabase.CopyAsset(saberStylePath, gamePath))
|
||||
{
|
||||
Debug.LogError("[SceneBuilder] SaberStyle.unity 복제 실패");
|
||||
return;
|
||||
}
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
|
||||
var scene = EditorSceneManager.OpenScene(gamePath, OpenSceneMode.Single);
|
||||
|
||||
// PlayableManager 제거 (PlayableDirector는 유지)
|
||||
var pm = Object.FindObjectOfType<PlayableManager>();
|
||||
if (pm != null)
|
||||
Object.DestroyImmediate(pm);
|
||||
|
||||
// SongController GO 생성
|
||||
var scGO = new GameObject("SongController");
|
||||
var songController = scGO.AddComponent<SongController>();
|
||||
|
||||
var cubePrefab = AssetDatabase.LoadAssetAtPath<Spawneable>(
|
||||
"Assets/VRBeatsKit/Prefabs/Spawneable/VR_BeatCube.prefab");
|
||||
var onLevelComplete = AssetDatabase.LoadAssetAtPath<GameEvent>(
|
||||
"Assets/VRBeatsKit/GameEvents/OnLevelComplete.asset");
|
||||
|
||||
// 카운트다운 캔버스 생성
|
||||
var canvasGO = new GameObject("CountdownCanvas");
|
||||
var canvas = canvasGO.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.sortingOrder = 100;
|
||||
canvasGO.AddComponent<CanvasScaler>();
|
||||
canvasGO.AddComponent<GraphicRaycaster>();
|
||||
|
||||
var countdownGO = new GameObject("CountdownText");
|
||||
countdownGO.transform.SetParent(canvasGO.transform, false);
|
||||
var cRect = countdownGO.AddComponent<RectTransform>();
|
||||
cRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
cRect.anchorMax = new Vector2(0.5f, 0.5f);
|
||||
cRect.pivot = new Vector2(0.5f, 0.5f);
|
||||
cRect.anchoredPosition = Vector2.zero;
|
||||
cRect.sizeDelta = new Vector2(400f, 200f);
|
||||
var cTmp = countdownGO.AddComponent<TextMeshProUGUI>();
|
||||
cTmp.text = "";
|
||||
cTmp.fontSize = 120f;
|
||||
cTmp.color = Color.white;
|
||||
cTmp.alignment = TextAlignmentOptions.Center;
|
||||
cTmp.fontStyle = FontStyles.Bold;
|
||||
countdownGO.SetActive(false);
|
||||
|
||||
// SongController 필드 연결
|
||||
var scSO = new SerializedObject(songController);
|
||||
scSO.FindProperty("cubePrefab") .objectReferenceValue = cubePrefab;
|
||||
scSO.FindProperty("onLevelComplete") .objectReferenceValue = onLevelComplete;
|
||||
scSO.FindProperty("countdownText") .objectReferenceValue = cTmp;
|
||||
scSO.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
// Build Settings 에 Game.unity 추가
|
||||
var scenes = EditorBuildSettings.scenes;
|
||||
bool exists = System.Array.Exists(scenes, s => s.path == gamePath);
|
||||
if (!exists)
|
||||
{
|
||||
var newList = new EditorBuildSettingsScene[scenes.Length + 1];
|
||||
System.Array.Copy(scenes, newList, scenes.Length);
|
||||
newList[scenes.Length] = new EditorBuildSettingsScene(gamePath, true);
|
||||
EditorBuildSettings.scenes = newList;
|
||||
}
|
||||
|
||||
EditorSceneManager.MarkSceneDirty(scene);
|
||||
EditorSceneManager.SaveScene(scene);
|
||||
Debug.Log("[SceneBuilder] ✓ Game.unity 생성 완료");
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────
|
||||
// ③ Menu — Rebuild SongSelect Panel
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user