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:
@@ -0,0 +1,146 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace VRSDK
|
||||
{
|
||||
/// <summary>
|
||||
/// Collision predictor help us to cast complex collider shapes and check for posibles collisions
|
||||
/// </summary>
|
||||
public class CollisionPredictor : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private LayerMask layerMask = new LayerMask();
|
||||
|
||||
private List<Collider> colliderList = null;
|
||||
private Transform collisionPredictorParent = null;
|
||||
private bool collisionCopyCreate = false;
|
||||
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (!collisionCopyCreate)
|
||||
{
|
||||
CreateCollisionCopy();
|
||||
SetLayerMask();
|
||||
RemoveInvalidColliders();
|
||||
collisionCopyCreate = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCollisionCopy()
|
||||
{
|
||||
GameObject clone = Instantiate( gameObject );
|
||||
RemoveComponents( clone );
|
||||
|
||||
if (clone.GetComponent<VR_OutlineHighlight>() != null)
|
||||
{
|
||||
Destroy( clone.GetComponent<VR_OutlineHighlight>() );
|
||||
}
|
||||
|
||||
if (clone.GetComponent<VR_Outline>() != null)
|
||||
{
|
||||
Destroy( clone.GetComponent<VR_Outline>() );
|
||||
}
|
||||
|
||||
clone.name = gameObject.name + "_CollisionPredictorCopy";
|
||||
clone.transform.localScale = clone.transform.localScale;
|
||||
collisionPredictorParent = clone.transform;
|
||||
colliderList = clone.GetComponentsInChildren<Collider>().ToList();
|
||||
}
|
||||
|
||||
private void SetLayerMask()
|
||||
{
|
||||
for (int n = 0; n < colliderList.Count; n++)
|
||||
{
|
||||
colliderList[n].gameObject.layer = LayerMask.NameToLayer( "IgnoreCollision" );
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveInvalidColliders()
|
||||
{
|
||||
for (int n = 0; n < colliderList.Count; n++)
|
||||
{
|
||||
if (colliderList[n] is MeshCollider)
|
||||
{
|
||||
Debug.LogWarning("Collider " + colliderList[n].name + " is a MeshCollider, CollisionPredictor does no support MeshColliders");
|
||||
Destroy(colliderList[n]);
|
||||
colliderList.RemoveAt( n );
|
||||
n--;
|
||||
}
|
||||
|
||||
else if (colliderList[n].isTrigger)
|
||||
{
|
||||
Debug.LogWarning("Collider " + colliderList[n].name + " is a trigger collider, removing it from CollisionPredictor");
|
||||
Destroy( colliderList[n] );
|
||||
colliderList.RemoveAt( n );
|
||||
n--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool WillCollisionAtPositionAndRotation(Vector3 position , Quaternion rotation )
|
||||
{
|
||||
//we need to create first our collision copy
|
||||
if (!collisionCopyCreate)
|
||||
return false;
|
||||
|
||||
//trasnlate the object to the desire postion and rotation
|
||||
collisionPredictorParent.position = position;
|
||||
collisionPredictorParent.rotation = rotation;
|
||||
|
||||
for (int n = 0; n < colliderList.Count; n++)
|
||||
{
|
||||
if (CheckCollisionAtPosition( colliderList[n] ))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private bool CheckCollisionAtPosition(Collider collider)
|
||||
{
|
||||
if (collider is SphereCollider)
|
||||
{
|
||||
return PhysicsExtensions.CheckSphere(collider as SphereCollider , layerMask , QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
if (collider is BoxCollider)
|
||||
{
|
||||
return PhysicsExtensions.CheckBox( collider as BoxCollider, layerMask, QueryTriggerInteraction.Ignore );
|
||||
}
|
||||
if (collider is CapsuleCollider)
|
||||
{
|
||||
return PhysicsExtensions.CheckCapsule(collider as CapsuleCollider , layerMask , QueryTriggerInteraction.Ignore);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
private void RemoveComponents(GameObject go)
|
||||
{
|
||||
Component[] componentArray = go.GetComponentsInChildren<Component>();
|
||||
|
||||
for (int n = 0; n < componentArray.Length; n++)
|
||||
{
|
||||
if (componentArray[n] != null)
|
||||
{
|
||||
if (componentArray[n] is Canvas)
|
||||
Destroy( componentArray[n].gameObject );
|
||||
|
||||
else if (CanDestroyComponent( componentArray[n] ))
|
||||
Destroy( componentArray[n] );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanDestroyComponent(Component c)
|
||||
{
|
||||
return !( c is Transform ) && !(c is Collider) && !( c is VR_OutlineHighlight) && !( c is VR_Outline );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 930b984cd4e14a3498a00dcde5dbcc15
|
||||
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/VRSDK/Physics/CollisionPredictor.cs
|
||||
uploadId: 546658
|
||||
@@ -0,0 +1,151 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace VRSDK
|
||||
{
|
||||
public class HandPhysics
|
||||
{
|
||||
private HistoryBuffer historyBuffer = null;
|
||||
private const float throwSmoothVelocity = 3.0f;
|
||||
private const float velocityModifier = 1.9f;
|
||||
private const float angularVeloctyModifier = 1.8f;
|
||||
private const int defaultSampleSize = 5;
|
||||
private const int handDirectionSampleSize = 5;
|
||||
private VR_CharacterController characterController = null;
|
||||
|
||||
private Transform trackingSpace = null;
|
||||
|
||||
|
||||
public Vector3 Velocity
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Vector3> velocityHistory = historyBuffer.VelocityHistory.Sample( defaultSampleSize );
|
||||
|
||||
Vector3 throwDirection = CalculateThrowDirection();
|
||||
|
||||
//calculate the EMA (Exponential Moving Average), just a way to predict the desire throw velocity base on previus velocities
|
||||
//you can read more here https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
float velocityMagnitudeEMA = CalculateMagnitudeEMA( velocityHistory, throwSmoothVelocity );
|
||||
|
||||
|
||||
return throwDirection * velocityMagnitudeEMA * velocityModifier;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 AngularVelocity
|
||||
{
|
||||
get
|
||||
{
|
||||
List<Vector3> angularVelocityHistory = historyBuffer.AngularVelocityHistory.Sample( defaultSampleSize );
|
||||
|
||||
//calculate the EMA (Exponential Moving Average), just a way to predict the desire throw velocity base on previus velocities
|
||||
//you can read more here https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
|
||||
return CalculateEMA( angularVelocityHistory, throwSmoothVelocity ) * angularVeloctyModifier;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public HandPhysics(HistoryBuffer buffer)
|
||||
{
|
||||
historyBuffer = buffer;
|
||||
characterController = MonoBehaviour.FindObjectOfType<VR_CharacterController>();
|
||||
|
||||
trackingSpace = VR_Manager.instance.Player.TrackingSpace;
|
||||
|
||||
}
|
||||
|
||||
public void ApplyThrowVelocity(VR_Grabbable grabbable)
|
||||
{
|
||||
|
||||
Rigidbody rb = grabbable.RB;
|
||||
|
||||
//apply the hand velocity and angular velocity
|
||||
ProcessThrowVelocity(rb);
|
||||
ProcessThrowAngularVelocity(rb);
|
||||
}
|
||||
|
||||
private void ProcessThrowVelocity(Rigidbody rb)
|
||||
{
|
||||
//add the throw velocity
|
||||
rb.AddForce( Velocity, ForceMode.VelocityChange );
|
||||
|
||||
if (characterController != null)
|
||||
{
|
||||
//add the character controller velocity, maybe this dont made to much sense at first but helps a lot when you throw a object while moving
|
||||
rb.AddForce( characterController.Velocity, ForceMode.VelocityChange );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ProcessThrowAngularVelocity(Rigidbody rb)
|
||||
{
|
||||
rb.AddTorque( AngularVelocity , ForceMode.VelocityChange );
|
||||
}
|
||||
|
||||
|
||||
private Vector3 CalculateThrowDirection()
|
||||
{
|
||||
List<Vector3> localPositionHistory = historyBuffer.LocalPositionHistory.Sample( handDirectionSampleSize );
|
||||
|
||||
Vector3 throwDirection = ( localPositionHistory[localPositionHistory.Count - 1] - localPositionHistory[0] ).normalized;
|
||||
|
||||
if (trackingSpace != null)
|
||||
{
|
||||
return trackingSpace.TransformDirection( throwDirection );
|
||||
}
|
||||
|
||||
return throwDirection;
|
||||
}
|
||||
|
||||
#region MATH
|
||||
//you can read more here https://en.wikipedia.org/wiki/Moving_average
|
||||
private float CalculateMagnitudeSMA(List<Vector3> buffer)
|
||||
{
|
||||
Vector3 sum = Vector3.zero;
|
||||
|
||||
for (int index = 0; index < buffer.Count - 1; index++)
|
||||
{
|
||||
sum += buffer[index];
|
||||
}
|
||||
|
||||
return ( sum / buffer.Count ).magnitude;
|
||||
}
|
||||
|
||||
private Vector3 CalculateSMA(List<Vector3> buffer)
|
||||
{
|
||||
Vector3 sum = Vector3.zero;
|
||||
|
||||
for (int index = 0; index < buffer.Count - 1; index++)
|
||||
{
|
||||
sum += buffer[index];
|
||||
}
|
||||
|
||||
return ( sum / buffer.Count );
|
||||
}
|
||||
|
||||
private Vector3 CalculateEMA(List<Vector3> buffer , float modifier)
|
||||
{
|
||||
Vector3 SMA = CalculateSMA(buffer);
|
||||
float smoothingConst = 2 / ( defaultSampleSize + 1 );
|
||||
Vector3 EMA = ( buffer[buffer.Count - 1] - SMA ) * ( smoothingConst * modifier );
|
||||
EMA += SMA;
|
||||
|
||||
return EMA;
|
||||
}
|
||||
|
||||
private float CalculateMagnitudeEMA(List<Vector3> buffer, float modifier)
|
||||
{
|
||||
float SMA = CalculateMagnitudeSMA( buffer );
|
||||
float smoothingConst = 2 / ( defaultSampleSize + 1 );
|
||||
float EMA = ( buffer[buffer.Count - 1].magnitude - SMA ) * ( smoothingConst * modifier );
|
||||
EMA += SMA;
|
||||
|
||||
return EMA;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ab181a07ebefb2f46b5bb44e5b9ca80e
|
||||
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/VRSDK/Physics/HandPhysics.cs
|
||||
uploadId: 546658
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace VRSDK
|
||||
{
|
||||
public class OverrideRigidBodyCOM : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Rigidbody rb = null;
|
||||
[SerializeField] private Transform com = null;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
rb.centerOfMass = com.transform.localPosition;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3e532847382ec149952fdad85267b3c
|
||||
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/VRSDK/Physics/OverrideRigidBodyCOM.cs
|
||||
uploadId: 546658
|
||||
Reference in New Issue
Block a user