fix: stabilize VR UI and song playback

This commit is contained in:
jongjae0305
2026-05-26 18:21:58 +09:00
parent 5e5e918c10
commit 182d2c90b9
5 changed files with 136 additions and 26 deletions
+9 -7
View File
@@ -4551,9 +4551,11 @@ MonoBehaviour:
m_GameObject: {fileID: 2094521061}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3f8ab482667b2f44691ffe7131ffbdb7, type: 3}
m_Script: {fileID: 11500000, guid: 504eaffe3c185bf469313a589c4026d0, type: 3}
m_Name:
m_EditorClassIdentifier:
button: {fileID: 2094521063}
sceneName: Menu
--- !u!1 &2138780048
GameObject:
m_ObjectHideFlags: 0
@@ -4893,12 +4895,12 @@ PrefabInstance:
- target: {fileID: 3968956848465607219, guid: f555cbb0b089fb64ca7c7a5b07f11520,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
value: 0.9238795
objectReference: {fileID: 0}
- target: {fileID: 3968956848465607219, guid: f555cbb0b089fb64ca7c7a5b07f11520,
type: 3}
propertyPath: m_LocalRotation.x
value: -0
value: 0.38268343
objectReference: {fileID: 0}
- target: {fileID: 3968956848465607219, guid: f555cbb0b089fb64ca7c7a5b07f11520,
type: 3}
@@ -4913,7 +4915,7 @@ PrefabInstance:
- target: {fileID: 3968956848465607219, guid: f555cbb0b089fb64ca7c7a5b07f11520,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
value: 45
objectReference: {fileID: 0}
- target: {fileID: 3968956848465607219, guid: f555cbb0b089fb64ca7c7a5b07f11520,
type: 3}
@@ -5103,12 +5105,12 @@ PrefabInstance:
- target: {fileID: 1002067974212273307, guid: e7173b1ee3369204eb181b376ede2a3e,
type: 3}
propertyPath: m_LocalRotation.w
value: 1
value: 0.9238795
objectReference: {fileID: 0}
- target: {fileID: 1002067974212273307, guid: e7173b1ee3369204eb181b376ede2a3e,
type: 3}
propertyPath: m_LocalRotation.x
value: -0
value: 0.38268343
objectReference: {fileID: 0}
- target: {fileID: 1002067974212273307, guid: e7173b1ee3369204eb181b376ede2a3e,
type: 3}
@@ -5123,7 +5125,7 @@ PrefabInstance:
- target: {fileID: 1002067974212273307, guid: e7173b1ee3369204eb181b376ede2a3e,
type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
value: 45
objectReference: {fileID: 0}
- target: {fileID: 1002067974212273307, guid: e7173b1ee3369204eb181b376ede2a3e,
type: 3}
+50 -2
View File
@@ -8,16 +8,64 @@ namespace VRBeats
// 나머지 씬: 바로 활성 상태로 추가.
public class VRPointerSetup : MonoBehaviour
{
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterSceneLoad)]
private static VRPointerSetup instance;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
private static void ResetStatics()
{
instance = null;
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
private static void AutoInject()
{
if (instance != null)
return;
var go = new GameObject("[VRPointerSetup]");
go.AddComponent<VRPointerSetup>();
}
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject);
}
private void OnEnable()
{
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnDisable()
{
SceneManager.sceneLoaded -= OnSceneLoaded;
}
private void Start()
{
bool isGameScene = SceneManager.GetActiveScene().name == "Game";
SetupActiveScene();
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
SetupScene(scene);
}
private static void SetupActiveScene()
{
SetupScene(SceneManager.GetActiveScene());
}
private static void SetupScene(Scene scene)
{
bool isGameScene = scene.name == "Game";
SetupControllers(disabledByDefault: isGameScene);
}
+28 -2
View File
@@ -11,6 +11,8 @@ namespace VRBeats
[SerializeField] private float fadeOutTime = 4.0f;
private AudioSource audioSource = null;
private double scheduledDspStartTime = -1.0;
private bool hasScheduledClip = false;
private void Start()
{
@@ -43,13 +45,37 @@ namespace VRBeats
public void PlayClip(AudioClip clip)
{
PlayClipScheduled(clip);
}
public double PlayClipScheduled(AudioClip clip, double delaySeconds = 0.1)
{
ResetThisComponent();
audioSource.Stop();
audioSource.clip = clip;
audioSource.Play();
audioSource.time = 0.0f;
scheduledDspStartTime = AudioSettings.dspTime + delaySeconds;
hasScheduledClip = true;
audioSource.PlayScheduled(scheduledDspStartTime);
return scheduledDspStartTime;
}
public float CurrentTime => audioSource != null ? audioSource.time : 0f;
public float CurrentTime
{
get
{
if (audioSource == null)
return 0.0f;
if (hasScheduledClip)
return (float)(AudioSettings.dspTime - scheduledDspStartTime);
return audioSource.time;
}
}
}
}
@@ -23,23 +23,48 @@ namespace VRBeats
public void DisableXRRayInteractorComponents()
{
if (rayInteractor != null)
rayInteractor.enabled = false;
if (interactorLineVisual != null)
interactorLineVisual.enabled = false;
if (lineRender != null)
lineRender.enabled = false;
// VRPointerController는 부모("LeftHand/RightHand Controller")에 추가되므로 InParent로 탐색
var pointer = GetComponentInParent<VRPointerController>();
if (pointer != null) pointer.enabled = false;
// VRPointerController 위치가 컨트롤러/레이 구조에 따라 달라질 수 있어서 계층 전체에서 찾는다.
var pointer = FindPointerController();
if (pointer != null)
pointer.enabled = false;
}
public void EnableXRRayInteractorComponents()
{
if (rayInteractor != null)
rayInteractor.enabled = true;
if (interactorLineVisual != null)
interactorLineVisual.enabled = true;
if (lineRender != null)
lineRender.enabled = true;
var pointer = GetComponentInParent<VRPointerController>();
if (pointer != null) pointer.enabled = true;
var pointer = FindPointerController();
if (pointer != null)
pointer.enabled = true;
}
private VRPointerController FindPointerController()
{
var pointer = GetComponent<VRPointerController>();
if (pointer != null)
return pointer;
pointer = GetComponentInParent<VRPointerController>();
if (pointer != null)
return pointer;
pointer = GetComponentInChildren<VRPointerController>(true);
if (pointer != null)
return pointer;
return transform.root.GetComponentInChildren<VRPointerController>(true);
}
}
+11 -2
View File
@@ -14,8 +14,8 @@ Meta Quest용 VR Beat Saber 클론. Beat Sage API로 노래를 자동 채보하
- Unity 버전: `6000.3.12f1`
- 현재 브랜치: `main`
- 원격 저장소: `origin` = `https://whdwo798.synology.me/whdwo798/BeatSaber.git`
- 최근 푸시 커밋: `10e9eba feat: improve VR menu pointer and BeatSaber flow`
- `dotnet build VRBeatSaber.slnx` 결과: 오류 0개, 경고 60개
- 최근 푸시 커밋: `5e5e918 docs: update project handoff status`
- `dotnet build VRBeatSaber.slnx` 결과: 오류 0개, 경고 0개
### 실제 씬 구성
@@ -56,14 +56,23 @@ SongCreator.unity
- `Assets/Script/VRPointerController.cs`, `VRPointerSetup.cs` 추가
- VR 컨트롤러 레이로 Unity UI 버튼을 직접 hover/click 처리한다.
- `Game` 씬에서는 게임오버 전까지 비활성화하고, 메뉴 계열 씬에서는 활성화한다.
- `Assets/Script/VRPointerSetup.cs`
- `DontDestroyOnLoad` 싱글턴으로 변경되어 `Menu -> SongCreator -> Game` 같은 씬 전환 후에도 포인터를 다시 주입한다.
- `SceneManager.sceneLoaded`마다 현재 씬 컨트롤러를 검사한다.
- `Assets/VRBeatsKit/Scripts/Core/VR_InteractorController.cs`
- XR Ray Interactor enable/disable 시 `VRPointerController`도 함께 제어한다.
- 컨트롤러 구조 차이를 고려해 현재 오브젝트, 부모, 자식, 루트 하위에서 `VRPointerController`를 찾는다.
- `Assets/VRBeatsKit/Scripts/Core/AudioManager.cs`
- `AudioSource.Play()` 대신 `PlayScheduled()`를 사용하고, `AudioSettings.dspTime` 기준으로 `CurrentTime`을 계산한다.
- MP3 재생 시작 시점과 노트 스폰 기준 시간이 프레임 상태에 따라 흔들리는 문제를 줄이기 위한 변경이다.
- `Assets/VRBeatsKit/Scripts/Core/VR_BeatCube.cs`
- `IsCutIntentValid()`를 public으로 변경하고 `maxCutAngle`을 추가했다.
- `Assets/VRBeatsKit/Scripts/Core/Cuttable.cs`
- 색상/방향/속도가 틀린 큐브는 절단 시각 효과도 발생하지 않도록 막았다.
- `Assets/Scenes/Game.unity`
- `SongController`가 큐브 프리팹, `OnLevelComplete`, 카운트다운 텍스트와 연결되어 있다.
- `GameOverPopup`의 Back 버튼에 깨진 스크립트 참조가 있어 `LoadSceneButton`으로 복구했다.
- 현재 사용 중인 좌/우 세이버 루트 회전을 `X 45도`로 보정해 컨트롤러에서 너무 수직으로 서는 문제를 줄였다.
- `Assets/VRBeatsKit/Scenes/Menu.unity`
- `SongSelectManager`, `DownloadManager`, `SongDetailPanel`, `SongLibrary`가 연결되어 있다.
- `VRPointerSetup``VR_Manager`에 추가되어 있다.