chore: initial commit
This commit is contained in:
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy.meta
Normal file
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 244b68853eb429a4a8fabd54b720495a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
598
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy/MMFeedback.cs
Normal file
598
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy/MMFeedback.cs
Normal file
@@ -0,0 +1,598 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A base class, meant to be extended, defining a Feedback. A Feedback is an action triggered by a MMFeedbacks, usually in reaction to the player's input or actions,
|
||||
/// to help communicate both emotion and legibility, improving game feel.
|
||||
/// To create a new feedback, extend this class and override its Custom methods, declared at the end of this class. You can look at the many examples for reference.
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
[System.Serializable]
|
||||
[ExecuteAlways]
|
||||
public abstract class MMFeedback : MonoBehaviour
|
||||
{
|
||||
/// whether or not this feedback is active
|
||||
[Tooltip("whether or not this feedback is active")]
|
||||
public bool Active = true;
|
||||
/// the name of this feedback to display in the inspector
|
||||
[Tooltip("the name of this feedback to display in the inspector")]
|
||||
public string Label = "MMFeedback";
|
||||
/// the chance of this feedback happening (in percent : 100 : happens all the time, 0 : never happens, 50 : happens once every two calls, etc)
|
||||
[Tooltip("the chance of this feedback happening (in percent : 100 : happens all the time, 0 : never happens, 50 : happens once every two calls, etc)")]
|
||||
[Range(0,100)]
|
||||
public float Chance = 100f;
|
||||
/// a number of timing-related values (delay, repeat, etc)
|
||||
[Tooltip("a number of timing-related values (delay, repeat, etc)")]
|
||||
public MMFeedbackTiming Timing;
|
||||
/// the Owner of the feedback, as defined when calling the Initialization method
|
||||
public GameObject Owner { get; set; }
|
||||
[HideInInspector]
|
||||
/// whether or not this feedback is in debug mode
|
||||
public bool DebugActive = false;
|
||||
/// set this to true if your feedback should pause the execution of the feedback sequence
|
||||
public virtual IEnumerator Pause { get { return null; } }
|
||||
/// if this is true, this feedback will wait until all previous feedbacks have run
|
||||
public virtual bool HoldingPause { get { return false; } }
|
||||
/// if this is true, this feedback will wait until all previous feedbacks have run, then run all previous feedbacks again
|
||||
public virtual bool LooperPause { get { return false; } }
|
||||
/// if this is true, this feedback will pause and wait until Resume() is called on its parent MMFeedbacks to resume execution
|
||||
public virtual bool ScriptDrivenPause { get; set; }
|
||||
/// if this is a positive value, the feedback will auto resume after that duration if it hasn't been resumed via script already
|
||||
public virtual float ScriptDrivenPauseAutoResume { get; set; }
|
||||
/// if this is true, this feedback will wait until all previous feedbacks have run, then run all previous feedbacks again
|
||||
public virtual bool LooperStart { get { return false; } }
|
||||
/// an overridable color for your feedback, that can be redefined per feedback. White is the only reserved color, and the feedback will revert to
|
||||
/// normal (light or dark skin) when left to White
|
||||
#if UNITY_EDITOR
|
||||
public virtual Color FeedbackColor { get { return Color.white; } }
|
||||
#endif
|
||||
/// returns true if this feedback is in cooldown at this time (and thus can't play), false otherwise
|
||||
public virtual bool InCooldown { get { return (Timing.CooldownDuration > 0f) && (FeedbackTime - _lastPlayTimestamp < Timing.CooldownDuration); } }
|
||||
/// if this is true, this feedback is currently playing
|
||||
public virtual bool IsPlaying { get; set; }
|
||||
|
||||
/// the time (or unscaled time) based on the selected Timing settings
|
||||
public float FeedbackTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
return Time.time;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Time.unscaledTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the delta time (or unscaled delta time) based on the selected Timing settings
|
||||
public float FeedbackDeltaTime
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
return Time.deltaTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Time.unscaledDeltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The total duration of this feedback :
|
||||
/// total = initial delay + duration * (number of repeats + delay between repeats)
|
||||
/// </summary>
|
||||
public float TotalDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((Timing != null) && (!Timing.ContributeToTotalDuration))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
float totalTime = 0f;
|
||||
|
||||
if (Timing == null)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (Timing.InitialDelay != 0)
|
||||
{
|
||||
totalTime += ApplyTimeMultiplier(Timing.InitialDelay);
|
||||
}
|
||||
|
||||
totalTime += FeedbackDuration;
|
||||
|
||||
if (Timing.NumberOfRepeats > 0)
|
||||
{
|
||||
float delayBetweenRepeats = ApplyTimeMultiplier(Timing.DelayBetweenRepeats);
|
||||
|
||||
totalTime += (Timing.NumberOfRepeats * FeedbackDuration) + (Timing.NumberOfRepeats * delayBetweenRepeats);
|
||||
}
|
||||
|
||||
return totalTime;
|
||||
}
|
||||
}
|
||||
|
||||
// the timestamp at which this feedback was last played
|
||||
public virtual float FeedbackStartedAt { get { return _lastPlayTimestamp; } }
|
||||
// the perceived duration of the feedback, to be used to display its progress bar, meant to be overridden with meaningful data by each feedback
|
||||
public virtual float FeedbackDuration { get { return 0f; } set { } }
|
||||
/// whether or not this feedback is playing right now
|
||||
public virtual bool FeedbackPlaying { get { return ((FeedbackStartedAt > 0f) && (Time.time - FeedbackStartedAt < FeedbackDuration)); } }
|
||||
|
||||
public virtual MMChannelData ChannelData(int channel) => _channelData.Set(MMChannelModes.Int, channel, null);
|
||||
|
||||
protected float _lastPlayTimestamp = -1f;
|
||||
protected int _playsLeft;
|
||||
protected bool _initialized = false;
|
||||
protected Coroutine _playCoroutine;
|
||||
protected Coroutine _infinitePlayCoroutine;
|
||||
protected Coroutine _sequenceCoroutine;
|
||||
protected Coroutine _repeatedPlayCoroutine;
|
||||
protected int _sequenceTrackID = 0;
|
||||
protected MMFeedbacks _hostMMFeedbacks;
|
||||
|
||||
protected float _beatInterval;
|
||||
protected bool BeatThisFrame = false;
|
||||
protected int LastBeatIndex = 0;
|
||||
protected int CurrentSequenceIndex = 0;
|
||||
protected float LastBeatTimestamp = 0f;
|
||||
protected bool _isHostMMFeedbacksNotNull;
|
||||
protected MMChannelData _channelData;
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
_hostMMFeedbacks = this.gameObject.GetComponent<MMFeedbacks>();
|
||||
_isHostMMFeedbacksNotNull = _hostMMFeedbacks != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the feedback and its timing related variables
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
public virtual void Initialization(GameObject owner)
|
||||
{
|
||||
_initialized = true;
|
||||
Owner = owner;
|
||||
_playsLeft = Timing.NumberOfRepeats + 1;
|
||||
_hostMMFeedbacks = this.gameObject.GetComponent<MMFeedbacks>();
|
||||
_channelData = new MMChannelData(MMChannelModes.Int, 0, null);
|
||||
|
||||
SetInitialDelay(Timing.InitialDelay);
|
||||
SetDelayBetweenRepeats(Timing.DelayBetweenRepeats);
|
||||
SetSequence(Timing.Sequence);
|
||||
|
||||
CustomInitialization(owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays the feedback
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
public virtual void Play(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
if (!Active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_initialized)
|
||||
{
|
||||
Debug.LogWarning("The " + this + " feedback is being played without having been initialized. Call Initialization() first.");
|
||||
}
|
||||
|
||||
// we check the cooldown
|
||||
if (InCooldown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Timing.InitialDelay > 0f)
|
||||
{
|
||||
_playCoroutine = StartCoroutine(PlayCoroutine(position, feedbacksIntensity));
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastPlayTimestamp = FeedbackTime;
|
||||
RegularPlay(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An internal coroutine delaying the initial play of the feedback
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator PlayCoroutine(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitFor(Timing.InitialDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitForUnscaled(Timing.InitialDelay);
|
||||
}
|
||||
_lastPlayTimestamp = FeedbackTime;
|
||||
RegularPlay(position, feedbacksIntensity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers delaying coroutines if needed
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
protected virtual void RegularPlay(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
if (Chance == 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Chance != 100f)
|
||||
{
|
||||
// determine the odds
|
||||
float random = Random.Range(0f, 100f);
|
||||
if (random > Chance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Timing.UseIntensityInterval)
|
||||
{
|
||||
if ((feedbacksIntensity < Timing.IntensityIntervalMin) || (feedbacksIntensity >= Timing.IntensityIntervalMax))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Timing.RepeatForever)
|
||||
{
|
||||
_infinitePlayCoroutine = StartCoroutine(InfinitePlay(position, feedbacksIntensity));
|
||||
return;
|
||||
}
|
||||
if (Timing.NumberOfRepeats > 0)
|
||||
{
|
||||
_repeatedPlayCoroutine = StartCoroutine(RepeatedPlay(position, feedbacksIntensity));
|
||||
return;
|
||||
}
|
||||
if (Timing.Sequence == null)
|
||||
{
|
||||
CustomPlayFeedback(position, feedbacksIntensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sequenceCoroutine = StartCoroutine(SequenceCoroutine(position, feedbacksIntensity));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal coroutine used for repeated play without end
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator InfinitePlay(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_lastPlayTimestamp = FeedbackTime;
|
||||
if (Timing.Sequence == null)
|
||||
{
|
||||
CustomPlayFeedback(position, feedbacksIntensity);
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitFor(Timing.DelayBetweenRepeats);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitForUnscaled(Timing.DelayBetweenRepeats);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sequenceCoroutine = StartCoroutine(SequenceCoroutine(position, feedbacksIntensity));
|
||||
|
||||
float delay = ApplyTimeMultiplier(Timing.DelayBetweenRepeats) + Timing.Sequence.Length;
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitFor(delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitForUnscaled(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal coroutine used for repeated play
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator RepeatedPlay(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
while (_playsLeft > 0)
|
||||
{
|
||||
_lastPlayTimestamp = FeedbackTime;
|
||||
_playsLeft--;
|
||||
if (Timing.Sequence == null)
|
||||
{
|
||||
CustomPlayFeedback(position, feedbacksIntensity);
|
||||
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitFor(Timing.DelayBetweenRepeats);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitForUnscaled(Timing.DelayBetweenRepeats);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sequenceCoroutine = StartCoroutine(SequenceCoroutine(position, feedbacksIntensity));
|
||||
|
||||
float delay = ApplyTimeMultiplier(Timing.DelayBetweenRepeats) + Timing.Sequence.Length;
|
||||
if (Timing.TimescaleMode == TimescaleModes.Scaled)
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitFor(delay);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return MMFeedbacksCoroutine.WaitForUnscaled(delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
_playsLeft = Timing.NumberOfRepeats + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A coroutine used to play this feedback on a sequence
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator SequenceCoroutine(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
yield return null;
|
||||
float timeStartedAt = FeedbackTime;
|
||||
float lastFrame = FeedbackTime;
|
||||
|
||||
BeatThisFrame = false;
|
||||
LastBeatIndex = 0;
|
||||
CurrentSequenceIndex = 0;
|
||||
LastBeatTimestamp = 0f;
|
||||
|
||||
if (Timing.Quantized)
|
||||
{
|
||||
while (CurrentSequenceIndex < Timing.Sequence.QuantizedSequence[0].Line.Count)
|
||||
{
|
||||
_beatInterval = 60f / Timing.TargetBPM;
|
||||
|
||||
if ((FeedbackTime - LastBeatTimestamp >= _beatInterval) || (LastBeatTimestamp == 0f))
|
||||
{
|
||||
BeatThisFrame = true;
|
||||
LastBeatIndex = CurrentSequenceIndex;
|
||||
LastBeatTimestamp = FeedbackTime;
|
||||
|
||||
for (int i = 0; i < Timing.Sequence.SequenceTracks.Count; i++)
|
||||
{
|
||||
if (Timing.Sequence.QuantizedSequence[i].Line[CurrentSequenceIndex].ID == Timing.TrackID)
|
||||
{
|
||||
CustomPlayFeedback(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
CurrentSequenceIndex++;
|
||||
}
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (FeedbackTime - timeStartedAt < Timing.Sequence.Length)
|
||||
{
|
||||
foreach (MMSequenceNote item in Timing.Sequence.OriginalSequence.Line)
|
||||
{
|
||||
if ((item.ID == Timing.TrackID) && (item.Timestamp >= lastFrame) && (item.Timestamp <= FeedbackTime - timeStartedAt))
|
||||
{
|
||||
CustomPlayFeedback(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
lastFrame = FeedbackTime - timeStartedAt;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all feedbacks from playing. Will stop repeating feedbacks, and call custom stop implementations
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
public virtual void Stop(Vector3 position, float feedbacksIntensity = 1.0f)
|
||||
{
|
||||
if (_playCoroutine != null) { StopCoroutine(_playCoroutine); }
|
||||
if (_infinitePlayCoroutine != null) { StopCoroutine(_infinitePlayCoroutine); }
|
||||
if (_repeatedPlayCoroutine != null) { StopCoroutine(_repeatedPlayCoroutine); }
|
||||
if (_sequenceCoroutine != null) { StopCoroutine(_sequenceCoroutine); }
|
||||
|
||||
_lastPlayTimestamp = 0f;
|
||||
_playsLeft = Timing.NumberOfRepeats + 1;
|
||||
if (Timing.InterruptsOnStop)
|
||||
{
|
||||
CustomStopFeedback(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls this feedback's custom reset
|
||||
/// </summary>
|
||||
public virtual void ResetFeedback()
|
||||
{
|
||||
_playsLeft = Timing.NumberOfRepeats + 1;
|
||||
CustomReset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to change this feedback's sequence at runtime
|
||||
/// </summary>
|
||||
/// <param name="newSequence"></param>
|
||||
public virtual void SetSequence(MMSequence newSequence)
|
||||
{
|
||||
Timing.Sequence = newSequence;
|
||||
if (Timing.Sequence != null)
|
||||
{
|
||||
for (int i = 0; i < Timing.Sequence.SequenceTracks.Count; i++)
|
||||
{
|
||||
if (Timing.Sequence.SequenceTracks[i].ID == Timing.TrackID)
|
||||
{
|
||||
_sequenceTrackID = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to specify a new delay between repeats at runtime
|
||||
/// </summary>
|
||||
/// <param name="delay"></param>
|
||||
public virtual void SetDelayBetweenRepeats(float delay)
|
||||
{
|
||||
Timing.DelayBetweenRepeats = delay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to specify a new initial delay at runtime
|
||||
/// </summary>
|
||||
/// <param name="delay"></param>
|
||||
public virtual void SetInitialDelay(float delay)
|
||||
{
|
||||
Timing.InitialDelay = delay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new value of the normalized time based on the current play direction of this feedback
|
||||
/// </summary>
|
||||
/// <param name="normalizedTime"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual float ApplyDirection(float normalizedTime)
|
||||
{
|
||||
return NormalPlayDirection ? normalizedTime : 1 - normalizedTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this feedback should play normally, or false if it should play in rewind
|
||||
/// </summary>
|
||||
public virtual bool NormalPlayDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Timing.PlayDirection)
|
||||
{
|
||||
case MMFeedbackTiming.PlayDirections.FollowMMFeedbacksDirection:
|
||||
return (_hostMMFeedbacks.Direction == MMFeedbacks.Directions.TopToBottom);
|
||||
case MMFeedbackTiming.PlayDirections.AlwaysNormal:
|
||||
return true;
|
||||
case MMFeedbackTiming.PlayDirections.AlwaysRewind:
|
||||
return false;
|
||||
case MMFeedbackTiming.PlayDirections.OppositeMMFeedbacksDirection:
|
||||
return !(_hostMMFeedbacks.Direction == MMFeedbacks.Directions.TopToBottom);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this feedback should play in the current parent MMFeedbacks direction, according to its MMFeedbacksDirectionCondition setting
|
||||
/// </summary>
|
||||
public virtual bool ShouldPlayInThisSequenceDirection
|
||||
{
|
||||
get
|
||||
{
|
||||
switch (Timing.MMFeedbacksDirectionCondition)
|
||||
{
|
||||
case MMFeedbackTiming.MMFeedbacksDirectionConditions.Always:
|
||||
return true;
|
||||
case MMFeedbackTiming.MMFeedbacksDirectionConditions.OnlyWhenForwards:
|
||||
return (_hostMMFeedbacks.Direction == MMFeedbacks.Directions.TopToBottom);
|
||||
case MMFeedbackTiming.MMFeedbacksDirectionConditions.OnlyWhenBackwards:
|
||||
return (_hostMMFeedbacks.Direction == MMFeedbacks.Directions.BottomToTop);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the t value at which to evaluate a curve at the end of this feedback's play time
|
||||
/// </summary>
|
||||
protected virtual float FinalNormalizedTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return NormalPlayDirection ? 1f : 0f;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the host MMFeedbacks' time multiplier to this feedback
|
||||
/// </summary>
|
||||
/// <param name="duration"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual float ApplyTimeMultiplier(float duration)
|
||||
{
|
||||
if (_isHostMMFeedbacksNotNull)
|
||||
{
|
||||
return _hostMMFeedbacks.ApplyTimeMultiplier(duration);
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method describes all custom initialization processes the feedback requires, in addition to the main Initialization method
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
protected virtual void CustomInitialization(GameObject owner) { }
|
||||
|
||||
/// <summary>
|
||||
/// This method describes what happens when the feedback gets played
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
protected abstract void CustomPlayFeedback(Vector3 position, float feedbacksIntensity = 1.0f);
|
||||
|
||||
/// <summary>
|
||||
/// This method describes what happens when the feedback gets stopped
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
protected virtual void CustomStopFeedback(Vector3 position, float feedbacksIntensity = 1.0f) { }
|
||||
|
||||
/// <summary>
|
||||
/// This method describes what happens when the feedback gets reset
|
||||
/// </summary>
|
||||
protected virtual void CustomReset() { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1b1d3a32ffe67b94187272c15134cae5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
924
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy/MMFeedbacks.cs
Normal file
924
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/Legacy/MMFeedbacks.cs
Normal file
@@ -0,0 +1,924 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using MoreMountains.Feedbacks;
|
||||
using System.Linq;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEditor.Experimental;
|
||||
using UnityEngine.Events;
|
||||
using Random = UnityEngine.Random;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A collection of MMFeedback, meant to be played altogether.
|
||||
/// This class provides a custom inspector to add and customize feedbacks, and public methods to trigger them, stop them, etc.
|
||||
/// You can either use it on its own, or bind it from another class and trigger it from there.
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class MMFeedbacks : MonoBehaviour
|
||||
{
|
||||
/// the possible directions MMFeedbacks can be played
|
||||
public enum Directions { TopToBottom, BottomToTop }
|
||||
/// the possible SafeModes (will perform checks to make sure no serialization error has damaged them)
|
||||
/// - nope : no safety
|
||||
/// - editor only : performs checks on enable
|
||||
/// - runtime only : performs checks on Awake
|
||||
/// - full : performs both editor and runtime checks, recommended setting
|
||||
public enum SafeModes { Nope, EditorOnly, RuntimeOnly, Full }
|
||||
|
||||
/// a list of MMFeedback to trigger
|
||||
public List<MMFeedback> Feedbacks = new List<MMFeedback>();
|
||||
|
||||
/// the possible initialization modes. If you use Script, you'll have to initialize manually by calling the Initialization method and passing it an owner
|
||||
/// Otherwise, you can have this component initialize itself at Awake or Start, and in this case the owner will be the MMFeedbacks itself
|
||||
public enum InitializationModes { Script, Awake, Start }
|
||||
/// the chosen initialization mode
|
||||
[Tooltip("the chosen initialization modes. If you use Script, you'll have to initialize manually by calling the " +
|
||||
"Initialization method and passing it an owner. Otherwise, you can have this component initialize " +
|
||||
"itself at Awake or Start, and in this case the owner will be the MMFeedbacks itself")]
|
||||
public InitializationModes InitializationMode = InitializationModes.Start;
|
||||
/// if you set this to true, the system will make changes to ensure that initialization always happens before play
|
||||
[Tooltip("if you set this to true, the system will make changes to ensure that initialization always happens before play")]
|
||||
public bool AutoInitialization = true;
|
||||
/// the selected safe mode
|
||||
[Tooltip("the selected safe mode")]
|
||||
public SafeModes SafeMode = SafeModes.Full;
|
||||
/// the selected direction
|
||||
[Tooltip("the selected direction these feedbacks should play in")]
|
||||
public Directions Direction = Directions.TopToBottom;
|
||||
/// whether or not this MMFeedbacks should invert its direction when all feedbacks have played
|
||||
[Tooltip("whether or not this MMFeedbacks should invert its direction when all feedbacks have played")]
|
||||
public bool AutoChangeDirectionOnEnd = false;
|
||||
/// whether or not to play this feedbacks automatically on Start
|
||||
[Tooltip("whether or not to play this feedbacks automatically on Start")]
|
||||
public bool AutoPlayOnStart = false;
|
||||
/// whether or not to play this feedbacks automatically on Enable
|
||||
[Tooltip("whether or not to play this feedbacks automatically on Enable")]
|
||||
public bool AutoPlayOnEnable = false;
|
||||
|
||||
/// if this is true, all feedbacks within that player will work on the specified ForcedTimescaleMode, regardless of their individual settings
|
||||
[Tooltip("if this is true, all feedbacks within that player will work on the specified ForcedTimescaleMode, regardless of their individual settings")]
|
||||
public bool ForceTimescaleMode = false;
|
||||
/// the time scale mode all feedbacks on this player should work on, if ForceTimescaleMode is true
|
||||
[Tooltip("the time scale mode all feedbacks on this player should work on, if ForceTimescaleMode is true")]
|
||||
[MMFCondition("ForceTimescaleMode", true)]
|
||||
public TimescaleModes ForcedTimescaleMode = TimescaleModes.Unscaled;
|
||||
/// a time multiplier that will be applied to all feedback durations (initial delay, duration, delay between repeats...)
|
||||
[Tooltip("a time multiplier that will be applied to all feedback durations (initial delay, duration, delay between repeats...)")]
|
||||
public float DurationMultiplier = 1f;
|
||||
/// a multiplier to apply to all timescale operations (1: normal, less than 1: slower operations, higher than 1: faster operations)
|
||||
[Tooltip("a multiplier to apply to all timescale operations (1: normal, less than 1: slower operations, higher than 1: faster operations)")]
|
||||
public float TimescaleMultiplier = 1f;
|
||||
/// if this is true, will expose a RandomDurationMultiplier. The final duration of each feedback will be : their base duration * DurationMultiplier * a random value between RandomDurationMultiplier.x and RandomDurationMultiplier.y
|
||||
[Tooltip("if this is true, will expose a RandomDurationMultiplier. The final duration of each feedback will be : their base duration * DurationMultiplier * a random value between RandomDurationMultiplier.x and RandomDurationMultiplier.y")]
|
||||
public bool RandomizeDuration = false;
|
||||
/// if RandomizeDuration is true, the min (x) and max (y) values for the random duration multiplier
|
||||
[Tooltip("if RandomizeDuration is true, the min (x) and max (y) values for the random duration multiplier")]
|
||||
[MMCondition("RandomizeDuration", true)]
|
||||
public Vector2 RandomDurationMultiplier = new Vector2(0.5f, 1.5f);
|
||||
/// if this is true, more editor-only, detailed info will be displayed per feedback in the duration slot
|
||||
[Tooltip("if this is true, more editor-only, detailed info will be displayed per feedback in the duration slot")]
|
||||
public bool DisplayFullDurationDetails = false;
|
||||
/// the timescale at which the player itself will operate. This notably impacts sequencing and pauses duration evaluation.
|
||||
[Tooltip("the timescale at which the player itself will operate. This notably impacts sequencing and pauses duration evaluation.")]
|
||||
public TimescaleModes PlayerTimescaleMode = TimescaleModes.Unscaled;
|
||||
|
||||
/// if this is true, this feedback will only play if its distance to RangeCenter is lower or equal to RangeDistance
|
||||
[Tooltip("if this is true, this feedback will only play if its distance to RangeCenter is lower or equal to RangeDistance")]
|
||||
public bool OnlyPlayIfWithinRange = false;
|
||||
/// when in OnlyPlayIfWithinRange mode, the transform to consider as the center of the range
|
||||
[Tooltip("when in OnlyPlayIfWithinRange mode, the transform to consider as the center of the range")]
|
||||
public Transform RangeCenter;
|
||||
/// when in OnlyPlayIfWithinRange mode, the distance to the center within which the feedback will play
|
||||
[Tooltip("when in OnlyPlayIfWithinRange mode, the distance to the center within which the feedback will play")]
|
||||
public float RangeDistance = 5f;
|
||||
/// when in OnlyPlayIfWithinRange mode, whether or not to modify the intensity of feedbacks based on the RangeFallOff curve
|
||||
[Tooltip("when in OnlyPlayIfWithinRange mode, whether or not to modify the intensity of feedbacks based on the RangeFallOff curve")]
|
||||
public bool UseRangeFalloff = false;
|
||||
/// the animation curve to use to define falloff (on the x 0 represents the range center, 1 represents the max distance to it)
|
||||
[Tooltip("the animation curve to use to define falloff (on the x 0 represents the range center, 1 represents the max distance to it)")]
|
||||
[MMFCondition("UseRangeFalloff", true)]
|
||||
public AnimationCurve RangeFalloff = new AnimationCurve(new Keyframe(0f, 1f), new Keyframe(1f, 0f));
|
||||
/// the values to remap the falloff curve's y axis' 0 and 1
|
||||
[Tooltip("the values to remap the falloff curve's y axis' 0 and 1")]
|
||||
[MMFVector("Zero","One")]
|
||||
public Vector2 RemapRangeFalloff = new Vector2(0f, 1f);
|
||||
/// whether or not to ignore MMSetFeedbackRangeCenterEvent, used to set the RangeCenter from anywhere
|
||||
[Tooltip("whether or not to ignore MMSetFeedbackRangeCenterEvent, used to set the RangeCenter from anywhere")]
|
||||
public bool IgnoreRangeEvents = false;
|
||||
|
||||
/// a duration, in seconds, during which triggering a new play of this MMFeedbacks after it's been played once will be impossible
|
||||
[Tooltip("a duration, in seconds, during which triggering a new play of this MMFeedbacks after it's been played once will be impossible")]
|
||||
public float CooldownDuration = 0f;
|
||||
/// a duration, in seconds, to delay the start of this MMFeedbacks' contents play
|
||||
[Tooltip("a duration, in seconds, to delay the start of this MMFeedbacks' contents play")]
|
||||
public float InitialDelay = 0f;
|
||||
/// whether this player can be played or not, useful to temporarily prevent play from another class, for example
|
||||
[Tooltip("whether this player can be played or not, useful to temporarily prevent play from another class, for example")]
|
||||
public bool CanPlay = true;
|
||||
/// if this is true, you'll be able to trigger a new Play while this feedback is already playing, otherwise you won't be able to
|
||||
[Tooltip("if this is true, you'll be able to trigger a new Play while this feedback is already playing, otherwise you won't be able to")]
|
||||
public bool CanPlayWhileAlreadyPlaying = true;
|
||||
/// the chance of this sequence happening (in percent : 100 : happens all the time, 0 : never happens, 50 : happens once every two calls, etc)
|
||||
[Tooltip("the chance of this sequence happening (in percent : 100 : happens all the time, 0 : never happens, 50 : happens once every two calls, etc)")]
|
||||
[Range(0,100)]
|
||||
public float ChanceToPlay = 100f;
|
||||
|
||||
/// the intensity at which to play this feedback. That value will be used by most feedbacks to tune their amplitude. 1 is normal, 0.5 is half power, 0 is no effect.
|
||||
/// Note that what this value controls depends from feedback to feedback, don't hesitate to check the code to see what it does exactly.
|
||||
[Tooltip("the intensity at which to play this feedback. That value will be used by most feedbacks to tune their amplitude. 1 is normal, 0.5 is half power, 0 is no effect." +
|
||||
"Note that what this value controls depends from feedback to feedback, don't hesitate to check the code to see what it does exactly.")]
|
||||
public float FeedbacksIntensity = 1f;
|
||||
|
||||
/// a number of UnityEvents that can be triggered at the various stages of this MMFeedbacks
|
||||
[Tooltip("a number of UnityEvents that can be triggered at the various stages of this MMFeedbacks")]
|
||||
public MMFeedbacksEvents Events;
|
||||
|
||||
/// a global switch used to turn all feedbacks on or off globally
|
||||
[Tooltip("a global switch used to turn all feedbacks on or off globally")]
|
||||
public static bool GlobalMMFeedbacksActive = true;
|
||||
|
||||
[HideInInspector]
|
||||
/// whether or not this MMFeedbacks is in debug mode
|
||||
public bool DebugActive = false;
|
||||
/// whether or not this MMFeedbacks is playing right now - meaning it hasn't been stopped yet.
|
||||
/// if you don't stop your MMFeedbacks it'll remain true of course
|
||||
public bool IsPlaying { get; protected set; }
|
||||
/// if this MMFeedbacks is playing the time since it started playing
|
||||
public virtual float ElapsedTime => IsPlaying ? GetTime() - _lastStartAt : 0f;
|
||||
/// the amount of times this MMFeedbacks has been played
|
||||
public int TimesPlayed { get; protected set; }
|
||||
/// whether or not the execution of this MMFeedbacks' sequence is being prevented and waiting for a Resume() call
|
||||
public bool InScriptDrivenPause { get; set; }
|
||||
/// true if this MMFeedbacks contains at least one loop
|
||||
public bool ContainsLoop { get; set; }
|
||||
/// true if this feedback should change play direction next time it's played
|
||||
public bool ShouldRevertOnNextPlay { get; set; }
|
||||
/// true if this player is forcing unscaled mode
|
||||
public bool ForcingUnscaledTimescaleMode { get { return (ForceTimescaleMode && ForcedTimescaleMode == TimescaleModes.Unscaled); } }
|
||||
/// The total duration (in seconds) of all the active feedbacks in this MMFeedbacks
|
||||
public virtual float TotalDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
float total = 0f;
|
||||
foreach (MMFeedback feedback in Feedbacks)
|
||||
{
|
||||
if ((feedback != null) && (feedback.Active))
|
||||
{
|
||||
if (total < feedback.TotalDuration)
|
||||
{
|
||||
total = feedback.TotalDuration;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ComputedInitialDelay + total;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float GetTime() { return (PlayerTimescaleMode == TimescaleModes.Scaled) ? Time.time : Time.unscaledTime; }
|
||||
public virtual float GetDeltaTime() { return (PlayerTimescaleMode == TimescaleModes.Scaled) ? Time.deltaTime : Time.unscaledDeltaTime; }
|
||||
public virtual float ComputedInitialDelay => ApplyTimeMultiplier(InitialDelay);
|
||||
|
||||
protected float _startTime = 0f;
|
||||
protected float _holdingMax = 0f;
|
||||
protected float _lastStartAt = -float.MaxValue;
|
||||
protected int _lastStartFrame = -1;
|
||||
protected bool _pauseFound = false;
|
||||
protected float _totalDuration = 0f;
|
||||
protected bool _shouldStop = false;
|
||||
protected const float _smallValue = 0.001f;
|
||||
protected float _randomDurationMultiplier = 1f;
|
||||
protected float _lastOnEnableFrame = -1;
|
||||
|
||||
#region INITIALIZATION
|
||||
|
||||
/// <summary>
|
||||
/// On Awake we initialize our feedbacks if we're in auto mode
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
// if our MMFeedbacks is in AutoPlayOnEnable mode, we add a little helper to it that will re-enable it if needed if the parent game object gets turned off and on again
|
||||
if (AutoPlayOnEnable)
|
||||
{
|
||||
MMFeedbacksEnabler enabler = GetComponent<MMFeedbacksEnabler>();
|
||||
if (enabler == null)
|
||||
{
|
||||
enabler = this.gameObject.AddComponent<MMFeedbacksEnabler>();
|
||||
}
|
||||
enabler.TargetMMFeedbacks = this;
|
||||
}
|
||||
|
||||
if ((InitializationMode == InitializationModes.Awake) && (Application.isPlaying))
|
||||
{
|
||||
Initialization(this.gameObject);
|
||||
}
|
||||
CheckForLoops();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Start we initialize our feedbacks if we're in auto mode
|
||||
/// </summary>
|
||||
protected virtual void Start()
|
||||
{
|
||||
if ((InitializationMode == InitializationModes.Start) && (Application.isPlaying))
|
||||
{
|
||||
Initialization(this.gameObject);
|
||||
}
|
||||
if (AutoPlayOnStart && Application.isPlaying)
|
||||
{
|
||||
PlayFeedbacks();
|
||||
}
|
||||
CheckForLoops();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Enable we initialize our feedbacks if we're in auto mode
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (AutoPlayOnEnable && Application.isPlaying)
|
||||
{
|
||||
PlayFeedbacks();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the MMFeedbacks, setting this MMFeedbacks as the owner
|
||||
/// </summary>
|
||||
public virtual void Initialization(bool forceInitIfPlaying = false)
|
||||
{
|
||||
Initialization(this.gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A public method to initialize the feedback, specifying an owner that will be used as the reference for position and hierarchy by feedbacks
|
||||
/// </summary>
|
||||
/// <param name="owner"></param>
|
||||
/// <param name="feedbacksOwner"></param>
|
||||
public virtual void Initialization(GameObject owner)
|
||||
{
|
||||
if ((SafeMode == MMFeedbacks.SafeModes.RuntimeOnly) || (SafeMode == MMFeedbacks.SafeModes.Full))
|
||||
{
|
||||
AutoRepair();
|
||||
}
|
||||
|
||||
IsPlaying = false;
|
||||
TimesPlayed = 0;
|
||||
_lastStartAt = -float.MaxValue;
|
||||
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (Feedbacks[i] != null)
|
||||
{
|
||||
Feedbacks[i].Initialization(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PLAY
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks using the MMFeedbacks' position as reference, and no attenuation
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacks()
|
||||
{
|
||||
PlayFeedbacksInternal(this.transform.position, FeedbacksIntensity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks and awaits until completion
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <param name="forceRevert"></param>
|
||||
public virtual async System.Threading.Tasks.Task PlayFeedbacksTask(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
PlayFeedbacks(position, feedbacksIntensity, forceRevert);
|
||||
while (IsPlaying)
|
||||
{
|
||||
await System.Threading.Tasks.Task.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks and awaits until completion
|
||||
/// </summary>
|
||||
public virtual async System.Threading.Tasks.Task PlayFeedbacksTask()
|
||||
{
|
||||
PlayFeedbacks();
|
||||
while (IsPlaying)
|
||||
{
|
||||
await System.Threading.Tasks.Task.Yield();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks, specifying a position and intensity. The position may be used by each Feedback and taken into account to spark a particle or play a sound for example.
|
||||
/// The feedbacks intensity is a factor that can be used by each Feedback to lower its intensity, usually you'll want to define that attenuation based on time or distance (using a lower
|
||||
/// intensity value for feedbacks happening further away from the Player).
|
||||
/// Additionally you can force the feedback to play in reverse, ignoring its current condition
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksOwner"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
public virtual void PlayFeedbacks(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
PlayFeedbacksInternal(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks using the MMFeedbacks' position as reference, and no attenuation, and in reverse (from bottom to top)
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksInReverse()
|
||||
{
|
||||
PlayFeedbacksInternal(this.transform.position, FeedbacksIntensity, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks using the MMFeedbacks' position as reference, and no attenuation, and in reverse (from bottom to top)
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksInReverse(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
PlayFeedbacksInternal(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks in the sequence, but only if this MMFeedbacks is playing in reverse order
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksOnlyIfReversed()
|
||||
{
|
||||
|
||||
if ( (Direction == Directions.BottomToTop && !ShouldRevertOnNextPlay)
|
||||
|| ((Direction == Directions.TopToBottom) && ShouldRevertOnNextPlay) )
|
||||
{
|
||||
PlayFeedbacks();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks in the sequence, but only if this MMFeedbacks is playing in reverse order
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksOnlyIfReversed(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
|
||||
if ( (Direction == Directions.BottomToTop && !ShouldRevertOnNextPlay)
|
||||
|| ((Direction == Directions.TopToBottom) && ShouldRevertOnNextPlay) )
|
||||
{
|
||||
PlayFeedbacks(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks in the sequence, but only if this MMFeedbacks is playing in normal order
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksOnlyIfNormalDirection()
|
||||
{
|
||||
if (Direction == Directions.TopToBottom)
|
||||
{
|
||||
PlayFeedbacks();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Plays all feedbacks in the sequence, but only if this MMFeedbacks is playing in normal order
|
||||
/// </summary>
|
||||
public virtual void PlayFeedbacksOnlyIfNormalDirection(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
if (Direction == Directions.TopToBottom)
|
||||
{
|
||||
PlayFeedbacks(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A public coroutine you can call externally when you want to yield in a coroutine of yours until the MMFeedbacks has stopped playing
|
||||
/// typically : yield return myFeedback.PlayFeedbacksCoroutine(this.transform.position, 1.0f, false);
|
||||
/// </summary>
|
||||
/// <param name="position">The position at which the MMFeedbacks should play</param>
|
||||
/// <param name="feedbacksIntensity">The intensity of the feedback</param>
|
||||
/// <param name="forceRevert">Whether or not the MMFeedbacks should play in reverse or not</param>
|
||||
/// <returns></returns>
|
||||
public virtual IEnumerator PlayFeedbacksCoroutine(Vector3 position, float feedbacksIntensity = 1.0f, bool forceRevert = false)
|
||||
{
|
||||
PlayFeedbacks(position, feedbacksIntensity, forceRevert);
|
||||
while (IsPlaying)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region SEQUENCE
|
||||
|
||||
/// <summary>
|
||||
/// An internal method used to play feedbacks, shouldn't be called externally
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
protected virtual void PlayFeedbacksInternal(Vector3 position, float feedbacksIntensity, bool forceRevert = false)
|
||||
{
|
||||
if (!CanPlay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPlaying && !CanPlayWhileAlreadyPlaying)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EvaluateChance())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// if we have a cooldown we prevent execution if needed
|
||||
if (CooldownDuration > 0f)
|
||||
{
|
||||
if (GetTime() - _lastStartAt < CooldownDuration)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if all MMFeedbacks are disabled globally, we stop and don't play
|
||||
if (!GlobalMMFeedbacksActive)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.gameObject.activeInHierarchy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ShouldRevertOnNextPlay)
|
||||
{
|
||||
Revert();
|
||||
ShouldRevertOnNextPlay = false;
|
||||
}
|
||||
|
||||
if (forceRevert)
|
||||
{
|
||||
Direction = (Direction == Directions.BottomToTop) ? Directions.TopToBottom : Directions.BottomToTop;
|
||||
}
|
||||
|
||||
ResetFeedbacks();
|
||||
this.enabled = true;
|
||||
TimesPlayed++;
|
||||
IsPlaying = true;
|
||||
_startTime = GetTime();
|
||||
_lastStartAt = _startTime;
|
||||
_totalDuration = TotalDuration;
|
||||
CheckForPauses();
|
||||
|
||||
if (ComputedInitialDelay > 0f)
|
||||
{
|
||||
StartCoroutine(HandleInitialDelayCo(position, feedbacksIntensity, forceRevert));
|
||||
}
|
||||
else
|
||||
{
|
||||
PreparePlay(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PreparePlay(Vector3 position, float feedbacksIntensity, bool forceRevert = false)
|
||||
{
|
||||
Events.TriggerOnPlay(this);
|
||||
|
||||
_holdingMax = 0f;
|
||||
CheckForPauses();
|
||||
|
||||
if (!_pauseFound)
|
||||
{
|
||||
PlayAllFeedbacks(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if at least one pause was found
|
||||
StartCoroutine(PausedFeedbacksCo(position, feedbacksIntensity));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void CheckForPauses()
|
||||
{
|
||||
_pauseFound = false;
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (Feedbacks[i] != null)
|
||||
{
|
||||
if ((Feedbacks[i].Pause != null) && (Feedbacks[i].Active) && (Feedbacks[i].ShouldPlayInThisSequenceDirection))
|
||||
{
|
||||
_pauseFound = true;
|
||||
}
|
||||
if ((Feedbacks[i].HoldingPause == true) && (Feedbacks[i].Active) && (Feedbacks[i].ShouldPlayInThisSequenceDirection))
|
||||
{
|
||||
_pauseFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void PlayAllFeedbacks(Vector3 position, float feedbacksIntensity, bool forceRevert = false)
|
||||
{
|
||||
// if no pause was found, we just play all feedbacks at once
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (FeedbackCanPlay(Feedbacks[i]))
|
||||
{
|
||||
Feedbacks[i].Play(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual IEnumerator HandleInitialDelayCo(Vector3 position, float feedbacksIntensity, bool forceRevert = false)
|
||||
{
|
||||
IsPlaying = true;
|
||||
yield return MMFeedbacksCoroutine.WaitFor(ComputedInitialDelay);
|
||||
PreparePlay(position, feedbacksIntensity, forceRevert);
|
||||
}
|
||||
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (_shouldStop)
|
||||
{
|
||||
if (HasFeedbackStillPlaying())
|
||||
{
|
||||
return;
|
||||
}
|
||||
IsPlaying = false;
|
||||
Events.TriggerOnComplete(this);
|
||||
ApplyAutoRevert();
|
||||
this.enabled = false;
|
||||
_shouldStop = false;
|
||||
}
|
||||
if (IsPlaying)
|
||||
{
|
||||
if (!_pauseFound)
|
||||
{
|
||||
if (GetTime() - _startTime > _totalDuration)
|
||||
{
|
||||
_shouldStop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if feedbacks are still playing
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool HasFeedbackStillPlaying()
|
||||
{
|
||||
int count = Feedbacks.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if ((Feedbacks[i] != null) && (Feedbacks[i].IsPlaying))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A coroutine used to handle the sequence of feedbacks if pauses are involved
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual IEnumerator PausedFeedbacksCo(Vector3 position, float feedbacksIntensity)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region STOP
|
||||
|
||||
/// <summary>
|
||||
/// Stops all further feedbacks from playing, without stopping individual feedbacks
|
||||
/// </summary>
|
||||
public virtual void StopFeedbacks()
|
||||
{
|
||||
StopFeedbacks(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all feedbacks from playing, with an option to also stop individual feedbacks
|
||||
/// </summary>
|
||||
public virtual void StopFeedbacks(bool stopAllFeedbacks = true)
|
||||
{
|
||||
StopFeedbacks(this.transform.position, 1.0f, stopAllFeedbacks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops all feedbacks from playing, specifying a position and intensity that can be used by the Feedbacks
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="feedbacksIntensity"></param>
|
||||
public virtual void StopFeedbacks(Vector3 position, float feedbacksIntensity = 1.0f, bool stopAllFeedbacks = true)
|
||||
{
|
||||
if (stopAllFeedbacks)
|
||||
{
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (Feedbacks[i] != null)
|
||||
{
|
||||
Feedbacks[i].Stop(position, feedbacksIntensity);
|
||||
}
|
||||
}
|
||||
}
|
||||
IsPlaying = false;
|
||||
StopAllCoroutines();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CONTROLS
|
||||
|
||||
/// <summary>
|
||||
/// Calls each feedback's Reset method if they've defined one. An example of that can be resetting the initial color of a flickering renderer.
|
||||
/// </summary>
|
||||
public virtual void ResetFeedbacks()
|
||||
{
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if ((Feedbacks[i] != null) && (Feedbacks[i].Active))
|
||||
{
|
||||
Feedbacks[i].ResetFeedback();
|
||||
}
|
||||
}
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the direction of this MMFeedbacks
|
||||
/// </summary>
|
||||
public virtual void Revert()
|
||||
{
|
||||
Events.TriggerOnRevert(this);
|
||||
Direction = (Direction == Directions.BottomToTop) ? Directions.TopToBottom : Directions.BottomToTop;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use this method to authorize or prevent this player from being played
|
||||
/// </summary>
|
||||
/// <param name="newState"></param>
|
||||
public virtual void SetCanPlay(bool newState)
|
||||
{
|
||||
CanPlay = newState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pauses execution of a sequence, which can then be resumed by calling ResumeFeedbacks()
|
||||
/// </summary>
|
||||
public virtual void PauseFeedbacks()
|
||||
{
|
||||
Events.TriggerOnPause(this);
|
||||
InScriptDrivenPause = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resumes execution of a sequence if a script driven pause is in progress
|
||||
/// </summary>
|
||||
public virtual void ResumeFeedbacks()
|
||||
{
|
||||
Events.TriggerOnResume(this);
|
||||
InScriptDrivenPause = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MODIFICATION
|
||||
|
||||
public virtual MMFeedback AddFeedback(System.Type feedbackType, bool add = true)
|
||||
{
|
||||
MMFeedback newFeedback;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
newFeedback = Undo.AddComponent(this.gameObject, feedbackType) as MMFeedback;
|
||||
}
|
||||
else
|
||||
{
|
||||
newFeedback = this.gameObject.AddComponent(feedbackType) as MMFeedback;
|
||||
}
|
||||
#else
|
||||
newFeedback = this.gameObject.AddComponent(feedbackType) as MMFeedback;
|
||||
#endif
|
||||
|
||||
newFeedback.hideFlags = HideFlags.HideInInspector;
|
||||
newFeedback.Label = FeedbackPathAttribute.GetFeedbackDefaultName(feedbackType);
|
||||
|
||||
AutoRepair();
|
||||
|
||||
return newFeedback;
|
||||
}
|
||||
|
||||
public virtual void RemoveFeedback(int id)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
Undo.DestroyObjectImmediate(Feedbacks[id]);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyImmediate(Feedbacks[id]);
|
||||
}
|
||||
#else
|
||||
DestroyImmediate(Feedbacks[id]);
|
||||
#endif
|
||||
|
||||
Feedbacks.RemoveAt(id);
|
||||
AutoRepair();
|
||||
}
|
||||
|
||||
#endregion MODIFICATION
|
||||
|
||||
#region HELPERS
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the chance of this feedback to play, and returns true if this feedback can play, false otherwise
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual bool EvaluateChance()
|
||||
{
|
||||
if (ChanceToPlay == 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ChanceToPlay != 100f)
|
||||
{
|
||||
// determine the odds
|
||||
float random = Random.Range(0f, 100f);
|
||||
if (random > ChanceToPlay)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether or not this MMFeedbacks contains one or more looper feedbacks
|
||||
/// </summary>
|
||||
protected virtual void CheckForLoops()
|
||||
{
|
||||
ContainsLoop = false;
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (Feedbacks[i] != null)
|
||||
{
|
||||
if (Feedbacks[i].LooperPause && Feedbacks[i].Active)
|
||||
{
|
||||
ContainsLoop = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will return true if the conditions defined in the specified feedback's Timing section allow it to play in the current play direction of this MMFeedbacks
|
||||
/// </summary>
|
||||
/// <param name="feedback"></param>
|
||||
/// <returns></returns>
|
||||
protected bool FeedbackCanPlay(MMFeedback feedback)
|
||||
{
|
||||
if (feedback == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (feedback.Timing == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (feedback.Timing.MMFeedbacksDirectionCondition == MMFeedbackTiming.MMFeedbacksDirectionConditions.Always)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (((Direction == Directions.TopToBottom) && (feedback.Timing.MMFeedbacksDirectionCondition == MMFeedbackTiming.MMFeedbacksDirectionConditions.OnlyWhenForwards))
|
||||
|| ((Direction == Directions.BottomToTop) && (feedback.Timing.MMFeedbacksDirectionCondition == MMFeedbackTiming.MMFeedbacksDirectionConditions.OnlyWhenBackwards)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Readies the MMFeedbacks to revert direction on the next play
|
||||
/// </summary>
|
||||
protected virtual void ApplyAutoRevert()
|
||||
{
|
||||
if (AutoChangeDirectionOnEnd)
|
||||
{
|
||||
ShouldRevertOnNextPlay = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies this feedback's time multiplier to a duration (in seconds)
|
||||
/// </summary>
|
||||
/// <param name="duration"></param>
|
||||
/// <returns></returns>
|
||||
public virtual float ApplyTimeMultiplier(float duration)
|
||||
{
|
||||
return duration * Mathf.Clamp(DurationMultiplier, _smallValue, Single.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity sometimes has serialization issues.
|
||||
/// This method fixes that by fixing any bad sync that could happen.
|
||||
/// </summary>
|
||||
public virtual void AutoRepair()
|
||||
{
|
||||
List<Component> components = components = new List<Component>();
|
||||
components = this.gameObject.GetComponents<Component>().ToList();
|
||||
foreach (Component component in components)
|
||||
{
|
||||
if (component is MMFeedback)
|
||||
{
|
||||
bool found = false;
|
||||
for (int i = 0; i < Feedbacks.Count; i++)
|
||||
{
|
||||
if (Feedbacks[i] == (MMFeedback)component)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
Feedbacks.Add((MMFeedback)component);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EVENTS
|
||||
|
||||
/// <summary>
|
||||
/// On Disable we stop all feedbacks
|
||||
/// </summary>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
/*if (IsPlaying)
|
||||
{
|
||||
StopFeedbacks();
|
||||
StopAllCoroutines();
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On validate, we make sure our DurationMultiplier remains positive
|
||||
/// </summary>
|
||||
protected virtual void OnValidate()
|
||||
{
|
||||
DurationMultiplier = Mathf.Clamp(DurationMultiplier, _smallValue, Single.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Destroy, removes all feedbacks from this MMFeedbacks to avoid any leftovers
|
||||
/// </summary>
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
IsPlaying = false;
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
// we remove all binders
|
||||
foreach (MMFeedback feedback in Feedbacks)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
DestroyImmediate(feedback);
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion EVENTS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 841b72de2996c5c40bfb394f3d0e0a98
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class added automatically by MMFeedbacks if they're in AutoPlayOnEnable mode
|
||||
/// This lets them play again should their parent game object be disabled/enabled
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class MMFeedbacksEnabler : MonoBehaviour
|
||||
{
|
||||
/// the MMFeedbacks to pilot
|
||||
public MMFeedbacks TargetMMFeedbacks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// On enable, we re-enable (and thus play) our MMFeedbacks if needed
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if ((TargetMMFeedbacks != null) && !TargetMMFeedbacks.enabled && TargetMMFeedbacks.AutoPlayOnEnable)
|
||||
{
|
||||
TargetMMFeedbacks.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd5c9de2e9b0d6540b318450df3fb297
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMChannels.meta
Normal file
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMChannels.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8f397bae44366904cb741a56fb7cc568
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 09e29c2242d13d64480d58af86fcb50f, type: 3}
|
||||
m_Name: ChannelA
|
||||
m_EditorClassIdentifier:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ba165fd91edb434aa2f1cd6f6b05885
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 09e29c2242d13d64480d58af86fcb50f, type: 3}
|
||||
m_Name: ChannelB
|
||||
m_EditorClassIdentifier:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7471a7ccb4c5bdb4e9c9d5b51f6db888
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!114 &11400000
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 09e29c2242d13d64480d58af86fcb50f, type: 3}
|
||||
m_Name: ChannelC
|
||||
m_EditorClassIdentifier:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 277be439bdc9329468f9e6d0799d54be
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 11400000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// The possible modes used to identify a channel, either via an int or a MMChannel scriptable object
|
||||
/// </summary>
|
||||
public enum MMChannelModes
|
||||
{
|
||||
Int,
|
||||
MMChannel
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A data structure used to pass channel information
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class MMChannelData
|
||||
{
|
||||
public MMChannelModes MMChannelMode;
|
||||
public int Channel;
|
||||
public MMChannel MMChannelDefinition;
|
||||
|
||||
public MMChannelData(MMChannelModes mode, int channel, MMChannel channelDefinition)
|
||||
{
|
||||
MMChannelMode = mode;
|
||||
Channel = channel;
|
||||
MMChannelDefinition = channelDefinition;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extensions class for MMChannelData
|
||||
/// </summary>
|
||||
public static class MMChannelDataExtensions
|
||||
{
|
||||
public static MMChannelData Set(this MMChannelData data, MMChannelModes mode, int channel, MMChannel channelDefinition)
|
||||
{
|
||||
data.MMChannelMode = mode;
|
||||
data.Channel = channel;
|
||||
data.MMChannelDefinition = channelDefinition;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A scriptable object you can create assets from, to identify Channels, used mostly (but not only) in feedbacks and shakers,
|
||||
/// to determine a channel of communication, usually between emitters and receivers
|
||||
/// </summary>
|
||||
[CreateAssetMenu(menuName = "MoreMountains/MMChannel", fileName = "MMChannel")]
|
||||
public class MMChannel : ScriptableObject
|
||||
{
|
||||
public static bool Match(MMChannelData dataA, MMChannelData dataB)
|
||||
{
|
||||
if (dataA.MMChannelMode != dataB.MMChannelMode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataA.MMChannelMode == MMChannelModes.Int)
|
||||
{
|
||||
return dataA.Channel == dataB.Channel;
|
||||
}
|
||||
else
|
||||
{
|
||||
return dataA.MMChannelDefinition == dataB.MMChannelDefinition;
|
||||
}
|
||||
}
|
||||
public static bool Match(MMChannelData dataA, MMChannelModes modeB, int channelB, MMChannel channelDefinitionB)
|
||||
{
|
||||
if (dataA == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (dataA.MMChannelMode != modeB)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dataA.MMChannelMode == MMChannelModes.Int)
|
||||
{
|
||||
return dataA.Channel == channelB;
|
||||
}
|
||||
else
|
||||
{
|
||||
return dataA.MMChannelDefinition == channelDefinitionB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 09e29c2242d13d64480d58af86fcb50f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player.meta
Normal file
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29c391bec7aa5dc4e914145af8159c87
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1220
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player/MMF_Feedback.cs
Normal file
1220
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player/MMF_Feedback.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1466e386bacf73e428d7e19707b9e185
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
[Serializable]
|
||||
public class MMF_Button
|
||||
{
|
||||
public delegate void ButtonMethod();
|
||||
|
||||
public string ButtonText;
|
||||
public ButtonMethod TargetMethod;
|
||||
|
||||
public MMF_Button(string buttonText, ButtonMethod method)
|
||||
{
|
||||
ButtonText = buttonText;
|
||||
TargetMethod = method;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecda383e35cc53e4ca2b9d5857dbd8f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
1682
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player/MMF_Player.cs
Normal file
1682
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMF_Player/MMF_Player.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6da43522623d4704e979466dc7650b65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Feedbacks;
|
||||
using UnityEngine;
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
using UnityEngine.InputSystem;
|
||||
#endif
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Add this debug component to a MMF Player, and you'll be able to play it at runtime at the press of a (customisable) key, useful when tweaking or debugging your feedbacks
|
||||
/// </summary>
|
||||
public class MMF_PlayerDebugInput : MonoBehaviour
|
||||
{
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
/// the button used to cause a debug play of this feedback
|
||||
public Key PlayKey = Key.P;
|
||||
#else
|
||||
/// the button used to cause a debug play of this feedback
|
||||
public KeyCode PlayButton = KeyCode.P;
|
||||
#endif
|
||||
|
||||
protected MMF_Player _player;
|
||||
|
||||
/// <summary>
|
||||
/// On Awake we store our MMF Player
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
_player = this.gameObject.GetComponent<MMF_Player>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Update, we play our feedback if the right button is pressed
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
bool keyPressed = false;
|
||||
|
||||
#if ENABLE_INPUT_SYSTEM && !ENABLE_LEGACY_INPUT_MANAGER
|
||||
keyPressed = Keyboard.current[PlayKey].wasPressedThisFrame;
|
||||
#else
|
||||
keyPressed = Input.GetKeyDown(PlayButton);
|
||||
#endif
|
||||
|
||||
if (keyPressed)
|
||||
{
|
||||
_player.PlayFeedbacks();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4474026fd06a48748b3ac7eef71e802d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A helper class added automatically by MMFeedbacks if they're in AutoPlayOnEnable mode
|
||||
/// This lets them play again should their parent game object be disabled/enabled
|
||||
/// </summary>
|
||||
[AddComponentMenu("")]
|
||||
public class MMF_PlayerEnabler : MonoBehaviour
|
||||
{
|
||||
/// the MMFeedbacks to pilot
|
||||
public virtual MMF_Player TargetMmfPlayer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// On enable, we re-enable (and thus play) our MMFeedbacks if needed
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if ((TargetMmfPlayer != null) && !TargetMmfPlayer.enabled && TargetMmfPlayer.AutoPlayOnEnable)
|
||||
{
|
||||
TargetMmfPlayer.enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 109f4af2c917dc74aa07733bafe2f548
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,123 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// A class collecting target acquisition settings
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class MMFeedbackTargetAcquisition
|
||||
{
|
||||
public enum Modes { None, Self, AnyChild, ChildAtIndex, Parent, FirstReferenceHolder, PreviousReferenceHolder, ClosestReferenceHolder, NextReferenceHolder, LastReferenceHolder }
|
||||
|
||||
/// the selected mode for target acquisition
|
||||
/// None : nothing will happen
|
||||
/// Self : the target will be picked on the MMF Player's game object
|
||||
/// AnyChild : the target will be picked on any of the MMF Player's child objects
|
||||
/// ChildAtIndex : the target will be picked on the child at index X of the MMF Player
|
||||
/// Parent : the target will be picked on the first parent where a matching target is found
|
||||
/// Various reference holders : the target will be picked on the specified reference holder in the list (either the first one, previous : first one found before this feedback in the list, closest in any direction from this feedback, the next one found, or the last one in the list)
|
||||
[Tooltip("the selected mode for target acquisition\n"+
|
||||
"None : nothing will happen\n"+
|
||||
"Self : the target will be picked on the MMF Player's game object\n"+
|
||||
"AnyChild : the target will be picked on any of the MMF Player's child objects\n"+
|
||||
"ChildAtIndex : the target will be picked on the child at index X of the MMF Player\n"+
|
||||
"Parent : the target will be picked on the first parent where a matching target is found\n"+
|
||||
"Various reference holders : the target will be picked on the specified reference holder in the list " +
|
||||
"(either the first one, previous : first one found before this feedback in the list, closest in any direction from this feedback, the next one found, or the last one in the list)")]
|
||||
public Modes Mode = Modes.None;
|
||||
|
||||
[MMFEnumCondition("Mode", (int)Modes.ChildAtIndex)]
|
||||
public int ChildIndex = 0;
|
||||
|
||||
private static MMF_ReferenceHolder _referenceHolder;
|
||||
|
||||
public static MMF_ReferenceHolder GetReferenceHolder(MMFeedbackTargetAcquisition settings, MMF_Player owner, int currentFeedbackIndex)
|
||||
{
|
||||
switch (settings.Mode)
|
||||
{
|
||||
case Modes.FirstReferenceHolder:
|
||||
return owner.GetFeedbackOfType<MMF_ReferenceHolder>(MMF_Player.AccessMethods.First, currentFeedbackIndex);
|
||||
case Modes.PreviousReferenceHolder:
|
||||
return owner.GetFeedbackOfType<MMF_ReferenceHolder>(MMF_Player.AccessMethods.Previous, currentFeedbackIndex);
|
||||
case Modes.ClosestReferenceHolder:
|
||||
return owner.GetFeedbackOfType<MMF_ReferenceHolder>(MMF_Player.AccessMethods.Closest, currentFeedbackIndex);
|
||||
case Modes.NextReferenceHolder:
|
||||
return owner.GetFeedbackOfType<MMF_ReferenceHolder>(MMF_Player.AccessMethods.Next, currentFeedbackIndex);
|
||||
case Modes.LastReferenceHolder:
|
||||
return owner.GetFeedbackOfType<MMF_ReferenceHolder>(MMF_Player.AccessMethods.Last, currentFeedbackIndex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static GameObject FindAutomatedTargetGameObject(MMFeedbackTargetAcquisition settings, MMF_Player owner, int currentFeedbackIndex)
|
||||
{
|
||||
if (owner.FeedbacksList[currentFeedbackIndex].ForcedReferenceHolder != null)
|
||||
{
|
||||
return owner.FeedbacksList[currentFeedbackIndex].ForcedReferenceHolder.GameObjectReference;
|
||||
}
|
||||
|
||||
_referenceHolder = GetReferenceHolder(settings, owner, currentFeedbackIndex);
|
||||
switch (settings.Mode)
|
||||
{
|
||||
case Modes.Self:
|
||||
return owner.gameObject;
|
||||
case Modes.ChildAtIndex:
|
||||
return owner.transform.GetChild(settings.ChildIndex).gameObject;
|
||||
case Modes.AnyChild:
|
||||
return owner.transform.GetChild(0).gameObject;
|
||||
case Modes.Parent:
|
||||
return owner.transform.parent.gameObject;
|
||||
case Modes.FirstReferenceHolder:
|
||||
case Modes.PreviousReferenceHolder:
|
||||
case Modes.ClosestReferenceHolder:
|
||||
case Modes.NextReferenceHolder:
|
||||
case Modes.LastReferenceHolder:
|
||||
return _referenceHolder?.GameObjectReference;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static T FindAutomatedTarget<T>(MMFeedbackTargetAcquisition settings, MMF_Player owner, int currentFeedbackIndex)
|
||||
{
|
||||
if (owner.FeedbacksList[currentFeedbackIndex].ForcedReferenceHolder != null)
|
||||
{
|
||||
return owner.FeedbacksList[currentFeedbackIndex].ForcedReferenceHolder.GameObjectReference.GetComponent<T>();
|
||||
}
|
||||
_referenceHolder = GetReferenceHolder(settings, owner, currentFeedbackIndex);
|
||||
switch (settings.Mode)
|
||||
{
|
||||
case Modes.Self:
|
||||
return owner.GetComponent<T>();
|
||||
case Modes.ChildAtIndex:
|
||||
return owner.transform.GetChild(settings.ChildIndex).gameObject.GetComponent<T>();
|
||||
case Modes.AnyChild:
|
||||
for (int i = 0; i < owner.transform.childCount; i++)
|
||||
{
|
||||
if (owner.transform.GetChild(i).GetComponent<T>() != null)
|
||||
{
|
||||
return owner.transform.GetChild(i).GetComponent<T>();
|
||||
}
|
||||
}
|
||||
return owner.GetComponentInChildren<T>();
|
||||
case Modes.Parent:
|
||||
return owner.transform.parent.GetComponentInParent<T>();
|
||||
case Modes.FirstReferenceHolder:
|
||||
case Modes.PreviousReferenceHolder:
|
||||
case Modes.ClosestReferenceHolder:
|
||||
case Modes.NextReferenceHolder:
|
||||
case Modes.LastReferenceHolder:
|
||||
return (_referenceHolder != null)
|
||||
? _referenceHolder.GameObjectReference.GetComponent<T>()
|
||||
: default(T);
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a40118e4d239fe146bb900366085611f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
137
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbackTiming.cs
Normal file
137
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbackTiming.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// the possible modes for the timescale
|
||||
public enum TimescaleModes { Scaled, Unscaled }
|
||||
|
||||
/// <summary>
|
||||
/// A class collecting delay, cooldown and repeat values, to be used to define the behaviour of each MMFeedback
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class MMFeedbackTiming
|
||||
{
|
||||
/// the possible ways this feedback can play based on the host MMFeedbacks' directions
|
||||
public enum MMFeedbacksDirectionConditions { Always, OnlyWhenForwards, OnlyWhenBackwards };
|
||||
/// the possible ways this feedback can play
|
||||
public enum PlayDirections { FollowMMFeedbacksDirection, OppositeMMFeedbacksDirection, AlwaysNormal, AlwaysRewind }
|
||||
|
||||
[Header("Timescale")]
|
||||
/// whether we're working on scaled or unscaled time
|
||||
[Tooltip("whether we're working on scaled or unscaled time")]
|
||||
public TimescaleModes TimescaleMode = TimescaleModes.Scaled;
|
||||
|
||||
[Header("Exceptions")]
|
||||
/// if this is true, holding pauses won't wait for this feedback to finish
|
||||
[Tooltip("if this is true, holding pauses won't wait for this feedback to finish")]
|
||||
public bool ExcludeFromHoldingPauses = false;
|
||||
/// whether to count this feedback in the parent MMFeedbacks(Player) total duration or not
|
||||
[Tooltip("whether to count this feedback in the parent MMFeedbacks(Player) total duration or not")]
|
||||
public bool ContributeToTotalDuration = true;
|
||||
|
||||
[Header("Delays")]
|
||||
/// the initial delay to apply before playing the delay (in seconds)
|
||||
[Tooltip("the initial delay to apply before playing the delay (in seconds)")]
|
||||
public float InitialDelay = 0f;
|
||||
/// the cooldown duration mandatory between two plays
|
||||
[Tooltip("the cooldown duration mandatory between two plays")]
|
||||
public float CooldownDuration = 0f;
|
||||
|
||||
[Header("Stop")]
|
||||
/// if this is true, this feedback will interrupt itself when Stop is called on its parent MMFeedbacks, otherwise it'll keep running
|
||||
[Tooltip("if this is true, this feedback will interrupt itself when Stop is called on its parent MMFeedbacks, otherwise it'll keep running")]
|
||||
public bool InterruptsOnStop = true;
|
||||
|
||||
[Header("Repeat")]
|
||||
/// the repeat mode, whether the feedback should be played once, multiple times, or forever
|
||||
[Tooltip("the repeat mode, whether the feedback should be played once, multiple times, or forever")]
|
||||
public int NumberOfRepeats = 0;
|
||||
/// if this is true, the feedback will be repeated forever
|
||||
[Tooltip("if this is true, the feedback will be repeated forever")]
|
||||
public bool RepeatForever = false;
|
||||
/// the delay (in seconds) between two firings of this feedback. This doesn't include the duration of the feedback.
|
||||
[Tooltip("the delay (in seconds) between two firings of this feedback. This doesn't include the duration of the feedback.")]
|
||||
public float DelayBetweenRepeats = 1f;
|
||||
|
||||
[Header("PlayCount")]
|
||||
/// the number of times this feedback's been played since its initialization (or last reset if SetPlayCountToZeroOnReset is true)
|
||||
[Tooltip("the number of times this feedback's been played since its initialization (or last reset if SetPlayCountToZeroOnReset is true)")]
|
||||
[MMFReadOnly]
|
||||
public int PlayCount = 0;
|
||||
/// whether or not to limit the amount of times this feedback can be played. beyond that amount, it won't play anymore
|
||||
[Tooltip("whether or not to limit the amount of times this feedback can be played. beyond that amount, it won't play anymore")]
|
||||
public bool LimitPlayCount = false;
|
||||
/// if LimitPlayCount is true, the maximum amount of times this feedback can be played
|
||||
[Tooltip("if LimitPlayCount is true, the maximum amount of times this feedback can be played")]
|
||||
[MMFCondition("LimitPlayCount", true)]
|
||||
public int MaxPlayCount = 3;
|
||||
/// if LimitPlayCount is true, whether or not to reset the play count to zero when the feedback is reset
|
||||
[Tooltip("if LimitPlayCount is true, whether or not to reset the play count to zero when the feedback is reset")]
|
||||
[MMFCondition("LimitPlayCount", true)]
|
||||
public bool SetPlayCountToZeroOnReset = false;
|
||||
|
||||
[Header("Play Direction")]
|
||||
/// this defines how this feedback should play when the host MMFeedbacks is played :
|
||||
/// - always (default) : this feedback will always play
|
||||
/// - OnlyWhenForwards : this feedback will only play if the host MMFeedbacks is played in the top to bottom direction (forwards)
|
||||
/// - OnlyWhenBackwards : this feedback will only play if the host MMFeedbacks is played in the bottom to top direction (backwards)
|
||||
[Tooltip("this defines how this feedback should play when the host MMFeedbacks is played :" +
|
||||
"- always (default) : this feedback will always play" +
|
||||
"- OnlyWhenForwards : this feedback will only play if the host MMFeedbacks is played in the top to bottom direction (forwards)" +
|
||||
"- OnlyWhenBackwards : this feedback will only play if the host MMFeedbacks is played in the bottom to top direction (backwards)")]
|
||||
public MMFeedbacksDirectionConditions MMFeedbacksDirectionCondition = MMFeedbacksDirectionConditions.Always;
|
||||
/// this defines the way this feedback will play. It can play in its normal direction, or in rewind (a sound will play backwards,
|
||||
/// an object normally scaling up will scale down, a curve will be evaluated from right to left, etc)
|
||||
/// - BasedOnMMFeedbacksDirection : will play normally when the host MMFeedbacks is played forwards, in rewind when it's played backwards
|
||||
/// - OppositeMMFeedbacksDirection : will play in rewind when the host MMFeedbacks is played forwards, and normally when played backwards
|
||||
/// - Always Normal : will always play normally, regardless of the direction of the host MMFeedbacks
|
||||
/// - Always Rewind : will always play in rewind, regardless of the direction of the host MMFeedbacks
|
||||
[Tooltip("this defines the way this feedback will play. It can play in its normal direction, or in rewind (a sound will play backwards," +
|
||||
" an object normally scaling up will scale down, a curve will be evaluated from right to left, etc)" +
|
||||
"- BasedOnMMFeedbacksDirection : will play normally when the host MMFeedbacks is played forwards, in rewind when it's played backwards" +
|
||||
"- OppositeMMFeedbacksDirection : will play in rewind when the host MMFeedbacks is played forwards, and normally when played backwards" +
|
||||
"- Always Normal : will always play normally, regardless of the direction of the host MMFeedbacks" +
|
||||
"- Always Rewind : will always play in rewind, regardless of the direction of the host MMFeedbacks")]
|
||||
public PlayDirections PlayDirection = PlayDirections.FollowMMFeedbacksDirection;
|
||||
|
||||
[Header("Intensity")]
|
||||
/// if this is true, intensity will be constant, even if the parent MMFeedbacks is played at a lower intensity
|
||||
[Tooltip("if this is true, intensity will be constant, even if the parent MMFeedbacks is played at a lower intensity")]
|
||||
public bool ConstantIntensity = false;
|
||||
/// if this is true, this feedback will only play if its intensity is higher or equal to IntensityIntervalMin and lower than IntensityIntervalMax
|
||||
[Tooltip("if this is true, this feedback will only play if its intensity is higher or equal to IntensityIntervalMin and lower than IntensityIntervalMax")]
|
||||
public bool UseIntensityInterval = false;
|
||||
/// the minimum intensity required for this feedback to play
|
||||
[Tooltip("the minimum intensity required for this feedback to play")]
|
||||
[MMFCondition("UseIntensityInterval", true)]
|
||||
public float IntensityIntervalMin = 0f;
|
||||
/// the maximum intensity required for this feedback to play
|
||||
[Tooltip("the maximum intensity required for this feedback to play")]
|
||||
[MMFCondition("UseIntensityInterval", true)]
|
||||
public float IntensityIntervalMax = 0f;
|
||||
|
||||
[Header("Sequence")]
|
||||
/// A MMSequence to use to play these feedbacks on
|
||||
[Tooltip("A MMSequence to use to play these feedbacks on")]
|
||||
public MMSequence Sequence;
|
||||
/// The MMSequence's TrackID to consider
|
||||
[Tooltip("The MMSequence's TrackID to consider")]
|
||||
public int TrackID = 0;
|
||||
/// whether or not to use the quantized version of the target sequence
|
||||
[Tooltip("whether or not to use the quantized version of the target sequence")]
|
||||
public bool Quantized = false;
|
||||
/// if using the quantized version of the target sequence, the BPM to apply to the sequence when playing it
|
||||
[Tooltip("if using the quantized version of the target sequence, the BPM to apply to the sequence when playing it")]
|
||||
[MMFCondition("Quantized", true)]
|
||||
public int TargetBPM = 120;
|
||||
|
||||
/// from any class, you can set UseScriptDrivenTimescale:true, from there, instead of looking at Time.time, Time.deltaTime (or their unscaled equivalents), this feedback will compute time based on the values you feed them via ScriptDrivenDeltaTime and ScriptDrivenTime
|
||||
public virtual bool UseScriptDrivenTimescale { get; set; }
|
||||
/// the value this feedback should use for delta time
|
||||
public virtual float ScriptDrivenDeltaTime { get; set; }
|
||||
/// the value this feedback should use for time
|
||||
public virtual float ScriptDrivenTime { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 293c62544284dea419e338a8524a7fb4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Coroutine helpers
|
||||
/// </summary>
|
||||
public static class MMFeedbacksCoroutine
|
||||
{
|
||||
/// <summary>
|
||||
/// Waits for the specified amount of frames
|
||||
/// use : yield return MMCoroutine.WaitFor(1);
|
||||
/// </summary>
|
||||
/// <param name="frameCount"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerator WaitForFrames(int frameCount)
|
||||
{
|
||||
while (frameCount > 0)
|
||||
{
|
||||
frameCount--;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the specified amount of seconds (using regular time)
|
||||
/// use : yield return MMCoroutine.WaitFor(1f);
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerator WaitFor(float seconds)
|
||||
{
|
||||
for (float timer = 0f; timer < seconds; timer += Time.deltaTime)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the specified amount of seconds (using unscaled time)
|
||||
/// use : yield return MMCoroutine.WaitForUnscaled(1f);
|
||||
/// </summary>
|
||||
/// <param name="seconds"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerator WaitForUnscaled(float seconds)
|
||||
{
|
||||
for (float timer = 0f; timer < seconds; timer += Time.unscaledDeltaTime)
|
||||
{
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fff95d1eb5038764b9fdf57b6818aa6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
306
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbacksEvents.cs
Normal file
306
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbacksEvents.cs
Normal file
@@ -0,0 +1,306 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Events triggered by a MMFeedbacks when playing a series of feedbacks
|
||||
/// - play : when a MMFeedbacks starts playing
|
||||
/// - pause : when a holding pause is met
|
||||
/// - resume : after a holding pause resumes
|
||||
/// - revert : when a MMFeedbacks reverts its play direction
|
||||
/// - complete : when a MMFeedbacks has played its last feedback
|
||||
///
|
||||
/// to listen to these events :
|
||||
///
|
||||
/// public virtual void OnMMFeedbacksEvent(MMFeedbacks source, EventTypes type)
|
||||
/// {
|
||||
/// // do something
|
||||
/// }
|
||||
///
|
||||
/// protected virtual void OnEnable()
|
||||
/// {
|
||||
/// MMFeedbacksEvent.Register(OnMMFeedbacksEvent);
|
||||
/// }
|
||||
///
|
||||
/// protected virtual void OnDisable()
|
||||
/// {
|
||||
/// MMFeedbacksEvent.Unregister(OnMMFeedbacksEvent);
|
||||
/// }
|
||||
///
|
||||
/// </summary>
|
||||
public struct MMFeedbacksEvent
|
||||
{
|
||||
static private event Delegate OnEvent;
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; }
|
||||
static public void Register(Delegate callback) { OnEvent += callback; }
|
||||
static public void Unregister(Delegate callback) { OnEvent -= callback; }
|
||||
|
||||
public enum EventTypes { Play, Pause, Resume, Revert, Complete, SkipToTheEnd, RestoreInitialValues, Loop, Enable, Disable, InitializationComplete }
|
||||
public delegate void Delegate(MMFeedbacks source, EventTypes type);
|
||||
static public void Trigger(MMFeedbacks source, EventTypes type)
|
||||
{
|
||||
OnEvent?.Invoke(source, type);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An event used to set the RangeCenter on all feedbacks that listen for it
|
||||
/// </summary>
|
||||
public struct MMSetFeedbackRangeCenterEvent
|
||||
{
|
||||
static private event Delegate OnEvent;
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void RuntimeInitialization() { OnEvent = null; }
|
||||
static public void Register(Delegate callback) { OnEvent += callback; }
|
||||
static public void Unregister(Delegate callback) { OnEvent -= callback; }
|
||||
|
||||
public delegate void Delegate(Transform newCenter);
|
||||
|
||||
static public void Trigger(Transform newCenter)
|
||||
{
|
||||
OnEvent?.Invoke(newCenter);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A subclass of MMFeedbacks, contains UnityEvents that can be played,
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class MMFeedbacksEvents
|
||||
{
|
||||
/// whether or not this MMFeedbacks should fire MMFeedbacksEvents
|
||||
[Tooltip("whether or not this MMFeedbacks should fire MMFeedbacksEvents")]
|
||||
public bool TriggerMMFeedbacksEvents = false;
|
||||
/// whether or not this MMFeedbacks should fire Unity Events
|
||||
[Tooltip("whether or not this MMFeedbacks should fire Unity Events")]
|
||||
public bool TriggerUnityEvents = true;
|
||||
/// This event will fire every time this MMFeedbacks gets played
|
||||
[Tooltip("This event will fire every time this MMFeedbacks gets played")]
|
||||
public UnityEvent OnPlay;
|
||||
/// This event will fire every time this MMFeedbacks starts a holding pause
|
||||
[Tooltip("This event will fire every time this MMFeedbacks starts a holding pause")]
|
||||
public UnityEvent OnPause;
|
||||
/// This event will fire every time this MMFeedbacks resumes after a holding pause
|
||||
[Tooltip("This event will fire every time this MMFeedbacks resumes after a holding pause")]
|
||||
public UnityEvent OnResume;
|
||||
/// This event will fire every time this MMFeedbacks reverts its play direction
|
||||
[Tooltip("This event will fire every time this MMFeedbacks reverts its play direction")]
|
||||
public UnityEvent OnRevert;
|
||||
/// This event will fire every time this MMFeedbacks plays its last MMFeedback
|
||||
[Tooltip("This event will fire every time this MMFeedbacks plays its last MMFeedback")]
|
||||
public UnityEvent OnComplete;
|
||||
/// This event will fire every time this MMFeedbacks gets restored to its initial values
|
||||
[Tooltip("This event will fire every time this MMFeedbacks gets restored to its initial values")]
|
||||
public UnityEvent OnRestoreInitialValues;
|
||||
/// This event will fire every time this MMFeedbacks gets skipped to the end
|
||||
[Tooltip("This event will fire every time this MMFeedbacks gets skipped to the end")]
|
||||
public UnityEvent OnSkipToTheEnd;
|
||||
/// This event will fire after the MMF Player is done initializing
|
||||
[Tooltip("This event will fire after the MMF Player is done initializing")]
|
||||
public UnityEvent OnInitializationComplete;
|
||||
/// This event will fire every time this MMFeedbacks' game object gets enabled
|
||||
[Tooltip("This event will fire every time this MMFeedbacks' game object gets enabled")]
|
||||
public UnityEvent OnEnable;
|
||||
/// This event will fire every time this MMFeedbacks' game object gets disabled
|
||||
[Tooltip("This event will fire every time this MMFeedbacks' game object gets disabled")]
|
||||
public UnityEvent OnDisable;
|
||||
|
||||
public virtual bool OnPlayIsNull { get; protected set; }
|
||||
public virtual bool OnPauseIsNull { get; protected set; }
|
||||
public virtual bool OnResumeIsNull { get; protected set; }
|
||||
public virtual bool OnRevertIsNull { get; protected set; }
|
||||
public virtual bool OnCompleteIsNull { get; protected set; }
|
||||
public virtual bool OnRestoreInitialValuesIsNull { get; protected set; }
|
||||
public virtual bool OnSkipToTheEndIsNull { get; protected set; }
|
||||
public virtual bool OnInitializationCompleteIsNull { get; protected set; }
|
||||
public virtual bool OnEnableIsNull { get; protected set; }
|
||||
public virtual bool OnDisableIsNull { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// On init we store for each event whether or not we have one to invoke
|
||||
/// </summary>
|
||||
public virtual void Initialization()
|
||||
{
|
||||
OnPlayIsNull = OnPlay == null;
|
||||
OnPauseIsNull = OnPause == null;
|
||||
OnResumeIsNull = OnResume == null;
|
||||
OnRevertIsNull = OnRevert == null;
|
||||
OnCompleteIsNull = OnComplete == null;
|
||||
OnRestoreInitialValuesIsNull = OnRestoreInitialValues == null;
|
||||
OnSkipToTheEndIsNull = OnSkipToTheEnd == null;
|
||||
OnInitializationCompleteIsNull = OnInitializationComplete == null;
|
||||
OnEnableIsNull = OnEnable == null;
|
||||
OnDisableIsNull = OnDisable == null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires Play events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnPlay(MMFeedbacks source)
|
||||
{
|
||||
if (!OnPlayIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnPlay.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Play);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires pause events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnPause(MMFeedbacks source)
|
||||
{
|
||||
if (!OnPauseIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnPause.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Pause);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires resume events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnResume(MMFeedbacks source)
|
||||
{
|
||||
if (!OnResumeIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnResume.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Resume);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires revert events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnRevert(MMFeedbacks source)
|
||||
{
|
||||
if (!OnRevertIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnRevert.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Revert);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires complete events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnComplete(MMFeedbacks source)
|
||||
{
|
||||
if (!OnCompleteIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnComplete.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Complete);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires skip events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnSkipToTheEnd(MMFeedbacks source)
|
||||
{
|
||||
if (!OnSkipToTheEndIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnSkipToTheEnd.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.SkipToTheEnd);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void TriggerOnInitializationComplete(MMFeedbacks source)
|
||||
{
|
||||
if (!OnInitializationCompleteIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnInitializationComplete.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.InitializationComplete);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires revert events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnRestoreInitialValues(MMFeedbacks source)
|
||||
{
|
||||
if (!OnRestoreInitialValuesIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnRestoreInitialValues.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.RestoreInitialValues);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires enable events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnEnable(MMF_Player source)
|
||||
{
|
||||
if (!OnEnableIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnEnable.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Enable);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fires disable events if needed
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public virtual void TriggerOnDisable(MMF_Player source)
|
||||
{
|
||||
if (!OnDisableIsNull && TriggerUnityEvents)
|
||||
{
|
||||
OnDisable.Invoke();
|
||||
}
|
||||
|
||||
if (TriggerMMFeedbacksEvents)
|
||||
{
|
||||
MMFeedbacksEvent.Trigger(source, MMFeedbacksEvent.EventTypes.Disable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 716ed1aa8290ba04ba69b411a3d8554a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
568
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbacksHelpers.cs
Normal file
568
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMFeedbacksHelpers.cs
Normal file
@@ -0,0 +1,568 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using MoreMountains.Tools;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
[AddComponentMenu("")]
|
||||
public class MMFeedbacksHelpers : MonoBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Remaps a value x in interval [A,B], to the proportional value in interval [C,D]
|
||||
/// </summary>
|
||||
/// <param name="x">The value to remap.</param>
|
||||
/// <param name="A">the minimum bound of interval [A,B] that contains the x value</param>
|
||||
/// <param name="B">the maximum bound of interval [A,B] that contains the x value</param>
|
||||
/// <param name="C">the minimum bound of target interval [C,D]</param>
|
||||
/// <param name="D">the maximum bound of target interval [C,D]</param>
|
||||
public static float Remap(float x, float A, float B, float C, float D)
|
||||
{
|
||||
float remappedValue = C + (x - A) / (B - A) * (D - C);
|
||||
return remappedValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A helper used to migrate values from an AnimationCurve field to a MMTweenType, useful when updating
|
||||
/// old feedbacks to use them without losing legacy values
|
||||
/// </summary>
|
||||
/// <param name="oldCurve"></param>
|
||||
/// <param name="newTweenType"></param>
|
||||
/// <param name="owner"></param>
|
||||
public static void MigrateCurve(AnimationCurve oldCurve, MMTweenType newTweenType, MMF_Player owner)
|
||||
{
|
||||
if ((oldCurve.keys.Length > 0) && (!newTweenType.Initialized))
|
||||
{
|
||||
newTweenType.Curve = oldCurve;
|
||||
newTweenType.MMTweenDefinitionType = MMTweenDefinitionTypes.AnimationCurve;
|
||||
oldCurve = null;
|
||||
newTweenType.Initialized = true;
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.Undo.RecordObject(owner, "Ports animation curve to tween system");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MMFReadOnlyAttribute : PropertyAttribute { }
|
||||
|
||||
[System.AttributeUsage(System.AttributeTargets.Field)]
|
||||
public class MMFInspectorButtonAttribute : PropertyAttribute
|
||||
{
|
||||
public readonly string MethodName;
|
||||
|
||||
public MMFInspectorButtonAttribute(string MethodName)
|
||||
{
|
||||
this.MethodName = MethodName;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)]
|
||||
public class MMFEnumConditionAttribute : PropertyAttribute
|
||||
{
|
||||
public string ConditionEnum = "";
|
||||
public bool Hidden = false;
|
||||
|
||||
BitArray bitArray = new BitArray(32);
|
||||
public bool ContainsBitFlag(int enumValue)
|
||||
{
|
||||
return bitArray.Get(enumValue);
|
||||
}
|
||||
|
||||
public MMFEnumConditionAttribute(string conditionBoolean, params int[] enumValues)
|
||||
{
|
||||
this.ConditionEnum = conditionBoolean;
|
||||
this.Hidden = true;
|
||||
|
||||
for (int i = 0; i < enumValues.Length; i++)
|
||||
{
|
||||
bitArray.Set(enumValues[i], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(MMFInspectorButtonAttribute))]
|
||||
public class MMFInspectorButtonPropertyDrawer : PropertyDrawer
|
||||
{
|
||||
private MethodInfo _eventMethodInfo = null;
|
||||
|
||||
public override void OnGUI(Rect position, SerializedProperty prop, GUIContent label)
|
||||
{
|
||||
MMFInspectorButtonAttribute inspectorButtonAttribute = (MMFInspectorButtonAttribute)attribute;
|
||||
|
||||
float buttonLength = position.width;
|
||||
Rect buttonRect = new Rect(position.x + (position.width - buttonLength) * 0.5f, position.y, buttonLength, position.height);
|
||||
|
||||
if (GUI.Button(buttonRect, inspectorButtonAttribute.MethodName))
|
||||
{
|
||||
System.Type eventOwnerType = prop.serializedObject.targetObject.GetType();
|
||||
string eventName = inspectorButtonAttribute.MethodName;
|
||||
|
||||
if (_eventMethodInfo == null)
|
||||
{
|
||||
_eventMethodInfo = eventOwnerType.GetMethod(eventName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
}
|
||||
|
||||
if (_eventMethodInfo != null)
|
||||
{
|
||||
_eventMethodInfo.Invoke(prop.serializedObject.targetObject, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning(string.Format("InspectorButton: Unable to find method {0} in {1}", eventName, eventOwnerType));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public class MMFInformationAttribute : PropertyAttribute
|
||||
{
|
||||
public enum InformationType { Error, Info, None, Warning }
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public string Message;
|
||||
public MessageType Type;
|
||||
public bool MessageAfterProperty;
|
||||
|
||||
public MMFInformationAttribute(string message, InformationType type, bool messageAfterProperty)
|
||||
{
|
||||
this.Message = message;
|
||||
if (type == InformationType.Error) { this.Type = UnityEditor.MessageType.Error; }
|
||||
if (type == InformationType.Info) { this.Type = UnityEditor.MessageType.Info; }
|
||||
if (type == InformationType.Warning) { this.Type = UnityEditor.MessageType.Warning; }
|
||||
if (type == InformationType.None) { this.Type = UnityEditor.MessageType.None; }
|
||||
this.MessageAfterProperty = messageAfterProperty;
|
||||
}
|
||||
#else
|
||||
public MMFInformationAttribute(string message, InformationType type, bool messageAfterProperty)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public class MMFHiddenAttribute : PropertyAttribute { }
|
||||
|
||||
[AttributeUsage(System.AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)]
|
||||
public class MMFConditionAttribute : PropertyAttribute
|
||||
{
|
||||
public string ConditionBoolean = "";
|
||||
public bool Hidden = false;
|
||||
public bool Negative = false;
|
||||
|
||||
public MMFConditionAttribute(string conditionBoolean)
|
||||
{
|
||||
this.ConditionBoolean = conditionBoolean;
|
||||
this.Hidden = false;
|
||||
this.Negative = false;
|
||||
}
|
||||
|
||||
public MMFConditionAttribute(string conditionBoolean, bool hideInInspector)
|
||||
{
|
||||
this.ConditionBoolean = conditionBoolean;
|
||||
this.Hidden = hideInInspector;
|
||||
this.Negative = false;
|
||||
}
|
||||
|
||||
public MMFConditionAttribute(string conditionBoolean, bool hideInInspector, bool negative)
|
||||
{
|
||||
this.ConditionBoolean = conditionBoolean;
|
||||
this.Hidden = hideInInspector;
|
||||
this.Negative = negative;
|
||||
}
|
||||
}
|
||||
|
||||
public class MMFVectorAttribute : PropertyAttribute
|
||||
{
|
||||
public readonly string[] Labels;
|
||||
|
||||
public MMFVectorAttribute(params string[] labels)
|
||||
{
|
||||
Labels = labels;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[CustomPropertyDrawer(typeof(MMFVectorAttribute))]
|
||||
public class MMVectorLabelsAttributeDrawer : PropertyDrawer
|
||||
{
|
||||
protected static readonly GUIContent[] originalLabels = new GUIContent[] { new GUIContent("X"), new GUIContent("Y"), new GUIContent("Z"), new GUIContent("W") };
|
||||
protected const int padding = 375;
|
||||
|
||||
public override float GetPropertyHeight(SerializedProperty property, GUIContent guiContent)
|
||||
{
|
||||
int ratio = (padding > Screen.width) ? 2 : 1;
|
||||
return ratio * base.GetPropertyHeight(property, guiContent);
|
||||
}
|
||||
|
||||
public override void OnGUI(Rect rect, SerializedProperty property, GUIContent guiContent)
|
||||
{
|
||||
MMFVectorAttribute vector = (MMFVectorAttribute)attribute;
|
||||
|
||||
if (property.propertyType == SerializedPropertyType.Vector2)
|
||||
{
|
||||
float[] fieldArray = new float[] { property.vector2Value.x, property.vector2Value.y };
|
||||
fieldArray = DrawFields(rect, fieldArray, ObjectNames.NicifyVariableName(property.name), EditorGUI.FloatField, vector, guiContent);
|
||||
property.vector2Value = new Vector2(fieldArray[0], fieldArray[1]);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.Vector3)
|
||||
{
|
||||
float[] fieldArray = new float[] { property.vector3Value.x, property.vector3Value.y, property.vector3Value.z };
|
||||
fieldArray = DrawFields(rect, fieldArray, ObjectNames.NicifyVariableName(property.name), EditorGUI.FloatField, vector, guiContent);
|
||||
property.vector3Value = new Vector3(fieldArray[0], fieldArray[1], fieldArray[2]);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.Vector4)
|
||||
{
|
||||
float[] fieldArray = new float[] { property.vector4Value.x, property.vector4Value.y, property.vector4Value.z, property.vector4Value.w };
|
||||
fieldArray = DrawFields(rect, fieldArray, ObjectNames.NicifyVariableName(property.name), EditorGUI.FloatField, vector, guiContent);
|
||||
property.vector4Value = new Vector4(fieldArray[0], fieldArray[1], fieldArray[2]);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.Vector2Int)
|
||||
{
|
||||
int[] fieldArray = new int[] { property.vector2IntValue.x, property.vector2IntValue.y };
|
||||
fieldArray = DrawFields(rect, fieldArray, ObjectNames.NicifyVariableName(property.name), EditorGUI.IntField, vector, guiContent);
|
||||
property.vector2IntValue = new Vector2Int(fieldArray[0], fieldArray[1]);
|
||||
}
|
||||
else if (property.propertyType == SerializedPropertyType.Vector3Int)
|
||||
{
|
||||
int[] array = new int[] { property.vector3IntValue.x, property.vector3IntValue.y, property.vector3IntValue.z };
|
||||
array = DrawFields(rect, array, ObjectNames.NicifyVariableName(property.name), EditorGUI.IntField, vector, guiContent);
|
||||
property.vector3IntValue = new Vector3Int(array[0], array[1], array[2]);
|
||||
}
|
||||
}
|
||||
|
||||
protected T[] DrawFields<T>(Rect rect, T[] vector, string mainLabel, System.Func<Rect, GUIContent, T, T> fieldDrawer, MMFVectorAttribute vectors, GUIContent originalGuiContent)
|
||||
{
|
||||
T[] result = vector;
|
||||
|
||||
bool shortSpace = (Screen.width < padding);
|
||||
|
||||
Rect mainLabelRect = rect;
|
||||
mainLabelRect.width = EditorGUIUtility.labelWidth;
|
||||
if (shortSpace)
|
||||
{
|
||||
mainLabelRect.height *= 0.5f;
|
||||
}
|
||||
|
||||
Rect fieldRect = rect;
|
||||
if (shortSpace)
|
||||
{
|
||||
fieldRect.height *= 0.5f;
|
||||
fieldRect.y += fieldRect.height;
|
||||
fieldRect.width = rect.width / vector.Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldRect.x += mainLabelRect.width;
|
||||
fieldRect.width = (rect.width - mainLabelRect.width) / vector.Length;
|
||||
}
|
||||
|
||||
GUIContent mainLabelContent = new GUIContent();
|
||||
mainLabelContent.text = mainLabel;
|
||||
mainLabelContent.tooltip = originalGuiContent.tooltip;
|
||||
EditorGUI.LabelField(mainLabelRect, mainLabelContent);
|
||||
|
||||
for (int i = 0; i < vector.Length; i++)
|
||||
{
|
||||
GUIContent label = vectors.Labels.Length > i ? new GUIContent(vectors.Labels[i]) : originalLabels[i];
|
||||
Vector2 labelSize = EditorStyles.label.CalcSize(label);
|
||||
EditorGUIUtility.labelWidth = Mathf.Max(labelSize.x + 5, 0.3f * fieldRect.width);
|
||||
result[i] = fieldDrawer(fieldRect, label, vector[i]);
|
||||
fieldRect.x += fieldRect.width;
|
||||
}
|
||||
|
||||
EditorGUIUtility.labelWidth = 0;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public class MMFHiddenPropertiesAttribute : Attribute
|
||||
{
|
||||
public string[] PropertiesNames;
|
||||
|
||||
public MMFHiddenPropertiesAttribute(params string[] propertiesNames)
|
||||
{
|
||||
PropertiesNames = propertiesNames;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An attribute used to group inspector fields under common dropdowns
|
||||
/// Implementation inspired by Rodrigo Prinheiro's work, available at https://github.com/RodrigoPrinheiro/unityFoldoutAttribute
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct, Inherited = true)]
|
||||
public class MMFInspectorGroupAttribute : PropertyAttribute
|
||||
{
|
||||
public string GroupName;
|
||||
public bool GroupAllFieldsUntilNextGroupAttribute;
|
||||
public int GroupColorIndex;
|
||||
public bool RequiresSetup;
|
||||
public bool ClosedByDefault;
|
||||
|
||||
public MMFInspectorGroupAttribute(string groupName, bool groupAllFieldsUntilNextGroupAttribute = false, int groupColorIndex = 24, bool requiresSetup = false, bool closedByDefault = false)
|
||||
{
|
||||
if (groupColorIndex > 139) { groupColorIndex = 139; }
|
||||
|
||||
this.GroupName = groupName;
|
||||
this.GroupAllFieldsUntilNextGroupAttribute = groupAllFieldsUntilNextGroupAttribute;
|
||||
this.GroupColorIndex = groupColorIndex;
|
||||
this.RequiresSetup = requiresSetup;
|
||||
this.ClosedByDefault = closedByDefault;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
|
||||
public class TmpAttribute : PropertyAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>The header text.</para>
|
||||
/// </summary>
|
||||
/// <footer><a href="https://docs.unity3d.com/2019.4/Documentation/ScriptReference/30_search.html?q=HeaderAttribute.header">`HeaderAttribute.header` on docs.unity3d.com</a></footer>
|
||||
public readonly string header;
|
||||
|
||||
/// <summary>
|
||||
/// <para>Add a header above some fields in the Inspector.</para>
|
||||
/// </summary>
|
||||
/// <param name="header">The header text.</param>
|
||||
/// <footer><a href="https://docs.unity3d.com/2019.4/Documentation/ScriptReference/30_search.html?q=HeaderAttribute">`HeaderAttribute` on docs.unity3d.com</a></footer>
|
||||
public TmpAttribute(string header) => this.header = header;
|
||||
}
|
||||
|
||||
public static class MMFeedbackStaticMethods
|
||||
{
|
||||
static List<Component> m_ComponentCache = new List<Component>();
|
||||
|
||||
/// <summary>
|
||||
/// Grabs a component without allocating memory uselessly
|
||||
/// </summary>
|
||||
/// <param name="this"></param>
|
||||
/// <param name="componentType"></param>
|
||||
/// <returns></returns>
|
||||
public static Component GetComponentNoAlloc(this GameObject @this, System.Type componentType)
|
||||
{
|
||||
@this.GetComponents(componentType, m_ComponentCache);
|
||||
var component = m_ComponentCache.Count > 0 ? m_ComponentCache[0] : null;
|
||||
m_ComponentCache.Clear();
|
||||
return component;
|
||||
}
|
||||
|
||||
public static Type MMFGetTypeByName(string name)
|
||||
{
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (Type type in assembly.GetTypes())
|
||||
{
|
||||
if (type.Name == name)
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Grabs a component without allocating memory uselessly
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="this"></param>
|
||||
/// <returns></returns>
|
||||
public static T MMFGetComponentNoAlloc<T>(this GameObject @this) where T : Component
|
||||
{
|
||||
@this.GetComponents(typeof(T), m_ComponentCache);
|
||||
Component component = m_ComponentCache.Count > 0 ? m_ComponentCache[0] : null;
|
||||
m_ComponentCache.Clear();
|
||||
return component as T;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Returns the object value of a target serialized property
|
||||
/// </summary>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
public static object MMFGetObjectValue(this SerializedProperty property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
string propertyPath = property.propertyPath.Replace(".Array.data[", "[");
|
||||
object targetObject = property.serializedObject.targetObject;
|
||||
var elements = propertyPath.Split('.');
|
||||
foreach (var element in elements)
|
||||
{
|
||||
if (!element.Contains("["))
|
||||
{
|
||||
targetObject = MMFGetPropertyValue(targetObject, element);
|
||||
}
|
||||
else
|
||||
{
|
||||
string elementName = element.Substring(0, element.IndexOf("["));
|
||||
int elementIndex = System.Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
|
||||
targetObject = MMFGetPropertyValue(targetObject, elementName, elementIndex);
|
||||
}
|
||||
}
|
||||
return targetObject;
|
||||
}
|
||||
|
||||
private static object MMFGetPropertyValue(object source, string propertyName)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Type propertyType = source.GetType();
|
||||
|
||||
while (propertyType != null)
|
||||
{
|
||||
FieldInfo fieldInfo = propertyType.GetField(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
if (fieldInfo != null)
|
||||
{
|
||||
return fieldInfo.GetValue(source);
|
||||
}
|
||||
PropertyInfo propertyInfo = propertyType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.Instance);
|
||||
if (propertyInfo != null)
|
||||
{
|
||||
return propertyInfo.GetValue(source, null);
|
||||
}
|
||||
propertyType = propertyType.BaseType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static object MMFGetPropertyValue(object source, string propertyName, int index)
|
||||
{
|
||||
var enumerable = MMFGetPropertyValue(source, propertyName) as System.Collections.IEnumerable;
|
||||
if (enumerable == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var enumerator = enumerable.GetEnumerator();
|
||||
for (int i = 0; i <= index; i++)
|
||||
{
|
||||
if (!enumerator.MoveNext())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return enumerator.Current;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Atttribute used to mark feedback class.
|
||||
/// The provided path is used to sort the feedback list displayed in the feedback manager dropdown
|
||||
/// </summary>
|
||||
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public class FeedbackPathAttribute : System.Attribute
|
||||
{
|
||||
public string Path;
|
||||
public string Name;
|
||||
|
||||
public FeedbackPathAttribute(string path)
|
||||
{
|
||||
Path = path;
|
||||
Name = path.Split('/').Last();
|
||||
}
|
||||
|
||||
static public string GetFeedbackDefaultName(System.Type type)
|
||||
{
|
||||
FeedbackPathAttribute attribute = type.GetCustomAttributes(false).OfType<FeedbackPathAttribute>().FirstOrDefault();
|
||||
return attribute != null ? attribute.Name : type.Name;
|
||||
}
|
||||
|
||||
static public string GetFeedbackDefaultPath(System.Type type)
|
||||
{
|
||||
FeedbackPathAttribute attribute = type.GetCustomAttributes(false).OfType<FeedbackPathAttribute>().FirstOrDefault();
|
||||
return attribute != null ? attribute.Path : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Atttribute used to mark feedback class.
|
||||
/// The contents allow you to specify a help text for each feedback
|
||||
/// </summary>
|
||||
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public class FeedbackHelpAttribute : System.Attribute
|
||||
{
|
||||
public string HelpText;
|
||||
|
||||
public FeedbackHelpAttribute(string helpText)
|
||||
{
|
||||
HelpText = helpText;
|
||||
}
|
||||
|
||||
static public string GetFeedbackHelpText(System.Type type)
|
||||
{
|
||||
FeedbackHelpAttribute attribute = type.GetCustomAttributes(false).OfType<FeedbackHelpAttribute>().FirstOrDefault();
|
||||
return attribute != null ? attribute.HelpText : "";
|
||||
}
|
||||
}
|
||||
|
||||
public static class MMF_FieldInfo
|
||||
{
|
||||
public static Dictionary<int, List<FieldInfo>> FieldInfoList = new Dictionary<int, List<FieldInfo>>();
|
||||
|
||||
|
||||
public static int GetFieldInfo(MMF_Feedback target, out List<FieldInfo> fieldInfoList)
|
||||
{
|
||||
Type targetType = target.GetType();
|
||||
int targetTypeHashCode = targetType.GetHashCode();
|
||||
|
||||
if (!FieldInfoList.TryGetValue(targetTypeHashCode, out fieldInfoList))
|
||||
{
|
||||
IList<Type> typeTree = targetType.GetBaseTypes();
|
||||
fieldInfoList = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic)
|
||||
.OrderByDescending(x => typeTree.IndexOf(x.DeclaringType))
|
||||
.ToList();
|
||||
FieldInfoList.Add(targetTypeHashCode, fieldInfoList);
|
||||
}
|
||||
|
||||
return fieldInfoList.Count;
|
||||
}
|
||||
|
||||
public static int GetFieldInfo(UnityEngine.Object target, out List<FieldInfo> fieldInfoList)
|
||||
{
|
||||
Type targetType = target.GetType();
|
||||
int targetTypeHashCode = targetType.GetHashCode();
|
||||
|
||||
if (!FieldInfoList.TryGetValue(targetTypeHashCode, out fieldInfoList))
|
||||
{
|
||||
IList<Type> typeTree = targetType.GetBaseTypes();
|
||||
fieldInfoList = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic)
|
||||
.OrderByDescending(x => typeTree.IndexOf(x.DeclaringType))
|
||||
.ToList();
|
||||
FieldInfoList.Add(targetTypeHashCode, fieldInfoList);
|
||||
}
|
||||
|
||||
return fieldInfoList.Count;
|
||||
}
|
||||
|
||||
public static IList<Type> GetBaseTypes(this Type t)
|
||||
{
|
||||
var types = new List<Type>();
|
||||
while (t.BaseType != null)
|
||||
{
|
||||
types.Add(t);
|
||||
t = t.BaseType;
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0542c719094b3924cbee2c7229b1f4eb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,34 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using MoreMountains.Feedbacks;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
public class MMFeedbacksInspectorColors : MonoBehaviour
|
||||
{
|
||||
public static Color32 GameObjectColor = new Color32(76, 174, 80, 255);
|
||||
public static Color32 PostProcessColor = new Color32(254, 234, 59, 255);
|
||||
public static Color32 RendererColor = new Color32(254, 151, 0, 255);
|
||||
public static Color32 TransformColor = new Color32(134, 209, 243, 255);
|
||||
public static Color32 CameraColor = new Color32(237, 0, 0, 255);
|
||||
public static Color32 SoundsColor = new Color32(155, 39, 175, 255);
|
||||
public static Color32 EventsColor = new Color32(232, 30, 99, 255);
|
||||
public static Color32 SceneColor = new Color32(232, 30, 99, 255);
|
||||
public static Color32 TimeColor = new Color32(240, 172, 172, 255);
|
||||
public static Color32 LightColor = new Color32(254, 192, 7, 255);
|
||||
public static Color32 ParticlesColor = new Color32(0, 149, 135, 255);
|
||||
public static Color32 UIColor = new Color32(225, 2, 65, 255);
|
||||
public static Color32 TMPColor = new Color32(135, 206, 250, 255);
|
||||
public static Color32 HapticsColor = new Color32(61, 206, 250, 255);
|
||||
public static Color32 FeedbacksColor = new Color32(105, 32, 133, 255);
|
||||
public static Color32 AnimationColor = new Color32(200, 48, 128, 255);
|
||||
public static Color32 SpringColor = new Color32(221, 230, 128, 255);
|
||||
|
||||
public static Color32 PauseColor = new Color32(98, 115, 0, 255);
|
||||
public static Color32 HoldingPauseColor = new Color32(0, 114, 61, 255);
|
||||
public static Color32 LooperColor = new Color32(12, 100, 128, 255);
|
||||
public static Color32 DebugColor = new Color32(255, 0, 0, 255);
|
||||
public static Color32 LooperStartColor = new Color32(166, 75, 5, 255);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97e6ede6fceb23f40805fef166a6f181
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
365
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMShaker.cs
Normal file
365
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMShaker.cs
Normal file
@@ -0,0 +1,365 @@
|
||||
using System;
|
||||
using MoreMountains.Tools;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
public class MMShaker : MMMonoBehaviour
|
||||
{
|
||||
[MMInspectorGroup("Shaker Settings", true, 3)]
|
||||
/// whether to listen on a channel defined by an int or by a MMChannel scriptable object. Ints are simple to setup but can get messy and make it harder to remember what int corresponds to what.
|
||||
/// MMChannel scriptable objects require you to create them in advance, but come with a readable name and are more scalable
|
||||
[Tooltip("whether to listen on a channel defined by an int or by a MMChannel scriptable object. Ints are simple to setup but can get messy and make it harder to remember what int corresponds to what. " +
|
||||
"MMChannel scriptable objects require you to create them in advance, but come with a readable name and are more scalable")]
|
||||
public MMChannelModes ChannelMode = MMChannelModes.Int;
|
||||
/// the channel to listen to - has to match the one on the feedback
|
||||
[Tooltip("the channel to listen to - has to match the one on the feedback")]
|
||||
[MMEnumCondition("ChannelMode", (int)MMChannelModes.Int)]
|
||||
public int Channel = 0;
|
||||
/// the MMChannel definition asset to use to listen for events. The feedbacks targeting this shaker will have to reference that same MMChannel definition to receive events - to create a MMChannel,
|
||||
/// right click anywhere in your project (usually in a Data folder) and go MoreMountains > MMChannel, then name it with some unique name
|
||||
[Tooltip("the MMChannel definition asset to use to listen for events. The feedbacks targeting this shaker will have to reference that same MMChannel definition to receive events - to create a MMChannel, " +
|
||||
"right click anywhere in your project (usually in a Data folder) and go MoreMountains > MMChannel, then name it with some unique name")]
|
||||
[MMEnumCondition("ChannelMode", (int)MMChannelModes.MMChannel)]
|
||||
public MMChannel MMChannelDefinition = null;
|
||||
/// the duration of the shake, in seconds
|
||||
[Tooltip("the duration of the shake, in seconds")]
|
||||
public float ShakeDuration = 0.2f;
|
||||
/// if this is true this shaker will play on awake
|
||||
[Tooltip("if this is true this shaker will play on awake")]
|
||||
public bool PlayOnAwake = false;
|
||||
/// if this is true, the shaker will shake permanently as long as its game object is active
|
||||
[Tooltip("if this is true, the shaker will shake permanently as long as its game object is active")]
|
||||
public bool PermanentShake = false;
|
||||
/// if this is true, a new shake can happen while shaking
|
||||
[Tooltip("if this is true, a new shake can happen while shaking")]
|
||||
public bool Interruptible = true;
|
||||
/// if this is true, this shaker will always reset target values, regardless of how it was called
|
||||
[Tooltip("if this is true, this shaker will always reset target values, regardless of how it was called")]
|
||||
public bool AlwaysResetTargetValuesAfterShake = false;
|
||||
/// if this is true, this shaker will ignore any value passed in an event that triggered it, and will instead use the values set on its inspector
|
||||
[Tooltip("if this is true, this shaker will ignore any value passed in an event that triggered it, and will instead use the values set on its inspector")]
|
||||
public bool OnlyUseShakerValues = false;
|
||||
/// a cooldown, in seconds, after a shake, during which no other shake can start
|
||||
[Tooltip("a cooldown, in seconds, after a shake, during which no other shake can start")]
|
||||
public float CooldownBetweenShakes = 0f;
|
||||
/// whether or not this shaker is shaking right now
|
||||
[Tooltip("whether or not this shaker is shaking right now")]
|
||||
[MMFReadOnly]
|
||||
public bool Shaking = false;
|
||||
|
||||
[HideInInspector]
|
||||
public bool ForwardDirection = true;
|
||||
|
||||
[HideInInspector]
|
||||
public TimescaleModes TimescaleMode = TimescaleModes.Scaled;
|
||||
|
||||
public virtual float GetTime() { return (TimescaleMode == TimescaleModes.Scaled) ? Time.time : Time.unscaledTime; }
|
||||
public virtual float GetDeltaTime() { return (TimescaleMode == TimescaleModes.Scaled) ? Time.deltaTime : Time.unscaledDeltaTime; }
|
||||
public virtual MMChannelData ChannelData => new MMChannelData(ChannelMode, Channel, MMChannelDefinition);
|
||||
|
||||
public virtual bool ListeningToEvents => _listeningToEvents;
|
||||
|
||||
[HideInInspector]
|
||||
internal bool _listeningToEvents = false;
|
||||
protected float _shakeStartedTimestamp = -Single.MaxValue;
|
||||
protected float _remappedTimeSinceStart;
|
||||
protected bool _resetShakerValuesAfterShake;
|
||||
protected bool _resetTargetValuesAfterShake;
|
||||
protected float _journey;
|
||||
|
||||
/// <summary>
|
||||
/// On Awake we grab our volume and profile
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
Initialization();
|
||||
// in case someone else trigger StartListening before Awake
|
||||
if (!_listeningToEvents)
|
||||
{
|
||||
StartListening();
|
||||
}
|
||||
Shaking = PlayOnAwake;
|
||||
this.enabled = PlayOnAwake;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to initialize your shaker
|
||||
/// </summary>
|
||||
protected virtual void Initialization()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this externally if you need to force a new initialization
|
||||
/// </summary>
|
||||
public virtual void ForceInitialization()
|
||||
{
|
||||
Initialization();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts shaking the values
|
||||
/// </summary>
|
||||
public virtual void StartShaking()
|
||||
{
|
||||
_journey = ForwardDirection ? 0f : ShakeDuration;
|
||||
|
||||
if (GetTime() - _shakeStartedTimestamp < CooldownBetweenShakes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Shaking)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.enabled = true;
|
||||
_shakeStartedTimestamp = GetTime();
|
||||
Shaking = true;
|
||||
GrabInitialValues();
|
||||
ShakeStarts();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes what happens when a shake starts
|
||||
/// </summary>
|
||||
protected virtual void ShakeStarts()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method designed to collect initial values
|
||||
/// </summary>
|
||||
protected virtual void GrabInitialValues()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Update, we shake our values if needed, or reset if our shake has ended
|
||||
/// </summary>
|
||||
protected virtual void Update()
|
||||
{
|
||||
if (Shaking || PermanentShake)
|
||||
{
|
||||
Shake();
|
||||
_journey += ForwardDirection ? GetDeltaTime() : -GetDeltaTime();
|
||||
}
|
||||
|
||||
if (Shaking && !PermanentShake && ((_journey < 0) || (_journey > ShakeDuration)))
|
||||
{
|
||||
Shaking = false;
|
||||
ShakeComplete();
|
||||
}
|
||||
|
||||
if (PermanentShake)
|
||||
{
|
||||
if (_journey < 0)
|
||||
{
|
||||
_journey = ShakeDuration;
|
||||
}
|
||||
|
||||
if (_journey > ShakeDuration)
|
||||
{
|
||||
_journey = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to implement shake over time
|
||||
/// </summary>
|
||||
protected virtual void Shake()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A method used to "shake" a flot over time along a curve
|
||||
/// </summary>
|
||||
/// <param name="curve"></param>
|
||||
/// <param name="remapMin"></param>
|
||||
/// <param name="remapMax"></param>
|
||||
/// <param name="relativeIntensity"></param>
|
||||
/// <param name="initialValue"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual float ShakeFloat(AnimationCurve curve, float remapMin, float remapMax, bool relativeIntensity, float initialValue)
|
||||
{
|
||||
float newValue = 0f;
|
||||
|
||||
float remappedTime = MMFeedbacksHelpers.Remap(_journey, 0f, ShakeDuration, 0f, 1f);
|
||||
|
||||
float curveValue = curve.Evaluate(remappedTime);
|
||||
newValue = MMFeedbacksHelpers.Remap(curveValue, 0f, 1f, remapMin, remapMax);
|
||||
if (relativeIntensity)
|
||||
{
|
||||
newValue += initialValue;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
protected virtual Color ShakeGradient(Gradient gradient)
|
||||
{
|
||||
float remappedTime = MMFeedbacksHelpers.Remap(_journey, 0f, ShakeDuration, 0f, 1f);
|
||||
return gradient.Evaluate(remappedTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the values on the target
|
||||
/// </summary>
|
||||
protected virtual void ResetTargetValues()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the values on the shaker
|
||||
/// </summary>
|
||||
protected virtual void ResetShakerValues()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes what happens when the shake is complete
|
||||
/// </summary>
|
||||
protected virtual void ShakeComplete()
|
||||
{
|
||||
_journey = ForwardDirection ? ShakeDuration : 0f;
|
||||
Shake();
|
||||
|
||||
if (_resetTargetValuesAfterShake || AlwaysResetTargetValuesAfterShake)
|
||||
{
|
||||
ResetTargetValues();
|
||||
}
|
||||
if (_resetShakerValuesAfterShake)
|
||||
{
|
||||
ResetShakerValues();
|
||||
}
|
||||
this.enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On enable we start shaking if needed
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
StartShaking();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On destroy we stop listening for events
|
||||
/// </summary>
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
StopListening();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On disable we complete our shake if it was in progress
|
||||
/// </summary>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (Shaking)
|
||||
{
|
||||
ShakeComplete();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts this shaker
|
||||
/// </summary>
|
||||
public virtual void Play()
|
||||
{
|
||||
if (GetTime() - _shakeStartedTimestamp < CooldownBetweenShakes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops this shaker
|
||||
/// </summary>
|
||||
public virtual void Stop()
|
||||
{
|
||||
Shaking = false;
|
||||
ShakeComplete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts listening for events
|
||||
/// </summary>
|
||||
public virtual void StartListening()
|
||||
{
|
||||
_listeningToEvents = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops listening for events
|
||||
/// </summary>
|
||||
public virtual void StopListening()
|
||||
{
|
||||
_listeningToEvents = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this shaker should listen to events, false otherwise
|
||||
/// </summary>
|
||||
/// <param name="channel"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool CheckEventAllowed(MMChannelData channelData, bool useRange = false, float range = 0f, Vector3 eventOriginPosition = default(Vector3))
|
||||
{
|
||||
if (!MMChannel.Match(channelData, ChannelMode, Channel, MMChannelDefinition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!this.gameObject.activeInHierarchy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useRange)
|
||||
{
|
||||
if (Vector3.Distance(this.transform.position, eventOriginPosition) > range)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float ComputeRangeIntensity(bool useRange, float rangeDistance, bool useRangeFalloff, AnimationCurve rangeFalloff, Vector2 remapRangeFalloff, Vector3 rangePosition)
|
||||
{
|
||||
if (!useRange)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
float distanceToCenter = Vector3.Distance(rangePosition, this.transform.position);
|
||||
|
||||
if (distanceToCenter > rangeDistance)
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
if (!useRangeFalloff)
|
||||
{
|
||||
return 1f;
|
||||
}
|
||||
|
||||
float normalizedDistance = MMMaths.Remap(distanceToCenter, 0f, rangeDistance, 0f, 1f);
|
||||
float curveValue = rangeFalloff.Evaluate(normalizedDistance);
|
||||
float newIntensity = MMMaths.Remap(curveValue, 0f, 1f, remapRangeFalloff.x, remapRangeFalloff.y);
|
||||
return newIntensity;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMShaker.cs.meta
Normal file
11
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/MMShaker.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25d31bbce27b6524192fba29ccfe28df
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/ObjectPool.meta
Normal file
8
Assets/Feel/MMFeedbacks/MMFeedbacks/Core/ObjectPool.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dea619c1b9acaab45aaea2653a61c4af
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,236 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
public class MMMiniObjectPooler : MonoBehaviour
|
||||
{
|
||||
/// the game object we'll instantiate
|
||||
public GameObject GameObjectToPool;
|
||||
/// the number of objects we'll add to the pool
|
||||
public int PoolSize = 20;
|
||||
/// if true, the pool will automatically add objects to the itself if needed
|
||||
public bool PoolCanExpand = true;
|
||||
/// if this is true, the pool will try not to create a new waiting pool if it finds one with the same name.
|
||||
public bool MutualizeWaitingPools = false;
|
||||
/// if this is true, all waiting and active objects will be regrouped under an empty game object. Otherwise they'll just be at top level in the hierarchy
|
||||
public bool NestWaitingPool = true;
|
||||
|
||||
/// this object is just used to group the pooled objects
|
||||
protected GameObject _waitingPool = null;
|
||||
protected MMMiniObjectPool _objectPool;
|
||||
protected const int _initialPoolsListCapacity = 5;
|
||||
|
||||
static List<MMMiniObjectPool> _pools = new List<MMMiniObjectPool>(_initialPoolsListCapacity);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a pooler to the static list if needed
|
||||
/// </summary>
|
||||
/// <param name="pool"></param>
|
||||
public static void AddPool(MMMiniObjectPool pool)
|
||||
{
|
||||
if (_pools == null)
|
||||
{
|
||||
_pools = new List<MMMiniObjectPool>(_initialPoolsListCapacity);
|
||||
}
|
||||
if (!_pools.Contains(pool))
|
||||
{
|
||||
_pools.Add(pool);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a pooler from the static list
|
||||
/// </summary>
|
||||
/// <param name="pool"></param>
|
||||
public static void RemovePool(MMMiniObjectPool pool)
|
||||
{
|
||||
_pools?.Remove(pool);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On awake we fill our object pool
|
||||
/// </summary>
|
||||
protected virtual void Awake()
|
||||
{
|
||||
FillObjectPool();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// On Destroy we remove ourselves from the list of poolers
|
||||
/// </summary>
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (_objectPool != null)
|
||||
{
|
||||
RemovePool(_objectPool);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks for an existing pooler for the same object, returns it if found, returns null otherwise
|
||||
/// </summary>
|
||||
/// <param name="objectToPool"></param>
|
||||
/// <returns></returns>
|
||||
public virtual MMMiniObjectPool ExistingPool(string poolName)
|
||||
{
|
||||
if (_pools == null)
|
||||
{
|
||||
_pools = new List<MMMiniObjectPool>(_initialPoolsListCapacity);
|
||||
}
|
||||
|
||||
if (_pools.Count == 0)
|
||||
{
|
||||
var pools = FindObjectsOfType<MMMiniObjectPool>();
|
||||
if (pools.Length > 0)
|
||||
{
|
||||
_pools.AddRange(pools);
|
||||
}
|
||||
}
|
||||
foreach (MMMiniObjectPool pool in _pools)
|
||||
{
|
||||
if ((pool != null) && (pool.name == poolName)/* && (pool.gameObject.scene == this.gameObject.scene)*/)
|
||||
{
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the waiting pool or tries to reuse one if there's already one available
|
||||
/// </summary>
|
||||
protected virtual void CreateWaitingPool()
|
||||
{
|
||||
if (!MutualizeWaitingPools)
|
||||
{
|
||||
// we create a container that will hold all the instances we create
|
||||
_objectPool = this.gameObject.AddComponent<MMMiniObjectPool>();
|
||||
_objectPool.PooledGameObjects = new List<GameObject>();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
MMMiniObjectPool waitingPool = ExistingPool(DetermineObjectPoolName(GameObjectToPool));
|
||||
|
||||
if (waitingPool != null)
|
||||
{
|
||||
_waitingPool = waitingPool.gameObject;
|
||||
_objectPool = waitingPool;
|
||||
}
|
||||
else
|
||||
{
|
||||
GameObject newPool = new GameObject();
|
||||
newPool.name = DetermineObjectPoolName(GameObjectToPool);
|
||||
SceneManager.MoveGameObjectToScene(newPool, this.gameObject.scene);
|
||||
_objectPool = newPool.AddComponent<MMMiniObjectPool>();
|
||||
_objectPool.PooledGameObjects = new List<GameObject>();
|
||||
AddPool(_objectPool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the name of the object pool.
|
||||
/// </summary>
|
||||
/// <returns>The object pool name.</returns>
|
||||
public static string DetermineObjectPoolName(GameObject gameObjectToPool)
|
||||
{
|
||||
return (gameObjectToPool.name + "_pool");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement this method to fill the pool with objects
|
||||
/// </summary>
|
||||
public virtual void FillObjectPool()
|
||||
{
|
||||
if (GameObjectToPool == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CreateWaitingPool();
|
||||
|
||||
int objectsToSpawn = PoolSize;
|
||||
|
||||
if (_objectPool != null)
|
||||
{
|
||||
objectsToSpawn -= _objectPool.PooledGameObjects.Count;
|
||||
}
|
||||
|
||||
// we add to the pool the specified number of objects
|
||||
for (int i = 0; i < objectsToSpawn; i++)
|
||||
{
|
||||
AddOneObjectToThePool();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implement this method to return a gameobject
|
||||
/// </summary>
|
||||
/// <returns>The pooled game object.</returns>
|
||||
public virtual GameObject GetPooledGameObject()
|
||||
{
|
||||
// we go through the pool looking for an inactive object
|
||||
for (int i = 0; i < _objectPool.PooledGameObjects.Count; i++)
|
||||
{
|
||||
if (!_objectPool.PooledGameObjects[i].gameObject.activeInHierarchy)
|
||||
{
|
||||
// if we find one, we return it
|
||||
return _objectPool.PooledGameObjects[i];
|
||||
}
|
||||
}
|
||||
// if we haven't found an inactive object (the pool is empty), and if we can extend it, we add one new object to the pool, and return it
|
||||
if (PoolCanExpand)
|
||||
{
|
||||
return AddOneObjectToThePool();
|
||||
}
|
||||
// if the pool is empty and can't grow, we return nothing.
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds one object of the specified type (in the inspector) to the pool.
|
||||
/// </summary>
|
||||
/// <returns>The one object to the pool.</returns>
|
||||
protected virtual GameObject AddOneObjectToThePool()
|
||||
{
|
||||
if (GameObjectToPool == null)
|
||||
{
|
||||
Debug.LogWarning("The " + gameObject.name + " ObjectPooler doesn't have any GameObjectToPool defined.", gameObject);
|
||||
return null;
|
||||
}
|
||||
GameObjectToPool.gameObject.SetActive(false);
|
||||
GameObject newGameObject = (GameObject)Instantiate(GameObjectToPool);
|
||||
SceneManager.MoveGameObjectToScene(newGameObject, this.gameObject.scene);
|
||||
if (NestWaitingPool)
|
||||
{
|
||||
newGameObject.transform.SetParent(_objectPool.transform);
|
||||
}
|
||||
newGameObject.name = GameObjectToPool.name + "-" + _objectPool.PooledGameObjects.Count;
|
||||
|
||||
_objectPool.PooledGameObjects.Add(newGameObject);
|
||||
|
||||
return newGameObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the object pool
|
||||
/// </summary>
|
||||
public virtual void DestroyObjectPool()
|
||||
{
|
||||
if (_waitingPool != null)
|
||||
{
|
||||
Destroy(_waitingPool.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MMMiniObjectPool : MonoBehaviour
|
||||
{
|
||||
[MMFReadOnly]
|
||||
public List<GameObject> PooledGameObjects;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5a2e3aab8bd306249afbd26c52205f46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using MoreMountains.Feedbacks;
|
||||
using System;
|
||||
|
||||
namespace MoreMountains.Feedbacks
|
||||
{
|
||||
/// <summary>
|
||||
/// Add this class to an object that you expect to pool from an objectPooler.
|
||||
/// Note that these objects can't be destroyed by calling Destroy(), they'll just be set inactive (that's the whole point).
|
||||
/// </summary>
|
||||
public class MMMiniPoolableObject : MonoBehaviour
|
||||
{
|
||||
public delegate void Events();
|
||||
public event Events OnSpawnComplete;
|
||||
|
||||
/// The life time, in seconds, of the object. If set to 0 it'll live forever, if set to any positive value it'll be set inactive after that time.
|
||||
public float LifeTime = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// Turns the instance inactive, in order to eventually reuse it.
|
||||
/// </summary>
|
||||
public virtual void Destroy()
|
||||
{
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the objects get enabled (usually after having been pooled from an ObjectPooler, we initiate its death countdown.
|
||||
/// </summary>
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (LifeTime > 0)
|
||||
{
|
||||
Invoke("Destroy", LifeTime);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When the object gets disabled (maybe it got out of bounds), we cancel its programmed death
|
||||
/// </summary>
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
CancelInvoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers the on spawn complete event
|
||||
/// </summary>
|
||||
public virtual void TriggerOnSpawnComplete()
|
||||
{
|
||||
if(OnSpawnComplete != null)
|
||||
{
|
||||
OnSpawnComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0b3f00a75cc857439b13b8bafb37894
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user