257 lines
7.5 KiB
C#
257 lines
7.5 KiB
C#
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using UnityEngine;
|
|||
|
using NeoSaveGames.Serialization;
|
|||
|
using NeoSaveGames;
|
|||
|
|
|||
|
namespace NeoFPS.Samples.SinglePlayer
|
|||
|
{
|
|||
|
[HelpURL("https://docs.neofps.com/manual/samplesref-mb-firingrangetarget.html")]
|
|||
|
public class FiringRangeTarget : MonoBehaviour, IDamageHandler, INeoSerializableComponent
|
|||
|
{
|
|||
|
[SerializeField, Tooltip("The damage threshold for the target to drop and register as a hit.")]
|
|||
|
private float m_DamageThreshold = 1f;
|
|||
|
|
|||
|
[SerializeField, Tooltip("The duration the target will be visible. If the target is not hit in this time it registers as a miss.")]
|
|||
|
private float m_PopupDuration = 0.5f;
|
|||
|
|
|||
|
[SerializeField, Tooltip("The axis to rotate the target around when it pops up.")]
|
|||
|
private Vector3 m_RotationAxis = new Vector3(1f, 0f, 0f);
|
|||
|
|
|||
|
[SerializeField, Tooltip("The rotation of the target around the specified axis when it is fully hidden.")]
|
|||
|
private float m_HiddenRotation = 180f;
|
|||
|
|
|||
|
private Transform m_RotationTransform = null;
|
|||
|
private FiringRangeSequencer m_Sequencer = null;
|
|||
|
private Coroutine m_SequenceCoroutine = null;
|
|||
|
private float m_Lerp = 0f;
|
|||
|
private float m_Timer = 0f;
|
|||
|
private bool m_Hit = false;
|
|||
|
private TargetState m_State = TargetState.Idle;
|
|||
|
|
|||
|
enum TargetState
|
|||
|
{
|
|||
|
Idle,
|
|||
|
Raising,
|
|||
|
Raised,
|
|||
|
Lowering
|
|||
|
}
|
|||
|
|
|||
|
protected bool initialised
|
|||
|
{
|
|||
|
get;
|
|||
|
private set;
|
|||
|
}
|
|||
|
|
|||
|
public bool hit
|
|||
|
{
|
|||
|
get { return m_Hit; }
|
|||
|
}
|
|||
|
|
|||
|
public virtual bool hidden
|
|||
|
{
|
|||
|
get { return m_SequenceCoroutine == null; }
|
|||
|
}
|
|||
|
|
|||
|
#if UNITY_EDITOR
|
|||
|
void OnValidate()
|
|||
|
{
|
|||
|
if (m_DamageThreshold < 1f)
|
|||
|
m_DamageThreshold = 1f;
|
|||
|
if (m_PopupDuration < 0.1f)
|
|||
|
m_PopupDuration = 0.1f;
|
|||
|
m_HiddenRotation = Mathf.Clamp(m_HiddenRotation, -180f, 180f);
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
public virtual void Initialise (FiringRangeSequencer sequencer)
|
|||
|
{
|
|||
|
m_Sequencer = sequencer;
|
|||
|
m_RotationTransform = transform;
|
|||
|
|
|||
|
if (!initialised)
|
|||
|
{
|
|||
|
m_RotationTransform.localRotation = Quaternion.AngleAxis(m_HiddenRotation, m_RotationAxis);
|
|||
|
m_Hit = false;
|
|||
|
initialised = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void Popup (float duration)
|
|||
|
{
|
|||
|
if (m_SequenceCoroutine != null)
|
|||
|
StopCoroutine (m_SequenceCoroutine);
|
|||
|
m_Timer = duration;
|
|||
|
m_SequenceCoroutine = StartCoroutine (SequencePopup (0f));
|
|||
|
}
|
|||
|
|
|||
|
public virtual void ResetTarget ()
|
|||
|
{
|
|||
|
if (!hidden && gameObject.activeInHierarchy)
|
|||
|
{
|
|||
|
if (m_SequenceCoroutine != null)
|
|||
|
StopCoroutine(m_SequenceCoroutine);
|
|||
|
m_SequenceCoroutine = StartCoroutine (SequenceReset(0f));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private IEnumerator SequencePopup (float lerp)
|
|||
|
{
|
|||
|
m_State = TargetState.Raising;
|
|||
|
float inversePopup = 1f / m_PopupDuration;
|
|||
|
|
|||
|
// Pop up
|
|||
|
m_Lerp = lerp;
|
|||
|
while (m_Lerp < 1f)
|
|||
|
{
|
|||
|
yield return null;
|
|||
|
m_Lerp += Time.deltaTime * inversePopup;
|
|||
|
|
|||
|
// Flip up
|
|||
|
m_RotationTransform.localRotation = Quaternion.AngleAxis (Mathf.Lerp (m_HiddenRotation, 0f, m_Lerp), m_RotationAxis);
|
|||
|
}
|
|||
|
|
|||
|
StartRaisedSequence();
|
|||
|
}
|
|||
|
|
|||
|
private IEnumerator SequenceRaised()
|
|||
|
{
|
|||
|
m_State = TargetState.Raised;
|
|||
|
while (!hit && m_Timer > 0f)
|
|||
|
{
|
|||
|
yield return null;
|
|||
|
m_Timer -= Time.deltaTime;
|
|||
|
}
|
|||
|
|
|||
|
yield return null;
|
|||
|
|
|||
|
StartResetSequence();
|
|||
|
}
|
|||
|
|
|||
|
private IEnumerator SequenceReset (float lerp)
|
|||
|
{
|
|||
|
m_State = TargetState.Lowering;
|
|||
|
float inversePopup = 1f / m_PopupDuration;
|
|||
|
|
|||
|
// Pop down
|
|||
|
m_Lerp = lerp;
|
|||
|
while (m_Lerp < 1f)
|
|||
|
{
|
|||
|
yield return null;
|
|||
|
m_Lerp += Time.deltaTime * inversePopup;
|
|||
|
|
|||
|
// Flip down
|
|||
|
m_RotationTransform.localRotation = Quaternion.AngleAxis (Mathf.Lerp (0f, m_HiddenRotation, m_Lerp), m_RotationAxis);
|
|||
|
}
|
|||
|
|
|||
|
if (m_Hit == true)
|
|||
|
m_Hit = false;
|
|||
|
else
|
|||
|
m_Sequencer.AddMiss();
|
|||
|
|
|||
|
yield return null;
|
|||
|
|
|||
|
m_State = TargetState.Idle;
|
|||
|
m_SequenceCoroutine = null;
|
|||
|
}
|
|||
|
|
|||
|
protected virtual void StartRaisedSequence()
|
|||
|
{
|
|||
|
m_SequenceCoroutine = StartCoroutine(SequenceRaised());
|
|||
|
}
|
|||
|
|
|||
|
protected virtual void StartResetSequence()
|
|||
|
{
|
|||
|
m_SequenceCoroutine = StartCoroutine(SequenceReset(0f));
|
|||
|
}
|
|||
|
|
|||
|
protected virtual void OnSequenceCompleted()
|
|||
|
{
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#region IDamageHandler Implementation
|
|||
|
|
|||
|
private DamageFilter m_InDamageFilter = DamageFilter.AllDamageAllTeams;
|
|||
|
public DamageFilter inDamageFilter
|
|||
|
{
|
|||
|
get { return m_InDamageFilter; }
|
|||
|
set { m_InDamageFilter = value; }
|
|||
|
}
|
|||
|
|
|||
|
public DamageResult AddDamage (float damage)
|
|||
|
{
|
|||
|
return AddDamage (damage, null);
|
|||
|
}
|
|||
|
|
|||
|
public DamageResult AddDamage (float damage, IDamageSource source)
|
|||
|
{
|
|||
|
if (damage >= m_DamageThreshold && !hidden && !hit)
|
|||
|
{
|
|||
|
m_Hit = true;
|
|||
|
m_Sequencer.AddHit();
|
|||
|
ResetTarget ();
|
|||
|
|
|||
|
// Report damage dealt
|
|||
|
if (damage > 0f && source != null && source.controller != null)
|
|||
|
source.controller.currentCharacter.ReportTargetHit(false);
|
|||
|
}
|
|||
|
return DamageResult.Standard;
|
|||
|
}
|
|||
|
|
|||
|
public DamageResult AddDamage(float damage, RaycastHit hit)
|
|||
|
{
|
|||
|
return AddDamage(damage);
|
|||
|
}
|
|||
|
|
|||
|
public DamageResult AddDamage(float damage, RaycastHit hit, IDamageSource source)
|
|||
|
{
|
|||
|
return AddDamage(damage, source);
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
private static readonly NeoSerializationKey k_StateKey = new NeoSerializationKey("state");
|
|||
|
private static readonly NeoSerializationKey k_LerpKey = new NeoSerializationKey("lerp");
|
|||
|
private static readonly NeoSerializationKey k_TimerKey = new NeoSerializationKey("timer");
|
|||
|
private static readonly NeoSerializationKey k_HitKey = new NeoSerializationKey("hit");
|
|||
|
|
|||
|
public virtual void WriteProperties(INeoSerializer writer, NeoSerializedGameObject nsgo, SaveMode saveMode)
|
|||
|
{
|
|||
|
if (m_SequenceCoroutine != null)
|
|||
|
{
|
|||
|
writer.WriteValue(k_StateKey, (int)m_State);
|
|||
|
writer.WriteValue(k_LerpKey, m_Lerp);
|
|||
|
writer.WriteValue(k_TimerKey, m_Timer);
|
|||
|
writer.WriteValue(k_HitKey, m_Hit);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public virtual void ReadProperties(INeoDeserializer reader, NeoSerializedGameObject nsgo)
|
|||
|
{
|
|||
|
int state = 0;
|
|||
|
if (reader.TryReadValue(k_StateKey, out state, 0))
|
|||
|
{
|
|||
|
m_State = (TargetState)state;
|
|||
|
reader.TryReadValue(k_LerpKey, out m_Lerp, m_Lerp);
|
|||
|
reader.TryReadValue(k_TimerKey, out m_Timer, m_Timer);
|
|||
|
reader.TryReadValue(k_HitKey, out m_Hit, m_Hit);
|
|||
|
|
|||
|
switch(m_State)
|
|||
|
{
|
|||
|
case TargetState.Raising:
|
|||
|
m_SequenceCoroutine = StartCoroutine(SequencePopup(m_Lerp));
|
|||
|
break;
|
|||
|
case TargetState.Raised:
|
|||
|
m_SequenceCoroutine = StartCoroutine(SequenceRaised());
|
|||
|
break;
|
|||
|
case TargetState.Lowering:
|
|||
|
m_SequenceCoroutine = StartCoroutine(SequenceReset(m_Lerp));
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
initialised = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|