轨迹插件引入
This commit is contained in:
1 parent
89b5275efd
commit
195893a1b0
1023 files changed
+378944
-18404
No files matched your search
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d39857b80c523445aeef7d47cfd5296
|
||||
folderAsset: yes
|
||||
timeCreated: 1447959531
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,533 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
#region ### Interfaces ###
|
||||
/// <summary>
|
||||
/// Interface for attributes that change group parsing options
|
||||
/// </summary>
|
||||
public interface IDTGroupParsingAttribute
|
||||
{
|
||||
string Path { get; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Interface for attributes that change field parsing options
|
||||
/// </summary>
|
||||
public interface IDTFieldParsingAttribute { }
|
||||
/// <summary>
|
||||
/// Interface for attributes that change group rendering options
|
||||
/// </summary>
|
||||
public interface IDTGroupRenderAttribute { }
|
||||
/// <summary>
|
||||
/// Interface for attributes that change field rendering options
|
||||
/// </summary>
|
||||
public interface IDTFieldRenderAttribute { }
|
||||
|
||||
#endregion
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
|
||||
public class DTVersionAttribute : System.Attribute
|
||||
{
|
||||
public readonly string Version;
|
||||
|
||||
public DTVersionAttribute(string version)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region ### Base Attributes ###
|
||||
//OPTIM: all the attributes bellow (and maybe DTVersionAttribute too) seem to be used only in editor code. If so, and there overhead is big, make their compilation conditional to gain performance in the build applications
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
public class DTAttribute : System.Attribute, IComparable
|
||||
{
|
||||
public int TypeSort { get; protected set; }
|
||||
public int Sort = 100;
|
||||
public bool ShowBelowProperty;
|
||||
public int Space;
|
||||
|
||||
public virtual int CompareTo(object obj)
|
||||
{
|
||||
DTAttribute other = (DTAttribute)obj;
|
||||
int v = ShowBelowProperty.CompareTo(other.ShowBelowProperty);
|
||||
if (v == 0)
|
||||
{
|
||||
int ts = TypeSort.CompareTo(other.TypeSort);
|
||||
if (ts == 0)
|
||||
return Sort.CompareTo(other.Sort);
|
||||
else
|
||||
return ts;
|
||||
}
|
||||
else
|
||||
return v;
|
||||
}
|
||||
|
||||
public DTAttribute(int sortOrder, bool showBelow = false)
|
||||
{
|
||||
TypeSort = sortOrder;
|
||||
ShowBelowProperty = showBelow;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Explicitely add the next field to a specific group
|
||||
/// </summary>
|
||||
public class GroupAttribute : DTAttribute, IDTGroupParsingAttribute, IDTGroupRenderAttribute
|
||||
{
|
||||
public string Path
|
||||
{
|
||||
get { return mPath; }
|
||||
protected set
|
||||
{
|
||||
PathIsAbsolute = !string.IsNullOrEmpty(value) && value.StartsWith("@");
|
||||
if (PathIsAbsolute)
|
||||
{
|
||||
mPath = value.Substring(1);
|
||||
if (string.IsNullOrEmpty(mPath))
|
||||
mPath = null;
|
||||
}
|
||||
else
|
||||
mPath = value;
|
||||
}
|
||||
}
|
||||
public bool PathIsAbsolute { get; private set; }
|
||||
|
||||
public bool Expanded = true;
|
||||
public bool Invisible;
|
||||
public string Label = null;
|
||||
public string Tooltip = null;
|
||||
public string HelpURL = null;
|
||||
|
||||
string mPath;
|
||||
|
||||
public GroupAttribute(string pathAndName)
|
||||
: base(15)
|
||||
{
|
||||
Path = pathAndName;
|
||||
}
|
||||
}
|
||||
|
||||
public class ActionAttribute : DTAttribute
|
||||
{
|
||||
public enum ActionEnum { Show, Hide, Enable, Disable, ShowInfo, ShowWarning, ShowError, Callback }
|
||||
public enum ActionPositionEnum { Above, Below }
|
||||
public ActionEnum Action = ActionEnum.Callback;
|
||||
public ActionPositionEnum Position = ActionPositionEnum.Below;
|
||||
public object ActionData;
|
||||
|
||||
MethodInfo mCallback;
|
||||
|
||||
protected ActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
|
||||
: base(50)
|
||||
{
|
||||
ActionData = actionData;
|
||||
Action = action;
|
||||
}
|
||||
|
||||
public void Callback(object classInstance)
|
||||
{
|
||||
string methodName = ActionData as string;
|
||||
if (!string.IsNullOrEmpty(methodName))
|
||||
{
|
||||
if (mCallback == null)
|
||||
mCallback = classInstance.GetType().MethodByName(methodName, true, true);
|
||||
|
||||
if (mCallback != null)
|
||||
mCallback.Invoke(classInstance, null);
|
||||
else
|
||||
Debug.LogWarningFormat("[DevTools] Unable to find method '{0}' at class '{1}' !", methodName, classInstance.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ConditionalAttribute : ActionAttribute
|
||||
{
|
||||
public enum OperatorEnum { AND, OR };
|
||||
public class Condition
|
||||
{
|
||||
public string FieldName;
|
||||
public FieldInfo FieldInfo;
|
||||
public PropertyInfo PropertyInfo;
|
||||
public object CompareTo;
|
||||
public bool CompareFalse;
|
||||
public OperatorEnum Operator;
|
||||
public MethodInfo MethodInfo;
|
||||
public string MethodName;
|
||||
}
|
||||
|
||||
public Condition[] Conditions;
|
||||
|
||||
|
||||
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false)
|
||||
: base(null, ActionEnum.Show)
|
||||
{
|
||||
TypeSort = 55;
|
||||
Conditions = new Condition[1]
|
||||
{
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty,
|
||||
CompareTo=compareTo,
|
||||
CompareFalse=compareFalse
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2)
|
||||
: base(null, ActionEnum.Show)
|
||||
{
|
||||
TypeSort = 55;
|
||||
Conditions = new Condition[2]
|
||||
{
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty,
|
||||
CompareTo=compareTo,
|
||||
CompareFalse=compareFalse
|
||||
},
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty2,
|
||||
CompareTo=compareTo2,
|
||||
CompareFalse=compareFalse2,
|
||||
Operator=op
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ConditionalAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2,
|
||||
string fieldOrProperty3, object compareTo3, bool compareFalse3)
|
||||
: base(null, ActionEnum.Show)
|
||||
{
|
||||
TypeSort = 55;
|
||||
Conditions = new Condition[3]
|
||||
{
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty,
|
||||
CompareTo=compareTo,
|
||||
CompareFalse=compareFalse
|
||||
},
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty2,
|
||||
CompareTo=compareTo2,
|
||||
CompareFalse=compareFalse2,
|
||||
Operator=op
|
||||
},
|
||||
new Condition()
|
||||
{
|
||||
FieldName=fieldOrProperty3,
|
||||
CompareTo=compareTo3,
|
||||
CompareFalse=compareFalse3,
|
||||
Operator=op
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ConditionalAttribute(string methodToQuery)
|
||||
: base(null, ActionEnum.Show)
|
||||
{
|
||||
TypeSort = 55;
|
||||
Conditions = new Condition[1]
|
||||
{
|
||||
new Condition()
|
||||
{
|
||||
MethodName=methodToQuery,
|
||||
CompareTo=null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public virtual bool ConditionMet(object classInstance)
|
||||
{
|
||||
bool result = evaluate(Conditions[0], classInstance);
|
||||
|
||||
for (int i = 1; i < Conditions.Length; i++)
|
||||
{
|
||||
Condition c = Conditions[i];
|
||||
switch (c.Operator)
|
||||
{
|
||||
case OperatorEnum.AND:
|
||||
result = result && evaluate(c, classInstance);
|
||||
break;
|
||||
case OperatorEnum.OR:
|
||||
result = result || evaluate(c, classInstance);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool evaluate(Condition cond, object classInstance)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(cond.MethodName))
|
||||
{
|
||||
if (cond.MethodInfo == null)
|
||||
cond.MethodInfo = classInstance.GetType().MethodByName(cond.MethodName, true, true);
|
||||
if (cond.MethodInfo != null)
|
||||
{
|
||||
if (cond.CompareFalse)
|
||||
return !(bool)cond.MethodInfo.Invoke(classInstance, null);
|
||||
else
|
||||
return (bool)cond.MethodInfo.Invoke(classInstance, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarningFormat("[DevTools] Unable to find method '{0}' at class '{1}' !", cond.MethodName, classInstance.GetType().Name);
|
||||
return cond.CompareFalse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cond.FieldInfo == null)
|
||||
{
|
||||
cond.FieldInfo = classInstance.GetType().FieldByName(cond.FieldName, true, true);
|
||||
if (cond.FieldInfo == null)
|
||||
{
|
||||
cond.PropertyInfo = classInstance.GetType().PropertyByName(cond.FieldName, true, true);
|
||||
}
|
||||
}
|
||||
object value = null;
|
||||
if (cond.FieldInfo != null)
|
||||
value = cond.FieldInfo.GetValue(classInstance);
|
||||
else if (cond.PropertyInfo != null)
|
||||
value = cond.PropertyInfo.GetValue(classInstance, null);
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
return (cond.CompareTo == null && !cond.CompareFalse);
|
||||
}
|
||||
else
|
||||
return value.Equals(cond.CompareTo) == !cond.CompareFalse;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Attributes handling Nesting/Parsing ###
|
||||
|
||||
public class SortOrderAttribute : DTAttribute, IDTFieldParsingAttribute
|
||||
{
|
||||
public SortOrderAttribute(int sort = 100) : base(0)
|
||||
{
|
||||
Sort = sort;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show children inside a section. Start the path with "@" to break out of any previous sections
|
||||
/// </summary>
|
||||
public class AsGroupAttribute : GroupAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
|
||||
{
|
||||
|
||||
public AsGroupAttribute(string pathAndName = null)
|
||||
: base(pathAndName)
|
||||
{
|
||||
TypeSort = 10;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show all subsequent fields inside a section rendered as a tab
|
||||
/// </summary>
|
||||
public class TabAttribute : GroupAttribute
|
||||
{
|
||||
public readonly string TabName;
|
||||
public readonly string TabBarName;
|
||||
|
||||
public TabAttribute(string pathAndName)
|
||||
: base("")
|
||||
{
|
||||
TypeSort = 10;
|
||||
string p;
|
||||
split(pathAndName, out p, out TabBarName, out TabName);
|
||||
Path = p;
|
||||
}
|
||||
|
||||
static bool split(string pathAndName, out string path, out string tabBar, out string tabname)
|
||||
{
|
||||
string[] p = pathAndName.Split('/');
|
||||
path = string.Empty;
|
||||
tabBar = string.Empty;
|
||||
tabname = pathAndName;
|
||||
if (p.Length == 0)
|
||||
return false;
|
||||
else if (p.Length == 1)
|
||||
{
|
||||
tabname = p[0];
|
||||
tabBar = "Default";
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tabname = p[p.Length - 1];
|
||||
tabBar = p[p.Length - 2];
|
||||
path = string.Join("/", p, 0, p.Length - 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show all subsequent fields inside a section.
|
||||
/// </summary>
|
||||
public class SectionAttribute : GroupAttribute
|
||||
{
|
||||
public bool Fixed;
|
||||
|
||||
public SectionAttribute(string name, bool expanded = true, bool fix = false, int sort = 100)
|
||||
: base(name)
|
||||
{
|
||||
Expanded = expanded;
|
||||
TypeSort = 10;
|
||||
Sort = sort;
|
||||
Fixed = fix;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ends any section
|
||||
/// </summary>
|
||||
public class NoSectionAttribute : SectionAttribute
|
||||
{
|
||||
public NoSectionAttribute() : base("")
|
||||
{
|
||||
TypeSort = 10;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Hides Field and doesn't enter children
|
||||
/// </summary>
|
||||
public class Hide : DTAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
|
||||
{
|
||||
public Hide() : base(20) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides Field, but enter children
|
||||
/// </summary>
|
||||
public class Inline : DTAttribute, IDTFieldParsingAttribute, IDTFieldRenderAttribute
|
||||
{
|
||||
public Inline() : base(21) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Attributes handling visual representation ###
|
||||
|
||||
public class ArrayExAttribute : DTAttribute, IDTFieldParsingAttribute
|
||||
{
|
||||
public bool Draggable = true;
|
||||
public bool ShowHeader = true;
|
||||
public bool ShowAdd = true;
|
||||
public bool ShowDelete = true;
|
||||
public bool DropTarget = true;
|
||||
|
||||
public ArrayExAttribute()
|
||||
: base(35)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Attributes handling actions and dynamic evaluation ###
|
||||
|
||||
|
||||
|
||||
public class FieldActionAttribute : ActionAttribute, IDTFieldRenderAttribute
|
||||
{
|
||||
public FieldActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
|
||||
: base(actionData, action) { }
|
||||
|
||||
}
|
||||
|
||||
public class GroupActionAttribute : ActionAttribute, IDTGroupRenderAttribute
|
||||
{
|
||||
public GroupActionAttribute(string actionData, ActionEnum action = ActionEnum.Callback)
|
||||
: base(actionData, action) { }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class FieldConditionAttribute : ConditionalAttribute, IDTFieldRenderAttribute
|
||||
{
|
||||
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false, ActionEnum action = ActionEnum.Show, object actionData = null, ActionPositionEnum position = ActionPositionEnum.Below)
|
||||
: base(fieldOrProperty, compareTo, compareFalse)
|
||||
{
|
||||
Action = action;
|
||||
ActionData = actionData;
|
||||
Position = position;
|
||||
}
|
||||
|
||||
|
||||
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2)
|
||||
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2) { }
|
||||
|
||||
|
||||
public FieldConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2,
|
||||
string fieldOrProperty3, object compareTo3, bool compareFalse3)
|
||||
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2, fieldOrProperty3, compareTo3, compareFalse3) { }
|
||||
|
||||
public FieldConditionAttribute(string methodToQuery)
|
||||
: base(methodToQuery) { }
|
||||
|
||||
}
|
||||
public class GroupConditionAttribute : ConditionalAttribute, IDTGroupRenderAttribute
|
||||
{
|
||||
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse = false)
|
||||
: base(fieldOrProperty, compareTo, compareFalse) { }
|
||||
|
||||
|
||||
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2)
|
||||
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2) { }
|
||||
|
||||
|
||||
public GroupConditionAttribute(string fieldOrProperty, object compareTo, bool compareFalse, OperatorEnum op,
|
||||
string fieldOrProperty2, object compareTo2, bool compareFalse2,
|
||||
string fieldOrProperty3, object compareTo3, bool compareFalse3)
|
||||
: base(fieldOrProperty, compareTo, compareFalse, op, fieldOrProperty2, compareTo2, compareFalse2, fieldOrProperty3, compareTo3, compareFalse3) { }
|
||||
|
||||
public GroupConditionAttribute(string methodToQuery)
|
||||
: base(methodToQuery) { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 353b27613f9971242bf7f5673b2529a5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
public class Couple<T1, T2>
|
||||
{
|
||||
public T1 First { get; set; }
|
||||
public T2 Second { get; set; }
|
||||
public Couple(T1 first, T2 second)
|
||||
{
|
||||
First = first;
|
||||
Second = second;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c3f887f64f3b22845b9c93163066963a
|
||||
timeCreated: 1525390532
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,989 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
public static class DTTween
|
||||
{
|
||||
public enum EasingMethod
|
||||
{
|
||||
Linear = 0,
|
||||
ExponentialIn = 1,
|
||||
ExponentialOut = 2,
|
||||
ExponentialInOut = 3,
|
||||
ExponentialOutIn = 4,
|
||||
CircularIn = 5,
|
||||
CircularOut = 6,
|
||||
CircularInOut = 7,
|
||||
CircularOutIn = 8,
|
||||
QuadraticIn = 9,
|
||||
QuadraticOut = 10,
|
||||
QuadraticInOut = 11,
|
||||
QuadraticOutIn = 12,
|
||||
SinusIn = 13,
|
||||
SinusOut = 14,
|
||||
SinusInOut = 15,
|
||||
SinusOutIn = 16,
|
||||
CubicIn = 17,
|
||||
CubicOut = 18,
|
||||
CubicInOut = 19,
|
||||
CubicOutIn = 20,
|
||||
QuarticIn = 21,
|
||||
QuarticOut = 22,
|
||||
QuarticInOut = 23,
|
||||
QuarticOutIn = 24,
|
||||
QuinticIn = 25,
|
||||
QuinticOut = 26,
|
||||
QuinticInOut = 27,
|
||||
QuinticOutIn = 28
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tween
|
||||
/// </summary>
|
||||
/// <param name="method">Tweening method</param>
|
||||
/// <param name="t">Time (0..1)</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float Ease(EasingMethod method, float t, float b, float c)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case EasingMethod.ExponentialIn: return ExpoIn(t, b, c);
|
||||
case EasingMethod.ExponentialOut: return ExpoOut(t, b, c);
|
||||
case EasingMethod.ExponentialInOut: return ExpoInOut(t, b, c);
|
||||
case EasingMethod.ExponentialOutIn: return ExpoOutIn(t, b, c);
|
||||
case EasingMethod.CircularIn: return CircIn(t, b, c);
|
||||
case EasingMethod.CircularOut: return CircOut(t, b, c);
|
||||
case EasingMethod.CircularInOut: return CircInOut(t, b, c);
|
||||
case EasingMethod.CircularOutIn: return CircOutIn(t, b, c);
|
||||
case EasingMethod.QuadraticIn: return QuadIn(t, b, c);
|
||||
case EasingMethod.QuadraticOut: return QuadOut(t, b, c);
|
||||
case EasingMethod.QuadraticInOut: return QuadInOut(t, b, c);
|
||||
case EasingMethod.QuadraticOutIn: return QuadOutIn(t, b, c);
|
||||
case EasingMethod.SinusIn: return SineIn(t, b, c);
|
||||
case EasingMethod.SinusOut: return SineOut(t, b, c);
|
||||
case EasingMethod.SinusInOut: return SineInOut(t, b, c);
|
||||
case EasingMethod.SinusOutIn: return SineOutIn(t, b, c);
|
||||
case EasingMethod.CubicIn: return CubicIn(t, b, c);
|
||||
case EasingMethod.CubicOut: return CubicOut(t, b, c);
|
||||
case EasingMethod.CubicInOut: return CubicInOut(t, b, c);
|
||||
case EasingMethod.CubicOutIn: return CubicOutIn(t, b, c);
|
||||
case EasingMethod.QuarticIn: return QuartIn(t, b, c);
|
||||
case EasingMethod.QuarticOut: return QuartOut(t, b, c);
|
||||
case EasingMethod.QuarticInOut: return QuartInOut(t, b, c);
|
||||
case EasingMethod.QuarticOutIn: return QuartOutIn(t, b, c);
|
||||
case EasingMethod.QuinticIn: return QuintIn(t, b, c);
|
||||
case EasingMethod.QuinticOut: return QuintOut(t, b, c);
|
||||
case EasingMethod.QuinticInOut: return QuintInOut(t, b, c);
|
||||
case EasingMethod.QuinticOutIn: return QuintOutIn(t, b, c);
|
||||
default: return Linear(t, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tween
|
||||
/// </summary>
|
||||
/// <param name="method">Tweening method</param>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float Ease(EasingMethod method, float t, float b, float c, float d)
|
||||
{
|
||||
switch (method)
|
||||
{
|
||||
case EasingMethod.ExponentialIn: return ExpoIn(t, b, c, d);
|
||||
case EasingMethod.ExponentialOut: return ExpoOut(t, b, c, d);
|
||||
case EasingMethod.ExponentialInOut : return ExpoInOut(t, b, c, d);
|
||||
case EasingMethod.ExponentialOutIn: return ExpoOutIn(t, b, c, d);
|
||||
case EasingMethod.CircularIn: return CircIn(t, b, c, d);
|
||||
case EasingMethod.CircularOut: return CircOut(t, b, c, d);
|
||||
case EasingMethod.CircularInOut: return CircInOut(t, b, c, d);
|
||||
case EasingMethod.CircularOutIn: return CircOutIn(t, b, c, d);
|
||||
case EasingMethod.QuadraticIn : return QuadIn(t, b, c, d);
|
||||
case EasingMethod.QuadraticOut : return QuadOut(t, b, c, d);
|
||||
case EasingMethod.QuadraticInOut : return QuadInOut(t, b, c, d);
|
||||
case EasingMethod.QuadraticOutIn : return QuadOutIn(t, b, c, d);
|
||||
case EasingMethod.SinusIn: return SineIn(t, b, c, d);
|
||||
case EasingMethod.SinusOut: return SineOut(t, b, c, d);
|
||||
case EasingMethod.SinusInOut : return SineInOut(t, b, c, d);
|
||||
case EasingMethod.SinusOutIn : return SineOutIn(t, b, c, d);
|
||||
case EasingMethod.CubicIn: return CubicIn(t, b, c, d);
|
||||
case EasingMethod.CubicOut: return CubicOut(t, b, c, d);
|
||||
case EasingMethod.CubicInOut: return CubicInOut(t, b, c, d);
|
||||
case EasingMethod.CubicOutIn: return CubicOutIn(t, b, c, d);
|
||||
case EasingMethod.QuarticIn: return QuartIn(t, b, c, d);
|
||||
case EasingMethod.QuarticOut: return QuartOut(t, b, c, d);
|
||||
case EasingMethod.QuarticInOut: return QuartInOut(t, b, c, d);
|
||||
case EasingMethod.QuarticOutIn: return QuartOutIn(t, b, c, d);
|
||||
case EasingMethod.QuinticIn: return QuintIn(t, b, c, d);
|
||||
case EasingMethod.QuinticOut: return QuintOut(t, b, c, d);
|
||||
case EasingMethod.QuinticInOut: return QuintInOut(t, b, c, d);
|
||||
case EasingMethod.QuinticOutIn: return QuintOutIn(t, b, c, d);
|
||||
default: return Linear(t, b, c, d);
|
||||
}
|
||||
}
|
||||
|
||||
#region ### Linear ###
|
||||
/// <summary>
|
||||
/// Linear tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float Linear(float t, float b, float c)
|
||||
{
|
||||
return c * Mathf.Clamp01(t) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Linear tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float Linear(float t, float b, float c, float d)
|
||||
{
|
||||
return c * t / d + b;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ### Exponential ###
|
||||
/// <summary>
|
||||
/// Exponential tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return (t == 1) ? b + c : c * (-Mathf.Pow(2, -10 * t) + 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Exponential tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoOut(float t, float b, float c, float d)
|
||||
{
|
||||
return (t == d) ? b + c : c * (-Mathf.Pow(2, -10 * t / d) + 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exponential tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return (t == 0) ? b : c * Mathf.Pow(2, 10 * (t-1)) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Exponential tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoIn(float t, float b, float c, float d)
|
||||
{
|
||||
return (t == 0) ? b : c * Mathf.Pow(2, 10 * (t / d - 1)) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exponential tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t == 0)
|
||||
return b;
|
||||
|
||||
if (t == 1)
|
||||
return b + c;
|
||||
|
||||
if ((t /= .5f) < 1)
|
||||
return c / 2 * Mathf.Pow(2, 10 * (t - 1)) + b;
|
||||
|
||||
return c / 2 * (-Mathf.Pow(2, -10 * --t) + 2) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Exponential tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if (t == 0)
|
||||
return b;
|
||||
|
||||
if (t == d)
|
||||
return b + c;
|
||||
|
||||
if ((t /= d / 2) < 1)
|
||||
return c / 2 * Mathf.Pow(2, 10 * (t - 1)) + b;
|
||||
|
||||
return c / 2 * (-Mathf.Pow(2, -10 * --t) + 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exponential tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return ExpoOut(t * 2, b, c / 2);
|
||||
|
||||
return ExpoIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
/// <summary>
|
||||
/// Exponential tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float ExpoOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return ExpoOut(t * 2, b, c / 2, d);
|
||||
|
||||
return ExpoIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Circular ###
|
||||
/// <summary>
|
||||
/// Circular tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * Mathf.Sqrt(1 - (t = t - 1) * t) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Circular tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircOut(float t, float b, float c, float d)
|
||||
{
|
||||
return c * Mathf.Sqrt(1 - (t = t / d - 1) * t) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Circular tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return -c * (Mathf.Sqrt(1 - t * t) - 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Circular tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircIn(float t, float b, float c, float d)
|
||||
{
|
||||
return -c * (Mathf.Sqrt(1 - (t /= d) * t) - 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Circular tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
|
||||
|
||||
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Circular tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
|
||||
|
||||
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Circular tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return CircOut(t * 2, b, c / 2);
|
||||
|
||||
return CircIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
/// <summary>
|
||||
/// Circular tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CircOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return CircOut(t * 2, b, c / 2, d);
|
||||
|
||||
return CircIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Quadratic ###
|
||||
/// <summary>
|
||||
/// Quadratic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return -c * t * (t - 2) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quadratic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadOut(float t, float b, float c, float d)
|
||||
{
|
||||
return -c * (t /= d) * (t - 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quadratic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * t * t + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quadratic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadIn(float t, float b, float c, float d)
|
||||
{
|
||||
return c * (t /= d) * t + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quadratic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
|
||||
|
||||
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quadratic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return -c / 2 * (Mathf.Sqrt(1 - t * t) - 1) + b;
|
||||
|
||||
return c / 2 * (Mathf.Sqrt(1 - (t -= 2) * t) + 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quadratic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return QuadOut(t * 2, b, c / 2);
|
||||
|
||||
return QuadIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quadratic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuadOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return QuadOut(t * 2, b, c / 2, d);
|
||||
|
||||
return QuadIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Sinus ###
|
||||
/// <summary>
|
||||
/// Sinusoidal tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineOut(float t, float b, float c)
|
||||
{
|
||||
return c * Mathf.Sin(Mathf.Clamp01(t) * (Mathf.PI / 2)) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sinusoidal tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineOut(float t, float b, float c, float d)
|
||||
{
|
||||
return c * Mathf.Sin(t / d * (Mathf.PI / 2)) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sinusoidal tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineIn(float t, float b, float c)
|
||||
{
|
||||
return -c * Mathf.Cos(Mathf.Clamp01(t) * (Mathf.PI / 2)) + c + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sinusoidal tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineIn(float t, float b, float c, float d)
|
||||
{
|
||||
return -c * Mathf.Cos(t / d * (Mathf.PI / 2)) + c + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sinusoidal tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return c / 2 * (Mathf.Sin(Mathf.PI * t / 2)) + b;
|
||||
|
||||
return -c / 2 * (Mathf.Cos(Mathf.PI * --t / 2) - 2) + b;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Sinusoidal tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return c / 2 * (Mathf.Sin(Mathf.PI * t / 2)) + b;
|
||||
|
||||
return -c / 2 * (Mathf.Cos(Mathf.PI * --t / 2) - 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sinusoidal tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return SineOut(t * 2, b, c / 2);
|
||||
|
||||
return SineIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sinusoidal tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float SineOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return SineOut(t * 2, b, c / 2, d);
|
||||
|
||||
return SineIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Cubic ###
|
||||
|
||||
/// <summary>
|
||||
/// Cubic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * ((t = t - 1) * t * t + 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Cubic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicOut(float t, float b, float c, float d)
|
||||
{
|
||||
return c * ((t = t / d - 1) * t * t + 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cubic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * t * t * t + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Cubic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicIn(float t, float b, float c, float d)
|
||||
{
|
||||
return c * (t /= d) * t * t + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cubic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return c / 2 * t * t * t + b;
|
||||
|
||||
return c / 2 * ((t -= 2) * t * t + 2) + b;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Cubic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return c / 2 * t * t * t + b;
|
||||
|
||||
return c / 2 * ((t -= 2) * t * t + 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cubic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return CubicOut(t * 2, b, c / 2);
|
||||
|
||||
return CubicIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cubic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float CubicOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return CubicOut(t * 2, b, c / 2, d);
|
||||
|
||||
return CubicIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Quartic ###
|
||||
|
||||
/// <summary>
|
||||
/// Quartic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return -c * ((t = t - 1) * t * t * t - 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quartic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartOut(float t, float b, float c, float d)
|
||||
{
|
||||
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quartic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * t * t * t * t + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quartic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartIn(float t, float b, float c, float d)
|
||||
{
|
||||
return c * (t /= d) * t * t * t + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quartic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return c / 2 * t * t * t * t + b;
|
||||
|
||||
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Quartic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return c / 2 * t * t * t * t + b;
|
||||
|
||||
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quartic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return QuartOut(t * 2, b, c / 2);
|
||||
|
||||
return QuartIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quartic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuartOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if ( t < d / 2 )
|
||||
return QuartOut( t * 2, b, c / 2, d );
|
||||
|
||||
return QuartIn( ( t * 2 ) - d, b + c / 2, c / 2, d );
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Quintic ###
|
||||
|
||||
/// <summary>
|
||||
/// Quintic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * ((t = t - 1) * t * t * t * t + 1) + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quintic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintOut(float t, float b, float c, float d)
|
||||
{
|
||||
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quintic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
return c * t * t * t * t * t + b;
|
||||
}
|
||||
/// <summary>
|
||||
/// Quintic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintIn(float t, float b, float c, float d)
|
||||
{
|
||||
return c * (t /= d) * t * t * t * t + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quintic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintInOut(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if ((t /= .5f) < 1)
|
||||
return c / 2 * t * t * t * t * t + b;
|
||||
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Quintic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintInOut(float t, float b, float c, float d)
|
||||
{
|
||||
if ((t /= d / 2) < 1)
|
||||
return c / 2 * t * t * t * t * t + b;
|
||||
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quintic tween with t clamped to 0..1
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintOutIn(float t, float b, float c)
|
||||
{
|
||||
t = Mathf.Clamp01(t);
|
||||
if (t < .5f)
|
||||
return QuintOut(t * 2, b, c / 2);
|
||||
return QuintIn((t * 2) - 1, b + c / 2, c / 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Quintic tween
|
||||
/// </summary>
|
||||
/// <param name="t">Time</param>
|
||||
/// <param name="b">Start value</param>
|
||||
/// <param name="c">Delta value</param>
|
||||
/// <param name="d">Duration</param>
|
||||
/// <returns>tweened value</returns>
|
||||
public static float QuintOutIn(float t, float b, float c, float d)
|
||||
{
|
||||
if (t < d / 2)
|
||||
return QuintOut(t * 2, b, c / 2, d);
|
||||
return QuintIn((t * 2) - d, b + c / 2, c / 2, d);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Add missing formulas (Elastic etc.) from here: http://wpf-animation.googlecode.com/svn/trunk/src/WPF/Animation/PennerDoubleAnimation.cs
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d57d3ecf09b87c9438cf6d0622a903fb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,120 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
|
||||
public class DTObjectDump
|
||||
{
|
||||
const int INDENTSPACES = 5;
|
||||
string mIndent;
|
||||
StringBuilder mSB;
|
||||
object mObject;
|
||||
|
||||
public DTObjectDump(object o, int indent = 0)
|
||||
{
|
||||
mIndent = new string(' ', indent);
|
||||
mSB = new StringBuilder();
|
||||
mObject = o;
|
||||
|
||||
#if !NETFX_CORE
|
||||
Type T = o.GetType();
|
||||
FieldInfo[] fia = T.GetAllFields(false, true);
|
||||
if (fia.Length > 0)
|
||||
AppendHeader("Fields");
|
||||
foreach (FieldInfo fi in fia)
|
||||
AppendMember(fi);
|
||||
|
||||
PropertyInfo[] pia = T.GetAllProperties(false, true);
|
||||
if (pia.Length > 0)
|
||||
AppendHeader("Properties");
|
||||
foreach (PropertyInfo pi in pia)
|
||||
AppendMember(pi);
|
||||
#endif
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return mSB.ToString();
|
||||
}
|
||||
|
||||
#if !NETFX_CORE
|
||||
void AppendHeader(string name)
|
||||
{
|
||||
mSB.AppendLine(mIndent + "<b>---===| " + name + " |===---</b>\n");
|
||||
}
|
||||
|
||||
void AppendMember(MemberInfo info)
|
||||
{
|
||||
Type type;
|
||||
string typeName;
|
||||
object value;
|
||||
|
||||
FieldInfo fi = info as FieldInfo;
|
||||
if (fi != null)
|
||||
{
|
||||
type = fi.FieldType;
|
||||
typeName = type.Name;
|
||||
value = fi.GetValue(mObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
PropertyInfo pi = info as PropertyInfo;
|
||||
type = pi.PropertyType;
|
||||
typeName = type.Name;
|
||||
value = pi.GetValue(mObject, null);
|
||||
}
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (typeof(IEnumerable).IsAssignableFrom(type))
|
||||
{
|
||||
string tmp = mIndent;
|
||||
int count = 0;
|
||||
IEnumerable ie = value as IEnumerable;
|
||||
if (ie != null)
|
||||
{
|
||||
if (type.GetEnumerableType().BaseType == typeof(ValueType))
|
||||
{
|
||||
foreach (object e in ie)
|
||||
tmp = string.Concat(tmp, string.Format("<b>{0}</b>: {1} ", (count++).ToString(), e.ToString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (typeof(IList).IsAssignableFrom(type))
|
||||
typeName = "IList<" + type.GetEnumerableType() + ">";
|
||||
tmp += "\n";
|
||||
foreach (object e in ie)
|
||||
tmp = string.Concat(tmp, string.Format("<b>{0}</b>: {1} ", (count++).ToString(), new DTObjectDump(e, mIndent.Length + INDENTSPACES).ToString()));
|
||||
}
|
||||
}
|
||||
mSB.Append(mIndent);
|
||||
mSB.AppendFormat("(<i>{0}</i>) <b>{1}[{2}]</b> = ", typeName, info.Name, count);
|
||||
mSB.AppendLine(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSB.Append(mIndent);
|
||||
mSB.AppendFormat("(<i>{0}</i>) <b>{1}</b> = ", typeName, info.Name);
|
||||
mSB.AppendLine(mIndent + value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64b16d693c41e7a44979cd06b6d9e47b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,1000 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System;
|
||||
using System.Text;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using UnityEngine.Events;
|
||||
using Object = UnityEngine.Object;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
public static class DTUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// The base Url for DT components' documentation
|
||||
/// </summary>
|
||||
public const string HelpUrlBase = "https://curvyeditor.com/doclink/";
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static MethodInfo mGetBuiltinExtraResourcesMethod;
|
||||
#endif
|
||||
|
||||
public static Material GetDefaultMaterial()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (mGetBuiltinExtraResourcesMethod == null)
|
||||
{
|
||||
BindingFlags bfs = BindingFlags.NonPublic | BindingFlags.Static;
|
||||
mGetBuiltinExtraResourcesMethod = typeof(UnityEditor.EditorGUIUtility).GetMethod("GetBuiltinExtraResource", bfs);
|
||||
}
|
||||
|
||||
Material result;
|
||||
if (mGetBuiltinExtraResourcesMethod == null)
|
||||
{
|
||||
result = null;
|
||||
Debug.LogError("Couldn't find method GetBuiltinExtraResource in type UnityEditor.EditorGUIUtility");
|
||||
}
|
||||
else
|
||||
result = (Material)mGetBuiltinExtraResourcesMethod.Invoke(null, new object[] { typeof(Material), "Default-Diffuse.mat" });
|
||||
|
||||
return result;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
public static bool IsEditorStateChange
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode && !UnityEditor.EditorApplication.isPlaying)
|
||||
return true;
|
||||
else
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private const string INDENT_STRING = " ";
|
||||
public static string FormatJson(string str)
|
||||
{
|
||||
int indent = 0;
|
||||
bool quoted = false;
|
||||
StringBuilder sb = new System.Text.StringBuilder();
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
{
|
||||
char ch = str[i];
|
||||
switch (ch)
|
||||
{
|
||||
case '{':
|
||||
case '[':
|
||||
sb.Append(ch);
|
||||
if (!quoted)
|
||||
{
|
||||
sb.AppendLine();
|
||||
Enumerable.Range(0, ++indent).ForEach(item => sb.Append(INDENT_STRING));
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
case ']':
|
||||
if (!quoted)
|
||||
{
|
||||
sb.AppendLine();
|
||||
Enumerable.Range(0, --indent).ForEach(item => sb.Append(INDENT_STRING));
|
||||
}
|
||||
sb.Append(ch);
|
||||
break;
|
||||
case '"':
|
||||
sb.Append(ch);
|
||||
bool escaped = false;
|
||||
int index = i;
|
||||
while (index > 0 && str[--index] == '\\')
|
||||
escaped = !escaped;
|
||||
if (!escaped)
|
||||
quoted = !quoted;
|
||||
break;
|
||||
case ',':
|
||||
sb.Append(ch);
|
||||
if (!quoted)
|
||||
{
|
||||
sb.AppendLine();
|
||||
Enumerable.Range(0, indent).ForEach(item => sb.Append(INDENT_STRING));
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
sb.Append(ch);
|
||||
if (!quoted)
|
||||
sb.Append(" ");
|
||||
break;
|
||||
default:
|
||||
sb.Append(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Much like HandleUtility.GetHandleSize(), but works for gizmos
|
||||
/// </summary>
|
||||
/// <param name="position"> handle position in world space</param>
|
||||
public static float GetHandleSize(Vector3 position)
|
||||
{
|
||||
Camera camera = Camera.current;
|
||||
|
||||
if (camera)
|
||||
{
|
||||
Transform cameraTransform = camera.transform;
|
||||
|
||||
Vector3 localDirection;
|
||||
localDirection.x = 0;
|
||||
localDirection.y = 0;
|
||||
localDirection.z = 1;
|
||||
Vector3 cameraZDirection = cameraTransform.TransformDirection(localDirection);
|
||||
|
||||
localDirection.x = 1;
|
||||
localDirection.y = 0;
|
||||
localDirection.z = 0;
|
||||
Vector3 cameraXDirection = cameraTransform.TransformDirection(localDirection);
|
||||
|
||||
|
||||
return GetHandleSize(Gizmos.matrix.MultiplyPoint3x4(position), camera, camera.pixelWidth * 0.5f, camera.pixelHeight * 0.5f, cameraTransform.position, cameraZDirection, cameraXDirection);
|
||||
}
|
||||
|
||||
return 20f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Much like HandleUtility.GetHandleSize(), but works for gizmos
|
||||
/// </summary>
|
||||
/// <param name="position"> handle position in world space</param>
|
||||
/// <param name="camera"> the camera through which the object is displayed</param>
|
||||
/// <param name="cameraCenterWidth"> camera.pixelWidth * 0.5f</param>
|
||||
/// <param name="cameraCenterHeight"> camera.pixelHeight * 0.5f</param>
|
||||
/// <param name="cameraPosition"> camera.transform.position</param>
|
||||
/// <param name="cameraZDirection"> camera.transform.forward</param>
|
||||
/// <param name="cameraXDirection"> camera.transform.right</param>
|
||||
public static float GetHandleSize(Vector3 position, Camera camera, float cameraCenterWidth, float cameraCenterHeight, Vector3 cameraPosition, Vector3 cameraZDirection, Vector3 cameraXDirection)
|
||||
{
|
||||
//inlined version of Vector3.Dot(positionMinusCameraPosition, cameraZDirection)
|
||||
float z = (position.x - cameraPosition.x) * cameraZDirection.x +
|
||||
(position.y - cameraPosition.y) * cameraZDirection.y +
|
||||
(position.z - cameraPosition.z) * cameraZDirection.z;
|
||||
|
||||
//OPTIM
|
||||
//If you reaaaally need those extra milliseconds, use this
|
||||
//return z * 0.15f;
|
||||
//This will give a good enough result. The problem with this method is that the handle size is unsatisfying when transitioning between Perspective and Orthogonal view, and when the scene view window is too narrow to the point that it changes the FOV of the scene view.
|
||||
|
||||
Vector3 b;
|
||||
{
|
||||
Vector3 camPosPlusDirB;
|
||||
camPosPlusDirB.x = cameraPosition.x + cameraZDirection.x * z + cameraXDirection.x;
|
||||
camPosPlusDirB.y = cameraPosition.y + cameraZDirection.y * z + cameraXDirection.y;
|
||||
camPosPlusDirB.z = cameraPosition.z + cameraZDirection.z * z + cameraXDirection.z;
|
||||
b = camera.WorldToScreenPoint(camPosPlusDirB, Camera.MonoOrStereoscopicEye.Mono);
|
||||
}
|
||||
|
||||
float aMinusBX = cameraCenterWidth - b.x;
|
||||
float aMinusBY = cameraCenterHeight - b.y;
|
||||
return 80f / (float)Math.Sqrt(aMinusBX * aMinusBX +
|
||||
aMinusBY * aMinusBY);
|
||||
}
|
||||
|
||||
public static void SetPlayerPrefs<T>(string key, T value)
|
||||
{
|
||||
Type tt = typeof(T);
|
||||
#if NETFX_CORE
|
||||
if (tt.GetTypeInfo().IsEnum)
|
||||
#else
|
||||
if (tt.IsEnum)
|
||||
#endif
|
||||
{
|
||||
PlayerPrefs.SetInt(key, Convert.ToInt32(Enum.Parse(typeof(T), value.ToString()) as Enum));
|
||||
}
|
||||
else if (tt.IsArray)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
else if (tt.Matches(typeof(int), typeof(Int32)))
|
||||
PlayerPrefs.SetInt(key, (value as int?).Value);
|
||||
else if (tt == typeof(string))
|
||||
PlayerPrefs.SetString(key, (value as string));
|
||||
else if (tt == typeof(float))
|
||||
PlayerPrefs.SetFloat(key, (value as float?).Value);
|
||||
else if (tt == typeof(bool))
|
||||
PlayerPrefs.SetInt(key, ((value as bool?).Value == true) ? 1 : 0);
|
||||
else if (tt == typeof(Color))
|
||||
PlayerPrefs.SetString(key, (value as Color?).Value.ToHtml());
|
||||
else
|
||||
Debug.LogError("[DevTools.SetEditorPrefs] Unsupported datatype: " + tt.Name);
|
||||
}
|
||||
|
||||
public static T GetPlayerPrefs<T>(string key, T defaultValue)
|
||||
{
|
||||
if (PlayerPrefs.HasKey(key))
|
||||
{
|
||||
Type tt = typeof(T);
|
||||
try
|
||||
{
|
||||
#if NETFX_CORE
|
||||
if (tt.GetTypeInfo().IsEnum || tt.Matches(typeof(int), typeof(Int32)))
|
||||
#else
|
||||
if (tt.IsEnum || tt.Matches(typeof(int), typeof(Int32)))
|
||||
#endif
|
||||
{
|
||||
return (T)(object)PlayerPrefs.GetInt(key, (int)(object)defaultValue);
|
||||
}
|
||||
else if (tt.IsArray)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
else if (tt == typeof(string))
|
||||
return (T)(object)PlayerPrefs.GetString(key, defaultValue.ToString());
|
||||
else if (tt == typeof(float))
|
||||
return (T)(object)PlayerPrefs.GetFloat(key, (float)(object)defaultValue);
|
||||
else if (tt == typeof(bool))
|
||||
return (T)(object)(PlayerPrefs.GetInt(key, ((bool)(object)defaultValue == true) ? 1 : 0) == 1);
|
||||
else if (tt == typeof(Color))
|
||||
return (T)(object)PlayerPrefs.GetString(key, ((Color)(object)defaultValue).ToHtml()).ColorFromHtml();
|
||||
else
|
||||
Debug.LogError("[DevTools.SetEditorPrefs] Unsupported datatype: " + tt.Name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static float RandomSign()
|
||||
{
|
||||
return UnityEngine.Random.Range(0, 2) * 2 - 1;
|
||||
}
|
||||
|
||||
public static string GetHelpUrl(object forClass)
|
||||
{
|
||||
return (forClass == null) ? string.Empty : GetHelpUrl(forClass.GetType());
|
||||
}
|
||||
|
||||
public static string GetHelpUrl(Type classType)
|
||||
{
|
||||
if (classType != null)
|
||||
{
|
||||
#if NETFX_CORE
|
||||
object[] attribs = (object[])classType.GetTypeInfo().GetCustomAttributes(typeof(HelpURLAttribute), true);
|
||||
#else
|
||||
object[] attribs = classType.GetCustomAttributes(typeof(HelpURLAttribute), true);
|
||||
#endif
|
||||
if (attribs.Length > 0)
|
||||
return (((HelpURLAttribute)attribs[0]).URL);
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public static Vector3 GetCenterPosition(Vector3 fallback, params Vector3[] vectors)
|
||||
{
|
||||
if (vectors.Length == 0)
|
||||
return fallback;
|
||||
Vector3 v = vectors[0];
|
||||
for (int i = 1; i < vectors.Length; i++)
|
||||
v += vectors[i];
|
||||
return (v / vectors.Length);
|
||||
}
|
||||
|
||||
public static T CreateGameObject<T>(Transform parent, string name) where T : MonoBehaviour
|
||||
{
|
||||
GameObject go = new GameObject(name);
|
||||
go.transform.parent = parent;
|
||||
go.transform.localPosition = Vector3.zero;
|
||||
go.transform.localRotation = Quaternion.identity;
|
||||
T cmp = go.AddComponent<T>();
|
||||
return cmp;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Returns true if the object's prefab instance status allows for deletion
|
||||
/// </summary>
|
||||
/// <param name="object">The input object to test</param>
|
||||
/// <param name="errorMessage"> Is set to empty string if method returns true, otherwise it is set to a message error, similar to the one display by Unity, explaining the issue.</param>
|
||||
/// <see cref="PrefabInstanceStatus"/>
|
||||
public static bool DoesPrefabStatusAllowDeletion(this UnityEngine.Object @object, out string errorMessage)
|
||||
{
|
||||
PrefabInstanceStatus prefabInstanceStatus = default;
|
||||
bool isDeletionAllowed = @object is GameObject == false || DoesPrefabStatusAllowDeletion((GameObject)@object, out prefabInstanceStatus);
|
||||
if (isDeletionAllowed == false)
|
||||
{
|
||||
errorMessage = $"You initiated an operation that leads to the deletion of the Game Object '{@object.name}', which is part of a Prefab instance.\n\n";
|
||||
if (prefabInstanceStatus == PrefabInstanceStatus.MissingAsset)
|
||||
errorMessage += "The Prefab asset is missing. You have to unpack the Prefab instance to be able to execute this operation";
|
||||
else
|
||||
errorMessage += $"Children of a Prefab instance cannot be deleted or moved, and components cannot be reordered. \n\nYou have to open the Prefab in Prefab Mode to restructure the Prefab Asset itself, or unpack the Prefab instance to remove its Prefab connection.";
|
||||
}
|
||||
else
|
||||
{
|
||||
errorMessage = string.Empty;
|
||||
}
|
||||
|
||||
return isDeletionAllowed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the object's prefab instance status allows for deletion
|
||||
/// </summary>
|
||||
/// <param name="gameObject">The input object to test</param>
|
||||
/// <param name="prefabInstanceStatus"> The prefab instance status of the gameObject</param>
|
||||
public static bool DoesPrefabStatusAllowDeletion(this GameObject gameObject, out PrefabInstanceStatus prefabInstanceStatus)
|
||||
{
|
||||
prefabInstanceStatus = PrefabUtility.GetPrefabInstanceStatus(gameObject);
|
||||
return prefabInstanceStatus != PrefabInstanceStatus.Connected || PrefabUtility.IsOutermostPrefabInstanceRoot(gameObject);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the code is executed in edit mode or not
|
||||
/// </summary>
|
||||
public static bool IsInEditMode
|
||||
{
|
||||
get
|
||||
{
|
||||
bool result;
|
||||
#if UNITY_EDITOR
|
||||
result = !Application.isPlaying;
|
||||
#else
|
||||
result = false;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DTTime
|
||||
{
|
||||
static float _EditorDeltaTime;
|
||||
static float _EditorLastTime;
|
||||
|
||||
public static double TimeSinceStartup
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
return UnityEditor.EditorApplication.timeSinceStartup;
|
||||
#endif
|
||||
return Time.timeSinceLevelLoad;
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Seems to me that this is not working properly. Probably because InitializeEditorTime and UpdateEditorTime are never called. Fix this before using it")]
|
||||
public static float deltaTime
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Application.isPlaying) ? Time.deltaTime : _EditorDeltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
public static void InitializeEditorTime()
|
||||
{
|
||||
_EditorLastTime = Time.realtimeSinceStartup;
|
||||
_EditorDeltaTime = 0;
|
||||
}
|
||||
|
||||
public static void UpdateEditorTime()
|
||||
{
|
||||
float cur = Time.realtimeSinceStartup;
|
||||
float timeDelta = (cur - _EditorLastTime);
|
||||
|
||||
_EditorDeltaTime = timeDelta;
|
||||
_EditorLastTime = cur;
|
||||
|
||||
/*
|
||||
if (frameDelta > 20 || timeDelta > 1)
|
||||
{
|
||||
_EditorLastFrame = Time.frameCount;
|
||||
_EditorLastTime = cur;
|
||||
_EditorDeltaTime = 0;
|
||||
}
|
||||
else if (frameDelta > 0)
|
||||
{
|
||||
_EditorDeltaTime = timeDelta / frameDelta;
|
||||
_EditorLastTime = cur;
|
||||
_EditorLastFrame = Time.frameCount;
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class TimeMeasure : Ring<long>
|
||||
{
|
||||
public System.Diagnostics.Stopwatch mWatch = new System.Diagnostics.Stopwatch();
|
||||
|
||||
public TimeMeasure(int size) : base(size)
|
||||
{ }
|
||||
|
||||
public void Start()
|
||||
{
|
||||
mWatch.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
mWatch.Stop();
|
||||
Add(mWatch.ElapsedTicks);
|
||||
mWatch.Reset();
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
mWatch.Stop();
|
||||
}
|
||||
|
||||
public double LastTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
return this[this.Count - 1];
|
||||
}
|
||||
}
|
||||
|
||||
public double LastMS
|
||||
{
|
||||
get
|
||||
{
|
||||
return LastTicks / (double)System.TimeSpan.TicksPerMillisecond;
|
||||
}
|
||||
}
|
||||
|
||||
public double AverageMS
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = 0;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d += this[i];
|
||||
|
||||
return DTMath.FixNaN((d / (double)System.TimeSpan.TicksPerMillisecond) / Count);
|
||||
}
|
||||
}
|
||||
|
||||
public double MinimumMS
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = long.MaxValue;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d = System.Math.Min(d, this[i]);
|
||||
|
||||
return DTMath.FixNaN(d / (double)System.TimeSpan.TicksPerMillisecond);
|
||||
}
|
||||
}
|
||||
|
||||
public double MaximumMS
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = long.MinValue;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d = System.Math.Max(d, this[i]);
|
||||
|
||||
return DTMath.FixNaN(d / (double)System.TimeSpan.TicksPerMillisecond);
|
||||
}
|
||||
}
|
||||
|
||||
public double AverageTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = 0;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d += this[i];
|
||||
return d / (double)Count;
|
||||
}
|
||||
}
|
||||
|
||||
public double MinimumTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = long.MaxValue;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d = System.Math.Min(d, this[i]);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
public double MaximumTicks
|
||||
{
|
||||
get
|
||||
{
|
||||
long d = 0;
|
||||
for (int i = 0; i < this.Count; i++)
|
||||
d = System.Math.Max(d, this[i]);
|
||||
return d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class DTMath
|
||||
{
|
||||
public static Vector3 ParallelTransportFrame(Vector3 up, Vector3 tan0, Vector3 tan1)
|
||||
{
|
||||
Vector3 A = Vector3.Cross(tan0, tan1);
|
||||
if (tan0 == -tan1) //Result is undefined in this case
|
||||
{
|
||||
Debug.LogWarning("[DevTools] ParallelTransportFrame's result is undefined for cases where tan0 == -tan1");
|
||||
}
|
||||
float a = Mathf.Atan2(A.magnitude, Vector3.Dot(tan0, tan1));
|
||||
return Quaternion.AngleAxis(Mathf.Rad2Deg * a, A) * up;
|
||||
}
|
||||
|
||||
public static Vector3 LeftTan(ref Vector3 tan, ref Vector3 up)
|
||||
{
|
||||
return Vector3.Cross(tan, up);
|
||||
}
|
||||
|
||||
public static Vector3 RightTan(ref Vector3 tan, ref Vector3 up)
|
||||
{
|
||||
return Vector3.Cross(up, tan);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Much like Mathf.Repeat(), but DTMath.Repeat(v,v) returns v instead of 0
|
||||
/// </summary>
|
||||
public static float Repeat(float t, float length)
|
||||
{
|
||||
return (t == length) ? t : t - Mathf.Floor(t / length) * length;
|
||||
}
|
||||
|
||||
public static double FixNaN(double v)
|
||||
{
|
||||
if (double.IsNaN(v))
|
||||
v = 0;
|
||||
return v;
|
||||
}
|
||||
|
||||
public static float FixNaN(float v)
|
||||
{
|
||||
if (float.IsNaN(v))
|
||||
v = 0;
|
||||
return v;
|
||||
}
|
||||
|
||||
public static Vector2 FixNaN(Vector2 v)
|
||||
{
|
||||
if (float.IsNaN(v.x))
|
||||
{
|
||||
v.x = 0;
|
||||
}
|
||||
if (float.IsNaN(v.y))
|
||||
{
|
||||
v.y = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixes NaN for Vector3
|
||||
/// </summary>
|
||||
/// <param name="v">a Vector3</param>
|
||||
/// <returns>the "cleaned up" vector</returns>
|
||||
public static Vector3 FixNaN(Vector3 v)
|
||||
{
|
||||
if (float.IsNaN(v.x))
|
||||
{
|
||||
v.x = 0;
|
||||
}
|
||||
if (float.IsNaN(v.y))
|
||||
{
|
||||
v.y = 0;
|
||||
}
|
||||
if (float.IsNaN(v.z))
|
||||
{
|
||||
v.z = 0;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a value from a source range to a destination range
|
||||
/// </summary>
|
||||
/// <param name="min">min destination value</param>
|
||||
/// <param name="max">max destination value</param>
|
||||
/// <param name="value">current source value</param>
|
||||
/// <param name="vMin">min source value</param>
|
||||
/// <param name="vMax">max source value</param>
|
||||
/// <returns></returns>
|
||||
public static float MapValue(float min, float max, float value, float vMin = -1, float vMax = 1)
|
||||
{
|
||||
return min + (max - min) * (value - vMin) / (vMax - vMin);
|
||||
}
|
||||
|
||||
public static float SnapPrecision(float value, int decimals)
|
||||
{
|
||||
return (decimals >= 0) ? (float)System.Math.Round(value, decimals) : value;
|
||||
}
|
||||
|
||||
public static Vector2 SnapPrecision(Vector2 value, int decimals)
|
||||
{
|
||||
if (decimals < 0)
|
||||
return value;
|
||||
|
||||
value.Set(SnapPrecision(value.x, decimals), SnapPrecision(value.y, decimals));
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3 SnapPrecision(Vector3 value, int decimals)
|
||||
{
|
||||
if (decimals < 0)
|
||||
return value;
|
||||
value.Set(SnapPrecision(value.x, decimals), SnapPrecision(value.y, decimals), SnapPrecision(value.z, decimals));
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the squared distance to the nearest point on a line
|
||||
/// </summary>
|
||||
/// <param name="l1">Line P1</param>
|
||||
/// <param name="l2">Line P2</param>
|
||||
/// <param name="p">a Point</param>
|
||||
/// <param name="frag">fragment on the line (0..1) of the nearest point</param>
|
||||
/// <returns>sqrMagnitude</returns>
|
||||
public static float LinePointDistanceSqr(Vector3 l1, Vector3 l2, Vector3 p, out float frag)
|
||||
{
|
||||
Vector3 v = l2;
|
||||
v.x -= l1.x;
|
||||
v.y -= l1.y;
|
||||
v.z -= l1.z;
|
||||
|
||||
Vector3 w = p;
|
||||
w.x -= l1.x;
|
||||
w.y -= l1.y;
|
||||
w.z -= l1.z;
|
||||
|
||||
float c1 = Vector3.Dot(w, v);
|
||||
if (c1 <= 0)
|
||||
{
|
||||
frag = 0;
|
||||
return w.sqrMagnitude;
|
||||
}
|
||||
float c2 = Vector3.Dot(v, v);
|
||||
if (c2 <= c1)
|
||||
{
|
||||
frag = 1;
|
||||
|
||||
Vector3 pMinusl2 = p;
|
||||
pMinusl2.x -= l2.x;
|
||||
pMinusl2.y -= l2.y;
|
||||
pMinusl2.z -= l2.z;
|
||||
|
||||
return pMinusl2.sqrMagnitude;
|
||||
}
|
||||
frag = c1 / c2;
|
||||
|
||||
Vector3 fragMulv = v;
|
||||
fragMulv.x *= frag;
|
||||
fragMulv.y *= frag;
|
||||
fragMulv.z *= frag;
|
||||
|
||||
Vector3 pb = l1;
|
||||
pb.x += fragMulv.x;
|
||||
pb.y += fragMulv.y;
|
||||
pb.z += fragMulv.z;
|
||||
|
||||
Vector3 pMinuspb = p;
|
||||
pMinuspb.x -= pb.x;
|
||||
pMinuspb.y -= pb.y;
|
||||
pMinuspb.z -= pb.z;
|
||||
|
||||
return pMinuspb.sqrMagnitude;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Collide a ray (point + direction) against a line segment and return the hit point
|
||||
/// </summary>
|
||||
/// <param name="r0">Ray position</param>
|
||||
/// <param name="dir">Ray direction</param>
|
||||
/// <param name="l1">Line P1</param>
|
||||
/// <param name="l2">Line P2</param>
|
||||
/// <param name="hit">Collision Point</param>
|
||||
/// <param name="frag">fragment on the line (0..1) of the collision point</param>
|
||||
/// <returns>true if collision occurs</returns>
|
||||
public static bool RayLineSegmentIntersection(Vector2 r0, Vector2 dir, Vector2 l1, Vector2 l2, out Vector2 hit, out float frag)
|
||||
{
|
||||
Vector2 s2 = l2 - l1;
|
||||
float t;
|
||||
frag = (-dir.y * (r0.x - l1.x) + dir.x * (r0.y - l1.y)) / (-s2.x * dir.y + dir.x * s2.y);
|
||||
t = (s2.x * (r0.y - l1.y) - s2.y * (r0.x - l1.x)) / (-s2.x * dir.y + dir.x * s2.y);
|
||||
|
||||
if (frag >= 0 && frag <= 1 && t > 0)
|
||||
{
|
||||
hit = new Vector2(r0.x + (t * dir.x), r0.y + (t * dir.y));
|
||||
return true;
|
||||
}
|
||||
hit = Vector2.zero;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection line segment between 2 lines (not segments).
|
||||
/// </summary>
|
||||
/// <returns>false if no solution can be found</returns>
|
||||
public static bool ShortestIntersectionLine(Vector3 line1A, Vector3 line1B,
|
||||
Vector3 line2A, Vector3 line2B, out Vector3 resultSegmentA, out Vector3 resultSegmentB)
|
||||
{
|
||||
// Algorithm is ported from the C algorithm of
|
||||
// Paul Bourke at http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline3d/
|
||||
resultSegmentA = Vector3.zero;
|
||||
resultSegmentB = Vector3.zero;
|
||||
|
||||
Vector3 p1 = line1A;
|
||||
Vector3 p2 = line1B;
|
||||
Vector3 p3 = line2A;
|
||||
Vector3 p4 = line2B;
|
||||
Vector3 p13 = p1 - p3;
|
||||
Vector3 p43 = p4 - p3;
|
||||
|
||||
if (p43.sqrMagnitude < Mathf.Epsilon)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Vector3 p21 = p2 - p1;
|
||||
if (p21.sqrMagnitude < Mathf.Epsilon)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
double d1343 = p13.x * (double)p43.x + (double)p13.y * p43.y + (double)p13.z * p43.z;
|
||||
double d4321 = p43.x * (double)p21.x + (double)p43.y * p21.y + (double)p43.z * p21.z;
|
||||
double d1321 = p13.x * (double)p21.x + (double)p13.y * p21.y + (double)p13.z * p21.z;
|
||||
double d4343 = p43.x * (double)p43.x + (double)p43.y * p43.y + (double)p43.z * p43.z;
|
||||
double d2121 = p21.x * (double)p21.x + (double)p21.y * p21.y + (double)p21.z * p21.z;
|
||||
|
||||
double denom = d2121 * d4343 - d4321 * d4321;
|
||||
if (System.Math.Abs(denom) < double.Epsilon)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
double numer = d1343 * d4321 - d1321 * d4343;
|
||||
|
||||
double mua = numer / denom;
|
||||
double mub = (d1343 + d4321 * (mua)) / d4343;
|
||||
resultSegmentA = new Vector3((float)(p1.x + mua * p21.x), (float)(p1.y + mua * p21.y), (float)(p1.z + mua * p21.z));
|
||||
resultSegmentB = new Vector3((float)(p3.x + mub * p43.x), (float)(p3.y + mub * p43.y), (float)(p3.z + mub * p43.z));
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection between two line segments
|
||||
/// </summary>
|
||||
/// <returns>false if no solution can be found</returns>
|
||||
public static bool LineLineIntersection(Vector3 line1A, Vector3 line1B, Vector3 line2A, Vector3 line2B, out Vector3 hitPoint)
|
||||
{
|
||||
Vector3 resB;
|
||||
if (ShortestIntersectionLine(line1A, line1B, line2A, line2B, out hitPoint, out resB))
|
||||
{
|
||||
if ((resB - hitPoint).sqrMagnitude <= Mathf.Epsilon * Mathf.Epsilon)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool LineLineIntersect(Vector2 line1A, Vector2 line1B, Vector2 line2A, Vector2 line2B, out Vector2 hitPoint, bool segmentOnly = true)
|
||||
{
|
||||
hitPoint = Vector2.zero;
|
||||
// Denominator for ua and ub are the same, so store this calculation
|
||||
double d =
|
||||
(line2B.y - line2A.y) * (line1B.x - line1A.x)
|
||||
-
|
||||
(line2B.x - line2A.x) * (line1B.y - line1A.y);
|
||||
|
||||
//n_a and n_b are calculated as seperate values for readability
|
||||
double n_a =
|
||||
(line2B.x - line2A.x) * (line1A.y - line2A.y)
|
||||
-
|
||||
(line2B.y - line2A.y) * (line1A.x - line2A.x);
|
||||
|
||||
double n_b =
|
||||
(line1B.x - line1A.x) * (line1A.y - line2A.y)
|
||||
-
|
||||
(line1B.y - line1A.y) * (line1A.x - line2A.x);
|
||||
|
||||
// Make sure there is not a division by zero - this also indicates that
|
||||
// the lines are parallel.
|
||||
// If n_a and n_b were both equal to zero the lines would be on top of each
|
||||
// other (coincidental). This check is not done because it is not
|
||||
// necessary for this implementation (the parallel check accounts for this).
|
||||
if (d == 0)
|
||||
return false;
|
||||
|
||||
// Calculate the intermediate fractional point that the lines potentially intersect.
|
||||
double ua = n_a / d;
|
||||
double ub = n_b / d;
|
||||
|
||||
// The fractional point will be between 0 and 1 inclusive if the lines
|
||||
// intersect. If the fractional calculation is larger than 1 or smaller
|
||||
// than 0 the lines would need to be longer to intersect.
|
||||
if (!segmentOnly || (ua >= 0d && ua <= 1d && ub >= 0d && ub <= 1d))
|
||||
{
|
||||
hitPoint.Set((float)(line1A.x + (ua * (line1B.x - line1A.x))), (float)(line1A.y + (ua * (line1B.y - line1A.y))));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool PointInsideTriangle(Vector3 A, Vector3 B, Vector3 C, Vector3 p, out float ac, out float ab, bool edgesAllowed)
|
||||
{
|
||||
Vector3 v0 = C - A;
|
||||
Vector3 v1 = B - A;
|
||||
Vector3 v2 = p - A;
|
||||
float dot00 = Vector3.Dot(v0, v0);
|
||||
float dot01 = Vector3.Dot(v0, v1);
|
||||
float dot02 = Vector3.Dot(v0, v2);
|
||||
float dot11 = Vector3.Dot(v1, v1);
|
||||
float dot12 = Vector3.Dot(v1, v2);
|
||||
float invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
|
||||
ac = (dot11 * dot02 - dot01 * dot12) * invDenom;
|
||||
ab = (dot00 * dot12 - dot01 * dot02) * invDenom;
|
||||
if (edgesAllowed)
|
||||
return ac >= 0 && ab >= 0 && (ac + ab < 1);
|
||||
else
|
||||
return ac > 0 && ab > 0 && (ac + ab < 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extended UnityEvent
|
||||
/// </summary>
|
||||
public class UnityEventEx<T0> : UnityEvent<T0>
|
||||
{
|
||||
object mCallerList;
|
||||
MethodInfo mCallsCount;
|
||||
int mCount = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Removes and adds a listener, ensuring it's only bound once
|
||||
/// </summary>
|
||||
/// <param name="call"></param>
|
||||
public void AddListenerOnce(UnityAction<T0> call)
|
||||
{
|
||||
base.RemoveListener(call);
|
||||
base.AddListener(call);
|
||||
CheckForListeners();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the event has any listeners at all
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool HasListeners()
|
||||
{
|
||||
if (mCallsCount == null)
|
||||
{
|
||||
FieldInfo fi = typeof(UnityEventBase).FieldByName("m_Calls", false, true);
|
||||
if (fi != null)
|
||||
{
|
||||
mCallerList = fi.GetValue(this);
|
||||
if (mCallerList != null)
|
||||
mCallsCount = mCallerList.GetType().PropertyByName("Count").GetGetMethod();
|
||||
}
|
||||
}
|
||||
if (mCount == -1)
|
||||
{
|
||||
if (mCallerList != null && mCallsCount != null)
|
||||
mCount = (int)mCallsCount.Invoke(mCallerList, null); //.GetValue(mCallerList, null);
|
||||
mCount += GetPersistentEventCount();
|
||||
}
|
||||
return (mCount > 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force HasListeners() to recheck for dynamically bound events
|
||||
/// </summary>
|
||||
public void CheckForListeners()
|
||||
{
|
||||
mCount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same functionality as various UnityEngine.Debug methods
|
||||
/// </summary>
|
||||
/// <remarks>Used to distinct between temporarly debug output and regular package output</remarks>
|
||||
public static class DTLog
|
||||
{
|
||||
public static void Log(object message)
|
||||
{
|
||||
Debug.Log(message);
|
||||
}
|
||||
|
||||
public static void Log(object message, UnityEngine.Object context)
|
||||
{
|
||||
Debug.Log(message, context);
|
||||
}
|
||||
|
||||
public static void LogError(object message)
|
||||
{
|
||||
Debug.LogError(message);
|
||||
}
|
||||
|
||||
public static void LogError(object message, UnityEngine.Object context)
|
||||
{
|
||||
Debug.LogError(message, context);
|
||||
}
|
||||
|
||||
public static void LogErrorFormat(string format, params object[] args)
|
||||
{
|
||||
Debug.LogErrorFormat(format, args);
|
||||
}
|
||||
|
||||
public static void LogErrorFormat(UnityEngine.Object context, string format, params object[] args)
|
||||
{
|
||||
Debug.LogErrorFormat(context, format, args);
|
||||
}
|
||||
|
||||
public static void LogException(Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
}
|
||||
|
||||
public static void LogException(Exception exception, UnityEngine.Object context)
|
||||
{
|
||||
Debug.LogException(exception, context);
|
||||
}
|
||||
|
||||
public static void LogFormat(string format, params object[] args)
|
||||
{
|
||||
Debug.LogFormat(format, args);
|
||||
}
|
||||
|
||||
public static void LogFormat(UnityEngine.Object context, string format, params object[] args)
|
||||
{
|
||||
Debug.LogFormat(context, format, args);
|
||||
}
|
||||
|
||||
public static void LogWarning(object message)
|
||||
{
|
||||
Debug.LogWarning(message);
|
||||
}
|
||||
|
||||
public static void LogWarning(object message, UnityEngine.Object context)
|
||||
{
|
||||
Debug.LogWarning(message, context);
|
||||
}
|
||||
|
||||
public static void LogWarningFormat(string format, params object[] args)
|
||||
{
|
||||
Debug.LogWarningFormat(format, args);
|
||||
}
|
||||
|
||||
public static void LogWarningFormat(UnityEngine.Object context, string format, params object[] args)
|
||||
{
|
||||
Debug.LogWarningFormat(context, format, args);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3a715a2c19f08a4ba8b136add10408f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,814 @@
|
||||
// =====================================================================
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45f5314b4eeb54743a8d8494c74a292b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,293 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
public enum DTMessageType
|
||||
{
|
||||
None = 0,
|
||||
Info = 1,
|
||||
Warning = 2,
|
||||
Error = 3,
|
||||
}
|
||||
|
||||
public enum AttributeOptionsFlags : int
|
||||
{
|
||||
None = 0,
|
||||
Compact = 1,
|
||||
Clipboard = 128,
|
||||
Zero = 256,
|
||||
One = 512,
|
||||
Negate = 1024,
|
||||
Full = 1920,
|
||||
FullCompact = 1921
|
||||
}
|
||||
|
||||
public class DTPropertyAttribute : PropertyAttribute
|
||||
{
|
||||
|
||||
public string Label;
|
||||
public string Tooltip;
|
||||
public string Color;
|
||||
public AttributeOptionsFlags Options;
|
||||
public int Precision = -1;
|
||||
|
||||
public DTPropertyAttribute(string label="", string tooltip = "")
|
||||
{
|
||||
Label = label;
|
||||
Tooltip = tooltip;
|
||||
}
|
||||
}
|
||||
|
||||
public class LabelAttribute : DTPropertyAttribute
|
||||
{
|
||||
public LabelAttribute() : base() { }
|
||||
public LabelAttribute(string label, string tooltip = "") : base(label, tooltip) { }
|
||||
}
|
||||
|
||||
public class ToggleButtonAttribute : DTPropertyAttribute
|
||||
{
|
||||
public ToggleButtonAttribute(string label = "", string tooltip = "") : base(label, tooltip)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class LayerAttribute : DTPropertyAttribute
|
||||
{
|
||||
public LayerAttribute(string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class TagAttribute : DTPropertyAttribute
|
||||
{
|
||||
public TagAttribute(string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class VectorExAttribute : DTPropertyAttribute
|
||||
{
|
||||
public VectorExAttribute(string label = "", string tooltip = "") : base(label, tooltip)
|
||||
{
|
||||
Options = AttributeOptionsFlags.Full;
|
||||
}
|
||||
}
|
||||
|
||||
public class AnimationCurveExAttribute : DTPropertyAttribute
|
||||
{
|
||||
public AnimationCurveExAttribute(string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
Options = AttributeOptionsFlags.Clipboard;
|
||||
}
|
||||
}
|
||||
|
||||
public class MinAttribute : DTPropertyAttribute
|
||||
{
|
||||
public float MinValue;
|
||||
public string MinFieldOrPropertyName;
|
||||
|
||||
public MinAttribute(float value, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinValue = value;
|
||||
}
|
||||
|
||||
public MinAttribute(string fieldOrProperty, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinFieldOrPropertyName = fieldOrProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public class PositiveAttribute : MinAttribute
|
||||
{
|
||||
public PositiveAttribute() : base(0) { }
|
||||
}
|
||||
|
||||
public class MaxAttribute : DTPropertyAttribute
|
||||
{
|
||||
public float MaxValue;
|
||||
public string MaxFieldOrPropertyName;
|
||||
|
||||
public MaxAttribute(float value, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MaxValue = value;
|
||||
}
|
||||
|
||||
public MaxAttribute(string fieldOrProperty, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MaxFieldOrPropertyName = fieldOrProperty;
|
||||
}
|
||||
}
|
||||
|
||||
public class RangeExAttribute : DTPropertyAttribute
|
||||
{
|
||||
public float MinValue;
|
||||
public string MinFieldOrPropertyName;
|
||||
public float MaxValue;
|
||||
public string MaxFieldOrPropertyName;
|
||||
public bool Slider=true;
|
||||
|
||||
public RangeExAttribute(float minValue, float maxValue, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinValue = minValue;
|
||||
MaxValue = maxValue;
|
||||
}
|
||||
|
||||
public RangeExAttribute(string minFieldOrProperty, float maxValue, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinFieldOrPropertyName = minFieldOrProperty;
|
||||
MaxValue = maxValue;
|
||||
}
|
||||
|
||||
public RangeExAttribute(float minValue, string maxFieldOrProperty, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinValue = minValue;
|
||||
MaxFieldOrPropertyName = maxFieldOrProperty;
|
||||
}
|
||||
|
||||
public RangeExAttribute(string minFieldOrProperty, string maxFieldOrProperty, string label = "", string tooltip = "")
|
||||
: base(label, tooltip)
|
||||
{
|
||||
MinFieldOrPropertyName = minFieldOrProperty;
|
||||
MaxFieldOrPropertyName = maxFieldOrProperty;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class MinMaxAttribute : DTPropertyAttribute
|
||||
{
|
||||
public readonly string MaxValueField;
|
||||
public float Min;
|
||||
public string MinBoundFieldOrPropertyName;
|
||||
public float Max;
|
||||
public string MaxBoundFieldOrPropertyName;
|
||||
|
||||
public MinMaxAttribute(string maxValueField, string label="", string tooltip="") : base(label, tooltip)
|
||||
{
|
||||
MaxValueField = maxValueField;
|
||||
Min = 0;
|
||||
Max = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumFlagAttribute : DTPropertyAttribute
|
||||
{
|
||||
public EnumFlagAttribute(string label = "", string tooltip = "") : base(label,tooltip) { }
|
||||
}
|
||||
|
||||
public class ObjectSelectorAttribute : DTPropertyAttribute
|
||||
{
|
||||
public ObjectSelectorAttribute(string label = "", string tooltip = "") : base(label, tooltip)
|
||||
{ }
|
||||
}
|
||||
|
||||
public class PathSelectorAttribute : DTPropertyAttribute
|
||||
{
|
||||
public enum DialogMode
|
||||
{
|
||||
OpenFile,
|
||||
OpenFolder,
|
||||
CreateFile
|
||||
}
|
||||
|
||||
public readonly DialogMode Mode;
|
||||
public string Title;
|
||||
public string Directory;
|
||||
public string Extension;
|
||||
public string DefaultName;
|
||||
|
||||
public PathSelectorAttribute(DialogMode mode=DialogMode.OpenFile)
|
||||
{
|
||||
Mode = mode;
|
||||
Directory = Application.dataPath;
|
||||
}
|
||||
}
|
||||
|
||||
public class EnumSelectionGridAttribute : DTPropertyAttribute
|
||||
{
|
||||
public EnumSelectionGridAttribute(string label="",string tooltip="") : base(label,tooltip) { }
|
||||
}
|
||||
|
||||
public class DTRegionAttribute : DTPropertyAttribute
|
||||
{
|
||||
public bool RegionIsOptional;
|
||||
public string RegionOptionsPropertyName;
|
||||
public bool UseSlider=true;
|
||||
}
|
||||
|
||||
public class FloatRegionAttribute : DTRegionAttribute { }
|
||||
public class IntRegionAttribute : DTRegionAttribute { }
|
||||
|
||||
public struct RegionOptions<T>
|
||||
{
|
||||
public string LabelFrom;
|
||||
public string LabelTo;
|
||||
public string OptionalTooltip;
|
||||
public DTValueClamping ClampFrom;
|
||||
public DTValueClamping ClampTo;
|
||||
public T FromMin;
|
||||
public T FromMax;
|
||||
public T ToMin;
|
||||
public T ToMax;
|
||||
|
||||
public static RegionOptions<T> Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return new RegionOptions<T>
|
||||
{
|
||||
OptionalTooltip = "Range",
|
||||
LabelFrom= "From",
|
||||
LabelTo= "To",
|
||||
ClampFrom = DTValueClamping.None,
|
||||
ClampTo = DTValueClamping.None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static RegionOptions<T> MinMax(T min, T max)
|
||||
{
|
||||
return new RegionOptions<T>
|
||||
{
|
||||
LabelFrom = "From",
|
||||
LabelTo = "To",
|
||||
ClampFrom = DTValueClamping.Range,
|
||||
ClampTo = DTValueClamping.Range,
|
||||
FromMin=min,
|
||||
FromMax=max,
|
||||
ToMin=min,
|
||||
ToMax=max
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public enum DTValueClamping
|
||||
{
|
||||
None = 0,
|
||||
Min = 1,
|
||||
Max = 2,
|
||||
Range = 3
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eed9fb14b94dcae4e826c61c76e7e7b2
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,473 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
#if ENABLE_IL2CPP == false
|
||||
using System.Linq.Expressions;
|
||||
#endif
|
||||
using System.Runtime.CompilerServices;
|
||||
using UnityEngine.Assertions;
|
||||
#if !UNITY_WSA && !UNITY_WEBGL
|
||||
using System.Threading;
|
||||
#endif
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A class to execute actions in a multi-threaded way
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the action input</typeparam>
|
||||
public class ThreadPoolWorker<T> : IDisposable
|
||||
{
|
||||
//TODO OPTIM Is ThreadPoolWorker still needed. Aren't all unity version handling .Net's parallel fors now?
|
||||
//TODO use Windows.System.Threading.ThreadPool.RunAsync(workItem => SomeMethod()); #if NETFX_CORE
|
||||
#if !UNITY_WSA && !UNITY_WEBGL
|
||||
private readonly SimplePool<QueuedCallback> queuedCallbackPool = new SimplePool<QueuedCallback>(4);
|
||||
private readonly SimplePool<LoopState<T>> loopStatePool = new SimplePool<LoopState<T>>(4);
|
||||
|
||||
private int _remainingWorkItems = 1;
|
||||
private ManualResetEvent _done = new ManualResetEvent(false);
|
||||
private WaitCallback handleWorkItemCallBack;
|
||||
private WaitCallback handleLoopCallBack;
|
||||
|
||||
public ThreadPoolWorker()
|
||||
{
|
||||
handleWorkItemCallBack = o =>
|
||||
{
|
||||
QueuedCallback queuedCallback = (QueuedCallback)o;
|
||||
try
|
||||
{
|
||||
queuedCallback.Callback(queuedCallback.State);
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (queuedCallbackPool)
|
||||
queuedCallbackPool.ReleaseItem(queuedCallback);
|
||||
DoneWorkItem();
|
||||
}
|
||||
};
|
||||
|
||||
handleLoopCallBack = state =>
|
||||
{
|
||||
LoopState<T> loopS = (LoopState<T>)state;
|
||||
for (int i = loopS.StartIndex; i <= loopS.EndIndex; i++)
|
||||
{
|
||||
loopS.Action(loopS.Items.ElementAt(i), i, loopS.ItemsCount);
|
||||
}
|
||||
lock (loopStatePool)
|
||||
loopStatePool.ReleaseItem(loopS);
|
||||
};
|
||||
}
|
||||
|
||||
[Obsolete("Use ParallelFor(Action<T,int,int> action, IEnumerable<T> list) instead")]
|
||||
public void ParralelFor(Action<T> action, List<T> list)
|
||||
{
|
||||
ParallelFor((item, itemIndex, itemsCount) => action(item), list, list.Count());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list)
|
||||
{
|
||||
ParallelFor(action, list, list.Count());
|
||||
}
|
||||
|
||||
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list, int elementsCount)
|
||||
{
|
||||
int threadsToUseCount;
|
||||
{
|
||||
int availableThreads;
|
||||
#if NET_4_6
|
||||
int temp;
|
||||
ThreadPool.GetAvailableThreads(out availableThreads, out temp);
|
||||
#else
|
||||
availableThreads = Environment.ProcessorCount - 1;
|
||||
#endif
|
||||
threadsToUseCount = 1 /*main thread*/ + Math.Min(availableThreads, Environment.ProcessorCount - 1 /*keep one processor for the main thead*/);
|
||||
}
|
||||
//BUG a bug in iterationPerThread leads to sometimes not using all available threads. For example, if you have 5 items and 4 cores, only 3 cores will be used
|
||||
int iterationPerThread = threadsToUseCount == 1
|
||||
? elementsCount
|
||||
: (int)Math.Ceiling((float)elementsCount / threadsToUseCount);
|
||||
#if CURVY_SANITY_CHECKS
|
||||
Assert.IsTrue(iterationPerThread * threadsToUseCount >= elementsCount);
|
||||
#endif
|
||||
int currentIndex = 0;
|
||||
while (currentIndex < elementsCount)
|
||||
{
|
||||
int endEndex = Math.Min(currentIndex + iterationPerThread - 1, elementsCount - 1);
|
||||
|
||||
if (endEndex == elementsCount - 1)
|
||||
for (int i = currentIndex; i <= endEndex; i++)
|
||||
action(list.ElementAt(i), i, elementsCount);
|
||||
else
|
||||
{
|
||||
|
||||
QueuedCallback queuedCallback;
|
||||
{
|
||||
lock (queuedCallbackPool)
|
||||
queuedCallback = queuedCallbackPool.GetItem();
|
||||
}
|
||||
|
||||
|
||||
LoopState<T> loopState;
|
||||
{
|
||||
lock (loopStatePool)
|
||||
loopState = loopStatePool.GetItem();
|
||||
}
|
||||
|
||||
loopState.Set((short)currentIndex,
|
||||
(short)endEndex,
|
||||
list,
|
||||
elementsCount,
|
||||
action);
|
||||
|
||||
queuedCallback.State = loopState;
|
||||
queuedCallback.Callback = handleLoopCallBack;
|
||||
|
||||
ThrowIfDisposed();
|
||||
//Debug.LogWarning("New thread " + " from "+ loopState.StartIndex + " to " + loopState.EndIndex);
|
||||
lock (_done)
|
||||
_remainingWorkItems++;
|
||||
ThreadPool.QueueUserWorkItem(handleWorkItemCallBack, queuedCallback);
|
||||
}
|
||||
currentIndex = endEndex + 1;
|
||||
}
|
||||
|
||||
WaitAll(-1, false);
|
||||
}
|
||||
|
||||
bool WaitAll(int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
DoneWorkItem();
|
||||
bool rv = _done.WaitOne(millisecondsTimeout, exitContext);
|
||||
lock (_done)
|
||||
{
|
||||
if (rv)
|
||||
{
|
||||
_remainingWorkItems = 1;
|
||||
_done.Reset();
|
||||
}
|
||||
else
|
||||
_remainingWorkItems++;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_done == null)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
|
||||
private void DoneWorkItem()
|
||||
{
|
||||
lock (_done)
|
||||
{
|
||||
--_remainingWorkItems;
|
||||
if (_remainingWorkItems == 0)
|
||||
_done.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_done != null)
|
||||
{
|
||||
((IDisposable)_done).Dispose();
|
||||
_done = null;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public ThreadPoolWorker()
|
||||
{
|
||||
}
|
||||
|
||||
[Obsolete("Use ParallelFor(Action<T,int,int> action, IEnumerable<T> list) instead")]
|
||||
public void ParralelFor(Action<T> action, List<T> list)
|
||||
{
|
||||
for (var i = 0; i < list.Count; i++)
|
||||
action(list[i]);
|
||||
}
|
||||
|
||||
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list)
|
||||
{
|
||||
ParallelFor(action, list, list.Count());
|
||||
}
|
||||
|
||||
public void ParallelFor(Action<T, int, int> action, IEnumerable<T> list, int elementsCount)
|
||||
{
|
||||
for (int i = 0; i < elementsCount; i++)
|
||||
action(list.ElementAt(i), i, elementsCount);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal class SimplePool<T> where T : new()
|
||||
{
|
||||
private readonly List<T> freeItemsBackfield;
|
||||
#if ENABLE_IL2CPP == false
|
||||
private static readonly Func<T> OptimizedInstantiator = Expression.Lambda<Func<T>>(
|
||||
Expression.New(typeof(T))
|
||||
).Compile();
|
||||
#endif
|
||||
|
||||
public SimplePool(int preCreatedElementsCount)
|
||||
{
|
||||
freeItemsBackfield = new List<T>();
|
||||
for (int i = 0; i < preCreatedElementsCount; i++)
|
||||
freeItemsBackfield.Add(
|
||||
#if ENABLE_IL2CPP == false
|
||||
OptimizedInstantiator.Invoke()
|
||||
#else
|
||||
new T()
|
||||
#endif
|
||||
);
|
||||
}
|
||||
|
||||
public T GetItem()
|
||||
{
|
||||
T item;
|
||||
if (freeItemsBackfield.Count == 0)
|
||||
item =
|
||||
#if ENABLE_IL2CPP == false
|
||||
OptimizedInstantiator.Invoke()
|
||||
#else
|
||||
new T()
|
||||
#endif
|
||||
;
|
||||
else
|
||||
{
|
||||
int lastIndex = freeItemsBackfield.Count - 1;
|
||||
item = freeItemsBackfield[lastIndex];
|
||||
freeItemsBackfield.RemoveAt(lastIndex);
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
public void ReleaseItem(T item)
|
||||
{
|
||||
freeItemsBackfield.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
#if !UNITY_WSA && !UNITY_WEBGL
|
||||
class QueuedCallback
|
||||
{
|
||||
public WaitCallback Callback;
|
||||
public object State;
|
||||
}
|
||||
|
||||
class LoopState<T>
|
||||
{
|
||||
public short StartIndex { get; private set; }
|
||||
public short EndIndex { get; private set; }
|
||||
public IEnumerable<T> Items { get; private set; }
|
||||
public int ItemsCount { get; private set; }
|
||||
public Action<T, int, int> Action { get; private set; }
|
||||
|
||||
public LoopState()
|
||||
{
|
||||
}
|
||||
|
||||
public LoopState(short startIndex, short endIndex, IEnumerable<T> items, int itemsCount, Action<T, int, int> action)
|
||||
{
|
||||
Set(startIndex, endIndex, items, itemsCount, action);
|
||||
}
|
||||
|
||||
public void Set(short startIndex, short endIndex, IEnumerable<T> items, int itemsCount, Action<T, int, int> action)
|
||||
{
|
||||
StartIndex = startIndex;
|
||||
EndIndex = endIndex;
|
||||
Items = items;
|
||||
ItemsCount = itemsCount;
|
||||
Action = action;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This class is not very optimized. For better performance, use the generic version of ThreadPoolWorker instead
|
||||
/// </summary>
|
||||
#if !UNITY_WSA && !UNITY_WEBGL
|
||||
[Obsolete("Use ThreadPoolWorker<T> instead")]
|
||||
public class ThreadPoolWorker : IDisposable
|
||||
{
|
||||
private int _remainingWorkItems = 1;
|
||||
private ManualResetEvent _done = new ManualResetEvent(false);
|
||||
|
||||
public void QueueWorkItem(WaitCallback callback)
|
||||
{
|
||||
QueueWorkItem(callback, null);
|
||||
}
|
||||
|
||||
public void QueueWorkItem(Action act)
|
||||
{
|
||||
QueueWorkItem(act, null);
|
||||
}
|
||||
|
||||
public void ParralelFor<T>(Action<T> action, List<T> list)
|
||||
{
|
||||
int threadsToUseCount;
|
||||
{
|
||||
int availableThreads;
|
||||
#if NET_4_6
|
||||
int temp;
|
||||
ThreadPool.GetAvailableThreads(out availableThreads, out temp);
|
||||
#else
|
||||
availableThreads = Environment.ProcessorCount - 1;
|
||||
#endif
|
||||
threadsToUseCount = 1 /*main thread*/ + Math.Min(availableThreads, Environment.ProcessorCount - 1 /*keep one processor for the main thead*/);
|
||||
}
|
||||
int iterationsCount = list.Count;
|
||||
if (threadsToUseCount == 1 || iterationsCount == 1)
|
||||
{
|
||||
for (int i = 0; i < iterationsCount; i++)
|
||||
{
|
||||
action(list[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int iterationPerThread = (int)Math.Ceiling((float)iterationsCount / threadsToUseCount);
|
||||
int currentIndex = 0;
|
||||
while (currentIndex < iterationsCount)
|
||||
{
|
||||
QueuedCallback queuedCallback = new QueuedCallback();
|
||||
|
||||
int endEndex = Math.Min(currentIndex + iterationPerThread, iterationsCount - 1);
|
||||
|
||||
LoopState<T> loopState = new LoopState<T>((short)currentIndex,
|
||||
(short)endEndex,
|
||||
list,
|
||||
iterationsCount,
|
||||
(item,
|
||||
itemIndex,
|
||||
itemsCount) => action(item));
|
||||
queuedCallback.State = loopState;
|
||||
|
||||
queuedCallback.Callback = state =>
|
||||
{
|
||||
LoopState<T> loopS = (LoopState<T>)state;
|
||||
for (int i = loopS.StartIndex; i <= loopS.EndIndex; i++)
|
||||
{
|
||||
loopS.Action(loopS.Items.ElementAt(i), i, iterationsCount);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
QueueWorkItem(queuedCallback);
|
||||
|
||||
currentIndex = endEndex + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueWorkItem(QueuedCallback callback)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
lock (_done)
|
||||
_remainingWorkItems++;
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(HandleWorkItem), callback);
|
||||
}
|
||||
|
||||
public void QueueWorkItem(WaitCallback callback, object state)
|
||||
{
|
||||
QueuedCallback qc = new QueuedCallback();
|
||||
qc.Callback = callback;
|
||||
qc.State = state;
|
||||
QueueWorkItem(qc);
|
||||
}
|
||||
|
||||
public void QueueWorkItem(Action act, object state)
|
||||
{
|
||||
QueuedCallback qc = new QueuedCallback();
|
||||
qc.Callback = (x => act());
|
||||
qc.State = state;
|
||||
QueueWorkItem(qc);
|
||||
}
|
||||
|
||||
public bool WaitAll()
|
||||
{
|
||||
return WaitAll(-1, false);
|
||||
}
|
||||
|
||||
public bool WaitAll(TimeSpan timeout, bool exitContext)
|
||||
{
|
||||
return WaitAll((int)timeout.TotalMilliseconds, exitContext);
|
||||
}
|
||||
|
||||
public bool WaitAll(int millisecondsTimeout, bool exitContext)
|
||||
{
|
||||
ThrowIfDisposed();
|
||||
DoneWorkItem();
|
||||
bool rv = _done.WaitOne(millisecondsTimeout, exitContext);
|
||||
lock (_done)
|
||||
{
|
||||
if (rv)
|
||||
{
|
||||
_remainingWorkItems = 1;
|
||||
_done.Reset();
|
||||
}
|
||||
else
|
||||
_remainingWorkItems++;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private void HandleWorkItem(object state)
|
||||
{
|
||||
QueuedCallback qc = (QueuedCallback)state;
|
||||
try
|
||||
{
|
||||
qc.Callback(qc.State);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DoneWorkItem();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoneWorkItem()
|
||||
{
|
||||
lock (_done)
|
||||
{
|
||||
--_remainingWorkItems;
|
||||
if (_remainingWorkItems == 0)
|
||||
_done.Set();
|
||||
}
|
||||
}
|
||||
|
||||
private void ThrowIfDisposed()
|
||||
{
|
||||
if (_done == null)
|
||||
throw new ObjectDisposedException(GetType().Name);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_done != null)
|
||||
{
|
||||
((IDisposable)_done).Dispose();
|
||||
_done = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public class ThreadPoolWorker{}
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a997f755ef82a3e439a530a09753ae2f
|
||||
timeCreated: 1433497656
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5743298d0dd43147bd9e4b9915a6434
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,388 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
#if CURVY_SANITY_CHECKS_PRIVATE
|
||||
using UnityEngine.Assertions;
|
||||
#endif
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A pool of instances of the UnityEngine.Component class
|
||||
/// </summary>
|
||||
[HelpURL(DTUtility.HelpUrlBase + "dtcomponentpool")]
|
||||
public class ComponentPool : DTVersionedMonoBehaviour, IPool, ISerializationCallbackReceiver
|
||||
{
|
||||
[SerializeField, HideInInspector]
|
||||
private string m_Identifier;
|
||||
|
||||
[Inline]
|
||||
[SerializeField]
|
||||
private PoolSettings m_Settings;
|
||||
|
||||
public PoolSettings Settings
|
||||
{
|
||||
get { return m_Settings; }
|
||||
set
|
||||
{
|
||||
if (m_Settings != value)
|
||||
m_Settings = value;
|
||||
if (m_Settings != null)
|
||||
m_Settings.OnValidate();
|
||||
}
|
||||
}
|
||||
|
||||
private PoolManager mManager;
|
||||
|
||||
public PoolManager Manager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mManager == null)
|
||||
mManager = GetComponent<PoolManager>();
|
||||
return mManager;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Due to bad design decisions, Identifier is used to store the type of the pooled objects. And the setter does nothing
|
||||
/// </summary>
|
||||
public string Identifier
|
||||
{
|
||||
get
|
||||
{
|
||||
#if CURVY_SANITY_CHECKS_PRIVATE
|
||||
Assert.IsNotNull(m_Identifier);
|
||||
#endif
|
||||
return m_Identifier;
|
||||
}
|
||||
set
|
||||
{
|
||||
throw new InvalidOperationException("Component pool's identifier should always indicate the pooled type's assembly qualified name");
|
||||
/*Here is why:
|
||||
In the Initialize method, m_Identifier is set as a fully qualified type name.
|
||||
The Type getter uses m_Identifier as a fully qualified type name.
|
||||
If needed, add a field that contains the pooled type name, and use it instead of Identifier when you need to find the right pool for the right component type*/
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of the pooled objects
|
||||
/// </summary>
|
||||
public Type Type
|
||||
{
|
||||
get
|
||||
{
|
||||
Type type = null;
|
||||
if (Identifier != null)
|
||||
type = Type.GetType(Identifier);
|
||||
|
||||
if (type == null)
|
||||
DTLog.LogWarning("[DevTools] ComponentPool's Type is an unknown type " + m_Identifier);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return mObjects.Count; }
|
||||
}
|
||||
|
||||
private List<Component> mObjects = new List<Component>();
|
||||
|
||||
private double mLastTime;
|
||||
private double mDeltaTime;
|
||||
|
||||
public void Initialize(Type type, PoolSettings settings)
|
||||
{
|
||||
m_Identifier = type.AssemblyQualifiedName;
|
||||
#if CURVY_SANITY_CHECKS_PRIVATE
|
||||
Assert.IsNotNull(m_Identifier);
|
||||
#endif
|
||||
m_Settings = settings;
|
||||
mLastTime = DTTime.TimeSinceStartup + UnityEngine.Random.Range(0, Settings.Speed);
|
||||
if (Settings.Prewarm)
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (Settings.Prewarm)
|
||||
Reset();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
private void OnValidate()
|
||||
{
|
||||
Settings = m_Settings;
|
||||
}
|
||||
#endif
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
SceneManager.sceneLoaded += OnSceneLoaded;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Threshold exceeded: Deleting item");
|
||||
destroy(mObjects[0]);
|
||||
mObjects.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
else if (Count < Settings.MinItems)
|
||||
{
|
||||
c = Mathf.Min(c, Settings.MinItems - Count);
|
||||
while (c-- > 0)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Below MinItems: Adding item");
|
||||
mObjects.Add(create());
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (Settings.Debug)
|
||||
log("Prewarm/Reset");
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSceneLoaded(Scene scn, LoadSceneMode mode)
|
||||
{
|
||||
for (int i = mObjects.Count - 1; i >= 0; i--)
|
||||
if (mObjects[i] == null)
|
||||
mObjects.RemoveAt(i);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Clear");
|
||||
for (int i = 0; i < Count; i++)
|
||||
destroy(mObjects[i]);
|
||||
mObjects.Clear();
|
||||
}
|
||||
|
||||
public void Push(Component item)
|
||||
{
|
||||
sendBeforePush(item);
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
item.gameObject.Destroy(false, true);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (item != null)
|
||||
{
|
||||
mObjects.Add(item);
|
||||
item.transform.parent = Manager.transform;
|
||||
item.gameObject.hideFlags = (Settings.Debug)
|
||||
? HideFlags.DontSave
|
||||
: HideFlags.HideAndDontSave;
|
||||
if (Settings.AutoEnableDisable)
|
||||
item.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public Component Pop(Transform parent = null)
|
||||
{
|
||||
Component item = null;
|
||||
if (Count > 0)
|
||||
{
|
||||
item = mObjects[0];
|
||||
mObjects.RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Settings.AutoCreate || !Application.isPlaying)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Auto create item");
|
||||
item = create();
|
||||
}
|
||||
}
|
||||
if (item)
|
||||
{
|
||||
item.gameObject.hideFlags = HideFlags.None;
|
||||
item.transform.parent = parent;
|
||||
if (Settings.AutoEnableDisable)
|
||||
item.gameObject.SetActive(true);
|
||||
sendAfterPop(item);
|
||||
if (Settings.Debug)
|
||||
log("Pop " + item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public T Pop<T>(Transform parent) where T : Component
|
||||
{
|
||||
return Pop(parent) as T;
|
||||
}
|
||||
|
||||
private Component create()
|
||||
{
|
||||
GameObject go = new GameObject();
|
||||
go.name = Identifier;
|
||||
go.transform.parent = Manager.transform;
|
||||
if (Settings.AutoEnableDisable)
|
||||
go.SetActive(false);
|
||||
Type componentType = Type;
|
||||
Component component = null;
|
||||
if (componentType != null)
|
||||
component = go.AddComponent(componentType);
|
||||
else
|
||||
DTLog.LogError(String.Format("[DevTools] ComponentPool {0} could not create component because the associated type is null", m_Identifier));
|
||||
return component;
|
||||
}
|
||||
|
||||
private void destroy(Component item)
|
||||
{
|
||||
if (item != null)
|
||||
item.gameObject.Destroy(false, true);
|
||||
}
|
||||
|
||||
private void setParent(Component item, Transform parent)
|
||||
{
|
||||
if (item != null)
|
||||
item.transform.parent = parent;
|
||||
}
|
||||
|
||||
private void sendAfterPop(Component item)
|
||||
{
|
||||
GameObject itemGameObject = item.gameObject;
|
||||
if (itemGameObject.activeSelf && itemGameObject.activeInHierarchy)
|
||||
//Send message works only for active game objects. Source: https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html
|
||||
itemGameObject.SendMessage(nameof(IPoolable.OnAfterPop), SendMessageOptions.DontRequireReceiver);
|
||||
else
|
||||
{
|
||||
if (item is IPoolable)
|
||||
((IPoolable)item).OnAfterPop();
|
||||
else
|
||||
DTLog.LogWarning("[Curvy] sendAfterPop could not send message because the receiver " + item.name + " is not active");
|
||||
}
|
||||
}
|
||||
|
||||
private void sendBeforePush(Component item)
|
||||
{
|
||||
GameObject itemGameObject = item.gameObject;
|
||||
if (itemGameObject.activeSelf && itemGameObject.activeInHierarchy)
|
||||
//Send message works only for active game objects. Source: https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html
|
||||
itemGameObject.SendMessage(nameof(IPoolable.OnBeforePush), SendMessageOptions.DontRequireReceiver);
|
||||
else
|
||||
{
|
||||
if (item is IPoolable)
|
||||
((IPoolable)item).OnBeforePush();
|
||||
else
|
||||
DTLog.LogWarning("[Curvy] sendBeforePush could not send message because the receiver " + item.name + " is not active");
|
||||
}
|
||||
}
|
||||
|
||||
private void log(string msg)
|
||||
{
|
||||
Debug.Log(string.Format("[{0}] ({1} items) {2}", Identifier, Count, msg));
|
||||
}
|
||||
|
||||
#region ISerializationCallbackReceiver
|
||||
/*! \cond PRIVATE */
|
||||
/// <summary>
|
||||
/// Implementation of UnityEngine.ISerializationCallbackReceiver
|
||||
/// Called automatically by Unity, is not meant to be called by Curvy's users
|
||||
/// </summary>
|
||||
public void OnBeforeSerialize()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of UnityEngine.ISerializationCallbackReceiver
|
||||
/// Called automatically by Unity, is not meant to be called by Curvy's users
|
||||
/// </summary>
|
||||
public void OnAfterDeserialize()
|
||||
{
|
||||
if (Type.GetType(m_Identifier) == null)
|
||||
{
|
||||
|
||||
//Handles cases where the component is migrated to another assembly (for example if using Unity's Assembly Definitions feature
|
||||
|
||||
const char separator = ',';
|
||||
const string separatorString = ",";
|
||||
string[] splittedAssemblyQualifiedName = m_Identifier.Split(separator);
|
||||
if (splittedAssemblyQualifiedName.Length >= 5)
|
||||
{
|
||||
string typeName = String.Join(separatorString, splittedAssemblyQualifiedName.SubArray(0, splittedAssemblyQualifiedName.Length - 4));
|
||||
//As you can see with this example:
|
||||
//"FluffyUnderware.Curvy.CurvySplineSegment, ToolBuddy.Curvy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"
|
||||
//the 4 last elements do not contain the type name. Keep in mind that a type name can include a ',' like Dictionary<int, List<double>>
|
||||
|
||||
#if UNITY_EDITOR
|
||||
var knownTypes = UnityEditor.TypeCache.GetTypesDerivedFrom(typeof(System.Object));
|
||||
#else
|
||||
|
||||
#if NETFX_CORE
|
||||
Type[] knownTypes = this.GetType().GetAllTypes();
|
||||
#else
|
||||
Type[] knownTypes = TypeExt.GetLoadedTypes();
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Type type = knownTypes.FirstOrDefault(t => t.FullName == typeName);
|
||||
if (type != null)
|
||||
{
|
||||
m_Identifier = type.AssemblyQualifiedName;
|
||||
#if CURVY_SANITY_CHECKS_PRIVATE
|
||||
Assert.IsNotNull(m_Identifier);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*! \endcond */
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62f98941b92097f49bd5ff480ec47122
|
||||
timeCreated: 1441006208
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,98 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
|
||||
public class DTSingleton<T> : MonoBehaviour, IDTSingleton where T : MonoBehaviour, IDTSingleton
|
||||
{
|
||||
static T _instance;
|
||||
static object _lock = new object();
|
||||
static bool applicationIsQuitting = false;
|
||||
bool isDuplicateInstance = false;
|
||||
|
||||
public static bool HasInstance
|
||||
{
|
||||
get { return _instance != null; }
|
||||
}
|
||||
|
||||
public static T Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
applicationIsQuitting = false;
|
||||
if (applicationIsQuitting)
|
||||
return null;
|
||||
|
||||
if (_instance == null)
|
||||
lock (_lock)
|
||||
if (_instance == null)
|
||||
{
|
||||
IEnumerable<Object> objectsOfTypeT = FindObjectsOfType(typeof(T)).Where(o => o != null);
|
||||
#if CURVY_SANITY_CHECKS
|
||||
if (objectsOfTypeT.Count() != FindObjectsOfType(typeof(T)).Length)
|
||||
DTLog.LogError("[Curvy] DTSingleton.Instance : FindObjectsOfType " + typeof(T).FullName + " returned at least one null reference");
|
||||
#endif
|
||||
|
||||
_instance = objectsOfTypeT.Any()
|
||||
? (T)objectsOfTypeT.ElementAt(0)
|
||||
: new GameObject().AddComponent<T>();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Awake()
|
||||
{
|
||||
T instance = Instance;
|
||||
lock (_lock)
|
||||
{
|
||||
if (instance == null)
|
||||
DTLog.LogError("[DevTools] DTSingleton instance was null. This happens if the instance is destroyed by the user, which is not a supported operation. If you did not destroy the singleton's instance, then please fill a bug report.");
|
||||
else
|
||||
if (GetInstanceID() != instance.GetInstanceID())
|
||||
{
|
||||
instance.MergeDoubleLoaded((IDTSingleton)this);
|
||||
this.isDuplicateInstance = true;
|
||||
this.Invoke(nameof(DestroySelf), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnDestroy()
|
||||
{
|
||||
lock (_lock)
|
||||
if (Application.isPlaying && !isDuplicateInstance)
|
||||
{
|
||||
applicationIsQuitting = true;
|
||||
_instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void MergeDoubleLoaded(IDTSingleton newInstance)
|
||||
{
|
||||
}
|
||||
|
||||
void DestroySelf()
|
||||
{
|
||||
gameObject.Destroy(false, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface IDTSingleton
|
||||
{
|
||||
void MergeDoubleLoaded(IDTSingleton newInstance);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be97567edb4b04d4b864736d9e10f36f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,41 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A MonoBehaviour with a version number, useful to handle upgrades if needed
|
||||
/// </summary>
|
||||
public abstract class DTVersionedMonoBehaviour : MonoBehaviour
|
||||
{
|
||||
[SerializeField, HideInInspector]
|
||||
string m_Version;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the version of this component
|
||||
/// </summary>
|
||||
public string Version
|
||||
{
|
||||
get { return m_Version; }
|
||||
protected set { m_Version = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Destroys the gameobject
|
||||
/// </summary>
|
||||
[Obsolete("Use ObjectExt.Destroy(...) instead")]
|
||||
public void Destroy()
|
||||
{
|
||||
gameObject.Destroy(false, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3c2cf93621e06d43b51fbde3128ce03
|
||||
timeCreated: 1441701724
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,58 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Add this script to a GameObject with a MeshFilter to ensure it will be properly duplicated in the editor!
|
||||
/// </summary>
|
||||
/// <remarks>On Duplicating, Awake() checks if the sharedMesh is already used in the scene. If yes, a new mesh will be created to ensure that each sharedMesh is unique</remarks>
|
||||
[ExecuteAlways]
|
||||
[RequireComponent(typeof(MeshFilter))]
|
||||
public abstract class DuplicateEditorMesh : DTVersionedMonoBehaviour
|
||||
{
|
||||
MeshFilter mFilter;
|
||||
|
||||
public MeshFilter Filter
|
||||
{
|
||||
get {
|
||||
if (mFilter == null)
|
||||
mFilter = GetComponent<MeshFilter>();
|
||||
return mFilter;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Awake()
|
||||
{
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
MeshFilter meshFilter = Filter;
|
||||
if (meshFilter && meshFilter.sharedMesh != null)
|
||||
{
|
||||
DuplicateEditorMesh[] otherWatchdogs = GameObject.FindObjectsOfType<DuplicateEditorMesh>();
|
||||
foreach (DuplicateEditorMesh dog in otherWatchdogs)
|
||||
{
|
||||
if (dog != this)
|
||||
{
|
||||
MeshFilter otherMF = dog.Filter;
|
||||
if (otherMF && otherMF.sharedMesh == meshFilter.sharedMesh)
|
||||
{
|
||||
Mesh m = new Mesh();
|
||||
m.name = otherMF.sharedMesh.name;
|
||||
meshFilter.mesh = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a648044f27666564a9cf38d201465713
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,24 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A component to display notes in the inspector
|
||||
/// </summary>
|
||||
[HelpURL(DTUtility.HelpUrlBase + "dtinspectornode")]
|
||||
public class InspectorNote : DTVersionedMonoBehaviour
|
||||
{
|
||||
|
||||
[TextArea(5,20)]
|
||||
[SerializeField]
|
||||
string m_Note;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eed5f71264f54224aaf071d6a2e69915
|
||||
timeCreated: 1442489791
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// Manages the object's pools components
|
||||
/// </summary>
|
||||
[HelpURL(DTUtility.HelpUrlBase + "dtpoolmanager")]
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
public class PoolManager : DTVersionedMonoBehaviour
|
||||
{
|
||||
[Section("General")]
|
||||
[SerializeField]
|
||||
bool m_AutoCreatePools = true;
|
||||
|
||||
[AsGroup(Expanded = false)]
|
||||
[SerializeField]
|
||||
PoolSettings m_DefaultSettings = new PoolSettings();
|
||||
|
||||
public bool AutoCreatePools
|
||||
{
|
||||
get { return m_AutoCreatePools; }
|
||||
set
|
||||
{
|
||||
if (m_AutoCreatePools != value)
|
||||
m_AutoCreatePools = value;
|
||||
}
|
||||
}
|
||||
|
||||
public PoolSettings DefaultSettings
|
||||
{
|
||||
get { return m_DefaultSettings; }
|
||||
set
|
||||
{
|
||||
if (m_DefaultSettings != value)
|
||||
m_DefaultSettings = value;
|
||||
if (m_DefaultSettings != null)
|
||||
m_DefaultSettings.OnValidate();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
public int Count { get { return Pools.Count + TypePools.Count; } }
|
||||
|
||||
public Dictionary<string, IPool> Pools = new Dictionary<string, IPool>();
|
||||
|
||||
public Dictionary<Type, IPool> TypePools = new Dictionary<Type, IPool>();
|
||||
|
||||
IPool[] mPools = new IPool[0];
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
DefaultSettings = m_DefaultSettings;
|
||||
}
|
||||
#endif
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
IsInitialized = false;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
Initialize();
|
||||
#endif
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
|
||||
if (mPools.Length != TypePools.Count)
|
||||
{
|
||||
System.Array.Resize(ref mPools, TypePools.Count);
|
||||
TypePools.Values.CopyTo(mPools, 0);
|
||||
}
|
||||
for (int i = 0; i < mPools.Length; i++)
|
||||
mPools[i].Update();
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
Pools.Clear();
|
||||
IPool[] goPools = GetComponents<IPool>();
|
||||
foreach (IPool p in goPools)
|
||||
{
|
||||
if (p is ComponentPool)
|
||||
{
|
||||
if(Pools.ContainsKey(p.Identifier) == false)
|
||||
Pools.Add(p.Identifier, p);
|
||||
else
|
||||
{
|
||||
DTLog.Log("[DevTools] Found a duplicated ComponentPool for type " + p.Identifier + ". The duplicated pool will be destroyed");
|
||||
(p as ComponentPool).Destroy(false, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p.Identifier = GetUniqueIdentifier(p.Identifier);
|
||||
Pools.Add(p.Identifier, p);
|
||||
}
|
||||
}
|
||||
|
||||
IsInitialized = true;
|
||||
}
|
||||
|
||||
public string GetUniqueIdentifier(string ident)
|
||||
{
|
||||
int num = 0;
|
||||
string id = ident;
|
||||
while (Pools.ContainsKey(id))
|
||||
id = ident + (++num).ToString();
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
public Pool<T> GetTypePool<T>()
|
||||
{
|
||||
IPool res = null;
|
||||
|
||||
if (!TypePools.TryGetValue(typeof(T), out res))
|
||||
{
|
||||
if (AutoCreatePools)
|
||||
{
|
||||
res = CreateTypePool<T>();
|
||||
}
|
||||
}
|
||||
return (Pool<T>)res;
|
||||
}
|
||||
|
||||
public ComponentPool GetComponentPool<T>() where T : Component
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
IPool res = null;
|
||||
|
||||
if (!Pools.TryGetValue(typeof(T).AssemblyQualifiedName, out res))
|
||||
{
|
||||
if (AutoCreatePools)
|
||||
{
|
||||
res = CreateComponentPool<T>();
|
||||
}
|
||||
}
|
||||
return (ComponentPool)res;
|
||||
}
|
||||
|
||||
public PrefabPool GetPrefabPool(string identifier, params GameObject[] prefabs)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
IPool pool;
|
||||
if (!Pools.TryGetValue(identifier, out pool))
|
||||
{
|
||||
if (AutoCreatePools)
|
||||
pool = CreatePrefabPool(identifier, null, prefabs);
|
||||
}
|
||||
return (PrefabPool)pool;
|
||||
}
|
||||
|
||||
public Pool<T> CreateTypePool<T>(PoolSettings settings = null)
|
||||
{
|
||||
PoolSettings s = settings ?? new PoolSettings(DefaultSettings);
|
||||
IPool res = null;
|
||||
if (!TypePools.TryGetValue(typeof(T), out res))
|
||||
{
|
||||
res = new Pool<T>(s);
|
||||
TypePools.Add(typeof(T), res);
|
||||
}
|
||||
return (Pool<T>)res;
|
||||
}
|
||||
|
||||
public ComponentPool CreateComponentPool<T>(PoolSettings settings = null) where T : Component
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
PoolSettings s = settings ?? new PoolSettings(DefaultSettings);
|
||||
IPool res = null;
|
||||
|
||||
if (!Pools.TryGetValue(typeof(T).AssemblyQualifiedName, out res))
|
||||
{
|
||||
res = gameObject.AddComponent<ComponentPool>();
|
||||
((ComponentPool)res).Initialize(typeof(T), s);
|
||||
Pools.Add(res.Identifier, res);
|
||||
}
|
||||
return (ComponentPool)res;
|
||||
}
|
||||
|
||||
public PrefabPool CreatePrefabPool(string name, PoolSettings settings = null, params GameObject[] prefabs)
|
||||
{
|
||||
if (!IsInitialized)
|
||||
Initialize();
|
||||
PoolSettings s = settings ?? new PoolSettings(DefaultSettings);
|
||||
|
||||
IPool pool = null;
|
||||
if (!Pools.TryGetValue(name, out pool))
|
||||
{
|
||||
PrefabPool p = gameObject.AddComponent<PrefabPool>();
|
||||
p.Initialize(name, s, prefabs);
|
||||
Pools.Add(name, p);
|
||||
return p;
|
||||
}
|
||||
return (PrefabPool)pool;
|
||||
}
|
||||
|
||||
public List<IPool> FindPools(string identifierStartsWith)
|
||||
{
|
||||
List<IPool> res = new List<IPool>();
|
||||
foreach (KeyValuePair<string, IPool> kv in Pools)
|
||||
if (kv.Key.StartsWith(identifierStartsWith))
|
||||
res.Add(kv.Value);
|
||||
return res;
|
||||
}
|
||||
|
||||
public void DeletePools(string startsWith)
|
||||
{
|
||||
List<IPool> toDelete = FindPools(startsWith);
|
||||
for (int i = toDelete.Count - 1; i >= 0; i--)
|
||||
DeletePool(toDelete[i]);
|
||||
|
||||
}
|
||||
|
||||
public void DeletePool(IPool pool)
|
||||
{
|
||||
if (pool is PrefabPool || pool is ComponentPool)
|
||||
{
|
||||
((MonoBehaviour) pool).Destroy(false, false);
|
||||
Pools.Remove(pool.Identifier);
|
||||
}
|
||||
}
|
||||
|
||||
public void DeletePool<T>()
|
||||
{
|
||||
TypePools.Remove(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d8a3277e999b164d99f199328dc5d2d
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,307 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using System.Collections;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevTools
|
||||
{
|
||||
/// <summary>
|
||||
/// A pool of instances of a prefab GameObject
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(PoolManager))]
|
||||
[HelpURL(DTUtility.HelpUrlBase + "dtprefabpool")]
|
||||
public class PrefabPool : DTVersionedMonoBehaviour, IPool
|
||||
{
|
||||
[FieldCondition(nameof(m_Identifier), "", false, ActionAttribute.ActionEnum.ShowWarning, "Please enter an identifier! (Select a prefab to set automatically)")]
|
||||
[SerializeField]
|
||||
string m_Identifier;
|
||||
|
||||
[SerializeField]
|
||||
List<GameObject> m_Prefabs = new List<GameObject>();
|
||||
|
||||
[Inline]
|
||||
[SerializeField]
|
||||
PoolSettings m_Settings;
|
||||
|
||||
|
||||
|
||||
PoolManager mManager;
|
||||
|
||||
public string Identifier
|
||||
{
|
||||
get { return m_Identifier; }
|
||||
set
|
||||
{
|
||||
if (m_Identifier != value)
|
||||
{
|
||||
string v = value;
|
||||
if (string.IsNullOrEmpty(m_Identifier))
|
||||
v = Manager.GetUniqueIdentifier(v);
|
||||
m_Identifier = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public List<GameObject> Prefabs
|
||||
{
|
||||
get { return m_Prefabs; }
|
||||
set
|
||||
{
|
||||
if (m_Prefabs != value)
|
||||
m_Prefabs = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public PoolSettings Settings
|
||||
{
|
||||
get { return m_Settings; }
|
||||
set
|
||||
{
|
||||
if (m_Settings != value)
|
||||
m_Settings = value;
|
||||
if (m_Settings != null)
|
||||
m_Settings.OnValidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public PoolManager Manager
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mManager == null)
|
||||
mManager = GetComponent<PoolManager>();
|
||||
return mManager;
|
||||
}
|
||||
}
|
||||
|
||||
List<GameObject> mObjects = new List<GameObject>();
|
||||
|
||||
double mLastTime;
|
||||
double mDeltaTime;
|
||||
|
||||
void Start()
|
||||
{
|
||||
if (Settings.Prewarm)
|
||||
Reset();
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
void OnValidate()
|
||||
{
|
||||
Settings = m_Settings;
|
||||
}
|
||||
#endif
|
||||
|
||||
public void Initialize(string ident, PoolSettings settings, params GameObject[] prefabs)
|
||||
{
|
||||
Identifier = ident;
|
||||
m_Settings = settings;
|
||||
Prefabs = new List<GameObject>(prefabs);
|
||||
mLastTime = DTTime.TimeSinceStartup + UnityEngine.Random.Range(0, Settings.Speed);
|
||||
if (Settings.Prewarm)
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Threshold exceeded: Deleting item");
|
||||
destroy(mObjects[0]);
|
||||
mObjects.RemoveAt(0);
|
||||
}
|
||||
}
|
||||
else if (Count < Settings.MinItems)
|
||||
{
|
||||
c = Mathf.Min(c, Settings.MinItems - Count);
|
||||
while (c-- > 0)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Below MinItems: Adding item");
|
||||
mObjects.Add(create());
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
if (Settings.Debug)
|
||||
log("Prewarm/Reset");
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Clear");
|
||||
for (int i = 0; i < Count; i++)
|
||||
destroy(mObjects[i]);
|
||||
mObjects.Clear();
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return mObjects.Count; }
|
||||
}
|
||||
|
||||
public GameObject Pop(Transform parent = null)
|
||||
{
|
||||
GameObject item = null;
|
||||
if (Count > 0)
|
||||
{
|
||||
item = mObjects[0];
|
||||
mObjects.RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Settings.AutoCreate || !Application.isPlaying)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Auto create item");
|
||||
item = create();
|
||||
}
|
||||
}
|
||||
if (item)
|
||||
{
|
||||
item.gameObject.hideFlags = HideFlags.None;
|
||||
item.transform.parent = parent;
|
||||
if (Settings.AutoEnableDisable)
|
||||
item.SetActive(true);
|
||||
sendAfterPop(item);
|
||||
if (Settings.Debug)
|
||||
log("Pop " + item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
public virtual void Push(GameObject item)
|
||||
{
|
||||
if (Settings.Debug)
|
||||
log("Push " + item);
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
item.gameObject.Destroy(false, true);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (item != null)
|
||||
{
|
||||
sendBeforePush(item);
|
||||
mObjects.Add(item);
|
||||
item.transform.parent = transform;
|
||||
item.gameObject.hideFlags = (Settings.Debug) ? HideFlags.DontSave : HideFlags.HideAndDontSave;
|
||||
if (Settings.AutoEnableDisable)
|
||||
item.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
GameObject create()
|
||||
{
|
||||
if (Prefabs.Count == 0)
|
||||
throw new InvalidOperationException(String.Format("[Curvy] The Prefab Pool '{0}' in game object '{1}' could not create a pool element because its Prefabs list is empty", this.Identifier, this.gameObject.name));
|
||||
|
||||
//TODO should this Random.Range call be deterministic?
|
||||
GameObject prefab = Prefabs[UnityEngine.Random.Range(0, Prefabs.Count)];
|
||||
GameObject result;
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
bool isPrefabAsset;
|
||||
//We have to check GetPrefabInstanceStatus first, because GetPrefabAssetType returns (as far as I understand) the same thing for both a prefab asset and a prefab instance
|
||||
if (PrefabUtility.GetPrefabInstanceStatus(prefab) == PrefabInstanceStatus.NotAPrefab)
|
||||
{
|
||||
PrefabAssetType prefabAssetType = PrefabUtility.GetPrefabAssetType(prefab);
|
||||
isPrefabAsset = prefabAssetType == PrefabAssetType.Regular || prefabAssetType == PrefabAssetType.Variant;
|
||||
}
|
||||
else
|
||||
isPrefabAsset = false;
|
||||
|
||||
result = isPrefabAsset
|
||||
? PrefabUtility.InstantiatePrefab(prefab) as GameObject
|
||||
: Instantiate(prefab);
|
||||
#else
|
||||
result = Instantiate(prefab);
|
||||
#endif
|
||||
}
|
||||
|
||||
result.name = prefab.name;
|
||||
result.transform.parent = transform;
|
||||
if (Settings.AutoEnableDisable)
|
||||
result.SetActive(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
void destroy(GameObject go)
|
||||
{
|
||||
go.Destroy(false, true);
|
||||
}
|
||||
|
||||
void log(string msg)
|
||||
{
|
||||
Debug.Log(string.Format("[{0}] ({1} items) {2}", Identifier, Count, msg));
|
||||
}
|
||||
|
||||
void setParent(Transform item, Transform parent)
|
||||
{
|
||||
if (item != null)
|
||||
item.parent = parent;
|
||||
}
|
||||
|
||||
void sendAfterPop(GameObject item)
|
||||
{
|
||||
item.SendMessage(nameof(IPoolable.OnAfterPop), SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
|
||||
void sendBeforePush(GameObject item)
|
||||
{
|
||||
item.SendMessage(nameof(IPoolable.OnBeforePush), SendMessageOptions.DontRequireReceiver);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4f3bdfa960a223645a04f11686263106
|
||||
timeCreated: 1440756755
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,3 @@
|
||||
==== DevTools ====
|
||||
Copyright (c) 2013-2017 FluffyUnderware, http://www.fluffyunderware.com
|
||||
All rights reserved.
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f607cd45dadce574d9ed3cc48faa6e28
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d3d90e3d3a0f5154b9e70b7bb0bb0cc3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
@@ -0,0 +1,100 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public interface IDTClipboardHandler
|
||||
{
|
||||
void Copy(object value);
|
||||
object Paste(object data);
|
||||
bool CanPasteFrom(Type clipboardType);
|
||||
}
|
||||
|
||||
public class DTVector2Clipboard : IDTClipboardHandler
|
||||
{
|
||||
|
||||
public void Copy(object value)
|
||||
{
|
||||
DT.ClipBoardSet(value);
|
||||
EditorGUIUtility.systemCopyBuffer = value.GetType().Name;
|
||||
}
|
||||
|
||||
public object Paste(object data)
|
||||
{
|
||||
if (data is Vector2)
|
||||
return (Vector2)data;
|
||||
else if (data is Vector3)
|
||||
return new Vector2(((Vector3)data).x, ((Vector3)data).y);
|
||||
else if (data is Vector4)
|
||||
return new Vector2(((Vector4)data).x, ((Vector4)data).y);
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
public bool CanPasteFrom(Type clipboardType)
|
||||
{
|
||||
return (clipboardType.Matches(typeof(Vector2), typeof(Vector3), typeof(Vector4)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DTVector3Clipboard : IDTClipboardHandler
|
||||
{
|
||||
|
||||
public void Copy(object value)
|
||||
{
|
||||
DT.ClipBoardSet(value);
|
||||
EditorGUIUtility.systemCopyBuffer = value.GetType().Name;
|
||||
}
|
||||
|
||||
public object Paste(object data)
|
||||
{
|
||||
if (data is Vector3)
|
||||
return (Vector3)data;
|
||||
else if (data is Vector4)
|
||||
return new Vector3(((Vector4)data).x, ((Vector4)data).y, ((Vector4)data).z);
|
||||
|
||||
return Vector2.zero;
|
||||
}
|
||||
|
||||
public bool CanPasteFrom(Type clipboardType)
|
||||
{
|
||||
return (clipboardType.Matches(typeof(Vector3), typeof(Vector4)));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class DTAnimationCurveClipboard : IDTClipboardHandler
|
||||
{
|
||||
public void Copy(object value)
|
||||
{
|
||||
DT.ClipBoardSet(value);
|
||||
}
|
||||
|
||||
public object Paste(object data)
|
||||
{
|
||||
AnimationCurve clp=(AnimationCurve)data;
|
||||
AnimationCurve curve=new AnimationCurve(clp.keys);
|
||||
curve.preWrapMode = clp.preWrapMode;
|
||||
curve.postWrapMode = clp.postWrapMode;
|
||||
return curve;
|
||||
}
|
||||
|
||||
public bool CanPasteFrom(Type clipboardType)
|
||||
{
|
||||
return clipboardType == typeof(AnimationCurve);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc604c1a18b9fa14da6b9d0f985e87e0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a5a9263e8df6d24fbf7ceb6a674385a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevTools;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
[CustomEditor(typeof(ComponentPool))]
|
||||
public class ComponentPoolEditor : DTEditor<ComponentPool>
|
||||
{
|
||||
protected override void OnCustomInspectorGUIBefore()
|
||||
{
|
||||
PoolManagerEditor.showBar(Target);
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 007dc0ff09c5d97478ad4fe856644878
|
||||
timeCreated: 1441008968
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,59 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevTools;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
[CustomEditor(typeof(PoolManager))]
|
||||
public class PoolManagerEditor : DTEditor<PoolManager>
|
||||
{
|
||||
|
||||
protected override void OnReadNodes()
|
||||
{
|
||||
Node.AddSection("Type Pools",typePoolsGUI);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void typePoolsGUI(DTInspectorNode node)
|
||||
{
|
||||
|
||||
foreach (KeyValuePair<Type, IPool> kv in Target.TypePools)
|
||||
{
|
||||
showBar(kv.Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void showBar(IPool pool)
|
||||
{
|
||||
Rect r = EditorGUILayout.GetControlRect(false, GUILayout.Height(20));
|
||||
float hi = Mathf.Max(pool.Count, pool.Settings.Threshold);
|
||||
Color c = Color.green;
|
||||
if (pool.Count < pool.Settings.MinItems)
|
||||
c = Color.yellow;
|
||||
else if (pool.Count > pool.Settings.Threshold)
|
||||
c = Color.red;
|
||||
DTGUI.PushContentColor(c);
|
||||
string s=pool.Identifier;
|
||||
if (!string.IsNullOrEmpty(s))
|
||||
{
|
||||
int i = s.IndexOf(",");
|
||||
if (i > 0)
|
||||
s = pool.Identifier.Substring(0, i);
|
||||
}
|
||||
EditorGUI.ProgressBar(r, pool.Count / hi, s + ":" + pool.Count.ToString());
|
||||
DTGUI.PopContentColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f1bf8d1bfc7bd84d88dbc07008b45ef
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,24 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevTools;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
[CustomEditor(typeof(PrefabPool),true)]
|
||||
public class PrefabPoolEditor : DTEditor<PrefabPool>
|
||||
{
|
||||
protected override void OnCustomInspectorGUIBefore()
|
||||
{
|
||||
PoolManagerEditor.showBar(Target);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 034101b2c8b1d0647afbae54cbffb340
|
||||
timeCreated: 1440757122
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,328 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using System.Reflection;
|
||||
using FluffyUnderware.DevTools;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
[InitializeOnLoad]
|
||||
public static class DT
|
||||
{
|
||||
const char editorPreferencesArraySeparator = ';';
|
||||
|
||||
public const string VERSION = "1.0.3";
|
||||
/// <summary>
|
||||
/// Global override for float precision rounding (e.g. DTPropertyAttribute.Precision)
|
||||
/// </summary>
|
||||
public static bool _UseSnapValuePrecision;
|
||||
|
||||
|
||||
public delegate void Callback();
|
||||
|
||||
public static List<DTProject> Projects
|
||||
{
|
||||
get
|
||||
{
|
||||
return mProjects.Values.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public static DTProject Project(string identifier)
|
||||
{
|
||||
DTProject prj = null;
|
||||
if (!mProjects.TryGetValue(identifier, out prj))
|
||||
{
|
||||
LoadProjects();
|
||||
if (!mProjects.TryGetValue(identifier, out prj))
|
||||
DTLog.LogError("[DevTools] Unable to find project '" + identifier + "' !");
|
||||
}
|
||||
|
||||
return prj;
|
||||
}
|
||||
|
||||
static object mClipboardData;
|
||||
static Dictionary<Type, IDTClipboardHandler> mClipboardHandlers = new Dictionary<Type, IDTClipboardHandler>();
|
||||
static Dictionary<string, DTProject> mProjects = new Dictionary<string, DTProject>();
|
||||
|
||||
static bool _compiling;
|
||||
|
||||
static DT()
|
||||
{
|
||||
EditorApplication.update += delayedInitialize;
|
||||
EditorApplication.update -= compileCheck;
|
||||
EditorApplication.update += compileCheck;
|
||||
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
foreach (DTProject prj in DT.Projects)
|
||||
prj.Clear();
|
||||
DT.Projects.Clear();
|
||||
}
|
||||
|
||||
public static void ReInitialize(bool loadProjects = true)
|
||||
{
|
||||
if (loadProjects)
|
||||
LoadProjects();
|
||||
|
||||
DTToolbar.Initialize();
|
||||
HandleProjectsKeyBindings();
|
||||
}
|
||||
|
||||
static void delayedInitialize()
|
||||
{
|
||||
EditorApplication.update -= delayedInitialize;
|
||||
LoadPreferences();
|
||||
DTSelection.Initialize();
|
||||
ReInitialize();
|
||||
// Register Clipboard-Handlers
|
||||
RegisterClipboardType(typeof(Vector2), new DTVector2Clipboard());
|
||||
RegisterClipboardType(typeof(Vector3), new DTVector3Clipboard());
|
||||
RegisterClipboardType(typeof(AnimationCurve), new DTAnimationCurveClipboard());
|
||||
}
|
||||
|
||||
static void compileCheck()
|
||||
{
|
||||
if (!_compiling)
|
||||
{
|
||||
_compiling = EditorApplication.isCompiling;
|
||||
if (_compiling)
|
||||
Clear();
|
||||
}
|
||||
else
|
||||
_compiling = EditorApplication.isCompiling;
|
||||
}
|
||||
|
||||
#region ### Clipboard ###
|
||||
public static void RegisterClipboardType(Type dataType, IDTClipboardHandler handler, bool overwriteExisting = false)
|
||||
{
|
||||
if (mClipboardHandlers.ContainsKey(dataType))
|
||||
{
|
||||
if (overwriteExisting)
|
||||
mClipboardHandlers[dataType] = handler;
|
||||
}
|
||||
else
|
||||
mClipboardHandlers.Add(dataType, handler);
|
||||
}
|
||||
|
||||
public static void ClipboardCopy(object data)
|
||||
{
|
||||
Type dataType = data.GetType();
|
||||
IDTClipboardHandler handler;
|
||||
if (mClipboardHandlers.TryGetValue(dataType, out handler))
|
||||
{
|
||||
handler.Copy(data);
|
||||
}
|
||||
else
|
||||
DTLog.LogError("[DevTools] No ClipboardHandler for data type '" + data.GetType().Name + "' found!");
|
||||
}
|
||||
|
||||
public static T ClipboardPaste<T>()
|
||||
{
|
||||
IDTClipboardHandler handler;
|
||||
if (mClipboardData != null && mClipboardHandlers.TryGetValue(typeof(T), out handler))
|
||||
{
|
||||
if (handler.CanPasteFrom(mClipboardData.GetType()))
|
||||
{
|
||||
return (T)handler.Paste(mClipboardData);
|
||||
}
|
||||
}
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public static bool ClipboardCanPasteTo<T>()
|
||||
{
|
||||
IDTClipboardHandler handler;
|
||||
if (mClipboardData != null && mClipboardHandlers.TryGetValue(typeof(T), out handler))
|
||||
{
|
||||
return handler.CanPasteFrom(mClipboardData.GetType());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void ClipBoardSet(object data)
|
||||
{
|
||||
mClipboardData = data;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Project Management ###
|
||||
|
||||
static void LoadProjects()
|
||||
{
|
||||
mProjects.Clear();
|
||||
TypeCache.TypeCollection types = TypeCache.GetTypesDerivedFrom(typeof(DTProject));
|
||||
foreach (Type t in types)
|
||||
{
|
||||
DTProject newProject = (DTProject)Activator.CreateInstance(t);
|
||||
mProjects.Add(newProject.Identifier, newProject);
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleProjectsKeyBindings()
|
||||
{
|
||||
foreach (DTProject prj in Projects)
|
||||
if (!prj.CheckKeyBindingNamesAreUnique())
|
||||
return;
|
||||
else
|
||||
prj.LoadKeyBindingRemappings();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### EditorPrefs-Helpers ###
|
||||
|
||||
static void LoadPreferences()
|
||||
{
|
||||
// Upgrade?
|
||||
string ver = GetEditorPrefs("FluffyUnderware.DevTools.Version", VERSION);
|
||||
if (ver != VERSION)
|
||||
{
|
||||
UpgradeDevTools(ver);
|
||||
SavePreferences();
|
||||
}
|
||||
}
|
||||
|
||||
static void SavePreferences()
|
||||
{
|
||||
SetEditorPrefs("FluffyUnderware.DevTools.Version", VERSION);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add code to handle upgrading (delete old pref-keys etc...) here
|
||||
/// </summary>
|
||||
/// <param name="oldVersion">the version stored in the EditorPrefs</param>
|
||||
static void UpgradeDevTools(string oldVersion)
|
||||
{
|
||||
DTLog.Log("[DevTools] Upgrading settings from " + oldVersion + " to " + VERSION);
|
||||
}
|
||||
|
||||
public static void SetEditorPrefs<T>(string key, T value)
|
||||
{
|
||||
Type tt = typeof(T);
|
||||
if (tt.IsEnum)
|
||||
{
|
||||
EditorPrefs.SetInt(key, Convert.ToInt32(Enum.Parse(typeof(T), value.ToString()) as Enum));
|
||||
}
|
||||
else if (tt.IsArray)
|
||||
{
|
||||
var list = (IList)value;
|
||||
string[] array = new string[list.Count];
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
array[i] = list[i].ToString();
|
||||
if(array[i].Contains(editorPreferencesArraySeparator))
|
||||
throw new ArgumentException(String.Format("value should not have any element containing a {0} character",editorPreferencesArraySeparator));
|
||||
}
|
||||
SetEditorPrefs(key, String.Join(editorPreferencesArraySeparator.ToString(), array));
|
||||
}
|
||||
else if (tt.Matches(typeof(int), typeof(Int32)))
|
||||
EditorPrefs.SetInt(key, (value as int?).Value);
|
||||
else if (tt == typeof(string))
|
||||
EditorPrefs.SetString(key, (value as string));
|
||||
else if (tt == typeof(float))
|
||||
EditorPrefs.SetFloat(key, (value as float?).Value);
|
||||
else if (tt == typeof(bool))
|
||||
EditorPrefs.SetBool(key, (value as bool?).Value);
|
||||
else if (tt == typeof(Color))
|
||||
EditorPrefs.SetString(key, (value as Color?).Value.ToHtml());
|
||||
else
|
||||
DTLog.LogError("[DevTools] SetEditorPrefs: Unsupported datatype: " + tt.Name);
|
||||
}
|
||||
|
||||
public static T GetEditorPrefs<T>(string key, T defaultValue)
|
||||
{
|
||||
if (EditorPrefs.HasKey(key))
|
||||
{
|
||||
Type tt = typeof(T);
|
||||
try
|
||||
{
|
||||
if (tt.IsEnum || tt.Matches(typeof(int), typeof(Int32)))
|
||||
{
|
||||
return (T)(object)EditorPrefs.GetInt(key, (int)(object)defaultValue);
|
||||
}
|
||||
else if (tt.IsArray)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
else if (tt == typeof(string))
|
||||
return (T)(object)EditorPrefs.GetString(key, defaultValue.ToString());
|
||||
else if (tt == typeof(float))
|
||||
return (T)(object)EditorPrefs.GetFloat(key, (float)(object)defaultValue);
|
||||
else if (tt == typeof(bool))
|
||||
return (T)(object)EditorPrefs.GetBool(key, (bool)(object)defaultValue);
|
||||
else if (tt == typeof(Color))
|
||||
return (T)(object)EditorPrefs.GetString(key, ((Color)(object)defaultValue).ToHtml()).ColorFromHtml();
|
||||
else
|
||||
DTLog.LogError("[DevTools] SetEditorPrefs: Unsupported datatype: " + tt.Name);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static string[] GetEditorPrefs(string key)
|
||||
{
|
||||
string arrayString = GetEditorPrefs(key, String.Empty);
|
||||
return String.IsNullOrEmpty(arrayString) ? new string[0] : arrayString.Split(editorPreferencesArraySeparator);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Utilities ###
|
||||
|
||||
/// <summary>
|
||||
/// Opens Unity's preferences window
|
||||
/// </summary>
|
||||
public static void OpenPreferencesWindow()
|
||||
{
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
SettingsService.OpenUserPreferences();
|
||||
#else
|
||||
Assembly asm = Assembly.GetAssembly(typeof(EditorWindow));
|
||||
Type T = asm.GetType("UnityEditor.PreferencesWindow");
|
||||
if (T != null)
|
||||
{
|
||||
MethodInfo M = T.GetMethod("ShowPreferencesWindow", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
if (M != null)
|
||||
M.Invoke(null, null);
|
||||
else
|
||||
Debug.LogError("Couldn't find method ShowPreferencesWindow in type " + T.FullName);
|
||||
}
|
||||
else
|
||||
Debug.LogError("Couldn't find type UnityEditor.PreferencesWindow in assembly " + asm.FullName);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if UNITY_2018_3_OR_NEWER
|
||||
/// <summary>
|
||||
/// Opens Unity's preferences window at a specific path. The path should be the same than the one of the associated <see cref="SettingsProvider"/>
|
||||
/// </summary>
|
||||
public static void OpenPreferencesWindow(string settingsPath)
|
||||
{
|
||||
SettingsService.OpenUserPreferences(settingsPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9ae275390dd4915458afdfbc1948f159
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,59 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public class DTDebugWindow : EditorWindow
|
||||
{
|
||||
string Label;
|
||||
public string Text
|
||||
{
|
||||
get { return string.Join("", texts); }
|
||||
set
|
||||
{
|
||||
int size = Mathf.FloorToInt(value.Length / 16382);
|
||||
texts = new string[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
texts[i] = value.Substring(i * 16382, 16382);
|
||||
}
|
||||
}
|
||||
}
|
||||
Vector2 scroll;
|
||||
|
||||
string[] texts;
|
||||
|
||||
GUIStyle stHtmlArea;
|
||||
|
||||
public static void Open(string label, string text, string windowName="Debug Dump")
|
||||
{
|
||||
DTDebugWindow win = EditorWindow.GetWindow<DTDebugWindow>(true, windowName);
|
||||
win.Label = label;
|
||||
win.Text = text;
|
||||
}
|
||||
|
||||
void OnGUI()
|
||||
{
|
||||
if (stHtmlArea == null)
|
||||
{
|
||||
stHtmlArea = new GUIStyle(EditorStyles.textArea);
|
||||
stHtmlArea.richText = true;
|
||||
}
|
||||
GUILayout.Label(Label, EditorStyles.boldLabel);
|
||||
scroll = GUILayout.BeginScrollView(scroll);
|
||||
for (int i=0;i<texts.Length;i++)
|
||||
if (i==texts.Length-1)
|
||||
GUILayout.TextArea(texts[i],stHtmlArea,GUILayout.ExpandHeight(true));
|
||||
else
|
||||
GUILayout.TextArea(texts[i], stHtmlArea);
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e278c12763ea71f4fbb47021df15a348
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,512 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevToolsEditor.Extensions;
|
||||
using FluffyUnderware.DevTools;
|
||||
using System;
|
||||
using UnityEditorInternal;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine.Events;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public class DTEditor<T> : Editor where T : UnityEngine.Object
|
||||
{
|
||||
#region ### Public Properties ###
|
||||
|
||||
/// <summary>
|
||||
/// Target Script
|
||||
/// </summary>
|
||||
public virtual T Target
|
||||
{
|
||||
get
|
||||
{
|
||||
return (target != null) ? target as T : null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the target is currently selected or not
|
||||
/// </summary>
|
||||
public bool TargetIsActive
|
||||
{
|
||||
get
|
||||
{
|
||||
if (target is MonoBehaviour)
|
||||
return (target != null && ((MonoBehaviour)target).transform == Selection.activeTransform) ? true : false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the target is part of a prefab
|
||||
/// </summary>
|
||||
/// <remarks>Has issues with the new prefab system. See the comments in the code that assigns IsPrefab's value to know more about this</remarks>
|
||||
// TODO Has issues with the new prefab system. See the comments in the code that assigns IsPrefab's value to know more about this
|
||||
[Obsolete("Will get removed in the next major version. Use Unity's PrefabUtility to know if this.Target is part of a prefab")]
|
||||
public bool IsPrefab { get; private set; }
|
||||
|
||||
public bool IsInsideInspector { get; private set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The Root node of all inspector fields
|
||||
/// </summary>
|
||||
public DTGroupNode Node
|
||||
{
|
||||
get
|
||||
{
|
||||
return mRootNode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The renderer used to render inspector fields
|
||||
/// </summary>
|
||||
public IDTInspectorNodeRenderer NodeRenderer
|
||||
{
|
||||
get
|
||||
{
|
||||
return mNodeRenderer;
|
||||
}
|
||||
set
|
||||
{
|
||||
mNodeRenderer = value;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether the inspector needs a repaint
|
||||
/// </summary>
|
||||
public bool NeedRepaint { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
DTGroupNode mRootNode;
|
||||
IDTInspectorNodeRenderer mNodeRenderer = new DTInspectorNodeDefaultRenderer();
|
||||
bool mEnterChildren;
|
||||
|
||||
#region ### Public Methods ###
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region ### Protected Methods (override to change inspector appearance) ###
|
||||
|
||||
protected virtual void OnEnable()
|
||||
{
|
||||
if (mRootNode == null)
|
||||
mRootNode = new DTGroupNode("Root");
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
Undo.undoRedoPerformed += OnUndoRedo;
|
||||
#pragma warning disable 618
|
||||
if (target != null)
|
||||
{
|
||||
PrefabAssetType prefabAssetType = PrefabUtility.GetPrefabAssetType(target);
|
||||
//BUG in the new prefab system, an instantiated prefab will have IsPrefab == true, while the documentation of the PrefabUtility.GetPrefabAssetType says otherwise. But is fixing this worth it knowing that IsPrefab is not used in Curvy when using the new prefab system?
|
||||
IsPrefab = prefabAssetType == PrefabAssetType.Regular || prefabAssetType == PrefabAssetType.Variant;
|
||||
}
|
||||
else
|
||||
IsPrefab = false;
|
||||
#pragma warning restore 618
|
||||
}
|
||||
|
||||
protected virtual void OnDisable()
|
||||
{
|
||||
if (mRootNode != null)
|
||||
mRootNode.Clear();
|
||||
Undo.undoRedoPerformed -= OnUndoRedo;
|
||||
}
|
||||
|
||||
protected virtual void OnReadNodes()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual void OnSceneGUI()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
RenderGUI(false);
|
||||
}
|
||||
|
||||
public void RenderGUI(bool embedded = false)
|
||||
{
|
||||
if (Target == null)
|
||||
return;
|
||||
#if UNITY_5_6_OR_NEWER
|
||||
serializedObject.UpdateIfRequiredOrScript();
|
||||
#else
|
||||
serializedObject.UpdateIfDirtyOrScript();
|
||||
#endif
|
||||
|
||||
if (Node.Count == 0)
|
||||
ReadNodes();
|
||||
|
||||
NeedRepaint = false;
|
||||
|
||||
if (mRootNode)
|
||||
{
|
||||
DTInspectorNode.IsInsideInspector = !embedded;
|
||||
IsInsideInspector = !embedded;
|
||||
OnCustomInspectorGUIBefore();
|
||||
renderNode(mRootNode);
|
||||
OnCustomInspectorGUI();
|
||||
}
|
||||
if (serializedObject.ApplyModifiedProperties())
|
||||
OnModified();
|
||||
|
||||
GUI.SetNextControlName("");
|
||||
|
||||
if (NeedRepaint)
|
||||
Repaint();
|
||||
}
|
||||
|
||||
protected virtual void OnModified()
|
||||
{
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Add custom GUI code here, rendered before the default inspector
|
||||
/// </summary>
|
||||
protected virtual void OnCustomInspectorGUIBefore()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add custom GUI code here, rendered after the default inspector
|
||||
/// </summary>
|
||||
protected virtual void OnCustomInspectorGUI()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to initialize a ReorderableList. Override to add custom behaviour
|
||||
/// </summary>
|
||||
/// <param name="node">field node</param>
|
||||
/// <param name="attribute">ArrayEx attribute of the field</param>
|
||||
protected virtual void SetupArrayEx(DTFieldNode node, ArrayExAttribute attribute)
|
||||
{
|
||||
// Defaults
|
||||
if (attribute.ShowHeader)
|
||||
{
|
||||
node.ArrayEx.drawHeaderCallback = (Rect r) =>
|
||||
{
|
||||
EditorGUI.LabelField(r, node.GUIContent);
|
||||
if (attribute.DropTarget)
|
||||
{
|
||||
Event ev = Event.current;
|
||||
switch (ev.type)
|
||||
{
|
||||
case EventType.DragUpdated:
|
||||
if (r.Contains(ev.mousePosition))
|
||||
{
|
||||
Type fieldType = node.serializedProperty.GetFieldType();
|
||||
//bug? this code is called when dragging a game object over the list of Input Spots, but not the list of Input Game Objects. Why? Both have the ArrayExAttribute with ShowHeader being true
|
||||
bool allowed = DragAndDrop.objectReferences.Length > 0 && DTEditorUtility.DragDropTypeMatch(fieldType);
|
||||
DragAndDrop.visualMode = allowed ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
|
||||
}
|
||||
break;
|
||||
case EventType.DragPerform:
|
||||
if (r.Contains(ev.mousePosition))
|
||||
{
|
||||
Object[] objs = DTEditorUtility.DragDropGetObjectsOfType(node.serializedProperty.GetFieldType());
|
||||
|
||||
foreach (Object o in objs)
|
||||
{
|
||||
int idx = node.serializedProperty.arraySize;
|
||||
node.serializedProperty.InsertArrayElementAtIndex(idx);
|
||||
node.serializedProperty.GetArrayElementAtIndex(idx).objectReferenceValue = o;
|
||||
}
|
||||
node.serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
node.ArrayEx.drawElementCallback = (Rect r, int index, bool isActive, bool isFocused) =>
|
||||
{
|
||||
SerializedProperty e = node.ArrayEx.serializedProperty.GetArrayElementAtIndex(index);
|
||||
if (e != null)
|
||||
EditorGUI.PropertyField(r, e);
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when UndoRedo occured
|
||||
/// </summary>
|
||||
public virtual void OnUndoRedo()
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// builds node tree and process parsing attributes
|
||||
/// </summary>
|
||||
public void ReadNodes()
|
||||
{
|
||||
DTGroupNode._serializedObject = serializedObject;
|
||||
SerializedProperty iterator = serializedObject.GetIterator();
|
||||
mRootNode.Clear();
|
||||
mEnterChildren = true;
|
||||
|
||||
DTGroupNode baseNode = mRootNode;
|
||||
DTGroupNode parentNode = baseNode;
|
||||
Stack<string> propertyPathStack = new Stack<string>();
|
||||
Stack<DTGroupNode> baseNodeStack = new Stack<DTGroupNode>();
|
||||
bool resetParent = false;
|
||||
|
||||
while (iterator.NextVisible(mEnterChildren))
|
||||
{
|
||||
mEnterChildren = false;
|
||||
if (iterator.name != "m_Script" && iterator.name != "InspectorFoldout")
|
||||
{
|
||||
// handle baseNode resetting (AsGroup etc...)
|
||||
while (propertyPathStack.Count > 0 && !iterator.propertyPath.StartsWith(propertyPathStack.Peek()))
|
||||
{
|
||||
propertyPathStack.Pop();
|
||||
baseNode = baseNodeStack.Pop();
|
||||
parentNode = baseNode;
|
||||
}
|
||||
|
||||
|
||||
DTFieldNode fieldNode = new DTFieldNode(iterator);
|
||||
|
||||
// get group parsing attributes
|
||||
List<Attribute> groupParseAttribs = iterator.GetAttributes(typeof(IDTGroupParsingAttribute));
|
||||
groupParseAttribs.Sort();
|
||||
// get field parsing attributes
|
||||
List<Attribute> parsingAttributes = iterator.GetAttributes(typeof(IDTFieldParsingAttribute));
|
||||
|
||||
foreach (IDTGroupParsingAttribute ga in groupParseAttribs)
|
||||
{
|
||||
|
||||
if (ga is TabAttribute)
|
||||
{
|
||||
TabAttribute tabA = (TabAttribute)ga;
|
||||
parentNode = baseNode.EnsurePath(tabA.Path, false);
|
||||
|
||||
if (!string.IsNullOrEmpty(tabA.TabBarName) && !string.IsNullOrEmpty(tabA.TabName))
|
||||
{
|
||||
if (!parentNode[tabA.TabBarName])
|
||||
parentNode = (DTGroupNode)parentNode.Add(new DTGroupNode(tabA.TabBarName, null, DTInspectorNode.RenderAsEnum.TabBar));
|
||||
else
|
||||
parentNode = (DTGroupNode)parentNode[tabA.TabBarName];
|
||||
if (!parentNode[tabA.TabName])
|
||||
parentNode = (DTGroupNode)parentNode.Add(new DTGroupNode(tabA.TabName, iterator, DTInspectorNode.RenderAsEnum.Tab));
|
||||
else
|
||||
parentNode = (DTGroupNode)parentNode[tabA.TabName];
|
||||
if (tabA.Sort != 100)
|
||||
parentNode.SortOrder = tabA.Sort;
|
||||
}
|
||||
else
|
||||
DTLog.LogWarningFormat("[DevTools] Skipping [Tab] on '{0}' because Path is missing TabBar or Tab!", iterator.propertyPath);
|
||||
|
||||
}
|
||||
else if (ga is SectionAttribute)
|
||||
{
|
||||
SectionAttribute sectionA = (SectionAttribute)ga;
|
||||
parentNode = createGroup(baseNode, sectionA.Path, iterator);
|
||||
if (sectionA.Sort != 100)
|
||||
parentNode.SortOrder = sectionA.Sort;
|
||||
}
|
||||
else if (ga is AsGroupAttribute)
|
||||
{
|
||||
AsGroupAttribute asGroupA = (AsGroupAttribute)ga;
|
||||
propertyPathStack.Push(fieldNode.SerializedPropertyPath);
|
||||
baseNodeStack.Push(baseNode);
|
||||
parentNode = createGroup((asGroupA.PathIsAbsolute) ? baseNode : parentNode, (asGroupA.Path == null) ? fieldNode.Name : asGroupA.Path + "/" + fieldNode.Name, iterator);
|
||||
baseNode = parentNode;
|
||||
|
||||
}
|
||||
else if (ga is GroupAttribute)
|
||||
{
|
||||
GroupAttribute groupA = (GroupAttribute)ga;
|
||||
parentNode = createGroup(baseNode, groupA.Path, iterator);
|
||||
if (groupA.Sort != 100)
|
||||
parentNode.SortOrder = groupA.Sort;
|
||||
resetParent = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (IDTFieldParsingAttribute pa in parsingAttributes)
|
||||
{
|
||||
if (pa is Hide)
|
||||
{
|
||||
fieldNode.Visible = false;
|
||||
fieldNode.ContentVisible = false;
|
||||
mEnterChildren = false;
|
||||
}
|
||||
else if (pa is AsGroupAttribute || pa is Inline)
|
||||
{
|
||||
fieldNode.Visible = false;
|
||||
fieldNode.ContentVisible = false;
|
||||
mEnterChildren = true;
|
||||
|
||||
}
|
||||
else if (pa is ArrayExAttribute)
|
||||
{
|
||||
ArrayExAttribute arrayA = (ArrayExAttribute)pa;
|
||||
fieldNode.ArrayEx = new ReorderableList(serializedObject, iterator, arrayA.Draggable, arrayA.ShowHeader, arrayA.ShowAdd, arrayA.ShowDelete);
|
||||
SetupArrayEx(fieldNode, arrayA);
|
||||
}
|
||||
else if (pa is SortOrderAttribute)
|
||||
{
|
||||
fieldNode.SortOrder = ((SortOrderAttribute)pa).Sort;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
parentNode.Add(fieldNode);
|
||||
if (resetParent)
|
||||
{
|
||||
parentNode = parentNode.Parent as DTGroupNode;
|
||||
resetParent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
OnReadNodes();
|
||||
Node.Sort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the node tree and process rendering attributes
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
void renderNode(DTInspectorNode node)
|
||||
{
|
||||
if (serializedObject == null)
|
||||
return;
|
||||
bool guistate = GUI.enabled;
|
||||
DTInspectorNode item;
|
||||
for (int i = 0; i < node.Items.Count; i++)
|
||||
{
|
||||
item = node[i];
|
||||
item.serializedObject = serializedObject;
|
||||
|
||||
if (item.Disabled)
|
||||
GUI.enabled = false;
|
||||
if (item is DTFieldNode)
|
||||
{
|
||||
|
||||
DTFieldNode field = (DTFieldNode)item;
|
||||
|
||||
field.serializedProperty = serializedObject.FindProperty(field.SerializedPropertyPath);
|
||||
if (field.serializedProperty == null)
|
||||
return;
|
||||
field.Calculate();
|
||||
|
||||
if (field.Visible)
|
||||
{
|
||||
foreach (ActionAttribute act in item.Actions)
|
||||
if (act.Position == ActionAttribute.ActionPositionEnum.Above)
|
||||
NodeRenderer.RenderAction(item, act, this, Target);
|
||||
|
||||
if (field.ArrayEx != null)
|
||||
{
|
||||
field.ArrayEx.serializedProperty = field.serializedProperty;
|
||||
field.ArrayEx.DoLayoutList();
|
||||
}
|
||||
else
|
||||
{
|
||||
NodeRenderer.RenderField(field);
|
||||
field.raiseOnRender();
|
||||
}
|
||||
|
||||
foreach (ActionAttribute act in item.Actions)
|
||||
if (act.Position == ActionAttribute.ActionPositionEnum.Below)
|
||||
NodeRenderer.RenderAction(item, act, this, Target);
|
||||
}
|
||||
|
||||
}
|
||||
else if (item is DTGroupNode)
|
||||
{
|
||||
DTGroupNode group = (DTGroupNode)item;
|
||||
group.Calculate();
|
||||
|
||||
if (group.Visible)
|
||||
{
|
||||
foreach (ActionAttribute act in item.Actions)
|
||||
if (act.Position == ActionAttribute.ActionPositionEnum.Above)
|
||||
NodeRenderer.RenderAction(item, act, this, Target);
|
||||
|
||||
if (group.Disabled)
|
||||
GUI.enabled = false;
|
||||
|
||||
switch (item.RenderAs)
|
||||
{
|
||||
case DTInspectorNode.RenderAsEnum.Section:
|
||||
NodeRenderer.RenderSectionHeader(group);
|
||||
if (group.ContentVisible)
|
||||
{
|
||||
renderNode(group);
|
||||
group.raiseOnRender();
|
||||
}
|
||||
NodeRenderer.RenderSectionFooter(group);
|
||||
break;
|
||||
case DTInspectorNode.RenderAsEnum.TabBar:
|
||||
NodeRenderer.RenderTabBarHeader(group, (group.MaxItemsPerRow == -1) ? group.Items.Count : group.MaxItemsPerRow);
|
||||
if (group.SelectedIndex > -1)
|
||||
{
|
||||
renderNode(group[group.SelectedIndex]);
|
||||
group[group.SelectedIndex].raiseOnRender();
|
||||
}
|
||||
NodeRenderer.RenderTabBarFooter(group);
|
||||
break;
|
||||
default:
|
||||
if (group.ContentVisible)
|
||||
renderNode(group);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (ActionAttribute act in item.Actions)
|
||||
if (act.Position == ActionAttribute.ActionPositionEnum.Below)
|
||||
NodeRenderer.RenderAction(item, act, this, Target);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
GUI.enabled = guistate;
|
||||
if (item.NeedRepaint)
|
||||
{
|
||||
item.NeedRepaint = false;
|
||||
NeedRepaint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a parent group for a field, creating the path if neccessary
|
||||
/// </summary>
|
||||
/// <param name="baseNode">"root" the path is applied to</param>
|
||||
/// <param name="path">node path</param>
|
||||
/// <param name="forProperty">field property the parent node is for</param>
|
||||
/// <returns></returns>
|
||||
DTGroupNode createGroup(DTGroupNode baseNode, string path, SerializedProperty forProperty)
|
||||
{
|
||||
DTGroupNode node = baseNode.EnsurePath(path, false, forProperty);
|
||||
return node;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74ac27c5ac9d99c4d8071cdcc6c7c3aa
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,851 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using FluffyUnderware.DevTools;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using FluffyUnderware.DevToolsEditor.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEditorInternal;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public delegate void DTInspectorNodeEvent(DTInspectorNode e);
|
||||
|
||||
/// <summary>
|
||||
/// Base Node class
|
||||
/// </summary>
|
||||
public class DTInspectorNode : IComparable
|
||||
{
|
||||
public static bool IsInsideInspector { get; set; }
|
||||
public event DTInspectorNodeEvent OnNodeRender;
|
||||
|
||||
public enum RenderAsEnum { Invisible, Section, TabBar, Tab, Field };
|
||||
/// <summary>
|
||||
/// How to render this node
|
||||
/// </summary>
|
||||
public RenderAsEnum RenderAs = RenderAsEnum.Field;
|
||||
/// <summary>
|
||||
/// Gets a list of actions of this node
|
||||
/// </summary>
|
||||
public List<ActionAttribute> Actions = new List<ActionAttribute>();
|
||||
/// <summary>
|
||||
/// Parent node
|
||||
/// </summary>
|
||||
public DTInspectorNode Parent;
|
||||
/// <summary>
|
||||
/// Child nodes
|
||||
/// </summary>
|
||||
public List<DTInspectorNode> Items = new List<DTInspectorNode>();
|
||||
/// <summary>
|
||||
/// Gets the number of child nodes
|
||||
/// </summary>
|
||||
public int Count { get { return Items.Count; } }
|
||||
/// <summary>
|
||||
/// Gets all attributes for this node
|
||||
/// </summary>
|
||||
public List<Attribute> Attributes;
|
||||
/// <summary>
|
||||
/// Node name
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the node is expanded/visible or not
|
||||
/// </summary>
|
||||
public virtual bool Expanded { get; set; }
|
||||
/// <summary>
|
||||
/// Whether this node needs a repaint
|
||||
/// </summary>
|
||||
public virtual bool NeedRepaint { get; set; }
|
||||
/// <summary>
|
||||
/// Whether child nodes are visible or not
|
||||
/// </summary>
|
||||
public virtual bool ContentVisible
|
||||
{
|
||||
get { return mContentVisible; }
|
||||
set { mContentVisible = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether this node is visible or not
|
||||
/// </summary>
|
||||
public virtual bool Visible
|
||||
{
|
||||
get { return mVisible; }
|
||||
set { mVisible = value; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether this node is disabled or not
|
||||
/// </summary>
|
||||
public virtual bool Disabled { get; set; }
|
||||
/// <summary>
|
||||
/// The serializedObject currently processed
|
||||
/// </summary>
|
||||
public SerializedObject serializedObject { get; set; }
|
||||
/// <summary>
|
||||
/// Label/Icon/Tooltip of this node
|
||||
/// </summary>
|
||||
public GUIContent GUIContent { get; set; }
|
||||
/// <summary>
|
||||
/// The absolute Path of this node
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Parent) ? Parent.Path + "/" + Name : Name;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The nesting Level of this node
|
||||
/// </summary>
|
||||
public int Level { get; private set; }
|
||||
/// <summary>
|
||||
/// Sort order
|
||||
/// </summary>
|
||||
public int SortOrder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a child node by it's index
|
||||
/// </summary>
|
||||
/// <param name="index">index of the child</param>
|
||||
/// <returns>a node or null</returns>
|
||||
public DTInspectorNode this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Items[index];
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets a child node by it's name
|
||||
/// </summary>
|
||||
/// <param name="name">name of the child</param>
|
||||
/// <returns>a node or null</returns>
|
||||
public DTInspectorNode this[string name]
|
||||
{
|
||||
get
|
||||
{
|
||||
return Items.Find(x => x.Name.Equals(name, StringComparison.CurrentCultureIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
public int Index { get; protected set; }
|
||||
|
||||
|
||||
|
||||
bool mContentVisible = true;
|
||||
bool mVisible = true;
|
||||
|
||||
|
||||
protected DTInspectorNode(string name)
|
||||
{
|
||||
SortOrder = 100;
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public virtual int CompareTo(object obj)
|
||||
{
|
||||
DTInspectorNode other = (DTInspectorNode)obj;
|
||||
int c = SortOrder.CompareTo(other.SortOrder);
|
||||
return (c != 0) ? c : Index.CompareTo(other.Index);
|
||||
}
|
||||
/// <summary>
|
||||
/// Clear everything the node contains
|
||||
/// </summary>
|
||||
public virtual void Clear()
|
||||
{
|
||||
for (int i = 0; i < Items.Count; i++)
|
||||
Items[i].Clear();
|
||||
Items.Clear();
|
||||
}
|
||||
|
||||
public virtual DTInspectorNode Add(DTInspectorNode node)
|
||||
{
|
||||
if (node != null)
|
||||
{
|
||||
Items.Add(node);
|
||||
node.Index = Items.IndexOf(node);
|
||||
node.Parent = this;
|
||||
node.Level = node.Parent.Level + 1;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public virtual void Calculate(bool firstRun = false) { }
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
foreach (DTInspectorNode it in Items)
|
||||
it.Delete();
|
||||
if (Parent)
|
||||
{
|
||||
Parent.Items.Remove(this);
|
||||
Parent.Sort(); // to reset Index
|
||||
}
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a certain attribute
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the attribute</typeparam>
|
||||
/// <returns>the attribute or null</returns>
|
||||
public T GetAttribute<T>() where T : Attribute
|
||||
{
|
||||
return Attributes.Find(x => x.GetType().IsAssignableFrom(typeof(T))) as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all attributes
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the attribute</typeparam>
|
||||
/// <returns>all attributes</returns>
|
||||
public T[] GetAllAttributes<T>() where T : Attribute
|
||||
{
|
||||
List<Attribute> res = Attributes.FindAll(x => x.GetType().IsAssignableFrom(typeof(T)));
|
||||
T[] r = new T[res.Count];
|
||||
for (int i = 0; i < res.Count; i++)
|
||||
r[i] = (T)res[i];
|
||||
return r;
|
||||
}
|
||||
|
||||
public static implicit operator bool(DTInspectorNode a)
|
||||
{
|
||||
return !object.ReferenceEquals(a, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search for a node at a specific path
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the node in question</typeparam>
|
||||
/// <param name="name">Path and Name of the node in question</param>
|
||||
/// <param name="node">The search result or null</param>
|
||||
/// <returns>true if found</returns>
|
||||
public bool FindNodeAt<T>(string pathAndName, out T node) where T : DTInspectorNode
|
||||
{
|
||||
string[] p = pathAndName.Split('/');
|
||||
node = null;
|
||||
DTInspectorNode N = this;
|
||||
for (int i = 0; i < p.Length; i++)
|
||||
{
|
||||
N = N[p[i]];
|
||||
if (N == null)
|
||||
return false;
|
||||
}
|
||||
node = N as T;
|
||||
return node != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Search all child nodes for a node of the given type with a certain name
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the node in question</typeparam>
|
||||
/// <param name="name">Name of the node in question</param>
|
||||
/// <param name="node">The search result or null</param>
|
||||
/// <returns>true if found</returns>
|
||||
public bool FindNode<T>(string name, out T node) where T : DTInspectorNode
|
||||
{
|
||||
node = this[name] as T;
|
||||
if (node != null)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
foreach (DTInspectorNode it in Items)
|
||||
if (it.FindNode(name, out node))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sort child nodes
|
||||
/// </summary>
|
||||
public void Sort()
|
||||
{
|
||||
Items.Sort();
|
||||
|
||||
foreach (DTInspectorNode it in Items)
|
||||
{
|
||||
it.Index = Items.IndexOf(it);
|
||||
if (it is DTGroupNode)
|
||||
it.Sort();
|
||||
}
|
||||
}
|
||||
|
||||
internal void raiseOnRender()
|
||||
{
|
||||
if (OnNodeRender != null)
|
||||
OnNodeRender(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Field Node class
|
||||
/// </summary>
|
||||
public class DTFieldNode : DTInspectorNode
|
||||
{
|
||||
public string SerializedPropertyPath;
|
||||
public SerializedProperty serializedProperty { get; set; }
|
||||
public bool IncludeChildren { get; set; }
|
||||
public ReorderableList ArrayEx { get; set; }
|
||||
public string Tooltip { get; set; }
|
||||
public string HelpURL { get; set; }
|
||||
|
||||
public DTFieldNode(SerializedProperty property) : base(property.name)
|
||||
{
|
||||
RenderAs = RenderAsEnum.Field;
|
||||
SerializedPropertyPath = property.propertyPath;
|
||||
Attributes = property.GetAttributes(typeof(IDTFieldRenderAttribute));
|
||||
Attributes.Sort();
|
||||
GUIContent = new GUIContent(ObjectNames.NicifyVariableName(Name));
|
||||
Calculate(true);
|
||||
}
|
||||
|
||||
public override void Calculate(bool firstRun = false)
|
||||
{
|
||||
IncludeChildren = true;
|
||||
Visible = true;
|
||||
Disabled = false;
|
||||
Actions.Clear();
|
||||
foreach (IDTFieldRenderAttribute a in Attributes)
|
||||
{
|
||||
if (a is Hide || a is AsGroupAttribute)
|
||||
{
|
||||
Visible = false;
|
||||
ContentVisible = false;
|
||||
IncludeChildren = false;
|
||||
}
|
||||
else if (a is Inline)
|
||||
{
|
||||
Visible = false;
|
||||
IncludeChildren = true;
|
||||
}
|
||||
|
||||
else if (serializedProperty != null)
|
||||
{
|
||||
if (a is FieldConditionAttribute)
|
||||
{
|
||||
FieldConditionAttribute condA = (FieldConditionAttribute)a;
|
||||
bool met = condA.ConditionMet(serializedProperty.serializedObject.targetObject);
|
||||
|
||||
switch (condA.Action)
|
||||
{
|
||||
case ConditionalAttribute.ActionEnum.Show:
|
||||
Visible = met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Hide:
|
||||
Visible = !met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Enable:
|
||||
Disabled = !met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Disable:
|
||||
Disabled = met;
|
||||
break;
|
||||
default:
|
||||
if (met)
|
||||
Actions.Add(condA);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if (a is FieldActionAttribute)
|
||||
{
|
||||
Actions.Add((FieldActionAttribute)a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Group node class
|
||||
/// </summary>
|
||||
public class DTGroupNode : DTInspectorNode
|
||||
{
|
||||
|
||||
static internal SerializedObject _serializedObject;
|
||||
|
||||
public override bool Expanded
|
||||
{
|
||||
get
|
||||
{
|
||||
return mState.target;
|
||||
}
|
||||
set
|
||||
{
|
||||
|
||||
if (mState.target != value)
|
||||
{
|
||||
mState.target = value;
|
||||
if (serializedObject != null)
|
||||
DTPersistentState.SetBool(serializedObject.targetObject.GetInstanceID().ToString() + Path, value);
|
||||
NeedRepaint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public float ExpandedFaded
|
||||
{
|
||||
get
|
||||
{
|
||||
return mState.faded;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ContentVisible
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.ContentVisible && ExpandedFaded != 0;
|
||||
}
|
||||
set
|
||||
{
|
||||
base.ContentVisible = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool NeedRepaint
|
||||
{
|
||||
get
|
||||
{
|
||||
return mState.isAnimating;
|
||||
}
|
||||
}
|
||||
|
||||
public string HelpURL { get; set; }
|
||||
public bool Fixed { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
#region --- Tab Bar Properties ---
|
||||
|
||||
public int SelectedIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Count > 0 ? Mathf.Clamp(mSelectedItem, 0, Count - 1) : -1;
|
||||
}
|
||||
set
|
||||
{
|
||||
mSelectedItem = Count > 0 ? Mathf.Clamp(value, 0, Count - 1) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
public DTInspectorNode SelectedItem
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SelectedIndex != -1) ? Items[SelectedIndex] : null;
|
||||
}
|
||||
set
|
||||
{
|
||||
SelectedIndex = (value == null) ? -1 : value.Index;
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxItemsPerRow { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
AnimBool mState = new AnimBool(true);
|
||||
int mSelectedItem = -1;
|
||||
|
||||
public DTGroupNode(string name, SerializedProperty forProperty = null, RenderAsEnum renderAs = RenderAsEnum.Section) : base(name)
|
||||
{
|
||||
RenderAs = renderAs;
|
||||
MaxItemsPerRow = -1;
|
||||
if (forProperty != null)
|
||||
{
|
||||
Attributes = forProperty.GetAttributes(typeof(IDTGroupRenderAttribute));
|
||||
Attributes.Sort();
|
||||
Calculate(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Attributes = new List<Attribute>();
|
||||
GUIContent = new GUIContent(ObjectNames.NicifyVariableName(Name));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void Calculate(bool firstRun = false)
|
||||
{
|
||||
Actions.Clear();
|
||||
foreach (IDTGroupRenderAttribute a in Attributes)
|
||||
{
|
||||
if (a is GroupAttribute)
|
||||
{
|
||||
GroupAttribute groupA = (GroupAttribute)a;
|
||||
if (groupA.Invisible)
|
||||
RenderAs = RenderAsEnum.Invisible;
|
||||
if (firstRun)
|
||||
{
|
||||
mState.value = groupA.Expanded;
|
||||
getAdditionalGroupParams(groupA);
|
||||
}
|
||||
}
|
||||
else if (serializedObject != null)
|
||||
{
|
||||
if (a is GroupConditionAttribute)
|
||||
{
|
||||
GroupConditionAttribute condA = (GroupConditionAttribute)a;
|
||||
bool met = condA.ConditionMet(serializedObject.targetObject);
|
||||
|
||||
switch (condA.Action)
|
||||
{
|
||||
case ConditionalAttribute.ActionEnum.Show:
|
||||
Visible = met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Hide:
|
||||
Visible = !met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Enable:
|
||||
Disabled = !met;
|
||||
break;
|
||||
case ConditionalAttribute.ActionEnum.Disable:
|
||||
Disabled = met;
|
||||
break;
|
||||
default:
|
||||
if (met)
|
||||
Actions.Add(condA);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if (a is GroupActionAttribute)
|
||||
{
|
||||
Actions.Add((GroupActionAttribute)a);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getAdditionalGroupParams(GroupAttribute a)
|
||||
{
|
||||
if (a == null)
|
||||
{
|
||||
GUIContent = new GUIContent(ObjectNames.NicifyVariableName(Name));
|
||||
}
|
||||
else
|
||||
{
|
||||
GUIContent = new GUIContent(a.Label ?? ObjectNames.NicifyVariableName(Name), a.Tooltip);
|
||||
HelpURL = a.HelpURL;
|
||||
}
|
||||
}
|
||||
|
||||
public DTGroupNode EnsurePath(string path, bool includesName = false, SerializedProperty forProperty = null)
|
||||
{
|
||||
DTGroupNode node = this;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
string[] p = path.Split('/');
|
||||
int hi = (includesName) ? p.Length - 1 : p.Length;
|
||||
for (int i = 0; i < hi; i++)
|
||||
{
|
||||
DTGroupNode sub = node[p[i]] as DTGroupNode;
|
||||
if (sub == null)
|
||||
{
|
||||
if (forProperty != null && i == hi - 1)
|
||||
sub = new DTGroupNode(p[i], forProperty);
|
||||
else
|
||||
sub = new DTGroupNode(p[i]);
|
||||
node.Add(sub);
|
||||
}
|
||||
node = sub;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
public GUIContent[] GetItemsGUIContent()
|
||||
{
|
||||
GUIContent[] res = new GUIContent[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
res[i] = this[i].GUIContent;
|
||||
return res;
|
||||
}
|
||||
|
||||
public DTGroupNode FindTabBarAt(string nameAndPath)
|
||||
{
|
||||
DTGroupNode node;
|
||||
if (FindNodeAt(nameAndPath, out node))
|
||||
if (node.RenderAs == RenderAsEnum.TabBar)
|
||||
return node;
|
||||
return null;
|
||||
}
|
||||
|
||||
public override DTInspectorNode Add(DTInspectorNode node)
|
||||
{
|
||||
node = base.Add(node);
|
||||
DTGroupNode grp = node as DTGroupNode;
|
||||
if (grp != null && _serializedObject != null && _serializedObject.targetObject != null)
|
||||
grp.mState.value = DTPersistentState.GetBool(_serializedObject.targetObject.GetInstanceID().ToString() + grp.Path, grp.mState.value);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
public DTGroupNode AddSection(string name, DTInspectorNodeEvent func)
|
||||
{
|
||||
DTInspectorNode item = this[name];
|
||||
if (item)
|
||||
{
|
||||
if (item.RenderAs != RenderAsEnum.Section)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = new DTGroupNode(name, null, RenderAsEnum.Section);
|
||||
Add(item);
|
||||
item.OnNodeRender += func;
|
||||
}
|
||||
return item as DTGroupNode;
|
||||
}
|
||||
|
||||
public DTGroupNode AddTab(string name, DTInspectorNodeEvent func)
|
||||
{
|
||||
if (RenderAs == RenderAsEnum.TabBar)
|
||||
{
|
||||
DTInspectorNode item = this[name];
|
||||
if (item)
|
||||
{
|
||||
if (item.RenderAs != RenderAsEnum.Tab)
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
item = new DTGroupNode(name, null, RenderAsEnum.Tab);
|
||||
Add(item);
|
||||
item.OnNodeRender += func;
|
||||
}
|
||||
return item as DTGroupNode;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static class DTPersistentState
|
||||
{
|
||||
static Dictionary<string, bool> _states = new Dictionary<string, bool>();
|
||||
|
||||
|
||||
public static bool GetBool(string ident, bool defaultState = true)
|
||||
{
|
||||
|
||||
bool res;
|
||||
if (!_states.TryGetValue(ident, out res))
|
||||
{
|
||||
res = defaultState;
|
||||
_states.Add(ident, defaultState);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static void SetBool(string ident, bool state)
|
||||
{
|
||||
if (!_states.ContainsKey(ident))
|
||||
_states.Add(ident, state);
|
||||
else
|
||||
_states[ident] = state;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region ### Renderer ###
|
||||
|
||||
public interface IDTInspectorNodeRenderer
|
||||
{
|
||||
void RenderTabBarHeader(DTGroupNode node, int maxItemsPerRow);
|
||||
void RenderTabBarFooter(DTGroupNode node);
|
||||
void RenderSectionHeader(DTGroupNode node);
|
||||
void RenderSectionFooter(DTGroupNode node);
|
||||
void RenderField(DTFieldNode node);
|
||||
void RenderAction(DTInspectorNode node, ActionAttribute action, System.Object editorObject, System.Object targetObject);
|
||||
}
|
||||
|
||||
public class DTInspectorNodeDefaultRenderer : IDTInspectorNodeRenderer
|
||||
{
|
||||
|
||||
|
||||
public static Texture HelpIcon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mHelpIcon == null)
|
||||
mHelpIcon = (Texture)EditorGUIUtility.Load("icons/_Help.png");
|
||||
return mHelpIcon;
|
||||
}
|
||||
}
|
||||
static Texture mHelpIcon;
|
||||
public static GUIStyle BoldFoldout
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mBoldFoldout == null)
|
||||
{
|
||||
mBoldFoldout = new GUIStyle(EditorStyles.foldout);
|
||||
mBoldFoldout.fontStyle = FontStyle.Bold;
|
||||
mBoldFoldout.margin.top += 2;
|
||||
mBoldFoldout.margin.bottom += 4;
|
||||
|
||||
}
|
||||
return mBoldFoldout;
|
||||
}
|
||||
}
|
||||
static GUIStyle mBoldFoldout;
|
||||
|
||||
public static GUIStyle TabbarButton
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mTabbarButton == null)
|
||||
{
|
||||
mTabbarButton = new GUIStyle(EditorStyles.toolbarButton);
|
||||
mTabbarButton.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
GUIStyle skinButton = GUI.skin.button;
|
||||
RectOffset skinButtonPadding = skinButton.padding;
|
||||
RectOffset skinButtonMargin = skinButton.margin;
|
||||
RectOffset skinButtonBorder = skinButton.border;
|
||||
RectOffset skinButtonOverflow = skinButton.overflow;
|
||||
|
||||
mTabbarButton.padding = new RectOffset(skinButtonPadding.left, skinButtonPadding.right, skinButtonPadding.top, skinButtonPadding.bottom);
|
||||
mTabbarButton.margin = new RectOffset(skinButtonMargin.left, skinButtonMargin.right, skinButtonMargin.top, skinButtonMargin.bottom);
|
||||
mTabbarButton.border = new RectOffset(skinButtonBorder.left, skinButtonBorder.right, skinButtonBorder.top, skinButtonBorder.bottom);
|
||||
mTabbarButton.overflow = new RectOffset(skinButtonOverflow.left, skinButtonOverflow.right, skinButtonOverflow.top, skinButtonOverflow.bottom);
|
||||
}
|
||||
|
||||
return mTabbarButton;
|
||||
}
|
||||
}
|
||||
static GUIStyle mTabbarButton;
|
||||
|
||||
public virtual void RenderAction(DTInspectorNode node, ActionAttribute action, System.Object editorObject, System.Object targetObject)
|
||||
{
|
||||
switch (action.Action)
|
||||
{
|
||||
case ActionAttribute.ActionEnum.ShowInfo:
|
||||
EditorGUILayout.HelpBox(action.ActionData as string, MessageType.Info);
|
||||
return;
|
||||
case ActionAttribute.ActionEnum.ShowWarning:
|
||||
EditorGUILayout.HelpBox(action.ActionData as string, MessageType.Warning);
|
||||
return;
|
||||
case ActionAttribute.ActionEnum.ShowError:
|
||||
EditorGUILayout.HelpBox(action.ActionData as string, MessageType.Error);
|
||||
return;
|
||||
case ActionAttribute.ActionEnum.Callback:
|
||||
action.Callback(editorObject);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void RenderField(DTFieldNode node)
|
||||
{
|
||||
EditorGUILayout.PropertyField(node.serializedProperty, node.IncludeChildren);
|
||||
}
|
||||
|
||||
public virtual void RenderSectionHeader(DTGroupNode node)
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
|
||||
string helpUrl = node.HelpURL;
|
||||
|
||||
Rect controlRect = EditorGUILayout.GetControlRect(false, 16);
|
||||
int xOffset = (node.Level <= 1 && DTInspectorNode.IsInsideInspector) ? 12 : 0;
|
||||
controlRect.x -= xOffset;
|
||||
|
||||
bool toggleState = node.Expanded;
|
||||
int indentLevel = RenderHeader(controlRect, xOffset, helpUrl, node.GUIContent, ref toggleState);
|
||||
|
||||
node.Expanded = toggleState;
|
||||
EditorGUILayout.BeginFadeGroup(node.ExpandedFaded);
|
||||
EditorGUI.indentLevel = (node.Level <= 1) ? indentLevel : indentLevel + 1;
|
||||
}
|
||||
|
||||
#pragma warning disable 162
|
||||
#pragma warning disable 429
|
||||
private static readonly Color SectionHeaderBackgroundColor =
|
||||
DTEditorUtility.UsesNewEditorUI ? new Color(1f, 1f, 1f) : new Color(0.8f, 0.8f, 0.8f);
|
||||
#pragma warning restore 429
|
||||
#pragma warning restore 162
|
||||
public static int RenderHeader(Rect controlRect, int xOffset, string helpUrl, GUIContent toggleGuiContent, ref bool toggleState)
|
||||
{
|
||||
int indentLevel = EditorGUI.indentLevel;
|
||||
EditorGUI.indentLevel = Mathf.Max(0, indentLevel - 1);
|
||||
controlRect = EditorGUI.IndentedRect(controlRect);
|
||||
DTGUI.PushColor(SectionHeaderBackgroundColor);
|
||||
const int additionalHeight = 4;
|
||||
Rect boxRectangle = new Rect(controlRect.x, controlRect.y - 2, controlRect.width + xOffset + 1, controlRect.height + additionalHeight);
|
||||
GUI.Box(boxRectangle, "", GUI.skin.box);
|
||||
DTHandles.DrawOutline(boxRectangle, Color.black);
|
||||
DTGUI.PopColor();
|
||||
|
||||
Rect expandClickableArea = new Rect(controlRect);
|
||||
bool hasHelp = !string.IsNullOrEmpty(helpUrl);
|
||||
if (hasHelp)
|
||||
expandClickableArea.width -= 12;
|
||||
|
||||
toggleState = GUI.Toggle(expandClickableArea, toggleState, toggleGuiContent, BoldFoldout);
|
||||
if (hasHelp)
|
||||
{
|
||||
if (GUI.Button(new Rect(boxRectangle.xMax - 20, boxRectangle.y + 3, 16, 16)
|
||||
, new GUIContent(HelpIcon, "Help"), new GUIStyle()))
|
||||
Application.OpenURL(helpUrl);
|
||||
}
|
||||
|
||||
GUILayout.Space(additionalHeight);
|
||||
return indentLevel;
|
||||
}
|
||||
|
||||
public virtual void RenderSectionFooter(DTGroupNode node)
|
||||
{
|
||||
if (node.Level > 1)
|
||||
EditorGUI.indentLevel--;
|
||||
EditorGUILayout.EndFadeGroup();
|
||||
}
|
||||
|
||||
public virtual void RenderTabBarHeader(DTGroupNode node, int maxItemsPerRow)
|
||||
{
|
||||
GUILayout.Space(4);
|
||||
|
||||
int s = GUILayout.SelectionGrid(node.SelectedIndex, node.GetItemsGUIContent(), Mathf.Min(node.Count, maxItemsPerRow), TabbarButton);
|
||||
if (s != node.SelectedIndex)
|
||||
{
|
||||
node.SelectedIndex = s;
|
||||
GUIUtility.keyboardControl = 0;
|
||||
}
|
||||
Color c = GUI.color;
|
||||
if (!EditorGUIUtility.isProSkin)
|
||||
GUI.color = new Color(c.r, c.g, c.b, 0.5f);
|
||||
|
||||
GUILayout.BeginVertical(GUI.skin.box);
|
||||
GUI.color = c;
|
||||
}
|
||||
|
||||
public virtual void RenderTabBarFooter(DTGroupNode node)
|
||||
{
|
||||
GUILayout.EndVertical();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c82ecfa39ebfe4a4984a5602b822ca2d
|
||||
timeCreated: 1436962698
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,159 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using System.Collections;
|
||||
using Object = UnityEngine.Object;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTEditorUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// Does Unity use the new editor's UI?
|
||||
/// </summary>
|
||||
public const bool UsesNewEditorUI
|
||||
#if UNITY_2019_3_OR_NEWER //new UI
|
||||
= true;
|
||||
#else
|
||||
= false;
|
||||
#endif
|
||||
|
||||
public static Camera ActiveCamera
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SceneView.currentDrawingSceneView) ? SceneView.currentDrawingSceneView.camera : Camera.current;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool DragDropTypeMatch(System.Type type)
|
||||
{
|
||||
foreach (Object go in DragAndDrop.objectReferences)
|
||||
{
|
||||
if (go is GameObject)
|
||||
{
|
||||
if (type.IsSubclassOf(typeof(Component)) == false)
|
||||
return false;
|
||||
if (((GameObject)go).GetComponent(type) == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (go.GetType() != type)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool DragDropTypeMatch<T>() where T : Object
|
||||
{
|
||||
return DragDropTypeMatch(typeof(T));
|
||||
}
|
||||
|
||||
public static Object[] DragDropGetObjectsOfType(System.Type type)
|
||||
{
|
||||
if (type.IsArrayOrList())
|
||||
return new Object[0];
|
||||
List<Object> res = new List<Object>();
|
||||
foreach (Object o in DragAndDrop.objectReferences)
|
||||
{
|
||||
if (o.GetType() == type)
|
||||
res.Add(o);
|
||||
else if (o is GameObject)
|
||||
{
|
||||
Component f = ((GameObject)o).GetComponent(type);
|
||||
if (f != null)
|
||||
res.Add(f);
|
||||
}
|
||||
}
|
||||
return res.ToArray();
|
||||
}
|
||||
|
||||
public static T CreateAsset<T>() where T : ScriptableObject
|
||||
{
|
||||
T asset = ScriptableObject.CreateInstance<T>();
|
||||
ProjectWindowUtil.CreateAsset(asset, "New " + typeof(T).Name + ".asset");
|
||||
AssetDatabase.SaveAssets();
|
||||
return asset;
|
||||
}
|
||||
|
||||
public static void ToggleSceneViewGizmos(bool gizmosOn)
|
||||
{
|
||||
ToggleSceneViewGizmos(gizmosOn, new string[0]);
|
||||
}
|
||||
|
||||
public static void ToggleSceneViewGizmos(bool gizmosOn, params string[] scriptClasses)
|
||||
{
|
||||
int val = gizmosOn ? 1 : 0;
|
||||
List<string> classes = new List<string>(scriptClasses);
|
||||
Assembly asm = Assembly.GetAssembly(typeof(Editor));
|
||||
System.Type type = asm.GetType("UnityEditor.AnnotationUtility");
|
||||
if (type != null)
|
||||
{
|
||||
MethodInfo getAnnotations = type.GetMethod("GetAnnotations", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (getAnnotations != null)
|
||||
{
|
||||
MethodInfo setGizmoEnabled = type.GetMethod("SetGizmoEnabled", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (setGizmoEnabled != null)
|
||||
{
|
||||
MethodInfo setIconEnabled = type.GetMethod("SetIconEnabled", BindingFlags.Static | BindingFlags.NonPublic);
|
||||
if (setIconEnabled != null)
|
||||
{
|
||||
object annotations = getAnnotations.Invoke(null, null);
|
||||
foreach (object annotation in (IEnumerable)annotations)
|
||||
{
|
||||
Type annotationType = annotation.GetType();
|
||||
FieldInfo classIdField = annotationType.GetField("classID", BindingFlags.Public | BindingFlags.Instance);
|
||||
FieldInfo scriptClassField = annotationType.GetField("scriptClass", BindingFlags.Public | BindingFlags.Instance);
|
||||
if (classIdField != null && scriptClassField != null)
|
||||
{
|
||||
int classId = (int)classIdField.GetValue(annotation);
|
||||
string scriptClass = (string)scriptClassField.GetValue(annotation);
|
||||
if (classes.Count == 0 || classes.Contains(scriptClass))
|
||||
{
|
||||
setGizmoEnabled.Invoke(null, new object[] { classId, scriptClass, val });
|
||||
setIconEnabled.Invoke(null, new object[] { classId, scriptClass, val });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
Debug.LogError("Couldn't find method SetIconEnabled in type " + type.FullName);
|
||||
}
|
||||
else
|
||||
Debug.LogError("Couldn't find method SetGizmoEnabled in type " + type.FullName);
|
||||
}
|
||||
else
|
||||
Debug.LogError("Couldn't find method GetAnnotations in type " + type.FullName);
|
||||
}
|
||||
else
|
||||
Debug.LogError("Couldn't find type UnityEditor.AnnotationUtility in assembly " + asm.FullName);
|
||||
}
|
||||
|
||||
public static List<T> LoadPrefabsContaining<T>(string path) where T : UnityEngine.Component
|
||||
{
|
||||
List<T> res = new List<T>();
|
||||
|
||||
string[] gos = AssetDatabase.FindAssets("t:gameobject", new string[] { path });
|
||||
foreach (string id in gos)
|
||||
{
|
||||
GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(id));
|
||||
T cmp = go.GetComponent<T>();
|
||||
if (cmp != null)
|
||||
res.Add(cmp);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d1ee7bd8bdd696409b8ed1a10163e05
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,201 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTGUI
|
||||
{
|
||||
static Stack<Color> _BGColorStack = new Stack<Color>();
|
||||
static Stack<Color> _ColorStack = new Stack<Color>();
|
||||
static Stack<Color> _ContentColorStack = new Stack<Color>();
|
||||
|
||||
|
||||
#region --- Colors ---
|
||||
|
||||
public static void PushColor(Color col)
|
||||
{
|
||||
_ColorStack.Push(GUI.color);
|
||||
GUI.color = col;
|
||||
}
|
||||
|
||||
public static void PopColor()
|
||||
{
|
||||
GUI.color = _ColorStack.Pop();
|
||||
}
|
||||
|
||||
public static void PushBackgroundColor(Color col)
|
||||
{
|
||||
_BGColorStack.Push(GUI.color);
|
||||
GUI.color = col;
|
||||
}
|
||||
|
||||
public static void PopBackgroundColor()
|
||||
{
|
||||
GUI.color = _BGColorStack.Pop();
|
||||
}
|
||||
|
||||
public static void PushContentColor(Color col)
|
||||
{
|
||||
_ContentColorStack.Push(GUI.contentColor);
|
||||
GUI.contentColor = col;
|
||||
}
|
||||
|
||||
public static void PopContentColor()
|
||||
{
|
||||
GUI.contentColor = _ContentColorStack.Pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- Events ---
|
||||
|
||||
public static bool IsClick
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Event.current!=null && Event.current.type == EventType.MouseDown && Event.current.button == 0);
|
||||
}
|
||||
}
|
||||
public static bool IsContextClick
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Event.current != null && Event.current.type == EventType.MouseDown && Event.current.button == 1);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsLayout { get { return Event.current != null && Event.current.type == EventType.Layout; } }
|
||||
public static bool IsRepaint { get { return Event.current != null && Event.current.type == EventType.Repaint; } }
|
||||
|
||||
public static Vector2 MousePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Event.current != null) ? Event.current.mousePosition : Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
public static void UseEvent(int hashcode, Event e)
|
||||
{
|
||||
int controlID = GUIUtility.GetControlID(hashcode, FocusType.Passive);
|
||||
GUIUtility.hotControl = controlID;
|
||||
e.Use();
|
||||
GUIUtility.hotControl = 0;
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region --- Controls ---
|
||||
|
||||
public static void HtmlHelpBox(string message, MessageType type = MessageType.None)
|
||||
{
|
||||
GUIContent c;
|
||||
switch (type)
|
||||
{
|
||||
case MessageType.Info:
|
||||
c = EditorGUIUtility.IconContent("console.infoicon");
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
c = EditorGUIUtility.IconContent("console.warningicon");
|
||||
break;
|
||||
case MessageType.Error:
|
||||
c = EditorGUIUtility.IconContent("console.erroricon");
|
||||
break;
|
||||
default:
|
||||
c = new GUIContent();
|
||||
break;
|
||||
}
|
||||
c.text=message;
|
||||
GUILayout.Label(c, DTStyles.HtmlHelpBox);
|
||||
}
|
||||
|
||||
public static Vector2 CompactVector2Field(Rect position, Vector2 value)
|
||||
{
|
||||
float labelWidth = 13f;
|
||||
|
||||
float num2 = (position.width - 2) / 2f;
|
||||
Rect position2 = new Rect(position);
|
||||
position2.width = num2;
|
||||
float labelWidth2 = EditorGUIUtility.labelWidth;
|
||||
int indentLevel = EditorGUI.indentLevel;
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
EditorGUI.indentLevel = 0;
|
||||
value.x = EditorGUI.FloatField(position2, "X", value.x);
|
||||
position2.x += num2 + 2f;
|
||||
value.y = EditorGUI.FloatField(position2, "Y", value.y);
|
||||
|
||||
EditorGUIUtility.labelWidth = labelWidth2;
|
||||
EditorGUI.indentLevel = indentLevel;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static Vector3 CompactVector3Field(Rect position, Vector3 value)
|
||||
{
|
||||
float labelWidth = 13f;
|
||||
float num2 = (position.width - 4) / 3f;
|
||||
Rect position2 = new Rect(position);
|
||||
position2.width = num2;
|
||||
float labelWidth2 = EditorGUIUtility.labelWidth;
|
||||
int indentLevel = EditorGUI.indentLevel;
|
||||
EditorGUIUtility.labelWidth = labelWidth;
|
||||
EditorGUI.indentLevel = 0;
|
||||
value.x = EditorGUI.FloatField(position2, "X", value.x);
|
||||
position2.x += num2 + 2f;
|
||||
value.y = EditorGUI.FloatField(position2, "Y", value.y);
|
||||
position2.x += num2 + 2f;
|
||||
value.z = EditorGUI.FloatField(position2, "Z", value.z);
|
||||
|
||||
EditorGUIUtility.labelWidth = labelWidth2;
|
||||
EditorGUI.indentLevel = indentLevel;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static bool LinkButton(Rect position, GUIContent content, ref bool repaint)
|
||||
{
|
||||
if (position.Contains(DTGUI.MousePosition))
|
||||
repaint = true;
|
||||
return GUI.Button(position, content, DTStyles.HtmlLinkLabel);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static float LabelWidth(string label)
|
||||
{
|
||||
return LabelWidth(new GUIContent(label));
|
||||
}
|
||||
public static float LabelWidth(GUIContent label)
|
||||
{
|
||||
return EditorStyles.label.CalcSize(label).x;
|
||||
}
|
||||
|
||||
public static Rect FromTo(this Vector2 start, Vector2 end)
|
||||
{
|
||||
Rect result = new Rect(start.x, start.y, end.x - start.x, end.y - start.y);
|
||||
if (result.width < 0f)
|
||||
{
|
||||
result.x += result.width;
|
||||
result.width = -result.width;
|
||||
}
|
||||
if (result.height < 0f)
|
||||
{
|
||||
result.y += result.height;
|
||||
result.height = -result.height;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ef286eb30f9728447bb8584e9a582119
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,538 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTHandles
|
||||
{
|
||||
public static class SnapSettings
|
||||
{
|
||||
private static float s_MoveSnapX;
|
||||
private static float s_MoveSnapY;
|
||||
private static float s_MoveSnapZ;
|
||||
private static float s_ScaleSnap;
|
||||
private static float s_RotationSnap;
|
||||
private static bool s_Initialized;
|
||||
|
||||
private static void Initialize()
|
||||
{
|
||||
if (!s_Initialized)
|
||||
{
|
||||
s_MoveSnapX = EditorPrefs.GetFloat("MoveSnapX", 1f);
|
||||
s_MoveSnapY = EditorPrefs.GetFloat("MoveSnapY", 1f);
|
||||
s_MoveSnapZ = EditorPrefs.GetFloat("MoveSnapZ", 1f);
|
||||
s_ScaleSnap = EditorPrefs.GetFloat("ScaleSnap", 0.1f);
|
||||
s_RotationSnap = EditorPrefs.GetFloat("RotationSnap", 15f);
|
||||
s_Initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Vector3 Move
|
||||
{
|
||||
get { return new Vector3(MoveX, MoveY, MoveZ); }
|
||||
}
|
||||
public static float MoveX
|
||||
{
|
||||
get { Initialize(); return s_MoveSnapX; }
|
||||
}
|
||||
public static float MoveY
|
||||
{
|
||||
get { Initialize(); return s_MoveSnapY; }
|
||||
}
|
||||
public static float MoveZ
|
||||
{
|
||||
get { Initialize(); return s_MoveSnapZ; }
|
||||
}
|
||||
public static float Rotation
|
||||
{
|
||||
get { Initialize(); return s_RotationSnap; }
|
||||
}
|
||||
public static float ScaleSnap
|
||||
{
|
||||
get { Initialize(); return s_ScaleSnap; }
|
||||
}
|
||||
}
|
||||
|
||||
public static bool MouseOverSceneView
|
||||
{
|
||||
get
|
||||
{
|
||||
return (SceneView.currentDrawingSceneView != null && Event.current != null) ?
|
||||
SceneView.currentDrawingSceneView.position.Contains(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)) : false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool SceneViewIsSelected
|
||||
{
|
||||
get
|
||||
{
|
||||
return SceneView.focusedWindow == SceneView.currentDrawingSceneView;
|
||||
}
|
||||
}
|
||||
|
||||
static Stack<Color> mHandlesColorstack = new Stack<Color>();
|
||||
|
||||
public static void PushHandlesColor(Color col)
|
||||
{
|
||||
mHandlesColorstack.Push(GUI.color);
|
||||
Handles.color = col;
|
||||
}
|
||||
|
||||
public static void PopHandlesColor()
|
||||
{
|
||||
Handles.color = mHandlesColorstack.Pop();
|
||||
}
|
||||
|
||||
static Stack<Matrix4x4> mHandlesMatrixstack = new Stack<Matrix4x4>();
|
||||
|
||||
public static void PushMatrix(Matrix4x4 matrix)
|
||||
{
|
||||
mHandlesMatrixstack.Push(matrix);
|
||||
Handles.matrix = matrix;
|
||||
}
|
||||
|
||||
public static void PopMatrix()
|
||||
{
|
||||
Handles.matrix = mHandlesMatrixstack.Pop();
|
||||
}
|
||||
|
||||
|
||||
public static void LabelIcon(Vector3 position, string text, Color labelColor)
|
||||
{
|
||||
GUIStyle iconStyle = GUI.skin.button;
|
||||
iconStyle.normal.textColor = labelColor;
|
||||
Handles.Label(position, text, iconStyle);
|
||||
}
|
||||
|
||||
public static bool Button(int id, Vector3 position, Quaternion direction, float size, float pickSize, bool useHandleSize, Handles.CapFunction capFunc, Color hoverCol, out bool isHovering)
|
||||
{
|
||||
Event current = Event.current;
|
||||
isHovering = false;
|
||||
if (useHandleSize)
|
||||
{
|
||||
float s = HandleUtility.GetHandleSize(position);
|
||||
size *= s;
|
||||
pickSize *= s;
|
||||
}
|
||||
switch (current.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if (HandleUtility.nearestControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = id;
|
||||
current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if ((current.button == 0 || current.button == 2) && GUIUtility.hotControl == id)
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
current.Use();
|
||||
if (HandleUtility.nearestControl == id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.MouseMove:
|
||||
if ((HandleUtility.nearestControl == id && current.button == 0) || (GUIUtility.keyboardControl == id && current.button == 2))
|
||||
{
|
||||
HandleUtility.Repaint();
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
{
|
||||
Color color = Handles.color;
|
||||
if (HandleUtility.nearestControl == id && GUI.enabled)
|
||||
{
|
||||
isHovering = true;
|
||||
Handles.color = hoverCol;
|
||||
}
|
||||
|
||||
capFunc(id, position, Quaternion.identity, size, EventType.Repaint);
|
||||
Handles.color = color;
|
||||
break;
|
||||
}
|
||||
case EventType.Layout:
|
||||
if (GUI.enabled)
|
||||
{
|
||||
HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(position, pickSize));
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool PositionHandle(Vector3 position, Quaternion rotation, out Vector3 delta)
|
||||
{
|
||||
Vector3 p = Handles.PositionHandle(position, rotation);
|
||||
delta = p - position;
|
||||
return delta != Vector3.zero;
|
||||
}
|
||||
|
||||
public static bool RotationHandle(Vector3 position, Quaternion rotation, out Quaternion newRotation)
|
||||
{
|
||||
newRotation = Handles.RotationHandle(rotation, position);
|
||||
return (newRotation != rotation);
|
||||
}
|
||||
|
||||
static Vector2 s_StartMousePosition;
|
||||
static Vector2 s_CurrentMousePosition;
|
||||
static float s_StartScale;
|
||||
|
||||
public static bool ScaleSlider(float scale, Vector3 position, Vector3 direction, Quaternion rotation, float length, float size, float snap, out float delta)
|
||||
{
|
||||
int id = GUIUtility.GetControlID("ScaleSliderHash".GetHashCode(), FocusType.Keyboard);
|
||||
float newScale = scale;
|
||||
Event current = Event.current;
|
||||
switch (current.GetTypeForControl(id))
|
||||
{
|
||||
case EventType.MouseDown:
|
||||
if ((HandleUtility.nearestControl == id && current.button == 0) || (GUIUtility.keyboardControl == id && current.button == 2))
|
||||
{
|
||||
GUIUtility.keyboardControl = id;
|
||||
GUIUtility.hotControl = id;
|
||||
s_CurrentMousePosition = (s_StartMousePosition = current.mousePosition);
|
||||
s_StartScale = scale;
|
||||
current.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(1);
|
||||
}
|
||||
break;
|
||||
case EventType.MouseUp:
|
||||
if (GUIUtility.hotControl == id && (current.button == 0 || current.button == 2))
|
||||
{
|
||||
GUIUtility.hotControl = 0;
|
||||
current.Use();
|
||||
EditorGUIUtility.SetWantsMouseJumping(0);
|
||||
}
|
||||
break;
|
||||
case EventType.MouseDrag:
|
||||
if (GUIUtility.hotControl == id)
|
||||
{
|
||||
s_CurrentMousePosition += current.delta;
|
||||
float num = 1f + HandleUtility.CalcLineTranslation(s_StartMousePosition, s_CurrentMousePosition, position, direction) * 0.1f;
|
||||
num = Handles.SnapValue(num, snap);
|
||||
newScale = s_StartScale * num;
|
||||
GUI.changed = true;
|
||||
current.Use();
|
||||
}
|
||||
break;
|
||||
case EventType.Repaint:
|
||||
{
|
||||
Color color = Color.white;
|
||||
if (id == GUIUtility.keyboardControl)
|
||||
{
|
||||
color = Handles.color;
|
||||
Handles.color = Handles.selectedColor;
|
||||
}
|
||||
//float num2 = size;
|
||||
//if (GUIUtility.hotControl == id)
|
||||
//{
|
||||
// num2 = size * scale / s_StartScale;
|
||||
//}
|
||||
Handles.CubeHandleCap(id, position + direction * length, rotation, HandleUtility.GetHandleSize(position) * size, EventType.Repaint);
|
||||
|
||||
Handles.DrawLine(position, position + direction * length);
|
||||
if (id == GUIUtility.keyboardControl)
|
||||
{
|
||||
Handles.color = color;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EventType.Layout:
|
||||
HandleUtility.AddControl(id, HandleUtility.DistanceToLine(position, position + direction * length));
|
||||
HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(position + direction * length, size));
|
||||
break;
|
||||
}
|
||||
delta = newScale - scale;
|
||||
|
||||
return (scale != newScale);
|
||||
}
|
||||
|
||||
public static Quaternion RotationHandle(Quaternion rotation, Vector3 position, float size)
|
||||
{
|
||||
Color staticColor = new Color(0.5f, 0.5f, 0.5f, 0f);
|
||||
float staticBlend = 0.6f;
|
||||
float handleSize = HandleUtility.GetHandleSize(position) * size;
|
||||
Color color = Handles.color;
|
||||
bool flag = !Tools.hidden && EditorApplication.isPlaying && ContainsStatic(Selection.gameObjects);
|
||||
Handles.color = ((!flag) ? Handles.xAxisColor : Color.Lerp(Handles.xAxisColor, staticColor, staticBlend));
|
||||
rotation = Handles.Disc(rotation, position, rotation * Vector3.right, handleSize, true, SnapSettings.Rotation);
|
||||
Handles.color = ((!flag) ? Handles.yAxisColor : Color.Lerp(Handles.yAxisColor, staticColor, staticBlend));
|
||||
rotation = Handles.Disc(rotation, position, rotation * Vector3.up, handleSize, true, SnapSettings.Rotation);
|
||||
Handles.color = ((!flag) ? Handles.zAxisColor : Color.Lerp(Handles.zAxisColor, staticColor, staticBlend));
|
||||
rotation = Handles.Disc(rotation, position, rotation * Vector3.forward, handleSize, true, SnapSettings.Rotation);
|
||||
if (!flag)
|
||||
{
|
||||
Handles.color = Handles.centerColor;
|
||||
rotation = Handles.Disc(rotation, position, Camera.current.transform.forward, handleSize * 1.1f, false, 0f);
|
||||
rotation = Handles.FreeRotateHandle(rotation, position, handleSize);
|
||||
}
|
||||
Handles.color = color;
|
||||
return rotation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 3D position handle
|
||||
/// </summary>
|
||||
/// <returns>The updated position</returns>
|
||||
/// <seealso cref="GUIUtility.GetControlID(int,FocusType)"/>
|
||||
public static Vector3 PositionHandle(int controlId1, int controlId2, int controlId3, Vector3 position, Quaternion rotation, float handleSize, float rectangleToSliderRatio = 0.15f, Color additionalColor = new Color())
|
||||
{
|
||||
float sliderSize = HandleUtility.GetHandleSize(position) * handleSize;
|
||||
float rectangleSize = sliderSize * rectangleToSliderRatio;
|
||||
|
||||
Vector3 snap = SnapSettings.Move;
|
||||
|
||||
Vector3 axis1 = rotation * Vector3.right;
|
||||
Vector3 axis2 = rotation * Vector3.up;
|
||||
Vector3 axis3 = rotation * Vector3.forward;
|
||||
Color axis1Color = new Color(0.9f, 0.3f, 0.1f) + additionalColor;
|
||||
Color axis2Color = new Color(0.6f, 0.9f, 0.3f) + additionalColor;
|
||||
Color axis3Color = new Color(0.2f, 0.4f, 0.9f) + additionalColor;
|
||||
|
||||
Vector3 updatedPosition = position;
|
||||
|
||||
Handles.color = axis1Color;
|
||||
updatedPosition += Handles.Slider(position, axis1, sliderSize, Handles.ArrowHandleCap, snap.x) - position;
|
||||
updatedPosition += Handles.Slider2D(controlId1, position, rectangleSize * (axis3 + axis2), Vector3.Cross(axis3, axis2), axis2, axis3, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.y, snap.z)) - position;
|
||||
|
||||
Handles.color = axis2Color;
|
||||
updatedPosition += Handles.Slider(position, axis2, sliderSize, Handles.ArrowHandleCap, snap.y) - position;
|
||||
updatedPosition += Handles.Slider2D(controlId2, position, rectangleSize * (axis1 + axis3), Vector3.Cross(axis1, axis3), axis3, axis1, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.z, snap.x)) - position;
|
||||
|
||||
Handles.color = axis3Color;
|
||||
updatedPosition += Handles.Slider(position, axis3, sliderSize, Handles.ArrowHandleCap, snap.z) - position;
|
||||
updatedPosition += Handles.Slider2D(controlId3, position, rectangleSize * (axis1 + axis2), Vector3.Cross(axis1, axis2), axis2, axis1, rectangleSize, Handles.RectangleHandleCap, new Vector2(snap.y, snap.x)) - position;
|
||||
|
||||
return updatedPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a 2D position handle
|
||||
/// </summary>
|
||||
/// <param name="size"></param>
|
||||
/// <param name="plane">0 for XY, 1 for XZ and 2 for YZ</param>
|
||||
/// <param name="controlId"><see cref="GUIUtility.GetControlID(int,FocusType)"/></param>
|
||||
/// <param name="position"></param>
|
||||
/// <param name="rotation"></param>
|
||||
/// <returns>The updated position</returns>
|
||||
public static Vector3 PositionHandle2D(int controlId, Vector3 position, Quaternion rotation, float size, int plane = 0)
|
||||
{
|
||||
float sliderSize = HandleUtility.GetHandleSize(position) * size;
|
||||
float rectSize = sliderSize * 0.15f;
|
||||
|
||||
Vector3 snap = SnapSettings.Move;
|
||||
|
||||
Vector3 axis1;
|
||||
Vector3 axis2;
|
||||
Color axis1Color;
|
||||
Color axis2Color;
|
||||
Color planeColor;
|
||||
{
|
||||
Color xColor = new Color(0.9f, 0.3f, 0.1f);
|
||||
Color yColor = new Color(0.6f, 0.9f, 0.3f);
|
||||
Color zColor = new Color(0.2f, 0.4f, 0.9f);
|
||||
|
||||
switch (plane)
|
||||
{
|
||||
//XY
|
||||
case 0:
|
||||
axis1 = rotation * Vector3.right;
|
||||
axis2 = rotation * Vector3.up;
|
||||
axis1Color = xColor;
|
||||
axis2Color = yColor;
|
||||
planeColor = zColor;
|
||||
break;
|
||||
//XZ
|
||||
case 1:
|
||||
axis1 = rotation * Vector3.right;
|
||||
axis2 = rotation * Vector3.forward;
|
||||
axis1Color = xColor;
|
||||
axis2Color = zColor;
|
||||
planeColor = yColor;
|
||||
break;
|
||||
//YZ
|
||||
case 2:
|
||||
axis1 = rotation * Vector3.up;
|
||||
axis2 = rotation * Vector3.forward;
|
||||
axis1Color = yColor;
|
||||
axis2Color = zColor;
|
||||
planeColor = xColor;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(plane));
|
||||
}
|
||||
}
|
||||
|
||||
Vector3 updatedPosition = position;
|
||||
|
||||
Handles.color = axis1Color;
|
||||
updatedPosition += Handles.Slider(position, axis1, sliderSize, Handles.ArrowHandleCap, snap.x) - position;
|
||||
|
||||
Handles.color = axis2Color;
|
||||
updatedPosition += Handles.Slider(position, axis2, sliderSize, Handles.ArrowHandleCap, snap.y) - position;
|
||||
|
||||
Handles.color = planeColor;
|
||||
updatedPosition += Handles.Slider2D(controlId, position, rectSize * (axis1 + axis2), Vector3.Cross(axis1, axis2), axis2, axis1, rectSize, Handles.RectangleHandleCap, new Vector2(snap.x, snap.y)) - position;
|
||||
|
||||
return updatedPosition;
|
||||
}
|
||||
|
||||
public static Vector3 TinyHandle2D(int id, Vector3 position, Quaternion rotation, float size, Handles.CapFunction func = null)
|
||||
{
|
||||
return TinyHandle2D(id, position, rotation * Vector3.forward, rotation * Vector3.up, rotation * Vector3.right, size, func);
|
||||
}
|
||||
|
||||
public static Vector3 TinyHandle2D(int id, Transform transform, float size, Handles.CapFunction func = null)
|
||||
{
|
||||
return TinyHandle2D(id, transform.position, transform.forward, transform.up, transform.right, size, func);
|
||||
}
|
||||
|
||||
public static Vector3 TinyHandle2D(int id, Vector3 pos, Vector3 forward, Vector3 up, Vector3 right, float size, Handles.CapFunction func = null)
|
||||
{
|
||||
return Handles.Slider2D(id, pos, forward, up, right, HandleUtility.GetHandleSize(pos) * size, func, SnapSettings.Move);
|
||||
}
|
||||
|
||||
public static void DrawSolidRectangleWithOutline(Vector2 center, Vector2 extends, Color backgroundColor, Color outlineColor)
|
||||
{
|
||||
DrawSolidRectangleWithOutline(new Rect(center.x - extends.x / 2, center.y - extends.y / 2, extends.x, extends.y), backgroundColor, outlineColor);
|
||||
}
|
||||
|
||||
public static void DrawSolidRectangleWithOutline(Rect r, Color backgroundColor, Color outlineColor)
|
||||
{
|
||||
Vector3[] verts = new Vector3[4]
|
||||
{
|
||||
new Vector3(r.xMin,r.yMin,0),
|
||||
new Vector3(r.xMax,r.yMin,0),
|
||||
new Vector3(r.xMax,r.yMax,0),
|
||||
new Vector3(r.xMin,r.yMax,0)
|
||||
};
|
||||
Handles.DrawSolidRectangleWithOutline(verts, backgroundColor, outlineColor);
|
||||
}
|
||||
|
||||
public static void DrawOutline(Rect r, Color outlineColor)
|
||||
{
|
||||
DrawSolidRectangleWithOutline(r, new Color(0, 0, 0, 0), outlineColor);
|
||||
}
|
||||
|
||||
public static void WireCubeCap(Vector3 position, Vector3 size)
|
||||
{
|
||||
Vector3 half = size / 2;
|
||||
// draw front
|
||||
Handles.DrawLine(position + new Vector3(-half.x, -half.y, half.z), position + new Vector3(half.x, -half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(-half.x, -half.y, half.z), position + new Vector3(-half.x, half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, half.y, half.z), position + new Vector3(half.x, -half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, half.y, half.z), position + new Vector3(-half.x, half.y, half.z));
|
||||
// draw back
|
||||
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(half.x, -half.y, -half.z));
|
||||
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(-half.x, half.y, -half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(half.x, -half.y, -half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(-half.x, half.y, -half.z));
|
||||
// draw corners
|
||||
Handles.DrawLine(position + new Vector3(-half.x, -half.y, -half.z), position + new Vector3(-half.x, -half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, -half.y, -half.z), position + new Vector3(half.x, -half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(-half.x, half.y, -half.z), position + new Vector3(-half.x, half.y, half.z));
|
||||
Handles.DrawLine(position + new Vector3(half.x, half.y, -half.z), position + new Vector3(half.x, half.y, half.z));
|
||||
}
|
||||
|
||||
public static void ArrowCap(Vector3 position, Vector3 direction, Color outlineColor, float length, float lineWidth = 0.4f, float headWidth = 1f, float headRatio = 0.3f, bool useHandleSize = true)
|
||||
{
|
||||
float size = (useHandleSize) ? HandleUtility.GetHandleSize(position) : 1;
|
||||
|
||||
ArrowCap(position, direction, Vector3.up, outlineColor, length, lineWidth, headWidth, headRatio, size);
|
||||
}
|
||||
|
||||
public static void ArrowCap(Vector3 position, Vector3 direction, Vector3 upDirection, Color outlineColor, float length, float lineWidth, float headWidth, float headRatio, float sizeMultiplier)
|
||||
{
|
||||
Vector3[] arrow = new Vector3[8];
|
||||
Vector3 start = position;
|
||||
length *= sizeMultiplier;
|
||||
Vector3 end = position + direction * length;
|
||||
|
||||
Vector3 right = Vector3.Cross(upDirection, direction).normalized;
|
||||
|
||||
float hh = length * headRatio;
|
||||
Vector3 rlw2 = right * lineWidth / 2 * sizeMultiplier;
|
||||
Vector3 rhw2 = right * headWidth / 2 * sizeMultiplier;
|
||||
arrow[0] = start - rlw2;
|
||||
arrow[1] = end - direction * hh - rlw2;
|
||||
arrow[2] = end - direction * hh - rhw2;
|
||||
arrow[3] = end;
|
||||
arrow[4] = end - direction * hh + rhw2;
|
||||
arrow[5] = end - direction * hh + rlw2;
|
||||
arrow[6] = start + rlw2;
|
||||
arrow[7] = arrow[0];
|
||||
|
||||
Handles.DrawAAConvexPolygon(arrow[2], arrow[3], arrow[4]);
|
||||
Handles.DrawAAConvexPolygon(arrow[0], arrow[1], arrow[5], arrow[6]);
|
||||
if (Handles.color != outlineColor)
|
||||
{
|
||||
Color c = Handles.color;
|
||||
Handles.color = outlineColor;
|
||||
Handles.DrawAAPolyLine(arrow);
|
||||
Handles.color = c;
|
||||
}
|
||||
}
|
||||
|
||||
public static void BoundsCap(Bounds b)
|
||||
{
|
||||
WireCubeCap(b.center, b.size);
|
||||
}
|
||||
|
||||
public static Vector3 TranslateByPixel(Vector3 position, float x, float y)
|
||||
{
|
||||
return TranslateByPixel(position, new Vector3(x, y));
|
||||
}
|
||||
public static Vector3 TranslateByPixel(Vector3 position, Vector3 translateBy)
|
||||
{
|
||||
Camera cam = SceneView.currentDrawingSceneView.camera;
|
||||
if (cam)
|
||||
return cam.ScreenToWorldPoint(cam.WorldToScreenPoint(position) + translateBy);
|
||||
else
|
||||
return position;
|
||||
}
|
||||
|
||||
public static Vector3 TranslateAligned(Vector3 position, GUIContent content, TextAlignment alignment = TextAlignment.Center, GUIStyle style = null)
|
||||
{
|
||||
if (style == null)
|
||||
style = EditorStyles.label;
|
||||
float w = style.CalcSize(content).x;
|
||||
switch (alignment)
|
||||
{
|
||||
case TextAlignment.Center:
|
||||
return TranslateByPixel(position, new Vector3(-w / 2, 0, 0));
|
||||
case TextAlignment.Right:
|
||||
return TranslateByPixel(position, new Vector3(w / 2, 0, 0));
|
||||
default:
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static bool ContainsStatic(GameObject[] objects)
|
||||
{
|
||||
if (objects == null || objects.Length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < objects.Length; i++)
|
||||
{
|
||||
if (objects[i] != null && objects[i].isStatic)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6a1d31433dbd4e43934fdeedb24a25b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,242 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using FluffyUnderware.DevTools;
|
||||
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public abstract class DTProject : IComparable
|
||||
{
|
||||
public string Identifier { get; private set; }
|
||||
public string Version { get; private set; }
|
||||
public DTResource Resource { get; protected set; }
|
||||
[Obsolete("Assumed to be always true")]
|
||||
public bool ShowToolbarInAllSceneViews = false;
|
||||
|
||||
public DTToolbarOrientation ToolbarOrientation
|
||||
{
|
||||
get { return mTBOrientation; }
|
||||
set
|
||||
{
|
||||
if (mTBOrientation != value)
|
||||
{
|
||||
mTBOrientation = value;
|
||||
if (!string.IsNullOrEmpty(Identifier))
|
||||
{
|
||||
SetEditorPrefs("ToolbarOrientation", ToolbarOrientation);
|
||||
}
|
||||
else
|
||||
DTLog.LogError("[DevTools] Project " + this.GetType().Name + "missing identifier!");
|
||||
DTToolbar.RecalcItemSize = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
public DTToolbarMode ToolbarMode
|
||||
{
|
||||
get { return mTBMode; }
|
||||
set
|
||||
{
|
||||
if (mTBMode != value)
|
||||
{
|
||||
mTBMode = value;
|
||||
if (!string.IsNullOrEmpty(Identifier))
|
||||
{
|
||||
SetEditorPrefs("ToolbarMode", ToolbarMode);
|
||||
}
|
||||
else
|
||||
DTLog.LogError("[DevTools] Project " + this.GetType().Name + "missing identifier!");
|
||||
DTToolbar.RecalcItemSize = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Dictionary<string, EditorKeyBinding> AdditionalKeyBindings = new Dictionary<string, EditorKeyBinding>();
|
||||
internal List<DTToolbarItem> ToolbarItems = new List<DTToolbarItem>();
|
||||
|
||||
static DTToolbarOrientation mTBOrientation = DTToolbarOrientation.Left;
|
||||
static DTToolbarMode mTBMode = DTToolbarMode.Full;
|
||||
|
||||
protected DTProject(string identifier, string version)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Version = version;
|
||||
LoadPreferences();
|
||||
}
|
||||
|
||||
internal void Clear()
|
||||
{
|
||||
ToolbarItems.Clear();
|
||||
AdditionalKeyBindings.Clear();
|
||||
}
|
||||
|
||||
public virtual void ResetPreferences()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void LoadPreferences()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Identifier))
|
||||
{
|
||||
// Upgrade?
|
||||
string ver=GetEditorPrefs("Version", Version);
|
||||
if (string.Compare(ver, Version)==-1)// (ver != Version)
|
||||
{
|
||||
UpgradePreferences(ver);
|
||||
SetEditorPrefs("Version", Version);
|
||||
}
|
||||
|
||||
mTBMode=GetEditorPrefs("ToolbarMode", ToolbarMode);
|
||||
mTBOrientation=GetEditorPrefs("ToolbarOrientation", ToolbarOrientation);
|
||||
|
||||
} else
|
||||
DTLog.LogError("[DevTools] Project "+this.GetType().Name+"missing identifier!");
|
||||
}
|
||||
|
||||
public virtual void SavePreferences()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Identifier))
|
||||
{
|
||||
SetEditorPrefs("Version",Version);
|
||||
SetEditorPrefs("ToolbarMode", ToolbarMode);
|
||||
SetEditorPrefs("ToolbarOrientation", ToolbarOrientation);
|
||||
} else
|
||||
DTLog.LogError("[DevTools] Project " + this.GetType().Name + "missing identifier!");
|
||||
}
|
||||
|
||||
public List<EditorKeyBinding> GetProjectBindings()
|
||||
{
|
||||
List<EditorKeyBinding> res = new List<EditorKeyBinding>();
|
||||
foreach (DTToolbarItem item in ToolbarItems)
|
||||
foreach (EditorKeyBinding binding in item.KeyBindings)
|
||||
if (binding.ShowInPrefs)
|
||||
res.Add(binding);
|
||||
res.AddRange(AdditionalKeyBindings.Values);
|
||||
res.Sort();
|
||||
return res;
|
||||
}
|
||||
|
||||
public EditorKeyBinding RegisterKeyBinding(EditorKeyBinding binding)
|
||||
{
|
||||
EditorKeyBinding b;
|
||||
if (AdditionalKeyBindings.TryGetValue(binding.Name,out b))
|
||||
return b;
|
||||
else {
|
||||
setKeyBindingFromPrefs(binding,false);
|
||||
AdditionalKeyBindings.Add(binding.Name, binding);
|
||||
return binding;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For all EditorKeyBindings registered to ToolbarItems of this project, check if Name is unique
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal bool CheckKeyBindingNamesAreUnique()
|
||||
{
|
||||
|
||||
Dictionary<string, object> dict = new Dictionary<string, object>();
|
||||
object exist;
|
||||
foreach (DTToolbarItem item in ToolbarItems)
|
||||
foreach (EditorKeyBinding binding in item.KeyBindings)
|
||||
{
|
||||
if (dict.TryGetValue(binding.Name, out exist)){
|
||||
DTLog.LogError(string.Format("[DevTools] KeyBindings need unique names! ({0} and {1}",item.GetType().Name,exist.GetType().Name));
|
||||
return false;
|
||||
} else
|
||||
dict.Add(binding.Name,item);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setKeyBindingFromPrefs(EditorKeyBinding binding, bool removePrefsIfEqual=true)
|
||||
{
|
||||
string newBindString = GetEditorPrefs(binding.Name, "");
|
||||
if (!string.IsNullOrEmpty(newBindString) ){
|
||||
// Same binding? Then remove prefs setting
|
||||
if (removePrefsIfEqual && newBindString == binding.ToPrefsString())
|
||||
DeleteEditorPrefs(binding.Name);
|
||||
else // load and set new binding
|
||||
binding.Set(newBindString);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For all EditorKeyBindings registered to ToolbarItems of this project, manage remappings stored in EditorPrefs
|
||||
/// </summary>
|
||||
internal void LoadKeyBindingRemappings()
|
||||
{
|
||||
// Toolbars
|
||||
foreach (DTToolbarItem item in ToolbarItems)
|
||||
for (int i=0;i<item.KeyBindings.Count;i++)
|
||||
setKeyBindingFromPrefs(item.KeyBindings[i]);
|
||||
}
|
||||
|
||||
protected virtual void UpgradePreferences(string oldVersion)
|
||||
{
|
||||
DTLog.Log(string.Format("[{0}] Upgrading project settings from {1} to {2}", Identifier, oldVersion, Version));
|
||||
}
|
||||
|
||||
public void SetEditorPrefs<T>(string key, T value)
|
||||
{
|
||||
DT.SetEditorPrefs(Identifier + "." + key, value);
|
||||
}
|
||||
|
||||
public T GetEditorPrefs<T>(string key, T defaultValue)
|
||||
{
|
||||
return DT.GetEditorPrefs(Identifier + "." + key, defaultValue);
|
||||
}
|
||||
|
||||
public string[] GetEditorPrefs(string key)
|
||||
{
|
||||
return DT.GetEditorPrefs(Identifier + "." + key);
|
||||
}
|
||||
|
||||
public bool HasEditorPrefs(string key)
|
||||
{
|
||||
return EditorPrefs.HasKey(Identifier + "." + key);
|
||||
}
|
||||
|
||||
public void DeleteEditorPrefs(string key)
|
||||
{
|
||||
EditorPrefs.DeleteKey(Identifier + "." + key);
|
||||
}
|
||||
|
||||
public T FindItem<T>() where T:DTToolbarItem
|
||||
{
|
||||
Type t = typeof(T);
|
||||
for (int i = 0; i < ToolbarItems.Count; i++)
|
||||
if (ToolbarItems[i].GetType() == t)
|
||||
return (T)ToolbarItems[i];
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void SetRadioGroupState(DTToolbarRadioButton active)
|
||||
{
|
||||
for (int i = 0; i < ToolbarItems.Count; i++)
|
||||
{
|
||||
DTToolbarRadioButton rad = ToolbarItems[i] as DTToolbarRadioButton;
|
||||
if (rad && rad.RadioGroup.Equals(active.RadioGroup, StringComparison.CurrentCultureIgnoreCase))
|
||||
rad.mOn = (rad == active);
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
DTProject other = (DTProject)obj;
|
||||
return Identifier.CompareTo(other.Identifier);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f699006303cd07e4fb52f3fd516ad100
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,160 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using System.Reflection;
|
||||
using FluffyUnderware.DevTools;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Class for loading image resources
|
||||
/// </summary>
|
||||
public abstract class DTResource
|
||||
{
|
||||
public Assembly ResourceDLL;
|
||||
public string ResourceNamespace = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Load an image defined by a packed string either from the Resource DLL or the Editor/Resources folder
|
||||
/// </summary>
|
||||
/// <param name="packedstring">string formed name (including namespace),width,height. E.g. "Assets/Editor/myIcon,16,16"</param>
|
||||
/// <returns>a Texture</returns>
|
||||
public virtual Texture2D LoadPacked(string packedstring,Assembly assembly=null, string resourcePath="")
|
||||
{
|
||||
if (!string.IsNullOrEmpty(packedstring))
|
||||
{
|
||||
string[] s = packedstring.Split(',');
|
||||
if (s.Length == 3)
|
||||
{
|
||||
try
|
||||
{
|
||||
int w = int.Parse(s[1], System.Globalization.CultureInfo.InvariantCulture);
|
||||
int h = int.Parse(s[2], System.Globalization.CultureInfo.InvariantCulture);
|
||||
return Load(s[0], w, h, assembly, resourcePath);
|
||||
}
|
||||
catch (Exception e) { Debug.LogException(e);}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load an image either from a Resource DLL or the Editor/Resources folder
|
||||
/// </summary>
|
||||
/// <param name="assembly">the resource DLL assembly</param>
|
||||
/// <param name="resourcePath">the resource path(namespace)</param>
|
||||
/// <param name="resourceName">name of the resource file (without extension)</param>
|
||||
/// <param name="width">width of the image (can be omitted when loading from texture file)</param>
|
||||
/// <param name="height">height of the image (can be omitted when loading from texture file)</param>
|
||||
/// <returns>a Texture</returns>
|
||||
public virtual Texture2D Load(string resourceName, int width=0, int height=0,Assembly assembly=null, string resourcePath="")
|
||||
{
|
||||
if (assembly == null)
|
||||
assembly = ResourceDLL;
|
||||
if (string.IsNullOrEmpty(resourcePath))
|
||||
resourcePath = ResourceNamespace;
|
||||
|
||||
Texture2D texture = null;
|
||||
if (string.IsNullOrEmpty(System.IO.Path.GetExtension(resourceName)))
|
||||
resourceName += ".png";
|
||||
|
||||
//if (!resourcePath.EndsWith("."))
|
||||
// resourcePath += ".";
|
||||
|
||||
if (assembly != null)
|
||||
{
|
||||
using (System.IO.Stream myStream = assembly.GetManifestResourceStream(resourcePath + resourceName))
|
||||
{
|
||||
if (myStream != null)
|
||||
{
|
||||
texture = new Texture2D(width, height, TextureFormat.ARGB32, false);
|
||||
texture.LoadImage(ReadToEnd(myStream));
|
||||
if (texture != null)
|
||||
{
|
||||
//texture.hideFlags = HideFlags.DontSave;
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try from file sys:
|
||||
texture = (Texture2D)Resources.Load($"Editor/{System.IO.Path.GetFileNameWithoutExtension(resourceName)}");
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
DTLog.LogWarning("Missing resource: " + resourcePath + resourceName);
|
||||
}
|
||||
//else
|
||||
// texture.hideFlags = HideFlags.DontSave;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return texture;
|
||||
|
||||
|
||||
}
|
||||
|
||||
protected static byte[] ReadToEnd(System.IO.Stream stream)
|
||||
{
|
||||
long originalPosition = stream.Position;
|
||||
stream.Position = 0;
|
||||
|
||||
try
|
||||
{
|
||||
byte[] readBuffer = new byte[4096];
|
||||
|
||||
int totalBytesRead = 0;
|
||||
int bytesRead;
|
||||
|
||||
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
|
||||
{
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if (totalBytesRead == readBuffer.Length)
|
||||
{
|
||||
int nextByte = stream.ReadByte();
|
||||
if (nextByte != -1)
|
||||
{
|
||||
byte[] temp = new byte[readBuffer.Length * 2];
|
||||
System.Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
|
||||
System.Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
|
||||
readBuffer = temp;
|
||||
totalBytesRead++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] buffer = readBuffer;
|
||||
if (readBuffer.Length != totalBytesRead)
|
||||
{
|
||||
buffer = new byte[totalBytesRead];
|
||||
System.Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
finally
|
||||
{
|
||||
stream.Position = originalPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public static Assembly FindResourceDLL(string name)
|
||||
{
|
||||
//BUG will return the wrong dll if there is another dll that starts with the same name
|
||||
return TypeExt.GetLoadedAssemblies().FirstOrDefault(asm => asm.FullName.StartsWith(name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e7542226fdb3e8458d6dd939f67c79e
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,136 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using System;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
[Obsolete("Now that SceneView has a OnSceneGUI method, there is seemingly no need to use DTSceneView which implements its own OnSceneGUI. It might even conflict with SceneView.OnSceneGUI")]
|
||||
#endif
|
||||
public class DTSceneView : SceneView
|
||||
{
|
||||
|
||||
#region ### Serialized fields ###
|
||||
#endregion
|
||||
|
||||
#region ### Public Properties ###
|
||||
|
||||
public bool In2DMode
|
||||
{
|
||||
get { return in2DMode; }
|
||||
set
|
||||
{
|
||||
in2DMode = value;
|
||||
}
|
||||
}
|
||||
|
||||
public SceneViewState State
|
||||
{
|
||||
get
|
||||
{
|
||||
return mStateField.GetValue(this) as SceneViewState;
|
||||
}
|
||||
set
|
||||
{
|
||||
mStateField.SetValue(this, value);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Privates Fields ###
|
||||
|
||||
FieldInfo mStateField;
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Unity Callbacks ###
|
||||
/*! \cond UNITY */
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
getInternals();
|
||||
SceneView.
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
duringSceneGui
|
||||
#else
|
||||
onSceneGUIDelegate
|
||||
#endif
|
||||
+= onScene;
|
||||
}
|
||||
|
||||
public override void OnDisable()
|
||||
{
|
||||
SceneView.
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
duringSceneGui
|
||||
#else
|
||||
onSceneGUIDelegate
|
||||
#endif
|
||||
-= onScene;
|
||||
base.OnDisable();
|
||||
}
|
||||
|
||||
/*! \endcond */
|
||||
#endregion
|
||||
|
||||
#region ### Public Static Methods ###
|
||||
#endregion
|
||||
|
||||
#region ### Public Methods ###
|
||||
|
||||
#if UNITY_2021_2_OR_NEWER
|
||||
protected new virtual void OnSceneGUI()
|
||||
#else
|
||||
protected virtual void OnSceneGUI()
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ### Privates & Internals ###
|
||||
/*! \cond PRIVATE */
|
||||
|
||||
void onScene(SceneView view)
|
||||
{
|
||||
if (EditorApplication.isCompiling)
|
||||
{
|
||||
SceneView.
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
duringSceneGui
|
||||
#else
|
||||
onSceneGUIDelegate
|
||||
#endif
|
||||
-= onScene;
|
||||
Close();
|
||||
GUIUtility.ExitGUI();
|
||||
}
|
||||
if (view == this)
|
||||
OnSceneGUI();
|
||||
}
|
||||
|
||||
void getInternals()
|
||||
{
|
||||
mStateField = GetType().FieldByName("m_SceneViewState", true, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! \endcond */
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97953e54a2c2482449099d6d8dfb2de4
|
||||
timeCreated: 1468747688
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,207 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTSelection
|
||||
{
|
||||
static GameObject mCurrentGameObject;
|
||||
static int mCurrentGameObjectsCount;
|
||||
|
||||
public static event DT.Callback OnSelectionChange;
|
||||
|
||||
internal static bool MuteEvents { get; set; }
|
||||
|
||||
public static int Count
|
||||
{
|
||||
get { return mCurrentGameObjectsCount; }
|
||||
}
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
EditorApplication.update -= checkSelection;
|
||||
//TODO listen on Selection.selectionChanged instead?
|
||||
EditorApplication.update += checkSelection;
|
||||
|
||||
}
|
||||
|
||||
public static bool HasComponent<T>(bool allowMultiSelection = false) where T : Component
|
||||
{
|
||||
if (allowMultiSelection)
|
||||
return mCurrentGameObjectsCount >= 1 && getSelection<T>().Count > 0;
|
||||
else
|
||||
return mCurrentGameObjectsCount == 1 && mCurrentGameObject != null && mCurrentGameObject.GetComponent(typeof(T)) != null;
|
||||
}
|
||||
|
||||
public static bool HasComponent<T, T1>(bool allowMultiSelection = false) where T : Component where T1 : Component
|
||||
{
|
||||
return HasComponent<T>(allowMultiSelection) ||
|
||||
HasComponent<T1>(allowMultiSelection);
|
||||
}
|
||||
|
||||
public static bool HasComponent<T, T1, T2>(bool allowMultiSelection = false)
|
||||
where T : Component
|
||||
where T1 : Component
|
||||
where T2 : Component
|
||||
{
|
||||
return HasComponent<T>(allowMultiSelection) ||
|
||||
HasComponent<T1>(allowMultiSelection) ||
|
||||
HasComponent<T2>(allowMultiSelection);
|
||||
}
|
||||
|
||||
public static bool HasComponent<T, T1, T2, T3>(bool allowMultiSelection = false)
|
||||
where T : Component
|
||||
where T1 : Component
|
||||
where T2 : Component
|
||||
where T3 : Component
|
||||
{
|
||||
return HasComponent<T>(allowMultiSelection) ||
|
||||
HasComponent<T1>(allowMultiSelection) ||
|
||||
HasComponent<T2>(allowMultiSelection) ||
|
||||
HasComponent<T3>(allowMultiSelection);
|
||||
}
|
||||
|
||||
public static bool HasComponent<T, T1, T2, T3, T4>(bool allowMultiSelection = false)
|
||||
where T : Component
|
||||
where T1 : Component
|
||||
where T2 : Component
|
||||
where T3 : Component
|
||||
where T4 : Component
|
||||
{
|
||||
return HasComponent<T>(allowMultiSelection) ||
|
||||
HasComponent<T1>(allowMultiSelection) ||
|
||||
HasComponent<T2>(allowMultiSelection) ||
|
||||
HasComponent<T3>(allowMultiSelection) ||
|
||||
HasComponent<T4>(allowMultiSelection);
|
||||
}
|
||||
|
||||
public static T GetAs<T>(bool allowMultiSelection = false) where T : Component
|
||||
{
|
||||
List<T> ar = getSelection<T>();
|
||||
if (ar.Count == 1 || (ar.Count > 0 && allowMultiSelection))
|
||||
return (T)ar[0];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<T> GetAllAs<T>() where T : Component
|
||||
{
|
||||
return getSelection<T>();
|
||||
}
|
||||
|
||||
public static GameObject GetGameObject(bool allowMultiSelection = false)
|
||||
{
|
||||
GameObject[] selectedGameObjects = Selection.gameObjects;
|
||||
if (selectedGameObjects.Length == 1 || (selectedGameObjects.Length > 1 && allowMultiSelection))
|
||||
return Selection.activeGameObject;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Vector3 GetPosition()
|
||||
{
|
||||
Vector3 p = Vector3.zero;
|
||||
List<Transform> tt = DTSelection.GetAllAs<Transform>();
|
||||
if (tt.Count > 0)
|
||||
{
|
||||
for (int i = 0; i < tt.Count; i++)
|
||||
p += tt[i].position;
|
||||
|
||||
p /= tt.Count;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static List<T> getSelection<T>() where T : Component
|
||||
{
|
||||
Object[] S = Selection.GetFiltered(typeof(T), SelectionMode.TopLevel | SelectionMode.ExcludePrefab | SelectionMode.Editable);
|
||||
List<T> L = new List<T>();
|
||||
foreach (Object O in S)
|
||||
if (O != null)
|
||||
{
|
||||
if (Selection.activeTransform == ((Component)O).transform)
|
||||
L.Insert(0, (T)O);
|
||||
else
|
||||
L.Add((T)O);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
Selection.objects = new Object[0];
|
||||
mCurrentGameObject = null;
|
||||
mCurrentGameObjectsCount = 0;
|
||||
raiseOnSelectionChange();
|
||||
}
|
||||
|
||||
public static void Set(params Object[] objects)
|
||||
{
|
||||
if (objects.Length > 0)
|
||||
{
|
||||
Selection.objects = objects;
|
||||
mCurrentGameObject = Selection.activeGameObject;
|
||||
mCurrentGameObjectsCount = Selection.gameObjects.Length;
|
||||
raiseOnSelectionChange();
|
||||
}
|
||||
else
|
||||
Clear();
|
||||
}
|
||||
|
||||
public static void AddGameObjects(params Component[] objects)
|
||||
{
|
||||
List<GameObject> gl = new List<GameObject>(objects.Length);
|
||||
foreach (Component c in objects)
|
||||
if (c != null && c.gameObject != null && !gl.Contains(c.gameObject) && !Selection.Contains(c))
|
||||
gl.Add(c.gameObject);
|
||||
if (gl.Count > 0)
|
||||
{
|
||||
GameObject[] cur = Selection.gameObjects;
|
||||
Selection.objects = cur.AddRange(gl.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetGameObjects(params Component[] objects)
|
||||
{
|
||||
if (objects.Length > 0)
|
||||
{
|
||||
List<GameObject> gl = new List<GameObject>(objects.Length);
|
||||
foreach (Component c in objects)
|
||||
if (c != null && c.gameObject != null && !gl.Contains(c.gameObject))
|
||||
gl.Add(c.gameObject);
|
||||
Selection.objects = gl.ToArray();
|
||||
mCurrentGameObject = Selection.activeGameObject;
|
||||
mCurrentGameObjectsCount = Selection.gameObjects.Length;
|
||||
raiseOnSelectionChange();
|
||||
}
|
||||
else
|
||||
Clear();
|
||||
}
|
||||
|
||||
static void checkSelection()
|
||||
{
|
||||
GameObject[] selectedGameObjects = Selection.gameObjects;
|
||||
if (!MuteEvents && Selection.activeGameObject != mCurrentGameObject || mCurrentGameObjectsCount != selectedGameObjects.Length)
|
||||
{
|
||||
mCurrentGameObject = Selection.activeGameObject;
|
||||
mCurrentGameObjectsCount = selectedGameObjects.Length;
|
||||
raiseOnSelectionChange();
|
||||
}
|
||||
}
|
||||
|
||||
static void raiseOnSelectionChange()
|
||||
{
|
||||
if (OnSelectionChange != null)
|
||||
OnSelectionChange();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb2b7daa166196847886162a590328f2
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,151 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using FluffyUnderware.DevToolsEditor.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public class DTStatusbar
|
||||
{
|
||||
string mSourceIdent;
|
||||
string mStatusBarMessage;
|
||||
MessageType mStatusBarMessageType;
|
||||
float mStatusBarMessageDelay;
|
||||
double mStatusBarMessageLastTime;
|
||||
|
||||
GUIStyle mMessageStyle;
|
||||
public GUIStyle MessageStyle
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mMessageStyle == null)
|
||||
{
|
||||
mMessageStyle = GetStyle();
|
||||
}
|
||||
return mMessageStyle;
|
||||
}
|
||||
set
|
||||
{
|
||||
mMessageStyle = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Visible = true;
|
||||
|
||||
|
||||
protected virtual GUIStyle GetStyle()
|
||||
{
|
||||
GUIStyle st=new GUIStyle(GUI.skin.GetStyle("HelpBox"));
|
||||
st.richText = true;
|
||||
return st;
|
||||
}
|
||||
|
||||
protected virtual void GetColors()
|
||||
{
|
||||
switch (mStatusBarMessageType)
|
||||
{
|
||||
case MessageType.Error:
|
||||
GUI.contentColor = Color.white;
|
||||
GUI.backgroundColor = Color.red.SkinAwareColor();
|
||||
break;
|
||||
case MessageType.Warning:
|
||||
GUI.contentColor = Color.white;
|
||||
GUI.backgroundColor = Color.yellow.SkinAwareColor();
|
||||
break;
|
||||
case MessageType.Info:
|
||||
GUI.contentColor = Color.yellow;
|
||||
break;
|
||||
default:
|
||||
GUI.contentColor = Color.white;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void Clear(string source="")
|
||||
{
|
||||
if (source == mSourceIdent || string.IsNullOrEmpty(mSourceIdent))
|
||||
{
|
||||
mStatusBarMessage = "";
|
||||
mStatusBarMessageType = MessageType.None;
|
||||
mStatusBarMessageDelay = 0;
|
||||
mStatusBarMessageLastTime = EditorApplication.timeSinceStartup;
|
||||
mSourceIdent = "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Set(string text, string source="",float delay = 0)
|
||||
{
|
||||
SetMessage(text, source,MessageType.None, delay);
|
||||
}
|
||||
|
||||
public void SetError(string text, string source = "", float delay = 0)
|
||||
{
|
||||
SetMessage(text,source, MessageType.Error, delay);
|
||||
}
|
||||
|
||||
public void SetInfo(string text, string source = "", float delay = 0)
|
||||
{
|
||||
SetMessage(text, source,MessageType.Info, delay);
|
||||
}
|
||||
|
||||
public void SetWarning(string text, string source = "", float delay = 0)
|
||||
{
|
||||
SetMessage(text, source,MessageType.Warning, delay);
|
||||
}
|
||||
|
||||
public void SetMessage(string msg = "", string source="",MessageType type = MessageType.None, float delay = 0)
|
||||
{
|
||||
mStatusBarMessage = msg;
|
||||
mStatusBarMessageType = type;
|
||||
mStatusBarMessageDelay = delay;
|
||||
mStatusBarMessageLastTime = EditorApplication.timeSinceStartup;
|
||||
mSourceIdent = source;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the StatusBar
|
||||
/// </summary>
|
||||
/// <param name="renderingZone">the rect to use</param>
|
||||
/// <param name="style">(Optional) A style to override the default style</param>
|
||||
/// <param name="fit">Allow the <paramref name="renderingZone"/> to be modified to fit the text to render</param>
|
||||
/// <returns>true if you should do a repaint (i.e. a color fade is in progress)</returns>
|
||||
public bool Render(Rect renderingZone, GUIStyle style=null, bool fit=false)
|
||||
{
|
||||
if (!Visible || string.IsNullOrEmpty(mStatusBarMessage))
|
||||
return false;
|
||||
|
||||
Color c = GUI.contentColor;
|
||||
//Color bc = GUI.backgroundColor;
|
||||
GetColors();
|
||||
GUI.contentColor = GUI.contentColor.Fade(mStatusBarMessageLastTime, mStatusBarMessageDelay, 1);
|
||||
bool repaint = (EditorApplication.timeSinceStartup-mStatusBarMessageDelay<mStatusBarMessageLastTime);
|
||||
if (fit)
|
||||
{
|
||||
Vector2 calcSize = EditorStyles.label.CalcSize(new GUIContent(mStatusBarMessage));
|
||||
float w=calcSize.x + 20;
|
||||
renderingZone.x += (renderingZone.width - w) / 2;
|
||||
renderingZone.width = w;
|
||||
float h = calcSize.y;
|
||||
renderingZone.y += renderingZone.height - h;
|
||||
renderingZone.height = h;
|
||||
}
|
||||
|
||||
EditorGUI.LabelField(renderingZone, mStatusBarMessage, style==null ? MessageStyle:style);
|
||||
GUI.contentColor = c;
|
||||
//GUI.backgroundColor = bc;
|
||||
return repaint;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e51fd46e878d264ead8664f69a93db9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,124 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using FluffyUnderware.DevToolsEditor.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTStyles
|
||||
{
|
||||
|
||||
public static GUIStyle BackdropHtmlLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mBackdropHtmlLabel == null)
|
||||
{
|
||||
mBackdropHtmlLabel = new GUIStyle();//GUI.skin.GetStyle("HelpBox"));
|
||||
mBackdropHtmlLabel.alignment = TextAnchor.MiddleCenter;
|
||||
Texture2D bgTex = new Texture2D(1, 1);
|
||||
bgTex.SetPixel(0, 0, new Color(1, 1, 1, 0.3f));
|
||||
bgTex.Apply();
|
||||
bgTex.hideFlags = HideFlags.DontSave;
|
||||
mBackdropHtmlLabel.padding = new RectOffset(1, 2, 0, 0);
|
||||
mBackdropHtmlLabel.normal.background = bgTex;
|
||||
mBackdropHtmlLabel.richText = true;
|
||||
}
|
||||
return mBackdropHtmlLabel;
|
||||
}
|
||||
}
|
||||
static GUIStyle mBackdropHtmlLabel;
|
||||
|
||||
static GUIStyle mTBButton;
|
||||
public static GUIStyle TBButton
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mTBButton == null)
|
||||
{
|
||||
mTBButton = new GUIStyle(GUI.skin.button);
|
||||
mTBButton.alignment = TextAnchor.MiddleLeft;
|
||||
mTBButton.padding.top = 4;
|
||||
mTBButton.padding.bottom = 2;
|
||||
}
|
||||
|
||||
return mTBButton;
|
||||
}
|
||||
}
|
||||
|
||||
static GUIStyle mTBBackground;
|
||||
public static GUIStyle TBBackground
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mTBBackground == null)
|
||||
{
|
||||
mTBBackground = new GUIStyle(GUI.skin.textArea);
|
||||
}
|
||||
return mTBBackground;
|
||||
}
|
||||
}
|
||||
|
||||
static GUIStyle mHtmlHelpBox;
|
||||
public static GUIStyle HtmlHelpBox
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mHtmlHelpBox == null)
|
||||
{
|
||||
mHtmlHelpBox= new GUIStyle(GUI.skin.GetStyle("HelpBox"));
|
||||
mHtmlHelpBox.richText = true;
|
||||
return mHtmlHelpBox;
|
||||
}
|
||||
return mHtmlHelpBox;
|
||||
}
|
||||
}
|
||||
|
||||
static GUIStyle mHtmlLabel;
|
||||
public static GUIStyle HtmlLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mHtmlLabel == null)
|
||||
{
|
||||
mHtmlLabel = new GUIStyle(GUI.skin.label);
|
||||
mHtmlLabel.richText = true;
|
||||
return mHtmlLabel;
|
||||
}
|
||||
return mHtmlLabel;
|
||||
}
|
||||
}
|
||||
|
||||
static GUIStyle mHtmlLinkLabel;
|
||||
public static GUIStyle HtmlLinkLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mHtmlLinkLabel == null)
|
||||
{
|
||||
mHtmlLinkLabel = new GUIStyle(EditorStyles.label);
|
||||
mHtmlLinkLabel.richText = true;
|
||||
int h = (int)mHtmlLinkLabel.lineHeight;
|
||||
mHtmlLinkLabel.normal.background = new Texture2D(1, h);
|
||||
Color lineCol = new Color(.44f, .57f, .79f);
|
||||
for (int i = 0; i < h; i++)
|
||||
mHtmlLinkLabel.normal.background.SetPixel(0, i, (i==3) ? lineCol : new Color(0,0,0,0));
|
||||
|
||||
mHtmlLinkLabel.normal.background.Apply();
|
||||
|
||||
mHtmlLinkLabel.hover.background = mHtmlLinkLabel.normal.background;
|
||||
mHtmlLinkLabel.hover.textColor = new Color(.44f, .57f, .79f);
|
||||
return mHtmlLinkLabel;
|
||||
}
|
||||
return mHtmlLinkLabel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7eda549546892154681be2d439a3445b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,412 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
public static class DTToolbar
|
||||
{
|
||||
const int ITEMSPACE = 10;
|
||||
const int PROJECTSPACE = 20;
|
||||
|
||||
public static bool RecalcItemSize = true;
|
||||
public static bool MouseOverToolbarElement { get; private set; }
|
||||
|
||||
static Vector2[] _MaxItemDimension = new Vector2[4]; // each Orientation has it's own max. Dimension
|
||||
/// <summary>
|
||||
/// Starting position for each side
|
||||
/// </summary>
|
||||
static Rect[] _InitialRects;
|
||||
/// <summary>
|
||||
/// Last item position for each side
|
||||
/// </summary>
|
||||
static Rect[] _ItemRect;
|
||||
|
||||
static Event _handleEvent;
|
||||
|
||||
internal static void Initialize()
|
||||
{
|
||||
RecalcItemSize = true;
|
||||
_handleEvent = null;
|
||||
loadItems();
|
||||
DTSelection.OnSelectionChange -= OnSelectionChange;
|
||||
DTSelection.OnSelectionChange += OnSelectionChange;
|
||||
SceneView.
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
duringSceneGui
|
||||
#else
|
||||
onSceneGUIDelegate
|
||||
#endif
|
||||
-= Render;
|
||||
SceneView.
|
||||
#if UNITY_2019_1_OR_NEWER
|
||||
duringSceneGui
|
||||
#else
|
||||
onSceneGUIDelegate
|
||||
#endif
|
||||
+= Render;
|
||||
EditorApplication.hierarchyWindowItemOnGUI -= onHierarchy;
|
||||
EditorApplication.hierarchyWindowItemOnGUI += onHierarchy;
|
||||
EditorApplication.update -= onUpdate;
|
||||
EditorApplication.update += onUpdate;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void loadItems()
|
||||
{
|
||||
foreach (DTProject prj in DT.Projects)
|
||||
prj.ToolbarItems.Clear();
|
||||
|
||||
TypeCache.TypeCollection toolbarItemTypes = TypeCache.GetTypesDerivedFrom(typeof(DTToolbarItem));
|
||||
toolbarItemTypes.Intersect(TypeCache.GetTypesWithAttribute<ToolbarItemAttribute>()).ForEach(type => Activator.CreateInstance(type));
|
||||
|
||||
foreach (DTProject prj in DT.Projects)
|
||||
prj.ToolbarItems.Sort();
|
||||
}
|
||||
|
||||
static void OnSelectionChange()
|
||||
{
|
||||
foreach (DTProject project in DT.Projects)
|
||||
foreach (DTToolbarItem item in project.ToolbarItems)
|
||||
item.OnSelectionChange();
|
||||
|
||||
RecalcItemSize = true;
|
||||
}
|
||||
|
||||
static void onHierarchy(int instanceID, Rect selectionRect)
|
||||
{
|
||||
if (Selection.instanceIDs.Contains(instanceID))
|
||||
_handleEvent = new Event(Event.current);
|
||||
}
|
||||
|
||||
static void onUpdate()
|
||||
{
|
||||
if (Event.current != null || _handleEvent != null)
|
||||
{
|
||||
DTSelection.MuteEvents = true;
|
||||
foreach (DTProject project in DT.Projects)
|
||||
foreach (DTToolbarItem item in project.ToolbarItems)
|
||||
{
|
||||
if (_handleEvent != null && item.Visible && item.Enabled)
|
||||
item.HandleEvents(_handleEvent);
|
||||
if (Event.current != null && item.Visible && item.Enabled)
|
||||
item.HandleEvents(Event.current);
|
||||
}
|
||||
DTSelection.MuteEvents = false;
|
||||
}
|
||||
_handleEvent = null;
|
||||
}
|
||||
|
||||
|
||||
static Rect getStatusBarRect()
|
||||
{
|
||||
Vector2 v = GUIUtility.GUIToScreenPoint(Vector2.zero);
|
||||
Rect r = SceneView.currentDrawingSceneView.position;
|
||||
// If SceneView is on another monitor, r.x doesn't start at 0, but GUIToScreenPoint gives the offset
|
||||
r.x -= v.x;
|
||||
r.y = _ItemRect[(int)DTToolbarOrientation.Bottom].y - 25;
|
||||
r.height = 20;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static bool detailOpen;
|
||||
|
||||
static void Render(SceneView view)
|
||||
{
|
||||
Event ev = Event.current;
|
||||
MouseOverToolbarElement = false;
|
||||
// Only let certain hotkeys pass
|
||||
if (!EditorGUIUtility.editingTextField)
|
||||
{
|
||||
GUIUtility.keyboardControl = GUIUtility.GetControlID(FocusType.Passive);
|
||||
}
|
||||
|
||||
List<DTProject> projects = DT.Projects;
|
||||
projects.Sort();
|
||||
|
||||
Handles.BeginGUI();
|
||||
GUI.skin = null; // to ensure light-skin is used if set in preferences (or not Pro)
|
||||
// Get largest item for each side
|
||||
if (RecalcItemSize)
|
||||
calcMaxItemDimension();
|
||||
|
||||
// Get starting position for each side
|
||||
_InitialRects = getInitialItemRect();
|
||||
_ItemRect = new Rect[4];
|
||||
_InitialRects.CopyTo(_ItemRect, 0);
|
||||
|
||||
DTToolbarItem lastItem = null;
|
||||
DTSelection.MuteEvents = true;
|
||||
|
||||
DTToolbarItem hovering = null;
|
||||
|
||||
foreach (DTProject project in projects)
|
||||
{
|
||||
int side = (int)project.ToolbarOrientation;
|
||||
if (lastItem && lastItem.Project.ToolbarOrientation != project.ToolbarOrientation)
|
||||
lastItem = null;
|
||||
List<DTToolbarItem> items = project.ToolbarItems;
|
||||
|
||||
// Render items
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].Visible)
|
||||
{
|
||||
Vector2 itemSize = items[i].GetItemSize(); // size of current item
|
||||
_ItemRect[side] = advanceItemRect(lastItem, items[i], itemSize); // advance by using the last itemRect and the new(current) size
|
||||
|
||||
items[i].mItemRect = _ItemRect[side]; // Store current item rect
|
||||
|
||||
|
||||
if (items[i].Enabled)
|
||||
{
|
||||
Handles.EndGUI();
|
||||
EditorKeyBinding.BindingsEnabled = false;
|
||||
if (items[i].mItemRect.Contains(DTGUI.MousePosition))
|
||||
{
|
||||
hovering = items[i];
|
||||
MouseOverToolbarElement = true;
|
||||
}
|
||||
items[i].OnSceneGUI();
|
||||
|
||||
EditorKeyBinding.BindingsEnabled = true;
|
||||
Handles.BeginGUI();
|
||||
}
|
||||
GUI.enabled = items[i].Enabled;
|
||||
items[i].Render(_ItemRect[side]);
|
||||
|
||||
GUI.enabled = true;
|
||||
if (ev != null && items[i].Enabled && (DTToolbarItem.FocusedItem == null || DTToolbarItem.FocusedItem == items[i]))
|
||||
items[i].HandleEvents(ev);
|
||||
|
||||
lastItem = items[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
DTSelection.MuteEvents = false;
|
||||
detailOpen = false;
|
||||
// Render items client area (Note: itemRect contains the last rendered item, a.k.a. a way to get the number of rows/cols needed
|
||||
foreach (DTProject project in projects)
|
||||
{
|
||||
List<DTToolbarItem> items = project.ToolbarItems;
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
if (items[i].Visible && items[i].ShowClientArea)
|
||||
{
|
||||
detailOpen = true;
|
||||
Rect clientRect = items[i].mItemRect;
|
||||
int side = (int)project.ToolbarOrientation;
|
||||
switch (project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Left:
|
||||
clientRect.x = _ItemRect[side].x + _MaxItemDimension[side].x + 5;
|
||||
break;
|
||||
case DTToolbarOrientation.Right:
|
||||
clientRect.x = _ItemRect[side].x - 5;
|
||||
break;
|
||||
case DTToolbarOrientation.Top:
|
||||
clientRect.y = _ItemRect[side].y + _MaxItemDimension[side].y + 5;
|
||||
break;
|
||||
case DTToolbarOrientation.Bottom:
|
||||
clientRect.y = _ItemRect[side].y - 5;
|
||||
break;
|
||||
}
|
||||
if (clientRect.width > 0 && clientRect.height > 0)
|
||||
{
|
||||
items[i].mBackgroundRects.Clear();
|
||||
MouseOverToolbarElement = MouseOverToolbarElement || clientRect.Contains(DTGUI.MousePosition);
|
||||
EditorKeyBinding.BindingsEnabled = false;
|
||||
items[i].RenderClientArea(clientRect);
|
||||
EditorKeyBinding.BindingsEnabled = true;
|
||||
if (DTGUI.IsClick)
|
||||
foreach (Rect r in items[i].mBackgroundRects)
|
||||
if (r.Contains(ev.mousePosition))
|
||||
DTGUI.UseEvent(items[i].GetHashCode(), ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle statusbar info when hovering over an item
|
||||
if (hovering != null)
|
||||
DTToolbarItem._StatusBar.Set(hovering.StatusBarInfo, "Info");
|
||||
else
|
||||
DTToolbarItem._StatusBar.Clear("Info");
|
||||
|
||||
// Render Statusbar
|
||||
DTToolbarItem._StatusBar.Render(getStatusBarRect(), null, true);
|
||||
|
||||
Handles.EndGUI();
|
||||
|
||||
}
|
||||
|
||||
static Rect[] getInitialItemRect()
|
||||
{
|
||||
Rect[] res = new Rect[4];
|
||||
for (int side = 0; side < 4; side++)
|
||||
{
|
||||
Rect r = new Rect();
|
||||
switch (side)
|
||||
{
|
||||
case (int)DTToolbarOrientation.Left:
|
||||
r.x = 5; r.y = 10;
|
||||
break;
|
||||
case (int)DTToolbarOrientation.Right:
|
||||
r.x = SceneView.currentDrawingSceneView.position.width - 10 - _MaxItemDimension[side].x;
|
||||
r.y = 115;
|
||||
break;
|
||||
case (int)DTToolbarOrientation.Top:
|
||||
float lft = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
|
||||
r.x = 5; r.y = 10;
|
||||
if (lft > 0)
|
||||
r.x += 10 + lft;
|
||||
break;
|
||||
default: // Bottom
|
||||
lft = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
|
||||
r.x = 5; r.y = SceneView.currentDrawingSceneView.position.height - _MaxItemDimension[side].y - 30;
|
||||
if (lft > 0)
|
||||
r.x += 10 + lft;
|
||||
break;
|
||||
}
|
||||
res[side] = r;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static Rect advanceItemRect(DTToolbarItem lastItem, DTToolbarItem newItem, Vector2 newItemSize)
|
||||
{
|
||||
Rect itemRect;
|
||||
float space = 0;
|
||||
int side = (int)newItem.Project.ToolbarOrientation;
|
||||
|
||||
if (lastItem != null)
|
||||
{
|
||||
itemRect = lastItem.mItemRect;
|
||||
if (lastItem.Project != newItem.Project)
|
||||
space = PROJECTSPACE;
|
||||
else
|
||||
space = (newItem.Order - lastItem.Order >= 10) ? ITEMSPACE : 0;
|
||||
}
|
||||
else
|
||||
itemRect = _InitialRects[side];
|
||||
|
||||
switch (newItem.Project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Left:
|
||||
itemRect.y += itemRect.height + 3 + space;
|
||||
if (itemRect.y + newItemSize.y > SceneView.currentDrawingSceneView.position.height - 30)
|
||||
{
|
||||
itemRect.y = 10;
|
||||
itemRect.x += _MaxItemDimension[(int)DTToolbarOrientation.Left].x + 5;
|
||||
}
|
||||
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Left].x;
|
||||
itemRect.height = newItemSize.y;
|
||||
break;
|
||||
case DTToolbarOrientation.Right:
|
||||
itemRect.y += itemRect.height + 3 + space;
|
||||
if (itemRect.y + newItemSize.y > SceneView.currentDrawingSceneView.position.height - 30)
|
||||
{
|
||||
itemRect.y = 10;
|
||||
itemRect.x -= _MaxItemDimension[(int)DTToolbarOrientation.Right].x + 5;
|
||||
}
|
||||
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Right].x;
|
||||
itemRect.height = newItemSize.y;
|
||||
break;
|
||||
case DTToolbarOrientation.Top:
|
||||
itemRect.x += 3 + itemRect.width + space;
|
||||
if (itemRect.x + newItemSize.x > SceneView.currentDrawingSceneView.position.width - _MaxItemDimension[(int)DTToolbarOrientation.Left].x - _MaxItemDimension[(int)DTToolbarOrientation.Right].x)
|
||||
{
|
||||
itemRect.x = _InitialRects[(int)DTToolbarOrientation.Top].x;
|
||||
itemRect.y += _MaxItemDimension[(int)DTToolbarOrientation.Top].y + 5;
|
||||
}
|
||||
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Top].x;
|
||||
itemRect.height = newItemSize.y;
|
||||
break;
|
||||
case DTToolbarOrientation.Bottom:
|
||||
itemRect.x += 3 + itemRect.width + space;
|
||||
if (itemRect.x + newItemSize.x > SceneView.currentDrawingSceneView.position.width - _MaxItemDimension[(int)DTToolbarOrientation.Left].x - _MaxItemDimension[(int)DTToolbarOrientation.Right].x)
|
||||
{
|
||||
itemRect.x = _InitialRects[(int)DTToolbarOrientation.Top].x;
|
||||
itemRect.y -= _MaxItemDimension[(int)DTToolbarOrientation.Bottom].y + 5;
|
||||
}
|
||||
itemRect.width = _MaxItemDimension[(int)DTToolbarOrientation.Bottom].x;
|
||||
itemRect.height = newItemSize.y;
|
||||
break;
|
||||
|
||||
}
|
||||
return itemRect;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For each side, find the item with the largest dimensions
|
||||
/// </summary>
|
||||
static void calcMaxItemDimension()
|
||||
{
|
||||
RecalcItemSize = false;
|
||||
_MaxItemDimension = new Vector2[4];
|
||||
int side;
|
||||
foreach (DTProject prj in DT.Projects)
|
||||
{
|
||||
side = (int)prj.ToolbarOrientation;
|
||||
foreach (DTToolbarItem item in prj.ToolbarItems)
|
||||
if (item.Visible)
|
||||
_MaxItemDimension[side] = Vector2.Max(_MaxItemDimension[side], item.GetItemSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static void SetRadioGroupState(DTToolbarRadioButton active)
|
||||
{
|
||||
active.Project.SetRadioGroupState(active);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DTToolbarMode : int
|
||||
{
|
||||
Text = 1,
|
||||
Icon = 2,
|
||||
Full = 15,
|
||||
}
|
||||
|
||||
public enum DTToolbarOrientation : int
|
||||
{
|
||||
Left = 0,
|
||||
Right = 1,
|
||||
Top = 2,
|
||||
Bottom = 3
|
||||
}
|
||||
|
||||
public class DTToolbarStatus : DTStatusbar
|
||||
{
|
||||
|
||||
protected override void GetColors()
|
||||
{
|
||||
GUI.contentColor = new Color(0, 0, 0, 0.75f);
|
||||
}
|
||||
protected override GUIStyle GetStyle()
|
||||
{
|
||||
GUIStyle style = base.GetStyle();
|
||||
style.alignment = TextAnchor.MiddleCenter;
|
||||
Texture2D bgTex = new Texture2D(1, 1);
|
||||
bgTex.SetPixel(0, 0, new Color(1, 1, 1, 0.5f));
|
||||
bgTex.Apply();
|
||||
bgTex.hideFlags = HideFlags.DontSave;
|
||||
style.normal.background = bgTex;
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5d3bf3b884967a3469804e83d059b658
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,365 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System;
|
||||
using FluffyUnderware.DevTools;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
|
||||
public abstract class DTToolbarItem : IComparable
|
||||
{
|
||||
/// <summary>
|
||||
/// If set, disables event processing for all other items.
|
||||
/// </summary>
|
||||
public static DTToolbarItem FocusedItem { get; set; }
|
||||
public Texture2D Icon
|
||||
{
|
||||
get
|
||||
{
|
||||
if (mIcon==null && !string.IsNullOrEmpty(mIconPackedString) && Project.Resource!=null)
|
||||
mIcon = Project.Resource.LoadPacked(mIconPackedString);
|
||||
return mIcon;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (mIcon != value)
|
||||
mIcon = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Label { get; set; }
|
||||
public string Tooltip { get; set; }
|
||||
public DTProject Project { get; set; }
|
||||
public int Order { get; set; }
|
||||
|
||||
public static DTToolbarStatus _StatusBar = new DTToolbarStatus();
|
||||
|
||||
public List<EditorKeyBinding> KeyBindings
|
||||
{
|
||||
get { return mKeyBindings;}
|
||||
}
|
||||
|
||||
public GUIStyle Style
|
||||
{
|
||||
get { return DTStyles.TBButton; }
|
||||
}
|
||||
|
||||
public GUIContent Content
|
||||
{
|
||||
get
|
||||
{
|
||||
string keyTT = (KeyBindings.Count>0) ? KeyBindings[0].ToTooltipString() : string.Empty;
|
||||
|
||||
switch (Project.ToolbarMode)
|
||||
{
|
||||
case DTToolbarMode.Icon:
|
||||
if (Icon != null)
|
||||
return new GUIContent(Icon, Tooltip + keyTT);
|
||||
else // fallback to "Text"
|
||||
return new GUIContent(Label, Tooltip + keyTT);
|
||||
case DTToolbarMode.Text:
|
||||
return new GUIContent(Label, Tooltip+keyTT);
|
||||
default:
|
||||
return new GUIContent(Label, Icon, Tooltip+keyTT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Visible
|
||||
{
|
||||
get { return mVisible; }
|
||||
set
|
||||
{
|
||||
if (mVisible != value)
|
||||
mVisible=value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
get { return mEnabled; }
|
||||
set
|
||||
{
|
||||
if (mEnabled != value)
|
||||
mEnabled = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool ShowClientArea
|
||||
{
|
||||
get { return mShowClientArea; }
|
||||
set
|
||||
{
|
||||
if (mShowClientArea != value)
|
||||
mShowClientArea = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsMouseOver
|
||||
{
|
||||
get
|
||||
{
|
||||
return mItemRect.Contains(DTGUI.MousePosition);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string StatusBarInfo
|
||||
{
|
||||
get { return string.Empty; }
|
||||
}
|
||||
|
||||
bool mVisible = true;
|
||||
bool mEnabled = true;
|
||||
bool mShowClientArea;
|
||||
string mIconPackedString;
|
||||
Texture2D mIcon;
|
||||
|
||||
List<EditorKeyBinding> mKeyBindings = new List<EditorKeyBinding>();
|
||||
|
||||
static DTToolbarItem _lastClickedItem;
|
||||
|
||||
internal Rect mItemRect;
|
||||
internal List<Rect> mBackgroundRects = new List<Rect>();
|
||||
|
||||
protected DTToolbarItem()
|
||||
{
|
||||
object[] attribs = this.GetType().GetCustomAttributes(typeof(ToolbarItemAttribute), true);
|
||||
if (attribs.Length > 0)
|
||||
{
|
||||
ToolbarItemAttribute a=(ToolbarItemAttribute)attribs[0];
|
||||
Project = DT.Project(a.Project);
|
||||
Project.ToolbarItems.Add(this);
|
||||
Label = a.Label;
|
||||
Tooltip = a.Tooltip;
|
||||
if (!string.IsNullOrEmpty(a.Icon))
|
||||
{
|
||||
mIconPackedString = (!EditorGUIUtility.isProSkin && !string.IsNullOrEmpty(a.IconLightSkin)) ? a.IconLightSkin : a.Icon;
|
||||
}
|
||||
Order = a.Order;
|
||||
OnSelectionChange();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region ### Events ###
|
||||
|
||||
public virtual void OnClick()
|
||||
{
|
||||
if (_lastClickedItem!=null && _lastClickedItem!=this)
|
||||
_lastClickedItem.OnOtherItemClicked(this);
|
||||
_lastClickedItem = this;
|
||||
}
|
||||
|
||||
public virtual void OnSelectionChange()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual void OnOtherItemClicked(DTToolbarItem other)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Renders the item (button etc.) itself inside a Handles.BeginGUI()/Handles.EndGUI() block
|
||||
/// </summary>
|
||||
/// <param name="r">area to use</param>
|
||||
public virtual void Render(Rect r)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders an item's client area (if ShowClientArea==true) inside a Handles.BeginGUI()/Handles.EndGUI() block
|
||||
/// </summary>
|
||||
/// <param name="r">initial Rect with x/y set to starting position</param>
|
||||
public virtual void RenderClientArea(Rect r)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regular OnSceneGUI call. Place preview etc. here
|
||||
/// </summary>
|
||||
public virtual void OnSceneGUI()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the size of the item
|
||||
/// </summary>
|
||||
/// <returns>the size the item needs</returns>
|
||||
public virtual Vector2 GetItemSize()
|
||||
{
|
||||
return Style.CalcSize(Content);
|
||||
}
|
||||
|
||||
public virtual void HandleEvents(Event e)
|
||||
{
|
||||
if (KeyBindings.Count > 0 && KeyBindings[0].IsTriggered(e))
|
||||
OnClick();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region ### GUI Orientation Helpers ###
|
||||
|
||||
/// <summary>
|
||||
/// Changes the rect to properly place an GUI control with a certain width and height.
|
||||
/// </summary>
|
||||
/// <remarks>Call this before the first GUI control, but after Background()</remarks>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
protected void SetElementSize(ref Rect r, float width, float height)
|
||||
{
|
||||
r.width = width;
|
||||
r.height = height;
|
||||
switch (Project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Right:
|
||||
r.x -= r.width;
|
||||
break;
|
||||
case DTToolbarOrientation.Bottom:
|
||||
r.y-=r.height;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
//SmallLineAdjust(ref r,height);
|
||||
Rect scene=SceneView.currentDrawingSceneView.position;
|
||||
|
||||
if (r.xMax > scene.width)
|
||||
r.xMin = scene.width - r.width;
|
||||
if (r.yMax > scene.height)
|
||||
r.yMin = scene.height - r.height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Draws a background, taking only position from the rect
|
||||
/// </summary>
|
||||
/// <param name="r"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <param name="guiStyle"></param>
|
||||
protected void Background(Rect r, float width, float height,GUIStyle guiStyle=null)
|
||||
{
|
||||
mBackgroundRects.Add(r);
|
||||
if (guiStyle == null)
|
||||
guiStyle = DTStyles.TBBackground;
|
||||
r.y -= 1;
|
||||
r.x -= 1;
|
||||
r.width = width + 2;
|
||||
r.height = height + 2;
|
||||
switch (Project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Right:
|
||||
r.x-=width;
|
||||
break;
|
||||
case DTToolbarOrientation.Bottom:
|
||||
r.y -= height;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
GUI.Box(r, "", guiStyle);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advance the rect to a new line, respecting Toolbar Orientation
|
||||
/// </summary>
|
||||
protected void Advance(ref Rect r) { Advance(ref r, r.height); }
|
||||
/// <summary>
|
||||
/// Advance the rect to a new line, respecting Toolbar Orientation
|
||||
/// </summary>
|
||||
protected void Advance(ref Rect r, float lastLineHeight)
|
||||
{
|
||||
switch (Project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Bottom:
|
||||
r.y -= lastLineHeight + 5;
|
||||
break;
|
||||
case DTToolbarOrientation.Left:
|
||||
r.x += r.width + 2;
|
||||
break;
|
||||
case DTToolbarOrientation.Right:
|
||||
r.x -= r.width + 2;
|
||||
break;
|
||||
default: // Top
|
||||
r.y += lastLineHeight + 5;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advance the rect to a new line below (or above) the existing rect, depending on Toolbar Orientation
|
||||
/// </summary>
|
||||
protected void AdvanceBelow(ref Rect r) { AdvanceBelow(ref r, r.height); }
|
||||
/// <summary>
|
||||
/// Advance the rect to a new line below (or above) the existing rect, depending on Toolbar Orientation
|
||||
/// </summary>
|
||||
protected void AdvanceBelow(ref Rect r, float lastLineHeight)
|
||||
{
|
||||
switch (Project.ToolbarOrientation)
|
||||
{
|
||||
case DTToolbarOrientation.Bottom:
|
||||
r.y -= lastLineHeight;
|
||||
break;
|
||||
default:
|
||||
r.y += lastLineHeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
DTToolbarItem other = (DTToolbarItem)obj;
|
||||
|
||||
if (Order == other.Order)
|
||||
return Label.CompareTo(other.Label);
|
||||
else
|
||||
return Order.CompareTo(other.Order);
|
||||
}
|
||||
|
||||
public static implicit operator bool(DTToolbarItem a)
|
||||
{
|
||||
return !object.ReferenceEquals(a, null);
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Class,AllowMultiple=false)]
|
||||
public class ToolbarItemAttribute : Attribute
|
||||
{
|
||||
public readonly string Project;
|
||||
public readonly int Order;
|
||||
public readonly string Label;
|
||||
public readonly string Tooltip;
|
||||
public readonly string Icon;
|
||||
public readonly string IconLightSkin;
|
||||
|
||||
public ToolbarItemAttribute(int order, string project, string label, string tooltip = "", string icon = "",string iconLight="") : this(project,label,tooltip,icon,iconLight,order)
|
||||
{
|
||||
}
|
||||
|
||||
public ToolbarItemAttribute(string project, string label, string tooltip = "", string icon = "", string iconLight="", int order = 0)
|
||||
{
|
||||
Project = project;
|
||||
Label = label;
|
||||
Tooltip = tooltip;
|
||||
Icon = icon;
|
||||
IconLightSkin = iconLight;
|
||||
Order = order;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c805355f5c0010740a0af2f9d9173e01
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,42 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEditor.AnimatedValues;
|
||||
using UnityEngine.Events;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor.Data{
|
||||
[System.Serializable]
|
||||
public class AnimVector2 : BaseAnimValue<Vector2>
|
||||
{
|
||||
[SerializeField]
|
||||
private Vector3 m_Value;
|
||||
public AnimVector2()
|
||||
: base(Vector2.zero)
|
||||
{
|
||||
}
|
||||
public AnimVector2(Vector3 value)
|
||||
: base(value)
|
||||
{
|
||||
}
|
||||
public AnimVector2(Vector2 value, UnityAction callback)
|
||||
: base(value, callback)
|
||||
{
|
||||
}
|
||||
protected override Vector2 GetValue()
|
||||
{
|
||||
this.m_Value = Vector2.Lerp(base.start, base.target, base.lerpPosition);
|
||||
return this.m_Value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{value} / {target}. Speed {speed}. Lerp {lerpPosition}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6ab6be8bc89cf594f8be7d642df90bf5
|
||||
timeCreated: 1440665948
|
||||
licenseType: Store
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,170 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
[System.Serializable]
|
||||
public class EditorKeyBinding : IComparable
|
||||
{
|
||||
public static bool BindingsEnabled = true;
|
||||
|
||||
public enum MouseButtonEnum
|
||||
{
|
||||
None = -1,
|
||||
Click = 0,
|
||||
RClick = 1,
|
||||
MClick = 2,
|
||||
}
|
||||
|
||||
public string Name;
|
||||
public string Description;
|
||||
public KeyCode Key;
|
||||
public bool Shift;
|
||||
public bool Control;
|
||||
public bool Alt;
|
||||
public bool Command;
|
||||
public MouseButtonEnum MouseButton;
|
||||
|
||||
public bool ShowInPrefs = true;
|
||||
|
||||
public EditorKeyBinding(string name, string description, KeyCode defKey = KeyCode.None, bool defShift = false, bool defControl = false, bool defAlt = false, bool defCommand = false, MouseButtonEnum defMouseButton = MouseButtonEnum.None)
|
||||
{
|
||||
Set(name, description, defKey, defShift, defControl, defAlt, defCommand, defMouseButton);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a binding from it's serialized string from the user preferences
|
||||
/// </summary>
|
||||
public EditorKeyBinding(string bindingString)
|
||||
{
|
||||
Set(bindingString);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the binding from it's serialized string from the user preferences
|
||||
/// </summary>
|
||||
public void Set(string bindingString)
|
||||
{
|
||||
string[] s = bindingString.Split(',');
|
||||
if (s.Length < 6) return;
|
||||
Name = s[0];
|
||||
Key = (KeyCode)System.Enum.Parse(typeof(KeyCode), s[1]);
|
||||
Shift = bool.Parse(s[2]);
|
||||
Control = bool.Parse(s[3]);
|
||||
Alt = bool.Parse(s[4]);
|
||||
Command = bool.Parse(s[5]);
|
||||
MouseButton = (MouseButtonEnum)System.Enum.Parse(typeof(MouseButtonEnum), s[6]);
|
||||
}
|
||||
|
||||
public void Set(string name, string description, KeyCode defKey = KeyCode.None, bool defShift = false, bool defControl = false, bool defAlt = false, bool defCommand = false, MouseButtonEnum defMouseButton = MouseButtonEnum.None)
|
||||
{
|
||||
Name = name;
|
||||
Description = description;
|
||||
Key = defKey;
|
||||
Shift = defShift;
|
||||
Control = defControl;
|
||||
Alt = defAlt;
|
||||
Command = defCommand;
|
||||
MouseButton = defMouseButton;
|
||||
}
|
||||
|
||||
|
||||
public bool IsTriggered(Event e=null)
|
||||
{
|
||||
|
||||
if (!BindingsEnabled)
|
||||
return false;
|
||||
|
||||
if (e == null)
|
||||
e = Event.current;
|
||||
if ((e.type == EventType.MouseDown && e.button == (int)MouseButton) ||
|
||||
(e.type == EventType.KeyDown && Key != KeyCode.None && e.keyCode == Key))
|
||||
return (e.shift == Shift &&
|
||||
e.control == Control &&
|
||||
e.alt == Alt &&
|
||||
e.command == Command);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnPreferencesGUI()
|
||||
{
|
||||
EditorGUI.BeginChangeCheck();
|
||||
Key = (KeyCode)EditorGUILayout.EnumPopup(new GUIContent(Name), Key);
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
Shift = GUILayout.Toggle(Shift, "Shift");
|
||||
Control = GUILayout.Toggle(Control, "Ctrl");
|
||||
Alt = GUILayout.Toggle(Alt, "Alt");
|
||||
Command = GUILayout.Toggle(Command, "Cmd");
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
MouseButton = (MouseButtonEnum)EditorGUILayout.EnumPopup(MouseButton);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
return EditorGUI.EndChangeCheck();
|
||||
}
|
||||
|
||||
public string ToTooltipString()
|
||||
{
|
||||
if (Key == KeyCode.None && MouseButton==MouseButtonEnum.None)
|
||||
return string.Empty;
|
||||
|
||||
List<string> s = new List<string>();
|
||||
if (Control)
|
||||
s.Add("Ctrl");
|
||||
if (Alt)
|
||||
s.Add("Alt");
|
||||
if (Shift)
|
||||
s.Add("Shift");
|
||||
if (Command)
|
||||
s.Add("Cmd");
|
||||
|
||||
string res = string.Join("-", s.ToArray());
|
||||
if (res.Length > 0)
|
||||
res += "-";
|
||||
|
||||
if (Key != KeyCode.None)
|
||||
return "(" + res + Key.ToString() + ")";
|
||||
else
|
||||
return "(" + res + MouseButton.ToString() + ")";
|
||||
}
|
||||
|
||||
public string ToPrefsString()
|
||||
{
|
||||
return Name + "," + ((int)Key).ToString() + "," + Shift.ToString() + "," + Control.ToString() + "," + Alt.ToString() + "," + Command.ToString() + "," + ((int)MouseButton).ToString();
|
||||
}
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
return Name.CompareTo(((EditorKeyBinding)obj).Name);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
EditorKeyBinding o = (EditorKeyBinding)obj;
|
||||
return (Name == o.Name &&
|
||||
Key == o.Key &&
|
||||
Shift == o.Shift &&
|
||||
Control == o.Control &&
|
||||
Alt == o.Alt &&
|
||||
Command == o.Command &&
|
||||
MouseButton == o.MouseButton);
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Name.GetHashCode() ^ Key.GetHashCode() ^ Shift.GetHashCode() ^ Control.GetHashCode() ^ Alt.GetHashCode() ^ Command.GetHashCode() ^ MouseButton.GetHashCode();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97a419ac824222d41b8a477f5151f56a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 08c16b9720ddc734eb7c69bbd147c2cd
|
||||
folderAsset: yes
|
||||
timeCreated: 1436273971
|
||||
licenseType: Store
|
||||
DefaultImporter:
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,248 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Reflection;
|
||||
using System.Collections.Generic;
|
||||
using FluffyUnderware.DevTools.Extensions;
|
||||
using Object = UnityEngine.Object;
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor.Extensions
|
||||
{
|
||||
|
||||
public static class ColorEditorExt
|
||||
{
|
||||
|
||||
public static Color SkinAwareColor(this Color c, bool subtle = false)
|
||||
{
|
||||
if (EditorGUIUtility.isProSkin)
|
||||
return c;
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
|
||||
if (subtle)
|
||||
return Color.Lerp(c, Color.white,
|
||||
DTEditorUtility.UsesNewEditorUI ? 0.9f : 0.7f);
|
||||
else
|
||||
{
|
||||
if (c == Color.white)
|
||||
return Color.black;
|
||||
else if (c == Color.black)
|
||||
return Color.white;
|
||||
else
|
||||
{
|
||||
float br = c.Brightness();
|
||||
if (br > 0.9)
|
||||
return Color.Lerp(c, Color.black, 0.7f);
|
||||
else
|
||||
return Color.Lerp(c, Color.black, 0.2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static float Brightness(this Color c)
|
||||
{
|
||||
return 0.299f * c.r + 0.587f * c.g + 0.114f * c.b;
|
||||
}
|
||||
|
||||
public static Color Fade(this Color c, double startTime, double stayTime, float fadeSpeed)
|
||||
{
|
||||
double d = EditorApplication.timeSinceStartup - startTime;
|
||||
if (stayTime == 0 || d < stayTime)
|
||||
return c;
|
||||
else
|
||||
return new Color(c.r, c.g, c.b, 1 - Mathf.Clamp01((float)(d - stayTime) / Mathf.Max(0, 0001f, fadeSpeed)));
|
||||
}
|
||||
}
|
||||
|
||||
public static class SerializePropertyExt
|
||||
{
|
||||
|
||||
public static GUIContent GetContent(this SerializedProperty property, string label, string tooltip)
|
||||
{
|
||||
if (string.IsNullOrEmpty(label))
|
||||
label = property.displayName;
|
||||
return new GUIContent(label, tooltip);
|
||||
}
|
||||
|
||||
public static List<System.Attribute> GetAttributes(this SerializedProperty property, params System.Type[] ofType)
|
||||
{
|
||||
List<System.Attribute> res = new List<System.Attribute>();
|
||||
Type objType = property.GetObjectType();
|
||||
if (objType != null)
|
||||
{
|
||||
FieldInfo fi = objType.FieldByName(property.name, true, true);
|
||||
|
||||
if (fi != null)
|
||||
{
|
||||
object[] attribs = fi.GetCustomAttributes(true);
|
||||
foreach (System.Attribute a in attribs)
|
||||
{
|
||||
|
||||
if (ofType.Length == 0)
|
||||
res.Add(a);
|
||||
else
|
||||
{
|
||||
Type at = a.GetType();
|
||||
foreach (Type t in ofType)
|
||||
if (t.IsAssignableFrom(at))
|
||||
{
|
||||
res.Add(a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the objec type the property is a children of.
|
||||
/// </summary>
|
||||
public static Type GetObjectType(this SerializedProperty property)
|
||||
{
|
||||
Type baseType = property.serializedObject.targetObject.GetType();
|
||||
if (property.depth > 0)
|
||||
{
|
||||
string[] path = property.Path();
|
||||
FieldInfo fi;
|
||||
for (int i = 0; i < path.Length - 1; i++)
|
||||
{
|
||||
fi = baseType.GetField(path[i], BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (fi == null)
|
||||
return null;
|
||||
baseType = fi.FieldType;
|
||||
}
|
||||
}
|
||||
return baseType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the field type
|
||||
/// </summary>
|
||||
public static Type GetFieldType(this SerializedProperty property)
|
||||
{
|
||||
Type parentType = GetObjectType(property);
|
||||
FieldInfo fi = parentType.FieldByName(property.name, true, true);
|
||||
if (fi != null)
|
||||
{
|
||||
Type tt = fi.FieldType;
|
||||
if (tt.IsGenericType && tt.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
return tt.GetGenericArguments()[0];
|
||||
}
|
||||
if (fi.FieldType.IsArray)
|
||||
return fi.FieldType.GetElementType();
|
||||
else
|
||||
return fi.FieldType;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string[] Path(this SerializedProperty property)
|
||||
{
|
||||
return property.propertyPath.Split('.');
|
||||
}
|
||||
|
||||
public static SerializedProperty Parent(this SerializedProperty property)
|
||||
{
|
||||
string[] path = property.propertyPath.Split('.');
|
||||
if (path.Length > 1)
|
||||
return property.serializedObject.FindProperty(path[path.Length - 2]);
|
||||
else
|
||||
return property;
|
||||
}
|
||||
|
||||
public static object GetParentObject(this SerializedProperty prop)
|
||||
{
|
||||
string path = prop.propertyPath.Replace(".Array.data[", "[");
|
||||
object obj = prop.serializedObject.targetObject;
|
||||
string[] elements = path.Split('.');
|
||||
for (int i = 0; i < elements.Length - 1; i++)
|
||||
{
|
||||
string element = elements[i];
|
||||
if (element.Contains("["))
|
||||
{
|
||||
string elementName = element.Substring(0, element.IndexOf("["));
|
||||
int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
|
||||
obj = getValue(obj, elementName, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
obj = getValue(obj, element);
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static object getValue(object source, string name)
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
Type type = source.GetType();
|
||||
FieldInfo f = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
||||
if (f == null)
|
||||
{
|
||||
PropertyInfo p = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
if (p == null)
|
||||
return null;
|
||||
return p.GetValue(source, null);
|
||||
}
|
||||
return f.GetValue(source);
|
||||
}
|
||||
|
||||
static object getValue(object source, string name, int index)
|
||||
{
|
||||
IEnumerable enumerable = getValue(source, name) as IEnumerable;
|
||||
IEnumerator enm = enumerable.GetEnumerator();
|
||||
while (index-- >= 0)
|
||||
enm.MoveNext();
|
||||
return enm.Current;
|
||||
}
|
||||
|
||||
|
||||
public static SerializedProperty Root(this SerializedProperty property)
|
||||
{
|
||||
string[] path = property.propertyPath.Split('.');
|
||||
if (path.Length > 0)
|
||||
return property.serializedObject.FindProperty(path[0]);
|
||||
else
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RectEditorExt
|
||||
{
|
||||
public static Rect ShiftXBy(this Rect r, float x)
|
||||
{
|
||||
r.x += x;
|
||||
r.width -= x;
|
||||
return new Rect(r);
|
||||
}
|
||||
|
||||
public static Rect WithoutLabel(this Rect r)
|
||||
{
|
||||
return r.ShiftXBy(EditorGUIUtility.labelWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21c52a1cbd7d1274f95e82c253456bda
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name":"FluffyUnderware.DevTools.Editor",
|
||||
"references":[
|
||||
"ToolBuddy.ArraysPooling",
|
||||
"FluffyUnderware.DevTools"
|
||||
],
|
||||
"includePlatforms":[
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms":[]
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8b1d373d568c0384aa3484d7d316284d
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1bc4fb38a6e10f34c8923621b5f349d7
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0565c5d04b968af40bece709cba9bf78
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
|
||||
public class DTToolbarButton : DTToolbarItem
|
||||
{
|
||||
|
||||
public override void Render(Rect r)
|
||||
{
|
||||
if (GUI.Button(r, Content,DTStyles.TBButton))
|
||||
OnClick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23190dcdedb51a74c914b829d6473b8c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
|
||||
public class DTToolbarRadioButton : DTToolbarToggleButton
|
||||
{
|
||||
public string RadioGroup = string.Empty;
|
||||
|
||||
protected bool AllOffAllowed {get; set;}
|
||||
|
||||
public override bool On
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.On;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (mOn!=value)
|
||||
{
|
||||
if (value == true)
|
||||
DTToolbar.SetRadioGroupState(this);
|
||||
else if (AllOffAllowed)
|
||||
{
|
||||
mOn = false;
|
||||
OnClick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ShowClientArea
|
||||
{
|
||||
get
|
||||
{
|
||||
return On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DTToolbarRadioButton(string radioGroupName, bool defaultOn=false)
|
||||
{
|
||||
RadioGroup = radioGroupName;
|
||||
AllOffAllowed = false;
|
||||
mOn = defaultOn;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d892a9daccfb1d04ba11b0f7ecb26c70
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
|
||||
public class DTToolbarRadioOffButton : DTToolbarRadioButton
|
||||
{
|
||||
|
||||
public override bool On
|
||||
{
|
||||
get
|
||||
{
|
||||
return base.On;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (mOn != value)
|
||||
{
|
||||
if (value == true)
|
||||
DTToolbar.SetRadioGroupState(this);
|
||||
else if (AllOffAllowed)
|
||||
{
|
||||
mOn = false;
|
||||
OnClick();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ShowClientArea
|
||||
{
|
||||
get
|
||||
{
|
||||
return On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public DTToolbarRadioOffButton(string radioGroupName, bool defaultOn=false)
|
||||
: base(radioGroupName,defaultOn)
|
||||
{
|
||||
AllOffAllowed = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 41cca5920ca067a40b4b73d4db3f20e5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
// =====================================================================
|
||||
// Copyright 2013-2017 Fluffy Underware
|
||||
// All rights reserved
|
||||
//
|
||||
// http://www.fluffyunderware.com
|
||||
// =====================================================================
|
||||
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace FluffyUnderware.DevToolsEditor
|
||||
{
|
||||
|
||||
public class DTToolbarToggleButton : DTToolbarItem
|
||||
{
|
||||
|
||||
public virtual bool On
|
||||
{
|
||||
get { return mOn; }
|
||||
set
|
||||
{
|
||||
if (mOn != value)
|
||||
{
|
||||
mOn = value;
|
||||
if (!mOn && FocusedItem == this)
|
||||
FocusedItem = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal bool mOn;
|
||||
|
||||
public override bool ShowClientArea
|
||||
{
|
||||
get
|
||||
{
|
||||
return On;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void OnOtherItemClicked(DTToolbarItem other)
|
||||
{
|
||||
base.OnOtherItemClicked(other);
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void OnClick()
|
||||
{
|
||||
base.OnClick();
|
||||
On = !On;
|
||||
}
|
||||
|
||||
public override void Render(Rect r)
|
||||
{
|
||||
bool b = GUI.Toggle(r, On, Content, DTStyles.TBButton);
|
||||
if (b!=On)
|
||||
OnClick();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53c6546eb43ea874e92c8a7b1a7ef015
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0b4e043cc24eb74ea52c17d8b10dc4a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c750636a308f02746931c9fd1a75c27a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"name":"FluffyUnderware.DevTools"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2c9fb26221d6a7458f7fa35de2f6a6f
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in new issue
Block a user