diff --git a/CHANGELOG.md b/CHANGELOG.md index ac2db5b..3f6acc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - X10D: Added `Vector4.WithY()` - X10D: Added `Vector4.WithZ()` - X10D: Added `Vector4.WithW()` +- X10D.Unity: Added `Singleton` class - X10D.Unity: Added `Color.Inverted()` - X10D.Unity: Added `Color.WithA()` - X10D.Unity: Added `Color.WithB()` diff --git a/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs b/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs new file mode 100644 index 0000000..9bf7a24 --- /dev/null +++ b/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs @@ -0,0 +1,45 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine; +using UnityEngine.TestTools; + +namespace X10D.Unity.Tests +{ + public class SingletonTests + { + [UnityTest] + public IEnumerator Singleton_ShouldReturnNewInstance_WhenNoInstanceExists() + { + TestBehaviour instance = Singleton.Instance; + Assert.IsTrue(instance); + Assert.IsTrue(instance.Flag); + + yield break; + } + + [UnityTest] + public IEnumerator Singleton_ShouldReturnSameInstance_WhenAccessedTwice() + { + TestBehaviour instance = Singleton.Instance; + Assert.IsTrue(instance); + Assert.AreEqual(instance, Singleton.Instance); + + yield break; + } + + [UnityTest] + public IEnumerator Singleton_ShouldReturnNewInstance_WhenDestroyed() + { + TestBehaviour instance = Singleton.Instance; + Assert.IsTrue(instance); + Object.Destroy(instance); + yield return null; + Assert.IsFalse(instance); + + // ReSharper disable once HeuristicUnreachableCode + instance = Singleton.Instance; + Assert.IsNotNull(instance); + Assert.IsTrue(instance.Flag); + } + } +} diff --git a/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs.meta b/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs.meta new file mode 100644 index 0000000..3f086ce --- /dev/null +++ b/X10D.Unity.Tests/Assets/Tests/SingletonTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5c51198d124f40859bd9298d3241d5a6 +timeCreated: 1652428949 \ No newline at end of file diff --git a/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs b/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs new file mode 100644 index 0000000..a603e1d --- /dev/null +++ b/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs @@ -0,0 +1,10 @@ +namespace X10D.Unity.Tests +{ + internal sealed class TestBehaviour : Singleton + { + public bool Flag + { + get => true; + } + } +} diff --git a/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs.meta b/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs.meta new file mode 100644 index 0000000..bc63104 --- /dev/null +++ b/X10D.Unity.Tests/Assets/Tests/TestBehaviour.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: df932718d20948ecbe9b0de8aa7bbaf4 +timeCreated: 1652428898 \ No newline at end of file diff --git a/X10D.Unity/src/Singleton.cs b/X10D.Unity/src/Singleton.cs new file mode 100644 index 0000000..8e506db --- /dev/null +++ b/X10D.Unity/src/Singleton.cs @@ -0,0 +1,42 @@ +using UnityEngine; + +namespace X10D.Unity; + +/// +/// Represents a class which implements the singleton pattern for a specific . This class is not +/// thread-safe. +/// +/// The type of the singleton. +public abstract class Singleton : MonoBehaviour + where T : Singleton +{ + private static Lazy s_instanceLazy = new(CreateLazyInstanceInternal, false); + + /// + /// Gets the instance of the singleton. + /// + /// The singleton instance. + public static T Instance + { + get => s_instanceLazy.Value; + } + + /// + /// Called when the object is destroyed. + /// + protected virtual void OnDestroy() + { + s_instanceLazy = new Lazy(CreateLazyInstanceInternal, false); + } + + private static T CreateLazyInstanceInternal() + { + if (FindObjectOfType() is { } instance) + { + return instance; + } + + var gameObject = new GameObject {name = typeof(T).Name}; + return gameObject.AddComponent(); + } +}