2019-04-16 16:32:17 -04:00
using System ;
2020-02-16 06:44:46 -05:00
using System.Collections.Generic ;
2018-10-30 15:05:06 -04:00
using UnityEngine ;
namespace UnityAtoms
{
2019-10-15 16:21:56 -04:00
/// <summary>
/// Generic base class for Variables. Inherits from `AtomBaseVariable<T>`.
/// </summary>
/// <typeparam name="T">The Variable value type.</typeparam>
2020-03-01 15:32:52 -05:00
/// <typeparam name="P">IPair of type `T`.</typeparam>
2019-10-15 16:21:56 -04:00
/// <typeparam name="E1">Event of type `AtomEvent<T>`.</typeparam>
/// <typeparam name="E2">Event of type `AtomEvent<T, T>`.</typeparam>
2020-02-16 06:44:46 -05:00
/// <typeparam name="F">Function of type `FunctionEvent<T, T>`.</typeparam>
2019-10-14 10:51:54 -04:00
[EditorIcon("atom-icon-lush")]
2020-03-01 15:32:52 -05:00
public abstract class AtomVariable < T , P , E1 , E2 , F > : AtomBaseVariable < T > , IGetEvent , ISetEvent
2020-03-01 20:26:06 -05:00
where P : struct , IPair < T >
2019-09-25 15:05:06 -04:00
where E1 : AtomEvent < T >
2020-03-01 15:32:52 -05:00
where E2 : AtomEvent < P >
2020-02-16 06:44:46 -05:00
where F : AtomFunction < T , T >
2018-10-30 15:05:06 -04:00
{
2019-10-15 16:21:56 -04:00
/// <summary>
/// The Variable value as a property.
/// </summary>
2019-10-20 17:21:16 -04:00
/// <returns>Get or set the Variable's value.</returns>
2020-03-08 07:32:41 -04:00
public override T Value { get = > _value ; set = > SetValue ( value ) ; }
2019-04-08 13:46:02 -04:00
2019-10-15 16:21:56 -04:00
/// <summary>
/// The inital value of the Variable.
/// </summary>
2019-04-08 13:46:02 -04:00
[SerializeField]
2019-10-03 15:34:57 -04:00
private T _initialValue = default ( T ) ;
2018-10-30 15:05:06 -04:00
2019-10-20 17:21:16 -04:00
/// <summary>
/// The inital Variable value as a property.
/// </summary>
/// <returns>Get the Variable's initial value.</returns>
2020-03-09 18:51:14 -04:00
public virtual T InitialValue { get = > _initialValue ; set = > _initialValue = value ; }
2019-10-20 17:21:16 -04:00
2019-10-15 16:21:56 -04:00
/// <summary>
/// The value the Variable had before its value got changed last time.
/// </summary>
2019-10-20 17:21:16 -04:00
/// <value>Get the Variable's old value.</value>
2020-03-08 07:32:41 -04:00
public T OldValue { get = > _oldValue ; }
2018-10-30 15:05:06 -04:00
[SerializeField]
2020-03-09 18:51:14 -04:00
protected T _oldValue ;
2018-10-30 15:05:06 -04:00
2019-10-15 16:21:56 -04:00
/// <summary>
/// Changed Event triggered when the Variable value gets changed.
/// </summary>
2018-10-30 15:05:06 -04:00
public E1 Changed ;
2019-10-15 16:21:56 -04:00
/// <summary>
/// Changed with history Event triggered when the Variable value gets changed.
/// </summary>
2018-10-30 15:05:06 -04:00
public E2 ChangedWithHistory ;
2020-02-16 06:44:46 -05:00
/// <summary>
/// When setting the value of a Variable the new value will be piped through all the pre change transformers, which allows you to create custom logic and restriction on for example what values can be set for this Variable.
/// </summary>
/// <value>Get the list of pre change transformers.</value>
public List < F > PreChangeTransformers
{
get = > _preChangeTransformers ;
set
{
if ( value = = null )
{
_preChangeTransformers . Clear ( ) ;
}
else
{
_preChangeTransformers = value ;
}
}
}
[SerializeField]
private List < F > _preChangeTransformers = new List < F > ( ) ;
2020-02-22 20:22:39 -05:00
protected abstract bool ValueEquals ( T other ) ;
2018-10-30 15:05:06 -04:00
2020-02-16 06:44:46 -05:00
private void OnValidate ( )
{
2020-03-09 18:51:14 -04:00
InitialValue = RunPreChangeTransformers ( InitialValue ) ;
2020-02-16 06:44:46 -05:00
_value = RunPreChangeTransformers ( _value ) ;
}
2019-03-17 18:43:20 -04:00
private void OnEnable ( )
{
2020-03-08 15:41:22 -04:00
_oldValue = InitialValue ;
_value = InitialValue ;
2019-11-14 06:59:11 -05:00
2019-03-17 18:43:20 -04:00
if ( Changed = = null ) return ;
Changed . Raise ( Value ) ;
}
2019-10-15 16:21:56 -04:00
/// <summary>
/// Reset the Variable to its `_initalValue`.
/// </summary>
/// <param name="shouldTriggerEvents">Set to `true` if Events should be triggered on reset, otherwise `false`.</param>
2020-03-11 16:11:27 -04:00
public override void Reset ( bool shouldTriggerEvents = false )
2019-09-29 18:26:53 -04:00
{
2019-09-30 16:55:33 -04:00
if ( ! shouldTriggerEvents )
{
_oldValue = _value ;
2020-03-08 15:41:22 -04:00
_value = InitialValue ;
2019-09-30 16:55:33 -04:00
}
else
{
2020-03-08 15:41:22 -04:00
SetValue ( InitialValue ) ;
2019-09-30 16:55:33 -04:00
}
2019-09-29 18:26:53 -04:00
}
2019-10-15 16:21:56 -04:00
/// <summary>
/// Set the Variable value.
/// </summary>
/// <param name="newValue">The new value to set.</param>
/// <returns>`true` if the value got changed, otherwise `false`.</returns>
2019-04-07 05:52:07 -04:00
public bool SetValue ( T newValue )
2018-10-30 15:05:06 -04:00
{
2020-02-16 06:44:46 -05:00
var preProcessedNewValue = RunPreChangeTransformers ( newValue ) ;
2020-02-22 20:22:39 -05:00
if ( ! ValueEquals ( preProcessedNewValue ) )
2018-10-30 15:05:06 -04:00
{
2019-04-08 13:46:02 -04:00
_oldValue = _value ;
2020-02-16 06:44:46 -05:00
_value = preProcessedNewValue ;
2019-04-08 13:46:02 -04:00
if ( Changed ! = null ) { Changed . Raise ( _value ) ; }
2020-03-01 20:26:06 -05:00
if ( ChangedWithHistory ! = null )
2020-03-01 15:32:52 -05:00
{
// NOTE: Doing new P() here, even though it is cleaner, generates garbage.
2020-03-01 20:26:06 -05:00
var pair = default ( P ) ;
pair . Item1 = _value ;
2020-03-01 15:32:52 -05:00
pair . Item2 = _oldValue ;
2020-03-01 20:26:06 -05:00
ChangedWithHistory . Raise ( pair ) ;
2020-03-01 15:32:52 -05:00
}
2019-04-05 07:40:26 -04:00
return true ;
2018-10-30 15:05:06 -04:00
}
2019-04-05 07:40:26 -04:00
return false ;
2018-10-30 15:05:06 -04:00
}
2019-10-15 16:21:56 -04:00
/// <summary>
/// Set the Variable value.
/// </summary>
/// <param name="variable">The value to set provided from another Variable.</param>
/// <returns>`true` if the value got changed, otherwise `false`.</returns>
2020-03-01 15:32:52 -05:00
public bool SetValue ( AtomVariable < T , P , E1 , E2 , F > variable )
2018-10-30 15:05:06 -04:00
{
return SetValue ( variable . Value ) ;
}
2019-04-08 13:46:02 -04:00
2019-04-16 16:32:17 -04:00
#region Observable
2019-10-15 16:21:56 -04:00
/// <summary>
/// Turn the Variable's change Event into an `IObservable<T>`. Makes the Variable's change Event compatible with for example UniRx.
/// </summary>
/// <returns>The Variable's change Event as an `IObservable<T>`.</returns>
2019-04-16 16:32:17 -04:00
public IObservable < T > ObserveChange ( )
{
if ( Changed = = null )
{
throw new Exception ( "You must assign a Changed event in order to observe variable changes." ) ;
}
return new ObservableEvent < T > ( Changed . Register , Changed . Unregister ) ;
}
2019-04-21 07:51:20 -04:00
2019-10-15 16:21:56 -04:00
/// <summary>
/// Turn the Variable's change with history Event into an `IObservable<T, T>`. Makes the Variable's change with history Event compatible with for example UniRx.
/// </summary>
/// <returns>The Variable's change Event as an `IObservable<T, T>`.</returns>
2020-03-01 15:32:52 -05:00
public IObservable < P > ObserveChangeWithHistory ( )
2019-04-21 07:51:20 -04:00
{
if ( ChangedWithHistory = = null )
{
throw new Exception ( "You must assign a ChangedWithHistory event in order to observe variable changes." ) ;
}
2020-03-01 15:32:52 -05:00
return new ObservableEvent < P > ( ChangedWithHistory . Register , ChangedWithHistory . Unregister ) ;
2019-04-21 07:51:20 -04:00
}
2019-04-16 16:32:17 -04:00
#endregion // Observable
2020-02-16 06:44:46 -05:00
private T RunPreChangeTransformers ( T value )
{
if ( _preChangeTransformers . Count < = 0 )
{
return value ;
}
var preProcessedValue = value ;
for ( var i = 0 ; i < _preChangeTransformers . Count ; + + i )
{
var Transformer = _preChangeTransformers [ i ] ;
if ( Transformer ! = null )
{
preProcessedValue = Transformer . Call ( preProcessedValue ) ;
}
}
return preProcessedValue ;
}
2020-03-01 15:32:52 -05:00
/// <summary>
2020-03-04 18:48:39 -05:00
/// Get event by type.
2020-03-01 15:32:52 -05:00
/// </summary>
/// <typeparam name="E"></typeparam>
/// <returns>The event.</returns>
public E GetEvent < E > ( ) where E : AtomEventBase
{
if ( typeof ( E ) = = typeof ( E1 ) )
return ( Changed as E ) ;
if ( typeof ( E ) = = typeof ( E2 ) )
return ( ChangedWithHistory as E ) ;
throw new Exception ( $"Event type {typeof(E)} not supported! Use {typeof(E1)} or {typeof(E2)}." ) ;
}
/// <summary>
2020-03-04 18:48:39 -05:00
/// Set event by type.
2020-03-01 15:32:52 -05:00
/// </summary>
/// <param name="e">The new event value.</param>
/// <typeparam name="E"></typeparam>
public void SetEvent < E > ( E e ) where E : AtomEventBase
{
if ( typeof ( E ) = = typeof ( E1 ) )
{
Changed = ( e as E1 ) ;
return ;
}
if ( typeof ( E ) = = typeof ( E2 ) )
{
ChangedWithHistory = ( e as E2 ) ;
return ;
}
throw new Exception ( $"Event type {typeof(E)} not supported! Use {typeof(E1)} or {typeof(E2)}." ) ;
}
2018-10-30 15:05:06 -04:00
}
2019-04-05 07:40:26 -04:00
}