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,38 @@
using UnityEngine;
namespace DamageSystem
{
public class DamageInfo
{
public float dmg = 0.0f;
public Vector3 hitDir = Vector3.zero;
public Vector3 hitPoint = Vector3.zero;
public float explosionRadius = 0.0f;
public float upwardsModifier = 0.0f;
public float hitForce = 0.0f;
public ForceMode forceMode = ForceMode.Impulse;
public DamageType damageType = DamageType.Shoot;
public bool canDismember = false;
public GameObject sender = null;
public DamageInfo() { }
public void ApplyImpact(Rigidbody rb)
{
if (rb == null)
return;
if (damageType == DamageType.Explosion)
{
rb.AddExplosionForce( hitForce, hitPoint, explosionRadius, upwardsModifier, forceMode );
}
else if (damageType == DamageType.Shoot)
{
rb.AddForceAtPosition( hitForce * hitDir, hitPoint, forceMode );
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7e92c731e4c9be642a714dcd2b239e1a
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/DamageInfo.cs
uploadId: 546658
@@ -0,0 +1,14 @@
using UnityEngine;
namespace DamageSystem
{
/// <summary>
/// Basic class for all damageable stuff
/// </summary>
public abstract class Damageable : MonoBehaviour
{
public abstract void DoDamage(DamageInfo info);
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: a3825a2e982e8a849baa900e14c416e0
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/Damageable.cs
uploadId: 546658
@@ -0,0 +1,56 @@
using UnityEngine;
namespace DamageSystem
{
/// <summary>
/// This component report all limb damage to the the AI
/// </summary>
public class DamageableLimb : Damageable
{
[SerializeField] private float damageMultiplier = 1.0f;
private DamageableManager owner = null;
//public Rigidbody RB { get { return rb; } }
public DamageableManager Owner { get { return owner; } }
public void SetOwner(DamageableManager owner)
{
this.owner = owner;
}
public override void DoDamage(DamageInfo info)
{
info.dmg *= damageMultiplier;
//owner.DoDamage(info, this);
}
private void ProcessHit(Rigidbody rb)
{
DamageInfo info = new DamageInfo();
info.damageType = DamageType.Physical;
info.hitDir = rb.linearVelocity.normalized;
info.dmg = rb.linearVelocity.magnitude * damageMultiplier;
info.hitForce = rb.linearVelocity.magnitude;
DoDamage(info);
}
private void OnCollisionEnter(Collision other)
{
//in this way we can respond to hits from objects and apply damage,
//like the player throwing a box to a enemy
if (other.rigidbody != null)
{
ProcessHit( other.rigidbody );
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0c6d2eeb5625078408a74f80b7bd56d7
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/DamageableLimb.cs
uploadId: 546658
@@ -0,0 +1,158 @@
using DamageSystem.Events;
using UnityEngine;
using UnityEngine.Events;
namespace DamageSystem
{
public enum DamageType
{
Explosion,
Shoot,
Physical
}
public class DamageableManager : MonoBehaviour
{
[SerializeField] protected float hp = 100.0f;
[SerializeField] private float regenerationSpeed = 0.0f;
[SerializeField] protected Rigidbody com = null;
[SerializeField] private OnDamageEvent onDamage = null;
[SerializeField] private UnityEvent onDie = null;
[SerializeField] protected OnValueChangeEvent onHPChangeEvent;
protected bool isDead = false;
private float maxHP = 0.0f;
public float HP { get { return hp; } }
public OnDamageEvent OnDamage { get { return onDamage; } }
public OnValueChangeEvent OnHPChangeEvent { get { return onHPChangeEvent; } }
public UnityEvent OnDie { get { return onDie; } }
public bool Invulnerable { get; set; }
public bool IsDead { get { return isDead; } }
public float MaxHP { get { return maxHP; } }
protected virtual void Awake()
{
maxHP = hp;
SetDamageablePartsOwner();
}
protected virtual void Update()
{
DoRegeneration();
}
protected virtual void DoRegeneration()
{
if(regenerationSpeed > 0.0f && !isDead)
{
ModifyHP( regenerationSpeed * Time.deltaTime );
}
}
protected virtual void SetDamageablePartsOwner()
{
//get all damageable
DamageablePart[] damageableArray = GetComponentsInChildren<DamageablePart>();
//set his owner
for (int n = 0; n < damageableArray.Length; n++)
{
damageableArray[n].SetOwner( this );
}
}
public virtual void DoDamage(DamageInfo info, DamageablePart damageable)
{
//if we are dead just apply the impact force
if (isDead)
{
ApplyImpactForce( info, damageable );
if (onDamage != null)
onDamage.Invoke( info , damageable );
return;
}
ModifyHP(-info.dmg);
if (hp <= 0.0f)
{
TriggerDieEvent();
ApplyImpactForce( info, damageable );
}
if (onDamage != null)
{
onDamage.Invoke( info, damageable );
}
}
protected virtual void ApplyImpactForce(DamageInfo info, DamageablePart damageable)
{
//if this AI if no dead it is being controlled by the Animator so dont apply any impact force
if (!IsDead)
return;
if (com != null && info.damageType == DamageType.Explosion)
{
com.AddExplosionForce( info.hitForce, info.hitPoint, info.explosionRadius, info.upwardsModifier, info.forceMode );
}
else if (damageable.RB != null && info.damageType == DamageType.Shoot)
{
damageable.RB.AddForceAtPosition( info.hitForce * info.hitDir, info.hitPoint, info.forceMode );
}
}
protected virtual void TriggerDieEvent()
{
if (onDie != null)
onDie.Invoke();
isDead = true;
}
public void SetupDamageableLimbs()
{
Collider[] colliderArray = transform.GetComponentsInChildren<Collider>();
for (int n = 0; n < colliderArray.Length; n++)
{
if (colliderArray[n].GetComponent<DamageableLimb>() == null)
{
colliderArray[n].gameObject.AddComponent<DamageableLimb>();
}
}
}
public virtual void Kill()
{
ModifyHP(float.MinValue);
if (onDie != null)
onDie.Invoke();
}
public virtual void ModifyHP(float v, bool triggerEvent = true)
{
hp += v;
if (hp < 0.0f)
hp = 0.0f;
if (hp > MaxHP)
hp = maxHP;
if (triggerEvent)
{
onHPChangeEvent.Invoke(hp);
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: d98d12e9d483ed74eb1c70105c7cce4d
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/DamageableManager.cs
uploadId: 546658
@@ -0,0 +1,31 @@
using UnityEngine;
namespace DamageSystem
{
public class DamageableManagerAI : DamageableManager
{
protected override void Awake()
{
base.Awake();
OnDamage.AddListener( ApplyImpactForce );
}
private void ApplyImpactForce(DamageInfo info, DamageablePart damageable)
{
//if this AI if no dead it is being controlled by the Animator so dont apply any impact force
if (!IsDead)
return;
if (com != null && info.damageType == DamageType.Explosion)
{
com.AddExplosionForce( info.hitForce, info.hitPoint, info.explosionRadius, info.upwardsModifier, info.forceMode );
}
else if (damageable.RB != null && info.damageType == DamageType.Shoot)
{
damageable.RB.AddForceAtPosition( info.hitForce * info.hitDir, info.hitPoint, info.forceMode );
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 6a8bf86371483fe4bb3449526154a5c7
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/DamageableManagerAI.cs
uploadId: 546658
@@ -0,0 +1,86 @@
using UnityEngine;
using UnityEngine.Serialization;
using VRSDK;
namespace DamageSystem
{
public class DamageablePart : Damageable
{
[FormerlySerializedAs("damageMultiplier")]
[SerializeField] private float m_damageMultiplier = 1.0f;
[FormerlySerializedAs("rb")]
[SerializeField] private Rigidbody m_rb = null;
private DamageableManager m_owner = null;
public Rigidbody RB => m_rb;
public DamageableManager Owner => m_owner;
public float DamageMultiplier => m_damageMultiplier;
private void Awake()
{
if(m_rb == null) m_rb = GetComponent<Rigidbody>();
}
public void ExternalSetup(float damageMultiplier, Rigidbody rb)
{
m_damageMultiplier = damageMultiplier;
m_rb = rb;
}
public void SetOwner(DamageableManager owner)
{
this.m_owner = owner;
}
public override void DoDamage(DamageInfo info)
{
if (m_owner == null)
{
return;
}
info.dmg *= m_damageMultiplier;
m_owner.DoDamage( info, this );
}
private void ProcessHit(Rigidbody rb , GameObject sender)
{
if (m_owner == null)
{
return;
}
DamageInfo info = new DamageInfo();
info.damageType = DamageType.Physical;
info.hitDir = rb.linearVelocity.normalized;
info.dmg = rb.linearVelocity.magnitude * m_damageMultiplier;
info.hitForce = rb.linearVelocity.magnitude;
info.sender = sender;
DoDamage( info );
}
private void OnCollisionEnter(Collision other)
{
//in this way we can respond to hits from objects and apply damage,
//like the player throwing a box to a enemy
if (other.rigidbody != null)
{
VR_Grabbable grabbable = VR_Manager.instance.GetGrabbableFromCollider(other.collider);
if (grabbable != null && grabbable.ObjectWasThrow && grabbable.LastInteractController != null)
{
GameObject sender = grabbable.LastInteractController.transform.root.gameObject;
ProcessHit( other.rigidbody, sender );
}
else
{
ProcessHit( other.rigidbody, other.gameObject );
}
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: b09e825e715e4d740bffea4f46d0b742
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/DamageablePart.cs
uploadId: 546658
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 21262b0943f423b4b91dc6d8b743e03a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -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
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 994fcaebc2010c345a36ce55b29394b8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
using UnityEngine.Events;
namespace DamageSystem.Events
{
[System.Serializable]
public class OnDamageEvent : UnityEvent<DamageInfo , DamageablePart> { }
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 08d138e6abccc3d4fb99c33db9808fe3
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/Events/OnDamageEvent.cs
uploadId: 546658
@@ -0,0 +1,9 @@
using DamageSystem.Dismember;
using UnityEngine.Events;
namespace DamageSystem.Events
{
[System.Serializable]
public class OnDismemberEvent : UnityEvent<DismemberPart> { }
}
@@ -0,0 +1,10 @@
fileFormatVersion: 2
guid: b652d6b75b904b7cbdcc24f54ca67181
timeCreated: 1669476741
AssetOrigin:
serializedVersion: 1
productId: 168243
packageName: VR Beats Kit
packageVersion: 2.0
assetPath: Assets/VRBeatsKit/Modules/VRDamageSystem/Events/OnDismemberEvent.cs
uploadId: 546658
@@ -0,0 +1,7 @@
using UnityEngine.Events;
namespace DamageSystem.Events
{
[System.Serializable]
public class OnValueChangeEvent : UnityEvent<float> { }
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 7688e570cdb728a498bc5f6ce954fb94
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/Events/OnValueChangeEvent.cs
uploadId: 546658
@@ -0,0 +1,109 @@
using UnityEngine;
using VRSDK;
namespace DamageSystem
{
public class Explosion : MonoBehaviour
{
#region INSPECTOR
[SerializeField] private float dmg = 200.0f;
[SerializeField] private float explosionRange = 0.0f;
[SerializeField] private float explosionForce = 0.0f;
[SerializeField] private float upwardsModifier = 0.0f;
[SerializeField] private bool autoExplode = true;
#endregion
private DamageInfo damageInfo = new DamageInfo();
private void Awake()
{
if (autoExplode)
{
Explode();
}
}
public virtual void Explode(GameObject sender = null)
{
Collider[] colliderArray = Physics.OverlapSphere( transform.position, explosionRange );
for (int n = 0; n < colliderArray.Length; n++)
{
Damageable damageable = colliderArray[n].GetComponent<Damageable>();
if (damageable != null)
{
float distance = Vector3.Distance(transform.position, damageable.transform.position);
float distanceFactor = Mathf.Abs((distance / explosionRange) - 1.0f); // a distance factor from 0.0f to 1.0f
//create damage info
DamageInfo info = CreateDamageInfo(distanceFactor);
info.sender = sender;
//send damage event
DoDamage(damageable, info);
}
else
{
Rigidbody rb = colliderArray[n].GetComponent<Rigidbody>();
if (rb != null)
{
ApplyImpactForce( rb );
}
else
{
VR_Grabbable grabbable = VR_Manager.instance.GetGrabbableFromCollider( colliderArray[n] );
if (grabbable != null && grabbable.RB != null)
{
ApplyImpactForce( grabbable.RB );
}
}
}
}
Destroy( gameObject , 5.0f);
}
private DamageInfo CreateDamageInfo(float distanceFactor)
{
if (damageInfo != null)
{
damageInfo = new DamageInfo();
}
damageInfo.hitForce = explosionForce;
damageInfo.hitPoint = transform.position;
damageInfo.explosionRadius = explosionRange;
damageInfo.upwardsModifier = upwardsModifier;
damageInfo.dmg = dmg * distanceFactor;
damageInfo.damageType = DamageType.Explosion;
return damageInfo;
}
protected virtual void ApplyImpactForce(Rigidbody rb)
{
rb.AddExplosionForce( explosionForce, transform.position, explosionRange, upwardsModifier );
}
protected virtual void DoDamage(Damageable damageable, DamageInfo info)
{
damageable.DoDamage(info);
}
private void OnDrawGizmosSelected()
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere( transform.position, explosionRange );
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 0c1fe70580def5e41bcb0e40dd75f8a2
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/Explosion.cs
uploadId: 546658
@@ -0,0 +1,109 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace DamageSystem
{
public class FastCollisionListener : MonoBehaviour
{
[SerializeField] private BoxCollider raycastCollider = null;
[SerializeField] private LayerMask layerMask;
[SerializeField] private int collisionAccuracy = 5;
private Pose[] poseArray;
private Pose lastPose;
private bool shouldFillPoses = false;
private BoxCollider raycastColliderClone = null;
private List<Collider> colliderHitThisFrame = new List<Collider>();
private void Awake()
{
poseArray = new Pose[ collisionAccuracy -1 ];
if (raycastCollider != null)
{
raycastColliderClone = Instantiate( raycastCollider );
raycastColliderClone.transform.localScale = raycastCollider.transform.lossyScale;
raycastColliderClone.isTrigger = true;
}
}
private void LateUpdate()
{
UpdateLastPositionAndRotation();
shouldFillPoses = true;
}
private void FillPoses()
{
if (!shouldFillPoses)
return;
shouldFillPoses = false;
float step = 1.0f / collisionAccuracy;
float currentStep = step;
int index = 0;
while (currentStep < 1.0f)
{
Vector3 position = Vector3.Lerp(lastPose.position, raycastCollider.transform.position, currentStep);
Quaternion rotation = Quaternion.Slerp(lastPose.rotation, raycastCollider.transform.rotation, currentStep);
poseArray[index].position = position;
poseArray[index].rotation = rotation;
currentStep += step;
index++;
}
}
private void UpdateLastPositionAndRotation()
{
lastPose.position = raycastCollider.transform.position;
lastPose.rotation = raycastCollider.transform.rotation;
}
private void PoseRaycast(Pose pose)
{
raycastColliderClone.transform.position = pose.position;
raycastColliderClone.transform.rotation = pose.rotation;
Collider[] colliderArray = PhysicsExtensions.OverlapBox(raycastColliderClone, layerMask, QueryTriggerInteraction.Ignore);
for (int n = 0; n < colliderArray.Length; n++)
{
if (colliderArray[n] == null) continue;
if (!colliderHitThisFrame.Contains(colliderArray[n]))
{
colliderHitThisFrame.Add(colliderArray[n]);
}
}
}
public List<Collider> CheckForCollisionsThisFrame()
{
colliderHitThisFrame = new List<Collider>();
FillPoses();
for (int n = 0; n < poseArray.Length; n++)
{
PoseRaycast( poseArray[n] );
}
return colliderHitThisFrame;
}
}
public struct Pose
{
public Vector3 position;
public Quaternion rotation;
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 742a6eb0064b358478b6424c10bf9f96
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/FastCollisionListener.cs
uploadId: 546658
@@ -0,0 +1,20 @@
using UnityEngine;
namespace DamageSystem
{
public class GlobalSurfaceDetails : MonoBehaviour
{
[SerializeField] private SurfaceDetails copyBase = null;
private void Awake()
{
SurfaceDetails[] surfaceDetailsArray = transform.GetComponentsInChildren<SurfaceDetails>();
for (int n = 0; n < surfaceDetailsArray.Length; n++)
{
surfaceDetailsArray[n].CopySettings(copyBase);
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: 95a98fddc3710d449a2db2c665062e5a
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/GlobalSurfaceDetails.cs
uploadId: 546658
@@ -0,0 +1,95 @@
using UnityEngine;
namespace DamageSystem
{
//this script details about how should a bullet bounce over this object
public class SurfaceDetails : Damageable
{
[SerializeField] private bool bulletsCanBounce = false;
[SerializeField] private bool stickArrows = true;
[SerializeField] private float bulletsSpeedLoseOnBounce = 0.20f;
[SerializeField] private AudioClip[] hitSoundArray = null;
[SerializeField] [Range(0.0f , 1.0f)] private float soundvolume = 1.0f;
[SerializeField] private GameObject[] hitEffectArray = null;
[SerializeField] private float lifeTime = 0.0f;
[SerializeField] private bool parentEffect = false;
[SerializeField] private Vector3 rotOffset = Vector3.zero;
[SerializeField] private float effectMinDelay = 0.0f;
[SerializeField] private float effectMaxDelay = 0.0f;
public bool BulletsCanBounce { get { return bulletsCanBounce; } }
public bool StickArrows { get { return stickArrows; } }
public float BulletsSpeedLoseOnBounce { get { return bulletsSpeedLoseOnBounce; } }
private AudioSource audioSource = null;
private float nextEffectTime = 0.0f;
public void CopySettings(SurfaceDetails surface)
{
bulletsCanBounce = surface.bulletsCanBounce;
bulletsSpeedLoseOnBounce = surface.bulletsSpeedLoseOnBounce;
hitSoundArray = surface.hitSoundArray;
hitEffectArray = surface.hitEffectArray;
soundvolume = surface.soundvolume;
lifeTime = surface.lifeTime;
parentEffect = surface.parentEffect;
rotOffset = surface.rotOffset;
effectMinDelay = surface.effectMinDelay;
effectMaxDelay = surface.effectMaxDelay;
}
public override void DoDamage(DamageInfo info)
{
if(nextEffectTime > Time.time)
return;
nextEffectTime = Random.Range(effectMinDelay, effectMaxDelay) + Time.time;
InstantiateHitEffect(info.hitPoint , Quaternion.LookRotation( info.hitDir ) * Quaternion.Euler(rotOffset));
PlayHitSound();
}
private void PlayHitSound()
{
if (hitSoundArray == null || hitSoundArray.Length == 0)
return;
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
if (audioSource == null)
{
audioSource = gameObject.AddComponent<AudioSource>();
}
}
if (audioSource.isPlaying)
audioSource.Stop();
audioSource.volume = Random.Range(soundvolume/2.0f , soundvolume);
audioSource.pitch = Random.Range(0.7f , 1.0f);
audioSource.clip = hitSoundArray[ Random.Range(0 , hitSoundArray.Length) ];
audioSource.Play();
}
private void InstantiateHitEffect(Vector3 position , Quaternion rotation)
{
if (hitEffectArray == null || hitEffectArray.Length == 0)
return;
GameObject go = Instantiate( hitEffectArray[Random.Range( 0, hitEffectArray.Length )], position, rotation);
Destroy( go, lifeTime );
if (parentEffect)
{
go.transform.parent = transform;
}
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: ebd6e5e201849d845bb8cd39dacac25f
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/SurfaceDetails.cs
uploadId: 546658
@@ -0,0 +1,97 @@
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using VRSDK;
namespace DamageSystem
{
//this script controls the melee weapons like the sword,
//in the demo scene and the weapons prefabs, all the weapons can be use as melee weapons to,
//so they use this script
public class VR_MeleeWeapon : MonoBehaviour
{
#region INSPECTOR
[SerializeField] protected FastCollisionListener fastCollisionListener = null;
[SerializeField] protected Transform rayStart = null;
[SerializeField] protected Transform rayEnd = null;
[SerializeField] protected float minSpeed = 0.0f;
[SerializeField] protected float dmg = 0.0f;
[SerializeField] protected float hitForce = 0.0f;
[SerializeField] protected float maxHitForce = 800.0f;
[SerializeField] protected bool canDismember = false;
#endregion
#region PRIVATE
private VR_Grabbable grabbable = null;
private List<Damageable> thisDamageableList = null;
protected DamageInfo damageInfoCache = new DamageInfo();
#endregion
private void Awake()
{
grabbable = GetComponent<VR_Grabbable>();
thisDamageableList = transform.GetComponentsInChildren<Damageable>().ToList();
}
private void Update()
{
//check if we are hitting something
//we do it in the fixed update because the player can move his hands very quickly
if ( grabbable.CurrentGrabState == GrabState.Grab && grabbable.GrabController.Velocity.magnitude > minSpeed )
{
List<Collider> hitColliders = fastCollisionListener.CheckForCollisionsThisFrame();
for (int n = 0; n < hitColliders.Count; n++)
{
TryDoDamage(hitColliders[n].transform, hitColliders[n].transform.position);
}
}
}
protected bool TryDoDamage(Transform target, Vector3 hitPoint)
{
Damageable[] damageableArray = target.GetComponents<Damageable>();
if (damageableArray != null && damageableArray.Length > 0)
{
for (int n = 0; n < damageableArray.Length; n++)
{
if (damageableArray[n] != null && !thisDamageableList.Contains( damageableArray[n]) )
{
RaycastHit hitInfo;
if (Physics.Linecast(rayStart.position, rayEnd.position, out hitInfo, 1 << target.gameObject.layer))
{
DamageInfo damageInfo = CreateDamageInfo(hitInfo.point);
damageableArray[n].DoDamage(damageInfo);
}
}
}
return true;
}
return false;
}
protected virtual DamageInfo CreateDamageInfo(Vector3 hitPoint)
{
Vector3 controllerVelocity = grabbable.GrabController.Velocity;
damageInfoCache.dmg = dmg * controllerVelocity.magnitude;
damageInfoCache.hitDir = controllerVelocity.normalized;
damageInfoCache.hitPoint = hitPoint;
damageInfoCache.hitForce = Mathf.Min( ( controllerVelocity * hitForce ).magnitude, maxHitForce );
damageInfoCache.sender = grabbable.GrabController != null ? grabbable.GrabController.transform.root.gameObject : null;
damageInfoCache.canDismember = canDismember;
return damageInfoCache;
}
}
}
@@ -0,0 +1,18 @@
fileFormatVersion: 2
guid: c68346ff56573a1429560a527ad447e0
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/VR_MeleeWeapon.cs
uploadId: 546658