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:
2026-05-21 23:37:34 +09:00
commit 4dad9e5d5b
1068 changed files with 175146 additions and 0 deletions
@@ -0,0 +1,46 @@
using UnityEngine;
using System.Collections.Generic;
namespace DamageSystem.Dismember
{
public enum BodyPartType
{
Head,
LeftArm,
LeftForeArm,
RightArm,
RightForeArm,
RightLeg,
RightCalf,
LeftLeg,
LeftCalf
}
public class DismemberManager : MonoBehaviour
{
[SerializeField] private List<DismemberPart> dismembertPartList = null;
private DamageableManager damageableManager = null;
private void Awake()
{
damageableManager = GetComponent<DamageableManager>();
for (int n = 0; n < dismembertPartList.Count; n++)
{
dismembertPartList[n].SetHP(damageableManager.HP);
dismembertPartList[n].OnDismember.AddListener( OnDismember );
}
}
private void OnDismember(DismemberPart part)
{
if(part.KillOnDismember)
{
damageableManager.Kill();
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 56a8f8f39db08334fadbaa616a558c61
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 168243
packageName: VR Beats Kit
packageVersion: 2.0
assetPath: Assets/VRBeatsKit/Modules/VRDamageSystem/Dismember/DismemberManager.cs
uploadId: 546658
@@ -0,0 +1,114 @@
using UnityEngine;
using System.Collections.Generic;
using DamageSystem.Events;
namespace DamageSystem.Dismember
{
public class DismemberPart : Damageable
{
[SerializeField] private Rigidbody bodyPartPrefab = null;
[SerializeField] private GameObject realBodyPart = null;
[SerializeField] private float resistance = 0.25f;
[SerializeField] private bool killOnDismember = false;
[SerializeField] private GameObject[] connectedBodyParts = null;
[SerializeField] private OnDismemberEvent onDismember = null;
private float hp = 0.0f;
private DamageablePart thisDamageablePart = null;
private Dictionary<Mesh, GameObject> bodyPartStateConnections = new Dictionary<Mesh, GameObject>();
private Rigidbody thisRB = null;
public bool KillOnDismember { get { return killOnDismember; } }
public float Resistance { get { return resistance; } }
public OnDismemberEvent OnDismember { get { return onDismember; } }
private void Awake()
{
thisDamageablePart = GetComponent<DamageablePart>();
thisRB = GetComponent<Rigidbody>();
}
private void Start()
{
CreateBodyPartStateConnections();
}
private void CreateBodyPartStateConnections()
{
for (int n = 0; n < connectedBodyParts.Length; n++)
{
SkinnedMeshRenderer skinnedMeshRender = connectedBodyParts[n].GetComponent<SkinnedMeshRenderer>();
if (skinnedMeshRender != null)
{
bodyPartStateConnections[skinnedMeshRender.sharedMesh] = skinnedMeshRender.gameObject;
}
}
}
public void SetHP(float hp)
{
this.hp = hp * resistance;
}
public override void DoDamage(DamageInfo info)
{
hp -= info.dmg * thisDamageablePart.DamageMultiplier;
if( CanDismember(info) )
{
Rigidbody bodyPart = CreateBodyPart();
info.ApplyImpact( bodyPart );
onDismember.Invoke(this);
}
}
private bool CanDismember(DamageInfo info)
{
return info.canDismember && hp <= 0.0f && realBodyPart.gameObject.activeInHierarchy;
}
private Rigidbody CreateBodyPart()
{
Rigidbody bodyPart = Instantiate( bodyPartPrefab, transform.position, transform.rotation );
DestroyUnnecesaryBodyParts(bodyPart.gameObject);
realBodyPart.gameObject.SetActive( false );
DisableConnectedBodyParts();
return bodyPart;
}
private void DisableConnectedBodyParts()
{
for (int n = 0; n < connectedBodyParts.Length; n++)
{
connectedBodyParts[n].SetActive(false);
}
}
//disable body parts that has been already dismember
private void DestroyUnnecesaryBodyParts(GameObject bodyPart)
{
foreach ( Transform child in bodyPart.transform)
{
MeshFilter meshRender = child.GetComponent<MeshFilter>();
if (meshRender != null)
{
GameObject bodyPartGO = null;
if (bodyPartStateConnections.TryGetValue( meshRender.sharedMesh, out bodyPartGO ))
{
meshRender.gameObject.SetActive( bodyPartGO.activeInHierarchy );
}
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 1c8caeb8b6a7ea946acfd1f6729a1d8d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
AssetOrigin:
serializedVersion: 1
productId: 168243
packageName: VR Beats Kit
packageVersion: 2.0
assetPath: Assets/VRBeatsKit/Modules/VRDamageSystem/Dismember/DismemberPart.cs
uploadId: 546658