unity-atoms/Packages/Core/Runtime/Variables/AtomVariable.cs

272 lines
9.3 KiB
C#
Raw Normal View History

using System;
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&lt;T&gt;`.
/// </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&lt;T&gt;`.</typeparam>
/// <typeparam name="E2">Event of type `AtomEvent&lt;T, T&gt;`.</typeparam>
/// <typeparam name="F">Function of type `FunctionEvent&lt;T, T&gt;`.</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>
where E1 : AtomEvent<T>
2020-03-01 15:32:52 -05:00
where E2 : AtomEvent<P>
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>
/// <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); }
/// <summary>
2020-03-30 11:07:10 -04:00
/// The initial 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-15 16:21:56 -04:00
/// <summary>
/// The value the Variable had before its value got changed last time.
/// </summary>
/// <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
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;
/// <summary>
/// Whether Changed Event should be triggered on OnEnable or not
/// </summary>
[SerializeField]
private bool _triggerChangedOnOnEnable = default;
/// <summary>
/// Whether ChangedWithHistory Event should be triggered on OnEnable or not
/// </summary>
[SerializeField]
private bool _triggerChangedWithHistoryOnOnEnable = default;
2020-03-19 19:48:23 -04:00
[SerializeField]
private T _oldValue;
/// <summary>
/// The inital value of the Variable.
/// </summary>
[SerializeField]
private T _initialValue = default(T);
/// <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
private void OnValidate()
{
2020-03-09 18:51:14 -04:00
InitialValue = RunPreChangeTransformers(InitialValue);
_value = RunPreChangeTransformers(_value);
}
Squashed commit of the following: commit 847eff037204d841546c0da772d2f341f9cf1e25 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sun Mar 17 22:09:38 2019 +0100 #17 - Serializable not needed for ScriptableObject commit 593b275e6394b9d589de8a7a20375145dfc4aa84 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sun Mar 17 21:59:33 2019 +0100 18 - IGameEvent<T1, T2> RegisterListener commit 40443ce9bd4b1c339aaf19cfcb119f2336608dae Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:35:04 2019 +0100 Remove some more warnings commit bd453110ac51a6ebe3d54368fcb039bdbe52e278 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:33:30 2019 +0100 Update README commit 90977b853a047c84efb5311768a09f4e8a1165b2 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:31:45 2019 +0100 Initialize to null to get rid off warnings commit 38b7f5c4ede195aa7198f567801c9cbeedc9b6f6 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:15:53 2019 +0100 More fixes to enable local unity project commit 404e1cbf88ed4431c61d3ece074e838e74ac5141 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:12:37 2019 +0100 Remove duplicated asmdef commit 5734300684e8a16553f213157cad1b4722b7cb7f Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:09:46 2019 +0100 Change files to include commit d1e42b119a6bc1577b1792459fa298e063652337 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:05:56 2019 +0100 Added root package json commit 1709a0347147d74460f653182bbaf8d15eb6154e Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 15:30:44 2019 +0100 #16 - Add test and examples Unity project commit a3ea1a133bf6727e011ba85c64569db45302e487 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 13:12:02 2019 +0100 #13 - Make usage of UPM (package manager) commit 492a30e905f6cf3f5899cb7080ef2bda73110f00 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 11:28:56 2019 +0100 Added extensions + code formatting fixes commit 709949a1016c236cfd363cf25392fedfd8d083ca Author: Oliver Biwer <soraphis@users.noreply.github.com> Date: Fri Mar 8 10:16:45 2019 +0100 More AtomicTags changes (#15) * - added assembly defintions, and unit tests - improved AtomicTags in regards of #8, #9 and #10 * Fixes #11 - Added Equality Members (inclusive HashCode) for ScriptableObjectVariableBase * removed Rider Plugins from git * Further AtomicTag optimization commit ae6584c879f182e727fe0a8d0aff4b0715829914 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 10:08:36 2019 +0100 Editor config commit 197d7067608600e4e2d13dc42db909ee8f8c75df Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 09:23:12 2019 +0100 Added editor config file commit 53d6adc07bbc2967c12c17227a1c31d9f1cfba77 Author: Oliver Biwer <soraphis@users.noreply.github.com> Date: Tue Mar 5 22:57:47 2019 +0100 More efficient AtomicTags (#12) * - added assembly defintions, and unit tests - improved AtomicTags in regards of #8, #9 and #10 * Fixes #11 - Added Equality Members (inclusive HashCode) for ScriptableObjectVariableBase * removed Rider Plugins from git commit 81209d83b5195300d4c2d54411cff3c7983f0d97 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Wed Dec 12 20:54:17 2018 +0100 Added MonoHooks + ColliderType + bug fixes commit c6b240cebbdc410341fb05204235842df5da9d73 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sat Dec 1 00:23:10 2018 +0100 Experimenting with adding UI state management commit dfd70a8944acbeabe5feba1cf6cff2be6802c470 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Nov 30 23:10:21 2018 +0100 Issue #6 - AtomicTags commit 8907763227f4d4c2a32c5684e7caa4d4a082eb16 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Nov 30 22:42:29 2018 +0100 First commit of v1.0.0
2019-03-17 18:43:20 -04:00
private void OnEnable()
{
2020-03-08 15:41:22 -04:00
_oldValue = InitialValue;
_value = InitialValue;
if (Changed != null && _triggerChangedOnOnEnable)
{
Changed.Raise(Value);
}
if (ChangedWithHistory != null && _triggerChangedWithHistoryOnOnEnable)
{
var pair = default(P);
pair.Item1 = _value;
pair.Item2 = _oldValue;
ChangedWithHistory.Raise(pair);
}
Squashed commit of the following: commit 847eff037204d841546c0da772d2f341f9cf1e25 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sun Mar 17 22:09:38 2019 +0100 #17 - Serializable not needed for ScriptableObject commit 593b275e6394b9d589de8a7a20375145dfc4aa84 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sun Mar 17 21:59:33 2019 +0100 18 - IGameEvent<T1, T2> RegisterListener commit 40443ce9bd4b1c339aaf19cfcb119f2336608dae Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:35:04 2019 +0100 Remove some more warnings commit bd453110ac51a6ebe3d54368fcb039bdbe52e278 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:33:30 2019 +0100 Update README commit 90977b853a047c84efb5311768a09f4e8a1165b2 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:31:45 2019 +0100 Initialize to null to get rid off warnings commit 38b7f5c4ede195aa7198f567801c9cbeedc9b6f6 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:15:53 2019 +0100 More fixes to enable local unity project commit 404e1cbf88ed4431c61d3ece074e838e74ac5141 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:12:37 2019 +0100 Remove duplicated asmdef commit 5734300684e8a16553f213157cad1b4722b7cb7f Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:09:46 2019 +0100 Change files to include commit d1e42b119a6bc1577b1792459fa298e063652337 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 16:05:56 2019 +0100 Added root package json commit 1709a0347147d74460f653182bbaf8d15eb6154e Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 15:30:44 2019 +0100 #16 - Add test and examples Unity project commit a3ea1a133bf6727e011ba85c64569db45302e487 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 13:12:02 2019 +0100 #13 - Make usage of UPM (package manager) commit 492a30e905f6cf3f5899cb7080ef2bda73110f00 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 11:28:56 2019 +0100 Added extensions + code formatting fixes commit 709949a1016c236cfd363cf25392fedfd8d083ca Author: Oliver Biwer <soraphis@users.noreply.github.com> Date: Fri Mar 8 10:16:45 2019 +0100 More AtomicTags changes (#15) * - added assembly defintions, and unit tests - improved AtomicTags in regards of #8, #9 and #10 * Fixes #11 - Added Equality Members (inclusive HashCode) for ScriptableObjectVariableBase * removed Rider Plugins from git * Further AtomicTag optimization commit ae6584c879f182e727fe0a8d0aff4b0715829914 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 10:08:36 2019 +0100 Editor config commit 197d7067608600e4e2d13dc42db909ee8f8c75df Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Mar 8 09:23:12 2019 +0100 Added editor config file commit 53d6adc07bbc2967c12c17227a1c31d9f1cfba77 Author: Oliver Biwer <soraphis@users.noreply.github.com> Date: Tue Mar 5 22:57:47 2019 +0100 More efficient AtomicTags (#12) * - added assembly defintions, and unit tests - improved AtomicTags in regards of #8, #9 and #10 * Fixes #11 - Added Equality Members (inclusive HashCode) for ScriptableObjectVariableBase * removed Rider Plugins from git commit 81209d83b5195300d4c2d54411cff3c7983f0d97 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Wed Dec 12 20:54:17 2018 +0100 Added MonoHooks + ColliderType + bug fixes commit c6b240cebbdc410341fb05204235842df5da9d73 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Sat Dec 1 00:23:10 2018 +0100 Experimenting with adding UI state management commit dfd70a8944acbeabe5feba1cf6cff2be6802c470 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Nov 30 23:10:21 2018 +0100 Issue #6 - AtomicTags commit 8907763227f4d4c2a32c5684e7caa4d4a082eb16 Author: Adam Ramberg <andersson.adam.89@gmail.com> Date: Fri Nov 30 22:42:29 2018 +0100 First commit of v1.0.0
2019-03-17 18:43:20 -04:00
}
2019-10-15 16:21:56 -04:00
/// <summary>
2020-03-30 11:07:10 -04:00
/// Reset the Variable to its `_initialValue`.
2019-10-15 16:21:56 -04:00
/// </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)
{
if (!shouldTriggerEvents)
{
_oldValue = _value;
2020-03-08 15:41:22 -04:00
_value = InitialValue;
}
else
{
2020-03-08 15:41:22 -04:00
SetValue(InitialValue);
}
}
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>
public bool SetValue(T newValue, bool forceEvent = false)
2018-10-30 15:05:06 -04:00
{
var preProcessedNewValue = RunPreChangeTransformers(newValue);
var changeValue = !ValueEquals(preProcessedNewValue);
var triggerEvents = changeValue || forceEvent;
if (changeValue)
2018-10-30 15:05:06 -04:00
{
_oldValue = _value;
_value = preProcessedNewValue;
}
if (triggerEvents)
{
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
}
2018-10-30 15:05:06 -04:00
}
return changeValue;
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);
}
#region Observable
2019-10-15 16:21:56 -04:00
/// <summary>
/// Turn the Variable's change Event into an `IObservable&lt;T&gt;`. Makes the Variable's change Event compatible with for example UniRx.
/// </summary>
/// <returns>The Variable's change Event as an `IObservable&lt;T&gt;`.</returns>
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&lt;T, T&gt;`. Makes the Variable's change with history Event compatible with for example UniRx.
/// </summary>
/// <returns>The Variable's change Event as an `IObservable&lt;T, T&gt;`.</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
}
#endregion // Observable
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>
/// Get event by type (allowing inheritance).
2020-03-01 15:32:52 -05:00
/// </summary>
/// <typeparam name="E"></typeparam>
/// <returns>Changed - If Changed (or ChangedWithHistory) are of type E
/// ChangedWithHistory - If not Changed but ChangedWithHistory is of type E
/// <exception cref="NotSupportedException">if none of the events are of type E</exception>
/// </returns>
2020-03-01 15:32:52 -05:00
public E GetEvent<E>() where E : AtomEventBase
{
if (Changed is E evt1)
return evt1;
if (ChangedWithHistory is E evt2)
return evt2;
2020-03-01 15:32:52 -05:00
throw new NotSupportedException($"Event type {typeof(E)} not supported! Use {typeof(E1)} or {typeof(E2)}.");
2020-03-01 15:32:52 -05:00
}
/// <summary>
2020-03-30 11:07:10 -04: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
}
}