Files
2026-05-26 18:54:56 +09:00

151 lines
5.0 KiB
C#

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.FindFirstObjectByType<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
}
}