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,287 @@
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Justin Larrabee <justonia@gmail.com>
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public static class PhysicsExtensions
|
||||
{
|
||||
//
|
||||
// Box
|
||||
//
|
||||
|
||||
public static bool BoxCast(BoxCollider box, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.BoxCast( center, halfExtents, direction, orientation, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static bool BoxCast(BoxCollider box, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.BoxCast( center, halfExtents, direction, out hitInfo, orientation, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static RaycastHit[] BoxCastAll(BoxCollider box, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.BoxCastAll( center, halfExtents, direction, orientation, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int BoxCastNonAlloc(BoxCollider box, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.BoxCastNonAlloc( center, halfExtents, direction, results, orientation, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static bool CheckBox(BoxCollider box, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.CheckBox( center, halfExtents, orientation, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static Collider[] OverlapBox(BoxCollider box, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.OverlapBox( center, halfExtents, orientation, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int OverlapBoxNonAlloc(BoxCollider box, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center, halfExtents;
|
||||
Quaternion orientation;
|
||||
box.ToWorldSpaceBox( out center, out halfExtents, out orientation );
|
||||
return Physics.OverlapBoxNonAlloc( center, halfExtents, results, orientation, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static void ToWorldSpaceBox(this BoxCollider box, out Vector3 center, out Vector3 halfExtents, out Quaternion orientation)
|
||||
{
|
||||
orientation = box.transform.rotation;
|
||||
center = box.transform.TransformPoint( box.center );
|
||||
var lossyScale = box.transform.lossyScale;
|
||||
var scale = AbsVec3( lossyScale );
|
||||
halfExtents = Vector3.Scale( scale, box.size ) * 0.5f;
|
||||
}
|
||||
|
||||
//
|
||||
// Sphere
|
||||
//
|
||||
|
||||
public static bool SphereCast(SphereCollider sphere, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.SphereCast( center, radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static RaycastHit[] SphereCastAll(SphereCollider sphere, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.SphereCastAll( center, radius, direction, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int SphereCastNonAlloc(SphereCollider sphere, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.SphereCastNonAlloc( center, radius, direction, results, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static bool CheckSphere(SphereCollider sphere, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.CheckSphere( center, radius, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static Collider[] OverlapSphere(SphereCollider sphere, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.OverlapSphere( center, radius, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int OverlapSphereNonAlloc(SphereCollider sphere, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 center;
|
||||
float radius;
|
||||
sphere.ToWorldSpaceSphere( out center, out radius );
|
||||
return Physics.OverlapSphereNonAlloc( center, radius, results, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static void ToWorldSpaceSphere(this SphereCollider sphere, out Vector3 center, out float radius)
|
||||
{
|
||||
center = sphere.transform.TransformPoint( sphere.center );
|
||||
radius = sphere.radius * MaxVec3( AbsVec3( sphere.transform.lossyScale ) );
|
||||
}
|
||||
|
||||
//
|
||||
// Capsule
|
||||
//
|
||||
|
||||
public static bool CapsuleCast(CapsuleCollider capsule, Vector3 direction, out RaycastHit hitInfo, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.CapsuleCast( point0, point1, radius, direction, out hitInfo, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static RaycastHit[] CapsuleCastAll(CapsuleCollider capsule, Vector3 direction, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.CapsuleCastAll( point0, point1, radius, direction, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int CapsuleCastNonAlloc(CapsuleCollider capsule, Vector3 direction, RaycastHit[] results, float maxDistance = Mathf.Infinity, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.CapsuleCastNonAlloc( point0, point1, radius, direction, results, maxDistance, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static bool CheckCapsule(CapsuleCollider capsule, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.CheckCapsule( point0, point1, radius, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static Collider[] OverlapCapsule(CapsuleCollider capsule, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.OverlapCapsule( point0, point1, radius, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static int OverlapCapsuleNonAlloc(CapsuleCollider capsule, Collider[] results, int layerMask = Physics.DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal)
|
||||
{
|
||||
Vector3 point0, point1;
|
||||
float radius;
|
||||
capsule.ToWorldSpaceCapsule( out point0, out point1, out radius );
|
||||
return Physics.OverlapCapsuleNonAlloc( point0, point1, radius, results, layerMask, queryTriggerInteraction );
|
||||
}
|
||||
|
||||
public static void ToWorldSpaceCapsule(this CapsuleCollider capsule, out Vector3 point0, out Vector3 point1, out float radius)
|
||||
{
|
||||
var center = capsule.transform.TransformPoint( capsule.center );
|
||||
radius = 0f;
|
||||
float height = 0f;
|
||||
Vector3 lossyScale = AbsVec3( capsule.transform.lossyScale );
|
||||
Vector3 dir = Vector3.zero;
|
||||
|
||||
switch (capsule.direction)
|
||||
{
|
||||
case 0: // x
|
||||
radius = Mathf.Max( lossyScale.y, lossyScale.z ) * capsule.radius;
|
||||
height = lossyScale.x * capsule.height;
|
||||
dir = capsule.transform.TransformDirection( Vector3.right );
|
||||
break;
|
||||
case 1: // y
|
||||
radius = Mathf.Max( lossyScale.x, lossyScale.z ) * capsule.radius;
|
||||
height = lossyScale.y * capsule.height;
|
||||
dir = capsule.transform.TransformDirection( Vector3.up );
|
||||
break;
|
||||
case 2: // z
|
||||
radius = Mathf.Max( lossyScale.x, lossyScale.y ) * capsule.radius;
|
||||
height = lossyScale.z * capsule.height;
|
||||
dir = capsule.transform.TransformDirection( Vector3.forward );
|
||||
break;
|
||||
}
|
||||
|
||||
if (height < radius * 2f)
|
||||
{
|
||||
dir = Vector3.zero;
|
||||
}
|
||||
|
||||
point0 = center + dir * ( height * 0.5f - radius );
|
||||
point1 = center - dir * ( height * 0.5f - radius );
|
||||
}
|
||||
|
||||
//
|
||||
// Util
|
||||
//
|
||||
|
||||
public static void SortClosestToFurthest(RaycastHit[] hits, int hitCount = -1)
|
||||
{
|
||||
if (hitCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hitCount < 0)
|
||||
{
|
||||
hitCount = hits.Length;
|
||||
}
|
||||
|
||||
Array.Sort<RaycastHit>( hits, 0, hitCount, ascendDistance );
|
||||
}
|
||||
|
||||
//
|
||||
// Private
|
||||
//
|
||||
|
||||
private class AscendingDistanceComparer : IComparer<RaycastHit>
|
||||
{
|
||||
public int Compare(RaycastHit h1, RaycastHit h2)
|
||||
{
|
||||
return h1.distance < h2.distance ? -1 : ( h1.distance > h2.distance ? 1 : 0 );
|
||||
}
|
||||
}
|
||||
|
||||
private static AscendingDistanceComparer ascendDistance = new AscendingDistanceComparer();
|
||||
|
||||
private static Vector3 AbsVec3(Vector3 v)
|
||||
{
|
||||
return new Vector3( Mathf.Abs( v.x ), Mathf.Abs( v.y ), Mathf.Abs( v.z ) );
|
||||
}
|
||||
|
||||
private static float MaxVec3(Vector3 v)
|
||||
{
|
||||
return Mathf.Max( v.x, Mathf.Max( v.y, v.z ) );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user