|
|
|
|
using UnityEasing;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace Projectiles
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Base class for all kinematic projectile types.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public abstract class KinematicProjectile : ProjectileBase, IBufferView<KinematicData>
|
|
|
|
|
{
|
|
|
|
|
// PUBLIC MEMBERS
|
|
|
|
|
|
|
|
|
|
public bool IsFinished { get; protected set; }
|
|
|
|
|
|
|
|
|
|
// PROTECTED MEMBERS
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
protected float _startSpeed = 40f;
|
|
|
|
|
[SerializeField, Tooltip("Projectile length improves hitting moving targets")]
|
|
|
|
|
protected float _length = 0f;
|
|
|
|
|
|
|
|
|
|
// PRIVATE MEMBERS
|
|
|
|
|
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private float _maxDistance = 200f;
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private float _maxTime = 5f;
|
|
|
|
|
[SerializeField, Tooltip("Time for interpolation between barrel position and actual fire path of the projectile")]
|
|
|
|
|
private float _interpolationDuration = 0.3f;
|
|
|
|
|
[SerializeField]
|
|
|
|
|
private Ease _interpolationEase = Ease.OutSine;
|
|
|
|
|
|
|
|
|
|
private Vector3 _startOffset;
|
|
|
|
|
private float _interpolationTime;
|
|
|
|
|
|
|
|
|
|
protected int _lifetimeTicks = -1;
|
|
|
|
|
|
|
|
|
|
// PUBLIC METHODS
|
|
|
|
|
|
|
|
|
|
public virtual KinematicData GetFireData(Vector3 firePosition, Vector3 fireDirection)
|
|
|
|
|
{
|
|
|
|
|
if (_lifetimeTicks < 0)
|
|
|
|
|
{
|
|
|
|
|
int maxDistanceTicks = Mathf.RoundToInt((_maxDistance / _startSpeed) * Context.Runner.TickRate);
|
|
|
|
|
int maxTimeTicks = Mathf.RoundToInt(_maxTime * Context.Runner.TickRate);
|
|
|
|
|
|
|
|
|
|
// GetFireData is called on prefab directly, but it is safe to save
|
|
|
|
|
// the value here as it does not change for different instances
|
|
|
|
|
_lifetimeTicks = maxDistanceTicks > 0 && maxTimeTicks > 0 ? Mathf.Min(maxDistanceTicks, maxTimeTicks)
|
|
|
|
|
: (maxDistanceTicks > 0 ? maxDistanceTicks : maxTimeTicks);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new KinematicData()
|
|
|
|
|
{
|
|
|
|
|
Position = firePosition,
|
|
|
|
|
Velocity = fireDirection * _startSpeed,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void OnFixedUpdate(ref KinematicData data)
|
|
|
|
|
{
|
|
|
|
|
if (Context.Runner.Tick >= data.FireTick + _lifetimeTicks)
|
|
|
|
|
{
|
|
|
|
|
data.IsFinished = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void Activate(ref KinematicData data)
|
|
|
|
|
{
|
|
|
|
|
var startPosition = Context.BarrelTransforms[data.BarrelIndex].position;
|
|
|
|
|
|
|
|
|
|
// Kinematic projectile visual starts at the barrel position and is slowly
|
|
|
|
|
// interpolated to its actual path that starts directly from camera.
|
|
|
|
|
transform.position = startPosition;
|
|
|
|
|
transform.rotation = Quaternion.LookRotation(data.Velocity);
|
|
|
|
|
|
|
|
|
|
_startOffset = startPosition - data.Position;
|
|
|
|
|
_interpolationTime = 0f;
|
|
|
|
|
|
|
|
|
|
IsFinished = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public virtual void Deactivate()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// IBufferView INTERFACE
|
|
|
|
|
|
|
|
|
|
public virtual void Render(ref KinematicData data, ref KinematicData fromData, float alpha)
|
|
|
|
|
{
|
|
|
|
|
if (data.IsFinished == true)
|
|
|
|
|
{
|
|
|
|
|
SpawnImpactVisual(data.ImpactPosition, data.ImpactNormal);
|
|
|
|
|
IsFinished = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var targetPosition = GetRenderPosition(ref data, ref fromData, alpha);
|
|
|
|
|
float interpolationProgress = 0f;
|
|
|
|
|
|
|
|
|
|
if (targetPosition != (Vector3)data.Position)
|
|
|
|
|
{
|
|
|
|
|
// Do not start interpolation until projectile should actually move
|
|
|
|
|
_interpolationTime += Time.deltaTime;
|
|
|
|
|
interpolationProgress = Mathf.Clamp01(_interpolationTime / _interpolationDuration);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var offset = Vector3.Lerp(_startOffset, Vector3.zero, _interpolationEase.Get(interpolationProgress));
|
|
|
|
|
|
|
|
|
|
var previousPosition = transform.position;
|
|
|
|
|
var nextPosition = targetPosition + offset;
|
|
|
|
|
var direction = nextPosition - previousPosition;
|
|
|
|
|
|
|
|
|
|
transform.position = nextPosition;
|
|
|
|
|
|
|
|
|
|
if (direction != Vector3.zero)
|
|
|
|
|
{
|
|
|
|
|
transform.rotation = Quaternion.LookRotation(direction);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PROTECTED METHODS
|
|
|
|
|
|
|
|
|
|
protected abstract Vector3 GetRenderPosition(ref KinematicData data, ref KinematicData fromData, float alpha);
|
|
|
|
|
}
|
|
|
|
|
}
|