轨迹插件引入

This commit is contained in:
lishuo committed 2022-09-06 13:47:45 +08:00
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);
}
}
}
@@ -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:
@@ -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);
}
}
}
@@ -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();
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 21c52a1cbd7d1274f95e82c253456bda
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -0,0 +1,11 @@
{
"name":"FluffyUnderware.DevTools.Editor",
"references":[
"ToolBuddy.ArraysPooling",
"FluffyUnderware.DevTools"
],
"includePlatforms":[
"Editor"
],
"excludePlatforms":[]
}
@@ -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:
@@ -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();
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 23190dcdedb51a74c914b829d6473b8c
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -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;
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d892a9daccfb1d04ba11b0f7ecb26c70
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -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;
}
}
}
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 41cca5920ca067a40b4b73d4db3f20e5
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
@@ -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();
}
}
}
@@ -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: