< Summary

Information
Class: Hertzole.ScriptableValues.ValueReference[T]
Assembly: Hertzole.ScriptableValues
File(s): ./Packages/se.hertzole.scriptable-values/Runtime/Values/ValueReference.cs
Tag: 52_28197466029
Line coverage
92%
Covered lines: 182
Uncovered lines: 15
Coverable lines: 197
Total lines: 449
Line coverage: 92.3%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

./Packages/se.hertzole.scriptable-values/Runtime/Values/ValueReference.cs

#LineLine coverage
 1#nullable enable
 2
 3using System;
 4using Hertzole.ScriptableValues.Helpers;
 5using UnityEngine;
 6#if SCRIPTABLE_VALUES_ADDRESSABLES
 7using UnityEngine.AddressableAssets;
 8using UnityEngine.ResourceManagement.AsyncOperations;
 9#endif
 10
 11namespace Hertzole.ScriptableValues
 12{
 13    /// <summary>
 14    ///     The type of value reference.
 15    /// </summary>
 16    public enum ValueReferenceType
 17    {
 18        /// <summary>
 19        ///     A value you can set in the editor.
 20        /// </summary>
 21        Constant = 0,
 22        /// <summary>
 23        ///     References a <see cref="ScriptableValue{T}" />.
 24        /// </summary>
 25        Reference = 1,
 26#if SCRIPTABLE_VALUES_ADDRESSABLES
 27        /// <summary>
 28        ///     References a <see cref="ScriptableValue{T}" /> that is addressable.
 29        /// </summary>
 30        Addressable = 2
 31#endif
 32    }
 33
 34    /// <summary>
 35    ///     Allows you to reference a <see cref="ScriptableValue{T}" /> or a constant value.
 36    /// </summary>
 37    /// <typeparam name="T">The type that the value should be.</typeparam>
 38    [Serializable]
 39#if ODIN_INSPECTOR
 40  [Sirenix.OdinInspector.DrawWithUnity]
 41#endif
 42    public class ValueReference<T>
 43    {
 44        [SerializeField]
 612045        internal T constantValue = default!;
 46        [SerializeField]
 612047        internal ScriptableValue<T> referenceValue = null!;
 48#if SCRIPTABLE_VALUES_ADDRESSABLES
 49        [SerializeField]
 612050        internal AssetReferenceT<ScriptableValue<T>> addressableReference = null!;
 51#endif
 52        [SerializeField]
 53        internal ValueReferenceType valueType;
 54
 55#if UNITY_EDITOR
 56        [NonSerialized]
 612057        internal T oldValue = default!;
 58#endif
 59
 60        /// <summary>
 61        ///     Get or set the current value. This can be changed at runtime.
 62        /// </summary>
 63        /// <remarks>
 64        ///     If <see cref="ValueType" /> is set to <see cref="ValueReferenceType.Constant" />, this will set the cons
 65        ///     If <see cref="ValueType" /> is set to <see cref="ValueReferenceType.Reference" />, this will set the val
 66        ///     referenced <see cref="ScriptableValue{T}" />.
 67        ///     If <see cref="ValueType" /> is set to <see cref="ValueReferenceType.Addressable" />, this will set the v
 68        ///     referenced <see cref="ScriptableValue{T}" /> if it is loaded.
 69        /// </remarks>
 70        public T Value
 71        {
 5023272            get { return GetValue(); }
 1791673            set { SetValue(value, true); }
 74        }
 75
 76        /// <summary>
 77        ///     Get the type of value reference.
 78        /// </summary>
 79        public ValueReferenceType ValueType
 80        {
 081            get { return valueType; }
 82        }
 83
 84        /// <summary>
 85        ///     Checks if the value is an addressable value.
 86        /// </summary>
 87        /// <remarks>This will always return <c>false</c> if the addressables package isn't installed.</remarks>
 88        public bool IsAddressable
 89        {
 90            get
 091            {
 92#if SCRIPTABLE_VALUES_ADDRESSABLES
 093                return valueType == ValueReferenceType.Addressable;
 94#else
 95        return false;
 96#endif
 097            }
 98        }
 99
 100        /// <summary>
 101        ///     Checks if the addressable asset is loaded.
 102        /// </summary>
 103        /// <remarks>This will always return <c>true</c> if the addressables package isn't installed.</remarks>
 104        public bool IsAddressableLoaded
 105        {
 106            get
 1980107            {
 108#if SCRIPTABLE_VALUES_ADDRESSABLES
 1980109                return AssetHandle.IsDone && AssetHandle.Result != null;
 110#else
 111        return true;
 112#endif
 1980113            }
 114        }
 115
 116        // These are only used for constant values, and as a buffer for addressable assets that are not loaded yet.
 117        internal event ValueEventHandler<T>? OnValueChangingInternal;
 118        internal event ValueEventHandler<T>? OnValueChangedInternal;
 119
 120        /// <summary>
 121        ///     Called before the current value is set.
 122        /// </summary>
 123        public event ValueEventHandler<T> OnValueChanging
 124        {
 125            add
 1524126            {
 1524127                if (valueType == ValueReferenceType.Reference)
 396128                {
 396129                    referenceValue.OnValueChanging += value;
 396130                }
 131#if SCRIPTABLE_VALUES_ADDRESSABLES
 1128132                else if (valueType == ValueReferenceType.Addressable && AssetHandle.IsValid() && AssetHandle.Result != n
 156133                {
 156134                    AssetHandle.Result.OnValueChanging += value;
 156135                }
 136#endif
 137                else
 972138                {
 972139                    OnValueChangingInternal += value;
 972140                }
 1524141            }
 142            remove
 1104143            {
 1104144                if (valueType == ValueReferenceType.Reference)
 396145                {
 396146                    referenceValue.OnValueChanging -= value;
 396147                }
 148#if SCRIPTABLE_VALUES_ADDRESSABLES
 708149                else if (valueType == ValueReferenceType.Addressable && AssetHandle.IsValid() && AssetHandle.Result != n
 312150                {
 312151                    AssetHandle.Result.OnValueChanging -= value;
 312152                }
 153#endif
 154                else
 396155                {
 396156                    OnValueChangingInternal -= value;
 396157                }
 1104158            }
 159        }
 160
 161        /// <summary>
 162        ///     Called after the current value is set.
 163        /// </summary>
 164        public event ValueEventHandler<T> OnValueChanged
 165        {
 166            add
 1836167            {
 1836168                if (valueType == ValueReferenceType.Reference)
 552169                {
 552170                    referenceValue.OnValueChanged += value;
 552171                }
 172#if SCRIPTABLE_VALUES_ADDRESSABLES
 1284173                else if (valueType == ValueReferenceType.Addressable && AssetHandle.IsValid() && AssetHandle.Result != n
 156174                {
 156175                    AssetHandle.Result.OnValueChanged += value;
 156176                }
 177#endif
 178                else
 1128179                {
 1128180                    OnValueChangedInternal += value;
 1128181                }
 1836182            }
 183            remove
 1416184            {
 1416185                if (valueType == ValueReferenceType.Reference)
 552186                {
 552187                    referenceValue.OnValueChanged -= value;
 552188                }
 189#if SCRIPTABLE_VALUES_ADDRESSABLES
 864190                else if (valueType == ValueReferenceType.Addressable && AssetHandle.IsValid() && AssetHandle.Result != n
 312191                {
 312192                    AssetHandle.Result.OnValueChanged -= value;
 312193                }
 194#endif
 195                else
 552196                {
 552197                    OnValueChangedInternal -= value;
 552198                }
 1416199            }
 200        }
 201
 202        /// <summary>
 203        ///     Creates a new <see cref="ValueReference{T}" /> of type <see cref="ValueReferenceType.Reference" />.
 204        /// </summary>
 156205        public ValueReference()
 156206        {
 156207            valueType = ValueReferenceType.Reference;
 156208            constantValue = default!;
 156209            referenceValue = null!;
 210#if SCRIPTABLE_VALUES_ADDRESSABLES
 156211            addressableReference = null!;
 212#endif
 156213        }
 214
 215        /// <summary>
 216        ///     Creates a new <see cref="ValueReference{T}" /> of type <see cref="ValueReferenceType.Constant" /> with t
 217        ///     constant value.
 218        /// </summary>
 219        /// <param name="constantValue">The constant value.</param>
 2100220        public ValueReference(T constantValue)
 2100221        {
 2100222            valueType = ValueReferenceType.Constant;
 2100223            this.constantValue = constantValue;
 2100224        }
 225
 226        /// <summary>
 227        ///     Creates a new <see cref="ValueReference{T}" /> of type <see cref="ValueReferenceType.Reference" /> with 
 228        ///     reference value.
 229        /// </summary>
 230        /// <param name="referenceValue">The reference value.</param>
 1740231        public ValueReference(ScriptableValue<T> referenceValue)
 1740232        {
 1740233            valueType = ValueReferenceType.Reference;
 1740234            this.referenceValue = referenceValue;
 1740235        }
 236
 237#if SCRIPTABLE_VALUES_ADDRESSABLES
 238        /// <summary>
 239        ///     Creates a new <see cref="ValueReference{T}" /> of type <see cref="ValueReferenceType.Addressable" /> wit
 240        ///     addressable reference.
 241        /// </summary>
 242        /// <param name="addressableReference">The addressable reference.</param>
 2124243        public ValueReference(AssetReferenceT<ScriptableValue<T>> addressableReference)
 2124244        {
 2124245            valueType = ValueReferenceType.Addressable;
 2124246            this.addressableReference = addressableReference;
 2124247        }
 248#endif
 249
 250        private T GetValue()
 16980251        {
 16980252            switch (valueType)
 253            {
 254                case ValueReferenceType.Constant:
 5184255                    return constantValue;
 256                case ValueReferenceType.Reference:
 5136257                    return referenceValue.Value;
 258#if SCRIPTABLE_VALUES_ADDRESSABLES
 259                case ValueReferenceType.Addressable:
 6348260                    if (AssetHandle.IsValid() && AssetHandle.IsDone && AssetHandle.Result != null)
 5952261                    {
 5952262                        return AssetHandle.Result.Value;
 263                    }
 264
 396265                    throw new InvalidOperationException(
 266                        $"Addressable asset is not loaded yet. Make sure you've called {nameof(LoadAddressableAssetAsync
 267#endif
 268                default:
 312269                    throw new NotSupportedException($"No supported value type for {valueType}.");
 270            }
 16272271        }
 272
 273        private void SetValue(in T value, in bool notify)
 7176274        {
 7176275            T previousValue = Value;
 6624276            if (EqualityHelper.Equals(previousValue, value))
 288277            {
 288278                return;
 279            }
 280
 6336281            switch (valueType)
 282            {
 283                case ValueReferenceType.Constant:
 2178284                    SetValueConstant(value, previousValue, notify);
 2178285                    break;
 286                case ValueReferenceType.Reference:
 2178287                    SetValueReference(value, notify);
 2178288                    break;
 289#if SCRIPTABLE_VALUES_ADDRESSABLES
 290                case ValueReferenceType.Addressable:
 1980291                    SetValueAddressable(value, notify);
 1980292                    break;
 293#endif
 294                default:
 0295                    throw new NotSupportedException($"No supported value type for {valueType}.");
 296            }
 6624297        }
 298
 299        private void SetValueConstant(in T value, in T previousValue, in bool notify)
 2178300        {
 2178301            if (notify)
 1866302            {
 1866303                OnValueChangingInternal?.Invoke(previousValue, value);
 1866304            }
 305
 2178306            constantValue = value;
 307
 2178308            if (notify)
 1866309            {
 1866310                OnValueChangedInternal?.Invoke(previousValue, value);
 1866311            }
 2178312        }
 313
 314        private void SetValueReference(in T value, in bool notify)
 2178315        {
 2178316            if (notify)
 1866317            {
 1866318                referenceValue.Value = value;
 1866319            }
 320            else
 312321            {
 312322                referenceValue.SetValueWithoutNotify(value);
 312323            }
 2178324        }
 325
 326#if SCRIPTABLE_VALUES_ADDRESSABLES
 327        private void SetValueAddressable(in T value, in bool notify)
 1980328        {
 1980329            if (IsAddressableLoaded)
 1980330            {
 1980331                if (notify)
 1602332                {
 1602333                    AssetHandle.Result.Value = value;
 1602334                }
 335                else
 378336                {
 378337                    AssetHandle.Result.SetValueWithoutNotify(value);
 378338                }
 1980339            }
 340            else
 0341            {
 0342                Debug.LogWarning("Addressable asset is not loaded yet. Make sure you've called LoadAddressableAssetAsync
 0343            }
 1980344        }
 345#endif
 346
 347        /// <inheritdoc cref="ScriptableValue{T}.SetValueWithoutNotify(T)" />
 348        public void SetValueWithoutNotify(T value)
 1020349        {
 1020350            SetValue(value, false);
 1020351        }
 352
 353#if SCRIPTABLE_VALUES_ADDRESSABLES
 354        private bool IsValidAddressable()
 0355        {
 0356            return valueType == ValueReferenceType.Addressable && AssetHandle.IsValid() && AssetHandle.IsDone && AssetHa
 0357        }
 358#endif
 359
 360#if UNITY_EDITOR
 361        internal void SetPreviousValue()
 48362        {
 48363            oldValue = constantValue;
 48364        }
 365
 366        internal void SetEditorValue()
 48367        {
 48368            if (!Application.isPlaying || EqualityHelper.Equals(oldValue, Value))
 36369            {
 36370                return;
 371            }
 372
 12373            OnValueChangingInternal?.Invoke(oldValue, Value);
 12374            OnValueChangedInternal?.Invoke(oldValue, Value);
 375
 12376            oldValue = Value;
 48377        }
 378#endif
 379
 380#if SCRIPTABLE_VALUES_ADDRESSABLES
 381        /// <summary>
 382        ///     The handle to the loaded addressable asset.
 383        /// </summary>
 384        /// <remarks>
 385        ///     This will be a default <see cref="AsyncOperationHandle{TObject}" /> if
 386        ///     <see cref="LoadAddressableAssetAsync" /> hasn't been called yet.
 387        /// </remarks>
 42708388        public AsyncOperationHandle<ScriptableValue<T>> AssetHandle { get; private set; }
 389
 390        /// <summary>
 391        ///     Loads the addressable asset asynchronously.
 392        /// </summary>
 393        /// <param name="onLoaded">Optional callback that is called when the asset is loaded.</param>
 394        /// <returns>The handle to the loaded addressable asset.</returns>
 395        /// <exception cref="NotSupportedException"><see cref="ValueType" /> is not <see cref="ValueReferenceType.Addres
 396        public AsyncOperationHandle<ScriptableValue<T>> LoadAddressableAssetAsync(Action<AsyncOperationHandle<Scriptable
 1572397        {
 1572398            if (valueType != ValueReferenceType.Addressable)
 0399            {
 0400                throw new NotSupportedException("The value type is not addressable.");
 401            }
 402
 1572403            AssetHandle = Addressables.LoadAssetAsync<ScriptableValue<T>>(addressableReference);
 404
 1572405            AssetHandle.Completed += OnAssetHandleOnCompleted;
 406
 1572407            return AssetHandle;
 408
 409            void OnAssetHandleOnCompleted(AsyncOperationHandle<ScriptableValue<T>> handle)
 1572410            {
 1572411                if (handle.Status == AsyncOperationStatus.Succeeded)
 1572412                {
 1572413                    referenceValue = handle.Result;
 1572414                    referenceValue.OnValueChanging += OnValueChangingInternal;
 1572415                    referenceValue.OnValueChanged += OnValueChangedInternal;
 1572416                }
 417
 1572418                onLoaded?.Invoke(handle);
 419                // Clean up the event as to not have any memory leaks.
 1572420                handle.Completed -= OnAssetHandleOnCompleted;
 1572421            }
 1572422        }
 423
 424        /// <summary>
 425        ///     Releases the addressable asset.
 426        /// </summary>
 427        public void ReleaseAddressableAsset()
 1572428        {
 1572429            if (valueType != ValueReferenceType.Addressable)
 0430            {
 0431                return;
 432            }
 433
 434            // Make sure to remove all old listeners.
 1572435            if (referenceValue != null)
 1572436            {
 1572437                referenceValue.OnValueChanging -= OnValueChangingInternal;
 1572438                referenceValue.OnValueChanged -= OnValueChangedInternal;
 1572439            }
 440
 1572441            if (AssetHandle.IsValid())
 1572442            {
 1572443                referenceValue = null!;
 1572444                Addressables.Release(AssetHandle);
 1572445            }
 1572446        }
 447#endif
 448    }
 449}