Skip to content

Commit

Permalink
Merge pull request #3 from hibzzgames/editor-support
Browse files Browse the repository at this point in the history
Adding support for Singletons in the Editor context
  • Loading branch information
sliptrixx authored Aug 1, 2023
2 parents 8f908a2 + e42de03 commit 8dd90a5
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 77 deletions.
193 changes: 117 additions & 76 deletions Scripts/Core/Singleton.cs
Original file line number Diff line number Diff line change
@@ -1,83 +1,124 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace Hibzz.Singletons
{
public class Singleton<T> : MonoBehaviour where T : Component
{
private static T instance;

/// <summary>
/// Gives a reference to the singleton instance. If none is available,
/// creates a new one and returns it
/// </summary>
public static T Instance
{
get
{
if (instance == null)
{
instance = RequestNewInstance();
}

return instance;
}
}

/// <summary>
/// Gives any available singleton instance.
/// If none was created or if one was destroyed, returns null.
/// </summary>
public static T AvailableInstance => instance;

// Used to create a new instance of the singleton
private static T RequestNewInstance()
{
T[] items = FindObjectsOfType<T>();
if (items.Length == 0)
{
// Using the reflection system, look if CreateNewInstance is being overriden
Type type = typeof(T);
MethodInfo method = type.GetMethod("CreateNewInstance", BindingFlags.NonPublic | BindingFlags.Static);

// If the reflection system can find an overriden version of the static function CreateNewInstance, then invoke it
if(method is not null)
{
return (T) method.Invoke(null, null);
}

// No overrides found, call base implementation
return CreateNewInstance();
}
else if (items.Length > 1)
{
// more than one instance found. So returning null
Debug.LogError("Multiple instances of the singleton found. So, cant determine what's the singleton. Returning null.");
return null;
}

// only one instance of type T found in the scene
return items[0];
}

// Overridable function used to create custom instance creation if needed
protected static T CreateNewInstance()
{
GameObject obj = new GameObject();
obj.name = typeof(T).Name + "Object";
return obj.AddComponent<T>();
}

// when the singleton object gets destroyed, we make sure that the
// static singleton reference is cleared
protected virtual void OnDestroy()
{
// making sure that this object is the static instance, before just
if (instance == this)
{
instance = null;
}
}
}
public class Singleton<T> : MonoBehaviour where T : Component
{
private static T instance;

/// <summary>
/// Gives a reference to the singleton instance. If none is available,
/// creates a new one and returns it
/// </summary>
public static T Instance
{
get
{
if (instance == null)
{
instance = RequestNewInstance();
}

return instance;
}
}

/// <summary>
/// Gives any available singleton instance.
/// If none was created or if one was destroyed, returns null.
/// </summary>
public static T AvailableInstance => instance;

// Used to create a new instance of the singleton
private static T RequestNewInstance()
{
T[] items = GetAllObjects();
if (items.Length == 0)
{
// Using the reflection system, look if CreateNewInstance is being overriden
Type type = typeof(T);
MethodInfo method = type.GetMethod("CreateNewInstance", BindingFlags.NonPublic | BindingFlags.Static);

// If the reflection system can find an overriden version of the static function CreateNewInstance, then invoke it
if(method is not null)
{
return (T) method.Invoke(null, null);
}

// No overrides found, call base implementation
return CreateNewInstance();
}
else if (items.Length > 1)
{
// more than one instance found. So returning null
Debug.LogError("Multiple instances of the singleton found. So, cant determine what's the singleton. Returning null.");
return null;
}

// only one instance of type T found in the scene
return items[0];
}

// Overridable function used to create custom instance creation if needed
protected static T CreateNewInstance()
{
GameObject obj = new GameObject();
obj.name = typeof(T).Name + "Object";
return obj.AddComponent<T>();
}

// Summary: Get all components of type T in the scene
// Remarks: This is an editor friendly version of FindObjectsOfType
// that works on singletons that exist on an editor context.
// The function is more expensive in editor context, however
// remains cheap in regular playmode.
protected static T[] GetAllObjects()
{
#if UNITY_EDITOR

// when the editor is in play mode, FindObjectsOfType will work
if (Application.isPlaying)
{
return FindObjectsOfType<T>();
}

// the editor is not in playmode, so in the editor context
// this container will store all objects found of type T in the scene
List<T> singletons = new List<T>();

GameObject[] rootObjects = SceneManager.GetActiveScene().GetRootGameObjects();
foreach (var rootObject in rootObjects)
{
var comps = rootObject.GetComponentsInChildren<T>();
foreach (var comp in comps)
{
singletons.Add(comp);
}
}

// convert the list to an array, as that's the return type expected
return singletons.ToArray();

#else

return FindObjectsOfType<T>();

#endif
}

// when the singleton object gets destroyed, we make sure that the
// static singleton reference is cleared
protected virtual void OnDestroy()
{
// making sure that this object is the static instance, before just
if (instance == this)
{
instance = null;
}
}
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "com.hibzz.singletons",
"version": "1.2.0",
"version": "1.3.0",
"displayName": "hibzz.singletons",
"description": "A library of singletons for Unity",
"author": {
Expand Down

0 comments on commit 8dd90a5

Please sign in to comment.