Files
BeatSaber/md/VRBeatSaber_BeatSage_Integration_Spec.md
T

6.9 KiB
Raw Blame History

VRBeatSaber 기술 기획서

비트세이지 데이터 연동 온디맨드 스트리밍 및 에디터 통합 시스템

작성일: 2026년 5월 20일 | 문서 버전: v1.0 | 프로젝트: VRBeatSaber


1. 개요 (Introduction)

본 시스템은 오큘러스 퀘스트(독립형 VR) 하드웨어의 제한된 컴퓨팅 자원(CPU, RAM) 및 저장 공간 한계를 극복하기 위해 설계된 플랫폼 형태의 리듬 게임 인프라이다.

기기 내부에 대용량 음원을 상주시키는 대신, 필요 시 서버에서 동적으로 다운로드(On-Demand)하고, 외부의 무료 오토 매핑 서비스인 **비트세이지(Beat Sage)**의 연산 결과물(비트세이버 표준 포맷)을 흡수하여 구현한다.

이를 통해 자체적인 AI 모델 구축 비용(서버 유지비, GPU 연산 리소스)을 0원으로 절감하는 동시에 대량의 하이퀄리티 맵 데이터를 빠르게 수급할 수 있다. 개발자는 파이썬 머신러닝 레이어를 직접 다루지 않고, 오직 유니티 C# 환경 내에서 비트세이버 포맷을 자체 JSON 포맷으로 변환하는 데이터 컨버터 및 온디맨드 다운로드 시스템 구축에만 집중한다.


2. 전체 데이터 파이프라인 (Data Pipeline)

전체 워크플로우는 다음 4단계 구조를 가진다.

[외부 수급] → [서버 저장 및 관리] → [유니티 다운로드 및 포맷 변환] → [에디터 수정/인게임 플레이]
단계 구성요소 역할
1 비트세이지 웹사이트 유저/개발자가 웹 인터페이스로 음원 입력 → 무료 AI 매핑 → 맵 데이터 패키지(.zip) 확보
2 자체 백엔드 서버 (Synology NAS) Web Station 정적 웹서버로 곡 메타데이터 리스트(songs.json), 음원 파일(.mp3), 비트세이지 원본 데이터 보관
3 유니티 클라이언트 UnityWebRequest로 무선 네트워크 스트리밍, 오큘러스 퀘스트 임시 경로에 다운로드 후 내부 컨버터로 자체 포맷 변환
4 맵 에디터 통합 변환 완료된 데이터를 MapEditor 씬으로 로드, 기계 생성 초안(80%) 기반으로 개발자가 정교하게 디렉팅 및 수정

3. 데이터 구조 비교 및 변환 명세 (Data Mapping Spec)

3.1 비트세이지 결과물 포맷 (비트세이버 표준 v2 포맷)

비트세이지에서 생성된 압축파일 내 Expert.dat 등의 파일 구조:

{
  "_version": "2.0.0",
  "_notes": [
    { "_time": 1.5, "_lineIndex": 0, "_lineLayer": 0, "_type": 0, "_cutDirection": 1 },
    { "_time": 2.3, "_lineIndex": 3, "_lineLayer": 0, "_type": 1, "_cutDirection": 0 }
  ]
}

3.2 현재 프로젝트 자체 포맷 (Map_[곡이름].json)

유니티 직렬화 및 기존 인게임 스폰 프레임워크가 요구하는 데이터 구조:

{
  "target": [
    { "time": 0.75, "position": 0, "colorType": 0 },
    { "time": 1.15, "position": 3, "colorType": 1 }
  ]
}

3.3 구조 변환 규칙 (Mapping Rules)

비트세이지 원본 필드 자체 프로젝트 매핑 필드 변환 연산 및 예외 처리 규칙
_time (float) time (float) 비트 단위 → 초 단위 환산
Time(s) = (_time × 60) / BPM
_lineIndex (int) position (int) 가로 라인 0~3 범위 1:1 매칭
_type (int) colorType (int) 0: 빨간색 노트(왼손)
1: 파란색 노트(오른손)
폭탄(value: 3)은 continue 처리로 필터링

⚠️ 주의사항: 비트세이지의 시간 축은 절대적인 초 단위가 아닌 BPM에 종속적인 비트 카운트이므로, 변환 연산 시 음원의 정확한 메타데이터 BPM 값이 필수적으로 제공되어야 동기화가 유지된다.


4. 유니티 클라이언트 코어 구현 (C#)

네트워크 비동기 수신처리와 수신 즉시 인메모리/로컬 캐시 직렬화 변환을 수행하기 위한 DTO 및 핵심 컨버터 C# 스크립트 설계 구조이다.

4.1 수신용 데이터 구조 클래스

using System;
using System.Collections.Generic;

[Serializable]
public class BeatSageRoot
{
    public string _version;
    public List<BeatSageNote> _notes;
}

[Serializable]
public class BeatSageNote
{
    public float _time;        // 비트 단위 시간
    public int _lineIndex;     // 가로 위치 (0~3)
    public int _lineLayer;     // 세로 위치
    public int _type;          // 타입 (0: Red, 1: Blue)
    public int _cutDirection;  // 베는 방향
}

4.2 포맷 컨버터 코어 로직 (BeatSageConverter.cs)

using UnityEngine;
using System.Collections.Generic;

public class BeatSageConverter : MonoBehaviour
{
    /// <summary>
    /// 비트세이지 .dat 텍스트 데이터를 우리 게임의 NoteData 리스트로 변환합니다.
    /// </summary>
    public List<NoteData> ConvertBeatSageToMyFormat(string rawJson, float songBPM)
    {
        BeatSageRoot sageData = JsonUtility.FromJson<BeatSageRoot>(rawJson);
        List<NoteData> convertedNotes = new List<NoteData>();

        if (sageData == null || sageData._notes == null) return convertedNotes;

        foreach (var sageNote in sageData._notes)
        {
            // 일반 노트(0: 빨강, 1: 파랑)만 처리하고 나머지 오브젝트는 제외
            if (sageNote._type != 0 && sageNote._type != 1) continue;

            // BPM 공식을 대입하여 실제 게임 시간(초) 계산
            float realTimeSeconds = (sageNote._time * 60f) / songBPM;

            NoteData myNote = new NoteData
            {
                time = realTimeSeconds,
                position = sageNote._lineIndex,
                colorType = sageNote._type
            };

            convertedNotes.Add(myNote);
        }

        Debug.Log($"[컨버터] 비트세이지 포맷 변환 성공: {convertedNotes.Count}개 노트 생성됨.");
        return convertedNotes;
    }
}

5. 디바이스 스토리지 최적화 (Oculus Quest)

온디맨드 다운로드 패턴

유저가 인게임 UI 또는 맵 에디터에서 특정 곡을 선택하여 트리거하기 전까지는 음원(.mp3)과 데이터(.json) 파일을 다운로드하지 않아 앱 기본 설치 용량을 최소화한다.

임시 캐시 경로 활용

모든 스트리밍 다운로드 리소스는 Application.temporaryCachePath에 저장하여 오큘러스 내부 저장소의 파일 오염을 방지하며, OS의 메모리 부족 시 정리 가능한 대상 영역으로 권한을 위임한다.

LRU (Least Recently Used) 삭제 메커니즘

게임 또는 툴 초기화 시 캐시 디렉터리 용량을 확인하여 누적 용량이 1GB를 초과할 경우, 파일 시스템의 '최근 접근 시간(Last Access Time)'이 가장 오래된 곡 폴더부터 순차적으로 Directory.Delete로 자동 소거한다.