additional package setup
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 402f66c64d0efe844a7df49bf86a7b6d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,103 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Extensions
|
||||
{
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Recursively changes game object and its children to specified layer
|
||||
/// </summary>
|
||||
/// <param name="layer">Layer to change all objects to</param>
|
||||
public static void SetLayerRecursively(this GameObject obj, int layer)
|
||||
{
|
||||
obj.layer = layer;
|
||||
foreach (Transform child in obj.transform)
|
||||
{
|
||||
child.gameObject.SetLayerRecursively(layer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the first child of the current GameObject that has the specified tag. Does not include itself.
|
||||
/// </summary>
|
||||
public static GameObject FindChildWithTag(this GameObject obj, string tag)
|
||||
{
|
||||
foreach(Transform child in obj.transform)
|
||||
{
|
||||
if(child.gameObject == obj) continue;
|
||||
|
||||
if(child.CompareTag(tag))
|
||||
return child.gameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an array containing all children of the current GameObject that have the specified tag. Does not include itself.
|
||||
/// </summary>
|
||||
public static GameObject[] FindChildrenWithTag(this GameObject obj, string tag)
|
||||
{
|
||||
var taggedArray = new GameObject[obj.transform.childCount];
|
||||
var index = 0;
|
||||
foreach(Transform child in obj.transform)
|
||||
{
|
||||
if(child.CompareTag(tag))
|
||||
{
|
||||
taggedArray[index] = child.gameObject;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if(index == 0) return null;
|
||||
|
||||
Array.Resize(ref taggedArray, index);
|
||||
return taggedArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a List containing all children of the current GameObject that have the specified tag. Does not include itself.
|
||||
/// </summary>
|
||||
public static List<GameObject> FindChildrenWithTagList(this GameObject obj, string tag)
|
||||
{
|
||||
var taggedList = new List<GameObject>();
|
||||
|
||||
foreach(Transform child in obj.transform)
|
||||
{
|
||||
if(child.gameObject == obj) continue;
|
||||
|
||||
if(child.CompareTag(tag))
|
||||
taggedList.Add(child.gameObject);
|
||||
}
|
||||
return taggedList;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Destroys all children of the GameObject not including itself\
|
||||
/// </summary>
|
||||
public static void DestroyChildren(this GameObject parent)
|
||||
{
|
||||
var children = new Transform[parent.transform.childCount];
|
||||
for (var i = 0; i < parent.transform.childCount; i++)
|
||||
children[i] = parent.transform.GetChild(i);
|
||||
for (var i = 0; i < children.Length; i++)
|
||||
GameObject.Destroy(children[i].gameObject);
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// Checks if a GameObject is tagged with any of the strings in the provided collection
|
||||
///</summary>
|
||||
public static bool CompareTags(this GameObject go, IEnumerable<string> tags)
|
||||
{
|
||||
foreach (var tag in tags)
|
||||
{
|
||||
if (go.CompareTag(tag))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 70306add1702cb54c8da10e7385f4afd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
namespace Boxfriend.Extensions
|
||||
{
|
||||
public static class MathExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if value is within specified range.
|
||||
/// </summary>
|
||||
/// <param name="min">Lowest value of the range</param>
|
||||
/// <param name="max">Largest value of the range</param>
|
||||
/// <returns>True if less than min and greater than max</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InRange(this int value, int min, int max) => (value >= min) && (value <= max);
|
||||
/// <summary>
|
||||
/// Checks if value is within specified range.
|
||||
/// </summary>
|
||||
/// <param name="min">Lowest value of the range</param>
|
||||
/// <param name="max">Largest value of the range</param>
|
||||
/// <returns>True if less than min and greater than max</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InRange(this float value, float min, float max) => (value >= min) && (value <= max);
|
||||
/// <summary>
|
||||
/// Checks if value is within specified range.
|
||||
/// </summary>
|
||||
/// <param name="min">Lowest value of the range</param>
|
||||
/// <param name="max">Largest value of the range</param>
|
||||
/// <returns>True if less than min and greater than max</returns>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool InRange(this double value, double min, double max) => (value >= min) && (value <= max);
|
||||
|
||||
public static Vector2 Rotate(this Vector2 vector, float degrees)
|
||||
{
|
||||
float sin = Mathf.Sin(degrees * Mathf.Deg2Rad);
|
||||
float cos = Mathf.Cos(degrees * Mathf.Deg2Rad);
|
||||
|
||||
float tx = vector.x;
|
||||
float ty = vector.y;
|
||||
vector.x = (cos * tx) - (sin * ty);
|
||||
vector.y = (sin * tx) + (cos * ty);
|
||||
return vector;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 750ee6f30f2644ffa9864f245b532846
|
||||
timeCreated: 1640819035
|
||||
@@ -0,0 +1,22 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine;
|
||||
namespace Boxfriend.Extensions
|
||||
{
|
||||
public static class StringExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if two strings are the same without case sensitivity.
|
||||
/// </summary>
|
||||
/// <param name="value">String being compared</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool CaseInsensitveEquals (this string str, string value) => (str.ToLower() == value.ToLower());
|
||||
|
||||
/// <summary>
|
||||
/// Applies a rich text color to string
|
||||
/// </summary>
|
||||
/// <param name="text">String to be colored</param>
|
||||
/// <param name="col">Unity Color applied to all of 'text'</param>
|
||||
public static string AddColor(this string text, Color col) => $"<color={ColorHexFromUnityColor(col)}>{text}</color>";
|
||||
public static string ColorHexFromUnityColor(this Color unityColor) => $"#{ColorUtility.ToHtmlStringRGBA(unityColor)}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ca16c6205a144610a6da0422c8bfa8c0
|
||||
timeCreated: 1640894511
|
||||
@@ -0,0 +1,27 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Extensions
|
||||
{
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static T GetComponentInInactiveParent<T>(this Transform transform) where T : Component
|
||||
{
|
||||
while(transform.parent != null)
|
||||
{
|
||||
transform = transform.parent;
|
||||
if(transform.TryGetComponent(out T comp))
|
||||
return comp;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void UnparentAll(this Transform transform)
|
||||
{
|
||||
foreach(Transform child in transform)
|
||||
{
|
||||
child.UnparentAll();
|
||||
}
|
||||
transform.SetParent(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f29e0d162e8b8054e86ab1bc8b687c8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,11 @@
|
||||
using UnityEngine;
|
||||
namespace Boxfriend.Extensions
|
||||
{
|
||||
public static class Vector2Extensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Changes a Vector2 into a Vector3 where the V2 Y axis is represented on the V3 Z axis
|
||||
/// </summary>
|
||||
public static Vector3 To3D(this Vector2 v2) => new Vector3(v2.x, 0, v2.y);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6f23bb55cb64524aca4cf21119a963b
|
||||
timeCreated: 1641338003
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "Utils",
|
||||
"rootNamespace": "Boxfriend",
|
||||
"references": [],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 753ee3d76e3343644abe0f9a98da4ded
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a3eedd59ab213c4fa2931489a6fe2ac
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Audio;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
public class AudioManager : SingletonBehaviour<AudioManager>
|
||||
{
|
||||
private ObjectPoolCircular<AudioSource> _sources;
|
||||
|
||||
[SerializeField] private AudioMixerGroup _audioMixer;
|
||||
[SerializeField] private AudioSource _sourcePrefab;
|
||||
|
||||
private const string _inWaitingName = "AudioManager - Ready";
|
||||
private void Awake () => _sources = new ObjectPoolCircular<AudioSource>(Create, x => x.enabled = true, ReturnSource, DestroySource, 32);
|
||||
|
||||
private AudioSource Create ()
|
||||
{
|
||||
AudioSource source;
|
||||
if (_sourcePrefab == null)
|
||||
{
|
||||
var go = new GameObject
|
||||
{
|
||||
name = _inWaitingName
|
||||
};
|
||||
go.transform.parent = transform;
|
||||
source = go.AddComponent<AudioSource>();
|
||||
} else
|
||||
{
|
||||
source = Instantiate(_sourcePrefab, Vector3.zero, Quaternion.identity);
|
||||
}
|
||||
|
||||
source.outputAudioMixerGroup = _audioMixer;
|
||||
source.enabled = false;
|
||||
return source;
|
||||
}
|
||||
|
||||
private AudioSource GetSource (string clipName, Vector3 position)
|
||||
{
|
||||
var source = _sources.FromPool();
|
||||
source.name = $"AudioManager - Playing: {clipName}";
|
||||
source.transform.position = position;
|
||||
|
||||
return source;
|
||||
}
|
||||
private void ReturnSource (AudioSource source)
|
||||
{
|
||||
source.name = _inWaitingName;
|
||||
source.clip = null;
|
||||
#if UNITY_2023_2_OR_NEWER
|
||||
source.resource = null;
|
||||
#endif
|
||||
source.enabled = false;
|
||||
}
|
||||
private void DestroySource (AudioSource source) => Destroy(source.gameObject);
|
||||
|
||||
public void PlayOneShot (AudioClip clip, float volume = 1f) => PlayOneShot(clip, Vector3.zero, volume);
|
||||
public void PlayOneShot (AudioClip clip, Vector3 position, float volume = 1f)
|
||||
{
|
||||
var source = GetSource(clip.name, position);
|
||||
source.PlayOneShot(clip, volume);
|
||||
StartCoroutine(ReturnWhenDone(source));
|
||||
}
|
||||
|
||||
#if !UNITY_2023_2_OR_NEWER
|
||||
public void Play (AudioClip clip) => Play(clip, Vector3.zero);
|
||||
public void Play (AudioClip clip, Vector3 position)
|
||||
{
|
||||
var source = GetSource(clip.name, position);
|
||||
source.clip = clip;
|
||||
source.Play();
|
||||
StartCoroutine(ReturnWhenDone(source));
|
||||
}
|
||||
#else
|
||||
public void Play (AudioResource resource) => Play(resource, Vector3.zero);
|
||||
public void Play(AudioResource resource, Vector3 position)
|
||||
{
|
||||
var source = GetSource(resource.name, position);
|
||||
source.resource = resource;
|
||||
source.Play();
|
||||
StartCoroutine(ReturnWhenDone(source));
|
||||
}
|
||||
#endif
|
||||
|
||||
private IEnumerator ReturnWhenDone (AudioSource source)
|
||||
{
|
||||
yield return new WaitUntil(() => !source.isPlaying);
|
||||
_sources.ToPool(source);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83b4266b5af7b0e43bdc7a1b68fc823d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
public class EventManager : Singleton<EventManager>
|
||||
{
|
||||
public delegate void Event(object arg, object sender);
|
||||
|
||||
private readonly Dictionary<string, Event> _events = new ();
|
||||
|
||||
public void RegisterEvent(string name)
|
||||
{
|
||||
if (_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} already registered");
|
||||
|
||||
_events.Add(name, null);
|
||||
}
|
||||
public void RegisterEvent (string name, Event callback)
|
||||
{
|
||||
if (_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} already registered");
|
||||
|
||||
_events.Add(name, callback);
|
||||
}
|
||||
|
||||
public void UnregisterEvent(string name)
|
||||
{
|
||||
if (!_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} not registered");
|
||||
|
||||
_events.Remove(name);
|
||||
}
|
||||
|
||||
public void SubscribeEvent (string name, Event callback)
|
||||
{
|
||||
if (!_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} not registered");
|
||||
|
||||
_events[name] += callback ?? throw new ArgumentNullException($"Event {name} callback is null");
|
||||
}
|
||||
|
||||
public void UnsubscribeEvent (string name, Event callback)
|
||||
{
|
||||
if (!_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} not registered");
|
||||
|
||||
_events[name] -= callback ?? throw new ArgumentNullException($"Event {name} callback is null");
|
||||
}
|
||||
|
||||
public void InvokeEvent (string name, object arg, object sender)
|
||||
{
|
||||
if (!_events.ContainsKey(name))
|
||||
throw new ArgumentException($"Event {name} not registered");
|
||||
|
||||
_events[name]?.Invoke(arg, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 11ec534d1045abc468621d74f78bfe8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
///<summary>
|
||||
/// Indicates a variable should only be assigned to in the inspector. Also allowed are field initializers and assignment in unity's Reset method.
|
||||
/// Requires Boxfriend.Analyzers to function.
|
||||
///</summary>
|
||||
public class InspectorOnlyAttribute : Attribute { }
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0c345dad59be440eadbaa7290ff86835
|
||||
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Pools objects of type T, will create new objects as necessary
|
||||
/// </summary>
|
||||
public class ObjectPool<T> where T : class
|
||||
{
|
||||
private readonly Stack<T> _stack = new ();
|
||||
private readonly Func<T> _objectCreator;
|
||||
private readonly Action<T> _returnObjectToPool, _getObjectFromPool, _destroyObject;
|
||||
private readonly int _maxSize;
|
||||
|
||||
/// <summary>
|
||||
/// Number of objects currently in the pool.
|
||||
/// </summary>
|
||||
public int Count => _stack.Count;
|
||||
|
||||
/// <param name="createObject">Creates and returns an object of the specified type.</param>
|
||||
/// <param name="getObjectFromPool">Action called on object when pulled from the pool or created.</param>
|
||||
/// <param name="returnObjectToPool">Action called on object when returned to pool.</param>
|
||||
/// <param name="onDestroyObject">Action called on object when it is to be destroyed. Can be null</param>
|
||||
/// <param name="defaultSize">Number of objects to immediately add to the pool</param>
|
||||
/// <param name="maxSize">Maximum number of objects in the pool</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public ObjectPool (Func<T> createObject, Action<T> getObjectFromPool, Action<T> returnObjectToPool, Action<T> onDestroyObject = null, int defaultSize = 10, int maxSize = 100)
|
||||
{
|
||||
if (maxSize < defaultSize)
|
||||
throw new ArgumentOutOfRangeException(nameof(maxSize), "maxSize must be greater than or equal to defaultSize");
|
||||
|
||||
if (defaultSize < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(defaultSize), "defaultSize must be greater than or equal to 0");
|
||||
|
||||
_returnObjectToPool = returnObjectToPool ?? throw new ArgumentNullException(nameof(returnObjectToPool));
|
||||
_getObjectFromPool = getObjectFromPool ?? throw new ArgumentNullException(nameof(getObjectFromPool));
|
||||
_objectCreator = createObject ?? throw new ArgumentNullException(nameof(createObject));
|
||||
_destroyObject = onDestroyObject;
|
||||
|
||||
_maxSize = maxSize;
|
||||
|
||||
for (var i = 0; i < defaultSize; i++)
|
||||
{
|
||||
ToPool(_objectCreator());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object from the pool or creates a new one if the pool is empty. Calls <see langword="Action"/> <see cref="_getObjectFromPool"/> on the object
|
||||
/// </summary>
|
||||
public T FromPool ()
|
||||
{
|
||||
var poolObject = _stack.Count > 0 ? _stack.Pop() : _objectCreator();
|
||||
_getObjectFromPool(poolObject);
|
||||
return poolObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the pool and calls <see langword="Action"/> <see cref="_returnObjectToPool"/> on it
|
||||
/// </summary>
|
||||
/// <param name="item">Item to be added</param>
|
||||
public void ToPool (T item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
|
||||
_returnObjectToPool(item);
|
||||
|
||||
if(_stack.Count >= _maxSize)
|
||||
{
|
||||
_destroyObject?.Invoke(item);
|
||||
return;
|
||||
}
|
||||
|
||||
_stack.Push(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the pool, calling <see langword="Action"/> <see cref="_destroyObject"/> on it if not null.
|
||||
/// </summary>
|
||||
public void EmptyPool()
|
||||
{
|
||||
if(_destroyObject is null)
|
||||
{
|
||||
_stack.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
while(_stack.Count > 0)
|
||||
{
|
||||
var obj = _stack.Pop();
|
||||
_destroyObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d79c194a0d7106f4dae9d2b503bae707
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Pools a specific number of objects, will reuse oldest active objects when all objects are in use.
|
||||
/// </summary>
|
||||
public class ObjectPoolCircular<T> where T : class
|
||||
{
|
||||
|
||||
private Queue<T> _activeQueue, _inactiveQueue;
|
||||
private readonly Func<T> _objectCreator;
|
||||
private readonly Action<T> _returnObjectToPool, _getObjectFromPool, _destroyObject;
|
||||
private readonly int _size;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of objects in the pool.
|
||||
/// </summary>
|
||||
public int Count => _size;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of currently active pooled objects
|
||||
/// </summary>
|
||||
public int ActiveCount => _activeQueue.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of currently inactive pooled objects
|
||||
/// </summary>
|
||||
public int InactiveCount => _inactiveQueue.Count;
|
||||
|
||||
/// <param name="createObject">Creates and returns an object of the specified type.</param>
|
||||
/// <param name="getObjectFromPool">Action called on object when pulled from the pool or created.</param>
|
||||
/// <param name="returnObjectToPool">Action called on object when returned to pool.</param>
|
||||
/// <param name="onDestroyObject">Action called on object when it is to be destroyed. Can be null</param>
|
||||
/// <param name="size">Total number of objects in the pool</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Size must be greater than zero</exception>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public ObjectPoolCircular (Func<T> createObject, Action<T> getObjectFromPool, Action<T> returnObjectToPool, Action<T> onDestroyObject = null, int size = 100)
|
||||
{
|
||||
if (size <= 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(size), "size must be greater than zero");
|
||||
|
||||
|
||||
_returnObjectToPool = returnObjectToPool ?? throw new ArgumentNullException(nameof(returnObjectToPool));
|
||||
_getObjectFromPool = getObjectFromPool ?? throw new ArgumentNullException(nameof(getObjectFromPool));
|
||||
_objectCreator = createObject ?? throw new ArgumentNullException(nameof(createObject));
|
||||
_destroyObject = onDestroyObject;
|
||||
|
||||
_size = size;
|
||||
_inactiveQueue = new(size);
|
||||
_activeQueue = new(size);
|
||||
for (var i = 0; i < size; i++)
|
||||
{
|
||||
var obj = _objectCreator();
|
||||
_returnObjectToPool(obj);
|
||||
_inactiveQueue.Enqueue(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an object from the pool or reuses the oldest active object if all pooled objects are in use. Calls <see langword="Action"/> <see cref="_getObjectFromPool"/> on the object
|
||||
/// Will call <see langword="Action"/> <see cref="_returnObjectToPool"/> if reusing an active object.
|
||||
/// </summary>
|
||||
public T FromPool ()
|
||||
{
|
||||
if(_inactiveQueue.Count + _activeQueue.Count == 0)
|
||||
throw new InvalidOperationException("Object pool has been cleared, there is nothing left to get");
|
||||
|
||||
|
||||
T poolObject;
|
||||
if(_inactiveQueue.Count == 0)
|
||||
{
|
||||
poolObject = _activeQueue.Dequeue();
|
||||
_returnObjectToPool(poolObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
poolObject = _inactiveQueue.Dequeue();
|
||||
}
|
||||
|
||||
_getObjectFromPool(poolObject);
|
||||
_activeQueue.Enqueue(poolObject);
|
||||
return poolObject;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an item to the pool and calls <see langword="Action"/> <see cref="_returnObjectToPool"/> on it
|
||||
/// Generates garbage if item is not the oldest object pulled from the pool
|
||||
/// </summary>
|
||||
/// <param name="item">Item to be added</param>
|
||||
public void ToPool (T item)
|
||||
{
|
||||
if (item == null) throw new ArgumentNullException(nameof(item));
|
||||
|
||||
if(_activeQueue.Peek() == item)
|
||||
_activeQueue.Dequeue();
|
||||
else
|
||||
_activeQueue = new Queue<T>(_activeQueue.Where(x => x != item));
|
||||
|
||||
_returnObjectToPool(item);
|
||||
_inactiveQueue.Enqueue(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all active items to the inactive queue
|
||||
/// </summary>
|
||||
public void ReturnAllToPool()
|
||||
{
|
||||
while(ActiveCount > 0)
|
||||
{
|
||||
var obj = _activeQueue.Dequeue();
|
||||
_returnObjectToPool(obj);
|
||||
_inactiveQueue.Enqueue(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all items from the pool, calling <see langword="Action"/> <see cref="_destroyObject"/> on it if not null.
|
||||
/// Does not call <see langword="Action"/> <see cref="_returnObjectToPool"/>.
|
||||
/// </summary>
|
||||
public void EmptyPool()
|
||||
{
|
||||
if(_destroyObject is null)
|
||||
{
|
||||
_activeQueue.Clear();
|
||||
_inactiveQueue.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
while(_activeQueue.Count > 0)
|
||||
{
|
||||
var obj = _activeQueue.Dequeue();
|
||||
_destroyObject(obj);
|
||||
}
|
||||
|
||||
while(_inactiveQueue.Count > 0)
|
||||
{
|
||||
var obj = _inactiveQueue.Dequeue();
|
||||
_destroyObject(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c9d2a88b19aa9ad4ab0b9adb718f2a0a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,81 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Class to draw debug information such as physics2d casts
|
||||
/// </summary>
|
||||
public static class PhysicsCastDebug
|
||||
{
|
||||
/// <summary>
|
||||
/// Casts a Physics2D BoxCast with debug lines drawn
|
||||
/// </summary>
|
||||
public static RaycastHit2D BoxCast(Vector2 origin,
|
||||
Vector2 size,
|
||||
float angle,
|
||||
Vector2 direction,
|
||||
float distance = 0,
|
||||
int layerMask = Physics2D.AllLayers,
|
||||
float minDepth = -Mathf.Infinity,
|
||||
float maxDepth = Mathf.Infinity)
|
||||
{
|
||||
var hit = Physics2D.BoxCast(origin, size, angle, direction, distance, layerMask, minDepth, maxDepth);
|
||||
|
||||
//Setting up the points to draw the origin box and end box
|
||||
var points = new Vector2[8];
|
||||
var width = size.x * 0.5f;
|
||||
var height = size.y * 0.5f;
|
||||
|
||||
points[0] = new Vector2(-width, height); //Upper left corner
|
||||
points[1] = new Vector2(width, height); //Upper right corner
|
||||
points[2] = new Vector2(width, -height); //Lower right corner
|
||||
points[3] = new Vector2(-width, -height); //Lower left corner
|
||||
|
||||
//Calculates origin box corners using provided angle and origin point
|
||||
var q = Quaternion.AngleAxis(angle, new Vector3(0, 0, 1));
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
points[i] = q * points[i];
|
||||
points[i] += origin;
|
||||
}
|
||||
|
||||
//Calculates end points using origin box points and provided distance
|
||||
var realDistance = direction.normalized * distance;
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
points[i + 4] = points[i] + realDistance;
|
||||
}
|
||||
|
||||
//Draw hit normal if a hit was detected
|
||||
if (hit) Debug.DrawLine(hit.point, hit.point + hit.normal.normalized*0.2f, Color.yellow);
|
||||
|
||||
//Draw boxes
|
||||
var color = hit ? Color.green : Color.red;
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var j = i == 3 ? 0 : i + 1;
|
||||
//Draws origin box using first 4 points
|
||||
Debug.DrawLine(points[i],points[j], color);
|
||||
}
|
||||
|
||||
//Exit early if distance is 0, don't need to draw end position or translation if there is no distance
|
||||
if (distance == 0) return hit;
|
||||
|
||||
//Draws end box using last 4 points
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var j = i == 3 ? 0 : i + 1;
|
||||
Debug.DrawLine(points[i+4],points[j+4], color);
|
||||
}
|
||||
//Shows translation from origin box to end box in grey
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
var j = i + 4;
|
||||
Debug.DrawLine(points[i],points[j], Color.grey);
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 749412a4d084e6d43902f628e84d38e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,14 @@
|
||||
using System;
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
public abstract class Singleton<T> where T : class, new()
|
||||
{
|
||||
public static T Instance => _instance;
|
||||
private static T _instance;
|
||||
|
||||
private static T InitializeSingleton (T obj = null) => _instance = obj ?? new T();
|
||||
|
||||
public Singleton():this(null){}
|
||||
public Singleton (T obj) => InitializeSingleton(obj);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d5aa066b76ef4b48b79d2921c301f50f
|
||||
timeCreated: 1640819249
|
||||
@@ -0,0 +1,40 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
public abstract class SingletonBehaviour<T> : MonoBehaviour where T : SingletonBehaviour<T>
|
||||
{
|
||||
private static T _instance;
|
||||
[SerializeField] protected bool _dontDestroy;
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if( _instance == null )
|
||||
{
|
||||
var go = new GameObject(typeof(T).Name);
|
||||
go.AddComponent<T>();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
if (_instance == null)
|
||||
_instance = value;
|
||||
else if (value != _instance)
|
||||
Destroy(value.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void __internalAwake ()
|
||||
{
|
||||
Instance = (T)this;
|
||||
|
||||
if(_dontDestroy)
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a532c8d866cc4ff1a614091cc5e3c80b
|
||||
timeCreated: 1640820898
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple timer class that will call an action on timer complete
|
||||
/// </summary>
|
||||
public class TimerUtil
|
||||
{
|
||||
public static TimerUtil Timer (Action action, float time, string name = "TimerObject")
|
||||
{
|
||||
GameObject timerObj = new GameObject(name, typeof(TimerMonoBehaviour));
|
||||
TimerUtil timerUtil = new TimerUtil(action, time, timerObj);
|
||||
timerObj.GetComponent<TimerMonoBehaviour>().onUpdate = timerUtil.UpdateTimer;
|
||||
return timerUtil;
|
||||
}
|
||||
|
||||
private class TimerMonoBehaviour : MonoBehaviour
|
||||
{
|
||||
public Action onUpdate;
|
||||
private void Update ()
|
||||
{
|
||||
onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private Action _act;
|
||||
private float _time;
|
||||
private GameObject _timerObj;
|
||||
|
||||
public bool isEnded { get; private set; }
|
||||
public bool isPaused { get; set; }
|
||||
public float TimeRemaining => _time;
|
||||
|
||||
private TimerUtil (Action action, float time, GameObject timerObj)
|
||||
{
|
||||
_act = action;
|
||||
_time = time;
|
||||
_timerObj = timerObj;
|
||||
|
||||
//Ensuring the bools are correctly initialized as false
|
||||
isEnded = false;
|
||||
isPaused = false;
|
||||
}
|
||||
|
||||
private void UpdateTimer ()
|
||||
{
|
||||
if (isEnded || isPaused) return;
|
||||
|
||||
_time -= Time.deltaTime;
|
||||
if (_time <= 0)
|
||||
{
|
||||
EndWithAction();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the timer and destroys associated GameObject. Cannot be undone
|
||||
/// </summary>
|
||||
public void EndTimer ()
|
||||
{
|
||||
isEnded = true;
|
||||
UnityEngine.Object.Destroy(_timerObj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends the timer and invokes its action. Cannot be undone.
|
||||
/// </summary>
|
||||
public void EndWithAction ()
|
||||
{
|
||||
_act();
|
||||
EndTimer();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds specified time to the currently active timer
|
||||
/// </summary>
|
||||
public void AddTime (float time)
|
||||
{
|
||||
if (isEnded) return;
|
||||
|
||||
_time += time;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f923888d5435594a9fd44c8ba579913
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Boxfriend.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Random useful methods
|
||||
/// </summary>
|
||||
public class Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the supplied ints have opposite signs
|
||||
/// </summary>
|
||||
public static bool OppositeSigns (int x, int y)
|
||||
{
|
||||
return ((x ^ y) < 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats number of bytes to string
|
||||
/// </summary>
|
||||
public static string FormatBytes(long bytes)
|
||||
{
|
||||
string[] Suffix = {"B", "KB", "MB", "GB", "TB"};
|
||||
int i;
|
||||
double dblSByte = bytes;
|
||||
for (i = 0; i < Suffix.Length && bytes >= 1000; i++, bytes /= 1000)
|
||||
{
|
||||
dblSByte = bytes / 1000.0;
|
||||
}
|
||||
|
||||
return $"{dblSByte:0.##} {Suffix[i]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cca214112e1e41a48b15b4b1e145cbd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user