2022-09-06 13:47:45 +08:00

815 lines
19 KiB
C#

// =====================================================================
// Copyright 2013-2017 Fluffy Underware
// All rights reserved
//
// http://www.fluffyunderware.com
// =====================================================================
using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Random = UnityEngine.Random;
namespace FluffyUnderware.DevTools
{
[System.Serializable]
public struct FloatRegion : IEquatable<FloatRegion>
{
public float From;
public float To;
public bool SimpleValue;
public FloatRegion(float value)
{
From = value;
To = value;
SimpleValue = true;
}
public FloatRegion(float A, float B)
{
this.From = A;
this.To = B;
SimpleValue = false;
}
public static FloatRegion ZeroOne
{
get { return new FloatRegion(0, 1); }
}
public void MakePositive()
{
if (To < From)
{
float temp = To;
To = From;
From = temp;
}
}
public void Clamp(float low, float high)
{
Low = Mathf.Clamp(Low, low, high);
High = Mathf.Clamp(High, low, high);
}
public bool Positive
{
get { return From <= To; }
}
public float Low
{
get { return (Positive) ? From : To; }
set
{
if (Positive)
From = value;
else
To = value;
}
}
public float High
{
get { return (Positive) ? To : From; }
set
{
if (Positive)
To = value;
else
From = value;
}
}
public float Random
{
get
{
return UnityEngine.Random.Range(From, To);
}
}
/// <summary>
/// Gets the next value in the range
/// <remarks>Depending on the value of <see cref="SimpleValue"/>, this call will or will not make the Random generator's seed progress</remarks>
/// </summary>
public float Next
{
get
{
if (SimpleValue)
return From;
else
return Random;
}
}
public float Length
{
get
{
return To - From;
}
}
public float LengthPositive
{
get { return (Positive) ? To - From : From - To; }
}
public override string ToString()
{
return string.Format("({0:F2}-{1:F2})", From, To);
}
public override int GetHashCode()
{
return From.GetHashCode() ^ To.GetHashCode() << 2;
}
public bool Equals(FloatRegion other)
{
return From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object other)
{
if (!(other is FloatRegion))
{
return false;
}
FloatRegion r = (FloatRegion)other;
return From.Equals(r.From) && To.Equals(r.To);
}
public static FloatRegion operator +(FloatRegion a, FloatRegion b)
{
return new FloatRegion(a.From + b.From, a.To + b.To);
}
public static FloatRegion operator -(FloatRegion a, FloatRegion b)
{
return new FloatRegion(a.From - b.From, a.To - b.To);
}
public static FloatRegion operator -(FloatRegion a)
{
return new FloatRegion(-a.From, -a.To);
}
public static FloatRegion operator *(FloatRegion a, float v)
{
return new FloatRegion(a.From * v, a.To * v);
}
public static FloatRegion operator *(float v, FloatRegion a)
{
return new FloatRegion(a.From * v, a.To * v);
}
public static FloatRegion operator /(FloatRegion a, float v)
{
return new FloatRegion(a.From / v, a.To / v);
}
public static bool operator ==(FloatRegion lhs, FloatRegion rhs)
{
return lhs.SimpleValue == rhs.SimpleValue && Mathf.Approximately(lhs.From, rhs.From) && Mathf.Approximately(lhs.To, rhs.To);
}
public static bool operator !=(FloatRegion lhs, FloatRegion rhs)
{
return lhs.SimpleValue != rhs.SimpleValue || !Mathf.Approximately(lhs.From, rhs.From) || !Mathf.Approximately(lhs.To, rhs.To);
}
}
[System.Serializable]
public struct IntRegion : IEquatable<IntRegion>
{
public int From;
public int To;
public bool SimpleValue;
public IntRegion(int value)
{
From = value;
To = value;
SimpleValue = true;
}
public IntRegion(int A, int B)
{
this.From = A;
this.To = B;
SimpleValue = false;
}
public static IntRegion ZeroOne
{
get { return new IntRegion(0, 1); }
}
public void MakePositive()
{
if (To < From)
{
int temp = To;
To = From;
From = temp;
}
}
public void Clamp(int low, int high)
{
Low = Mathf.Clamp(Low, low, high);
High = Mathf.Clamp(High, low, high);
}
public bool Positive
{
get { return From <= To; }
}
public int Low
{
get { return (Positive) ? From : To; }
set
{
if (Positive)
From = value;
else
To = value;
}
}
public int High
{
get { return (Positive) ? To : From; }
set
{
if (Positive)
To = value;
else
From = value;
}
}
public int Random
{
get
{
return UnityEngine.Random.Range(From, To);
}
}
public int Length
{
get
{
return To - From;
}
}
public int LengthPositive
{
get { return (Positive) ? To - From : From - To; }
}
public override string ToString()
{
return string.Format("({0}-{1})", From, To);
}
public override int GetHashCode()
{
return From.GetHashCode() ^ To.GetHashCode() << 2;
}
public bool Equals(IntRegion other)
{
return From.Equals(other.From) && To.Equals(other.To);
}
public override bool Equals(object other)
{
if (!(other is IntRegion))
{
return false;
}
IntRegion r = (IntRegion)other;
return From.Equals(r.From) && To.Equals(r.To);
}
public static IntRegion operator +(IntRegion a, IntRegion b)
{
return new IntRegion(a.From + b.From, a.To + b.To);
}
public static IntRegion operator -(IntRegion a, IntRegion b)
{
return new IntRegion(a.From - b.From, a.To - b.To);
}
public static IntRegion operator -(IntRegion a)
{
return new IntRegion(-a.From, -a.To);
}
public static IntRegion operator *(IntRegion a, int v)
{
return new IntRegion(a.From * v, a.To * v);
}
public static IntRegion operator *(int v, IntRegion a)
{
return new IntRegion(a.From * v, a.To * v);
}
public static IntRegion operator /(IntRegion a, int v)
{
return new IntRegion(a.From / v, a.To / v);
}
public static bool operator ==(IntRegion lhs, IntRegion rhs)
{
return lhs.From == rhs.From && lhs.To == rhs.To && lhs.SimpleValue != rhs.SimpleValue;
}
public static bool operator !=(IntRegion lhs, IntRegion rhs)
{
return lhs.From != rhs.From || lhs.To != rhs.To || lhs.SimpleValue != rhs.SimpleValue;
}
}
/// <summary>
/// Simple but effective ShuffleBag<T> implementation
/// </summary>
public class WeightedRandom<T>
{
List<T> mData;
int mCurrentPosition = -1;
T mCurrentItem;
public int Seed { get; set; }
public bool RandomizeSeed { get; set; }
private int Capacity { get { return mData.Capacity; } }
public int Size { get { return mData.Count; } }
public WeightedRandom(int initCapacity = 0)
{
mData = new List<T>(initCapacity);
}
public WeightedRandom(int initCapacity, int seed):this(initCapacity)
{
Seed = seed;
}
/// <summary>
/// Adds items to the bag
/// </summary>
/// <param name="item">the item</param>
/// <param name="amount">number of times this item should be added</param>
public void Add(T item, int amount)
{
for (int i = 0; i < amount; i++)
mData.Add(item);
mCurrentPosition = Size - 1;
}
/// <summary>
/// Gets a random item from the bag
/// </summary>
/// <returns>an item</returns>
public T Next()
{
if (mCurrentPosition < 1)
{
mCurrentPosition = Size - 1;
mCurrentItem = mData[0];
return mCurrentItem;
}
Random.State s = Random.state;
if (RandomizeSeed)
Seed = Random.Range(0, int.MaxValue);
Random.InitState(Seed);
int idx = Random.Range(0, mCurrentPosition);
Random.state = s;
mCurrentItem = mData[idx];
mData[idx] = mData[mCurrentPosition];
mData[mCurrentPosition] = mCurrentItem;
mCurrentPosition--;
return mCurrentItem;
}
/// <summary>
/// Refill the bag
/// </summary>
public void Reset()
{
mCurrentPosition = Size - 1;
}
/// <summary>
/// Clear the bag
/// </summary>
public void Clear()
{
mData.Clear();
mCurrentPosition = -1;
}
}
public class Ring<T> : IList<T>
{
List<T> mList;
public int Size { get; private set; }
int mIndex;
public Ring(int size)
{
mList = new List<T>(size);
Size = size;
}
public void Add(T item)
{
if (mList.Count == Size)
{
mList[mIndex++] = item;
if (mIndex == mList.Count)
mIndex = 0;
}
else
mList.Add(item);
}
public void Clear()
{
mList.Clear();
mIndex = 0;
}
public int IndexOf(T item)
{
return mList.IndexOf(item);
}
public void Insert(int index, T item)
{
throw new System.NotSupportedException();
}
public void RemoveAt(int index)
{
throw new System.NotSupportedException();
}
public T this[int index]
{
get
{
return mList[index];
}
set
{
mList[index] = value;
}
}
public IEnumerator GetEnumerator()
{
return mList.GetEnumerator();
}
public bool Contains(T item)
{
return mList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
mList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return mList.Count; }
}
public bool IsReadOnly
{
get { throw new System.NotSupportedException(); }
}
public bool Remove(T item)
{
return mList.Remove(item);
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
throw new System.NotImplementedException();
}
}
public class Pool<T> : IPool
{
List<T> mObjects = new List<T>();
public string Identifier { get; set; }
public PoolSettings Settings { get; protected set; }
public System.Type Type
{
get { return typeof(T); }
}
double mLastTime;
double mDeltaTime;
public Pool(PoolSettings settings = null)
{
Settings = settings ?? new PoolSettings();
Identifier = typeof(T).FullName;
mLastTime = DTTime.TimeSinceStartup + UnityEngine.Random.Range(0, Settings.Speed);
if (Settings.Prewarm)
Reset();
}
public void Update()
{
mDeltaTime += DTTime.TimeSinceStartup - mLastTime;
mLastTime = DTTime.TimeSinceStartup;
if (Settings.Speed > 0)
{
int c = (int)(mDeltaTime / Settings.Speed);
mDeltaTime -= c;
if (Count > Settings.Threshold)
{
c = Mathf.Min(c, Count - Settings.Threshold);
while (c-- > 0)
{
destroy(mObjects[0]);
mObjects.RemoveAt(0);
log("Threshold exceeded: Deleting item");
}
}
else if (Count < Settings.MinItems)
{
c = Mathf.Min(c, Settings.MinItems - Count);
while (c-- > 0)
{
mObjects.Add(create());
log("Below MinItems: Adding item");
}
}
}
else
mDeltaTime = 0;
}
public void Reset()
{
if (Application.isPlaying)
{
while (Count < Settings.MinItems)
{
mObjects.Add(create());
}
while (Count > Settings.Threshold)
{
destroy(mObjects[0]);
mObjects.RemoveAt(0);
}
log("Prewarm/Reset");
}
}
public void Clear()
{
log("Clear");
for (int i = 0; i < Count; i++)
destroy(mObjects[i]);
mObjects.Clear();
}
public int Count
{
get { return mObjects.Count; }
}
public virtual T Pop(Transform parent = null)
{
T item = default(T);
if (Count > 0)
{
item = mObjects[0];
mObjects.RemoveAt(0);
}
else
{
if (Settings.AutoCreate || !Application.isPlaying)
{
log("Auto create item");
item = create();
}
}
if (item != null)
{
sendAfterPop(item);
setParent(item, parent);
log("Pop " + item);
}
return item;
}
public virtual void Push(T item)
{
log("Push " + item);
if (Application.isPlaying && item != null)
{
sendBeforePush(item);
mObjects.Add(item);
}
}
protected virtual void sendBeforePush(T item)
{
if (item is IPoolable)
((IPoolable)item).OnBeforePush();
}
protected virtual void sendAfterPop(T item)
{
if (item is IPoolable)
((IPoolable)item).OnAfterPop();
}
protected virtual void setParent(T item, Transform parent)
{
}
protected virtual T create()
{
return System.Activator.CreateInstance<T>();
}
protected virtual void destroy(T item)
{
}
void log(string msg)
{
if (Settings.Debug)
Debug.Log(string.Format("[{0}] ({1} items) {2}", Identifier, Count, msg));
}
}
[System.Serializable]
public class PoolSettings
{
[SerializeField]
bool m_Prewarm = false;
[SerializeField]
bool m_AutoCreate = true;
[SerializeField]
bool m_AutoEnableDisable = true;
[Positive]
[SerializeField]
int m_MinItems = 0;
[Positive]
[SerializeField]
int m_Threshold = 0;
[Positive]
[SerializeField]
float m_Speed = 1;
public bool Debug;
public PoolSettings() { }
public PoolSettings(PoolSettings src)
{
Prewarm = src.Prewarm;
AutoCreate = src.AutoCreate;
MinItems = src.MinItems;
Threshold = src.Threshold;
Speed = src.Speed;
Debug = src.Debug;
}
public bool Prewarm
{
get { return m_Prewarm; }
set
{
if (m_Prewarm != value)
m_Prewarm = value;
}
}
public bool AutoCreate
{
get { return m_AutoCreate; }
set
{
if (m_AutoCreate != value)
m_AutoCreate = value;
}
}
public bool AutoEnableDisable
{
get { return m_AutoEnableDisable; }
set
{
if (m_AutoEnableDisable != value)
m_AutoEnableDisable = value;
}
}
public int MinItems
{
get { return m_MinItems; }
set
{
int v = Mathf.Max(0, value);
if (m_MinItems != v)
m_MinItems = v;
}
}
public int Threshold
{
get { return m_Threshold; }
set
{
int v = Mathf.Max(MinItems, value);
if (m_Threshold != v)
m_Threshold = v;
}
}
public float Speed
{
get { return m_Speed; }
set
{
float v = Mathf.Max(0, value);
if (m_Speed != v)
m_Speed = v;
}
}
public void OnValidate()
{
MinItems = m_MinItems;
Threshold = m_Threshold;
Speed = m_Speed;
}
}
public interface IPool
{
string Identifier { get; set; }
PoolSettings Settings { get; }
void Clear();
void Reset();
void Update();
int Count { get; }
}
public interface IPoolable
{
void OnBeforePush();
void OnAfterPop();
}
}