fix: stabilize VR UI and song playback
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
audioSource.clip = clip;
|
||||
audioSource.Play();
|
||||
PlayClipScheduled(clip);
|
||||
}
|
||||
|
||||
public float CurrentTime => audioSource != null ? audioSource.time : 0f;
|
||||
public double PlayClipScheduled(AudioClip clip, double delaySeconds = 0.1)
|
||||
{
|
||||
ResetThisComponent();
|
||||
audioSource.Stop();
|
||||
audioSource.clip = clip;
|
||||
audioSource.time = 0.0f;
|
||||
|
||||
scheduledDspStartTime = AudioSettings.dspTime + delaySeconds;
|
||||
hasScheduledClip = true;
|
||||
audioSource.PlayScheduled(scheduledDspStartTime);
|
||||
|
||||
return scheduledDspStartTime;
|
||||
}
|
||||
|
||||
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
@@ -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`에 추가되어 있다.
|
||||
|
||||
Reference in New Issue
Block a user