基础内容

必要插件安装
缓动曲线和动画基础
ElementFolder,Track与其次级模块,PathNode重构
This commit is contained in:
SoulliesOfficial
2025-01-26 21:10:16 -05:00
parent 40f63dd2bd
commit 8d0abec75f
9320 changed files with 2950357 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to destroy a GameObject.</summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanDestroy")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Destroy")]
public class LeanDestroy : MonoBehaviour
{
public enum ExecuteType
{
OnFirstFrame,
AfterDelay,
AfterDelayUnscaled,
Manually
}
/// <summary>This allows you to control when the <b>Target</b> GameObject will be destroyed.
/// OnFirstFrame = As soon as Update runs (this component must be enabled).
/// AfterDelay = After the specified amount of <b>Seconds</b> has elapsed.
/// AfterDelayUnscaled = The same as AfterDelay, but using unscaledDeltaTime.
/// Manually = You must manually call the <b>DestroyNow</b> method.</summary>
public ExecuteType Execute { set { execute = value; } get { return execute; } } [SerializeField] private ExecuteType execute = ExecuteType.Manually;
/// <summary>The GameObject that will be destroyed.
/// None/null = This GameObject.</summary>
public GameObject Target { set { target = value; } get { return target; } } [SerializeField] private GameObject target;
/// <summary>The amount of seconds remaining until the GameObject is destroyed.</summary>
public float Seconds { set { seconds = value; } get { return seconds; } } [SerializeField] private float seconds = -1.0f;
protected virtual void Update()
{
switch (execute)
{
case ExecuteType.OnFirstFrame:
{
DestroyNow();
}
break;
case ExecuteType.AfterDelay:
{
seconds -= Time.deltaTime;
if (seconds <= 0.0f)
{
DestroyNow();
}
}
break;
case ExecuteType.AfterDelayUnscaled:
{
seconds -= Time.unscaledDeltaTime;
if (seconds <= 0.0f)
{
DestroyNow();
}
}
break;
}
}
/// <summary>You can manually call this method to destroy the specified GameObject immediately.</summary>
public void DestroyNow()
{
execute = ExecuteType.Manually;
Destroy(target != null ? target : gameObject);
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanDestroy;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanDestroy_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
Draw("target", "The GameObject that will be destroyed.\n\nNone/null = This GameObject.");
Draw("execute", "This allows you to control when the <b>Target</b> GameObject will be destroyed.\n\nOnFirstFrame = As soon as Update runs (this component must be enabled).\n\nAfterDelay = After the specified amount of <b>Seconds</b> has elapsed.\n\nAfterDelayUnscaled = The same as AfterDelay, but using unscaledDeltaTime.\n\nManually = You must manually call the <b>DestroyNow</b> method.");
if (Any(tgts, t => t.Execute == LeanDestroy.ExecuteType.AfterDelay || t.Execute == LeanDestroy.ExecuteType.AfterDelayUnscaled))
{
BeginIndent();
Draw("seconds", "The amount of seconds remaining until the GameObject is destroyed.");
EndIndent();
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 683e90f239f8a2b42b3533e9316fd5f3
timeCreated: 1480377129
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,147 @@
using UnityEngine;
using UnityEngine.Events;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to convert values like ints and floats into formatted text that can be shown in the UI. To use this component, simply call one of the <b>SetString</b> methods, and it will output the formatted string to the <b>OnString</b> event, which can be connected to UI text, etc.</b></summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanFormatString")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Format String")]
public class LeanFormatString : MonoBehaviour
{
[System.Serializable] public class StringEvent : UnityEvent<string> {}
/// <summary>The final text will use this string formatting, where {0} is the first value, {1} is the second, etc. Formatting uses standard <b>string.Format</b> style.</summary>
public string Format { set { format = value; } get { return format; } } [SerializeField] [Multiline] private string format = "Current Value = {0}";
/// <summary>Based on the <b>Send</b> setting, this event will be invoked.
/// String = The .</summary>
public StringEvent OnString { get { if (onString == null) onString = new StringEvent(); return onString; } } [SerializeField] private StringEvent onString;
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(string a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(string a, string b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(int a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(int a, int b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(float a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(float a, float b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector2 a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector2 a, Vector2 b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector3 a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector3 a, Vector3 b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector4 a)
{
SendString(a);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(Vector4 a, Vector4 b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(float a, int b)
{
SendString(a, b);
}
/// <summary>This method will convert the input arguments into a formatted string, and output it to the <b>OnString</b> event.</summary>
public void SetString(int a, float b)
{
SendString(a, b);
}
private void SendString(object a)
{
if (onString != null)
{
onString.Invoke(string.Format(format, a));
}
}
private void SendString(object a, object b)
{
if (onString != null)
{
onString.Invoke(string.Format(format, a, b));
}
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanFormatString;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanFormatString_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
BeginError(Any(tgts, t => string.IsNullOrEmpty(t.Format)));
Draw("format", "The final text will use this string formatting, where {0} is the first value, {1} is the second, etc. Formatting uses standard <b>string.Format</b> style.");
EndError();
Separator();
Draw("onString");
}
}
}
#endif

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b7efa857135ee3242bb57cd8a91087c8
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:

View File

@@ -0,0 +1,404 @@
using UnityEngine;
using System.Collections.Generic;
namespace Lean.Common
{
/// <summary>This component stores a list of points that form a path.</summary>
[ExecuteInEditMode]
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanPath")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Path")]
public class LeanPath : MonoBehaviour
{
/// <summary>The points along the path.</summary>
[Tooltip("The points along the path.")]
public List<Vector3> Points;
/// <summary>Do these points loop back to the start?</summary>
[Tooltip("Do these points loop back to the start?")]
public bool Loop;
/// <summary>The coordinate system for the points.</summary>
[Tooltip("The coordinate system for the points.")]
public Space Space = Space.Self;
/// <summary>The amount of lines between each path point when read from LeanScreenDepth.</summary>
[Tooltip("The amount of lines between each path point when read from LeanScreenDepth.")]
public int Smoothing = 1;
/// <summary>This allows you to draw a visual of the path using a <b>LineRenderer</b>.</summary>
[Tooltip("This allows you to draw a visual of the path using a LineRenderer.")]
public LineRenderer Visual;
public static Vector3 LastWorldNormal = Vector3.forward;
public int PointCount
{
get
{
if (Points != null)
{
var count = Points.Count;
if (count >= 2)
{
if (Loop == true)
{
return count + 1;
}
else
{
return count;
}
}
}
return 0;
}
}
public int GetPointCount(int smoothing = -1)
{
if (Points != null)
{
if (smoothing < 0)
{
smoothing = Smoothing;
}
var count = Points.Count;
if (count >= 2 && smoothing >= 1)
{
if (Loop == true)
{
return count * smoothing + 1;
}
else
{
return (count - 1) * smoothing + 1;
}
}
}
return 0;
}
public Vector3 GetSmoothedPoint(float index)
{
if (Points == null)
{
throw new System.IndexOutOfRangeException();
}
var count = Points.Count;
if (count < 2)
{
throw new System.Exception();
}
// Get int and fractional part of float index
var i = (int)index;
var t = Mathf.Abs(index - i);
// Get 4 control points
var a = GetPointRaw(i - 1, count);
var b = GetPointRaw(i , count);
var c = GetPointRaw(i + 1, count);
var d = GetPointRaw(i + 2, count);
// Interpolate and return
var p = default(Vector3);
p.x = CubicInterpolate(a.x, b.x, c.x, d.x, t);
p.y = CubicInterpolate(a.y, b.y, c.y, d.y, t);
p.z = CubicInterpolate(a.z, b.z, c.z, d.z, t);
return p;
}
public Vector3 GetPoint(int index, int smoothing = -1)
{
if (Points == null)
{
throw new System.IndexOutOfRangeException();
}
if (smoothing < 0)
{
smoothing = Smoothing;
}
if (smoothing < 1)
{
throw new System.ArgumentOutOfRangeException();
}
var count = Points.Count;
if (count < 2)
{
throw new System.Exception();
}
if (smoothing > 0)
{
return GetSmoothedPoint(index / (float)smoothing);
}
return GetPointRaw(index, count);
}
private Vector3 GetPointRaw(int index, int count)
{
if (Loop == true)
{
index = Mod(index, count);
}
else
{
index = Mathf.Clamp(index, 0, count - 1);
}
var point = Points[index];
if (Space == Space.Self)
{
point = transform.TransformPoint(point);
}
return point;
}
public void SetLine(Vector3 a, Vector3 b)
{
if (Points == null)
{
Points = new List<Vector3>();
}
else
{
Points.Clear();
}
Points.Add(a);
Points.Add(b);
}
public bool TryGetClosest(Vector3 position, ref Vector3 closestPoint, ref int closestIndexA, ref int closestIndexB, int smoothing = -1)
{
var count = GetPointCount(smoothing);
if (count >= 2)
{
var indexA = 0;
var pointA = GetPoint(indexA, smoothing);
var closestDistance = float.PositiveInfinity;
for (var i = 1; i < count; i++)
{
var indexB = i;
var pointB = GetPoint(indexB, smoothing);
var point = GetClosestPoint(position, pointA, pointB - pointA);
var distance = Vector3.Distance(position, point);
if (distance < closestDistance)
{
closestIndexA = indexA;
closestIndexB = i;
closestPoint = point;
closestDistance = distance;
LastWorldNormal = Vector3.Normalize(point - pointB);
}
pointA = pointB;
indexA = indexB;
}
return true;
}
return false;
}
public bool TryGetClosest(Vector3 position, ref Vector3 closestPoint, int smoothing = -1)
{
var closestIndexA = default(int);
var closestIndexB = default(int);
return TryGetClosest(position, ref closestPoint, ref closestIndexA, ref closestIndexB, smoothing);
}
public bool TryGetClosest(Ray ray, ref Vector3 closestPoint, ref int closestIndexA, ref int closestIndexB, int smoothing = -1)
{
var count = GetPointCount(smoothing);
if (count >= 2)
{
var indexA = 0;
var pointA = GetPoint(0, smoothing);
var closestDistance = float.PositiveInfinity;
for (var i = 1; i < count; i++)
{
var pointB = GetPoint(i, smoothing);
var point = GetClosestPoint(ray, pointA, pointB - pointA);
var distance = GetClosestDistance(ray, point);
if (distance < closestDistance)
{
closestIndexA = indexA;
closestIndexB = i;
closestPoint = point;
closestDistance = distance;
LastWorldNormal = Vector3.Normalize(point - pointB);
}
pointA = pointB;
indexA = i;
}
return true;
}
return false;
}
public bool TryGetClosest(Ray ray, ref Vector3 currentPoint, int smoothing = -1)
{
var closestIndexA = default(int);
var closestIndexB = default(int);
return TryGetClosest(ray, ref currentPoint, ref closestIndexA, ref closestIndexB, smoothing);
}
public bool TryGetClosest(Ray ray, ref Vector3 currentPoint, int smoothing = -1, float maximumDelta = -1.0f)
{
if (maximumDelta > 0.0f)
{
var closestPoint = currentPoint;
if (TryGetClosest(ray, ref closestPoint, smoothing) == true)
{
// Move toward closest point
var targetPoint = Vector3.MoveTowards(currentPoint, closestPoint, maximumDelta);
return TryGetClosest(targetPoint, ref currentPoint, smoothing);
}
return false;
}
return TryGetClosest(ray, ref currentPoint, smoothing);
}
private Vector3 GetClosestPoint(Vector3 position, Vector3 origin, Vector3 direction)
{
var denom = Vector3.Dot(direction, direction);
// If the line doesn't point anywhere, return origin
if (denom == 0.0f)
{
return origin;
}
var dist01 = Vector3.Dot(position - origin, direction) / denom;
return origin + direction * Mathf.Clamp01(dist01);
}
private Vector3 GetClosestPoint(Ray ray, Vector3 origin, Vector3 direction)
{
var crossA = Vector3.Cross(ray.direction, direction);
var denom = Vector3.Dot(crossA, crossA);
// If lines are parallel, we can return any point on line
if (denom == 0.0f)
{
return origin;
}
var crossB = Vector3.Cross(ray.direction, ray.origin - origin);
var dist01 = Vector3.Dot(crossA, crossB) / denom;
return origin + direction * Mathf.Clamp01(dist01);
}
private float GetClosestDistance(Ray ray, Vector3 point)
{
var denom = Vector3.Dot(ray.direction, ray.direction);
// If the ray doesn't point anywhere, return distance from origin to point
if (denom == 0.0f)
{
return Vector3.Distance(ray.origin, point);
}
var dist01 = Vector3.Dot(point - ray.origin, ray.direction) / denom;
return Vector3.Distance(point, ray.GetPoint(dist01));
}
private int Mod(int a, int b)
{
a %= b; return a < 0 ? a + b : a;
}
private float CubicInterpolate(float a, float b, float c, float d, float t)
{
var tt = t * t;
var ttt = tt * t;
var e = a - b;
var f = d - c;
var g = f - e;
var h = e - g;
var i = c - a;
return g * ttt + h * tt + i * t + b;
}
public void UpdateVisual()
{
if (Visual != null)
{
var count = GetPointCount();
Visual.positionCount = count;
for (var i = 0; i < count; i++)
{
Visual.SetPosition(i, GetPoint(i));
}
}
}
protected virtual void Update()
{
UpdateVisual();
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmosSelected()
{
var count = GetPointCount();
if (count >= 2)
{
var pointA = GetPoint(0);
for (var i = 1; i < count; i++)
{
var pointB = GetPoint(i);
Gizmos.DrawLine(pointA, pointB);
pointA = pointB;
}
}
}
#endif
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: f3b515ca1c988ce43853bd6120e9b94b
timeCreated: 1512088730
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,200 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component stores information about a 3D plane. By default this plane lays on the XY axis, or faces the Z axis.</summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanPlane")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Plane")]
public class LeanPlane : MonoBehaviour
{
/// <summary>Should the plane be clamped on the x axis?</summary>
public bool ClampX;
public float MinX;
public float MaxX;
/// <summary>Should the plane be clamped on the y axis?</summary>
public bool ClampY;
public float MinY;
public float MaxY;
/// <summary>The distance between each position snap on the x axis.</summary>
public float SnapX;
/// <summary>The distance between each position snap on the x axis.</summary>
public float SnapY;
public Vector3 GetClosest(Vector3 position, float offset = 0.0f)
{
// Transform point to plane space
var point = transform.InverseTransformPoint(position);
// Clamp values?
if (ClampX == true)
{
point.x = Mathf.Clamp(point.x, MinX, MaxX);
}
if (ClampY == true)
{
point.y = Mathf.Clamp(point.y, MinY, MaxY);
}
// Snap values?
if (SnapX != 0.0f)
{
point.x = Mathf.Round(point.x / SnapX) * SnapX;
}
if (SnapY != 0.0f)
{
point.y = Mathf.Round(point.y / SnapY) * SnapY;
}
// Reset Z to plane
point.z = 0.0f;
// Transform back into world space
return transform.TransformPoint(point) + transform.forward * offset;
}
public bool TryRaycast(Ray ray, ref Vector3 hit, float offset = 0.0f, bool getClosest = true)
{
var normal = transform.forward;
var point = transform.position + normal * offset;
var distance = default(float);
if (RayToPlane(point, normal, ray, ref distance) == true)
{
hit = ray.GetPoint(distance);
if (getClosest == true)
{
hit = GetClosest(hit, offset);
}
return true;
}
return false;
}
public Vector3 GetClosest(Ray ray, float offset = 0.0f)
{
var normal = transform.forward;
var point = transform.position + normal * offset;
var distance = default(float);
if (RayToPlane(point, normal, ray, ref distance) == true)
{
return GetClosest(ray.GetPoint(distance), offset);
}
return point;
}
#if UNITY_EDITOR
protected virtual void OnDrawGizmosSelected()
{
Gizmos.matrix = transform.localToWorldMatrix;
var x1 = MinX;
var x2 = MaxX;
var y1 = MinY;
var y2 = MaxY;
if (ClampX == false)
{
x1 = -1000.0f;
x2 = 1000.0f;
}
if (ClampY == false)
{
y1 = -1000.0f;
y2 = 1000.0f;
}
if (ClampX == false && ClampY == false)
{
Gizmos.DrawLine(new Vector3( x1, 0.0f), new Vector3( x2, 0.0f));
Gizmos.DrawLine(new Vector3(0.0f, y1), new Vector3(0.0f, y2));
}
else
{
Gizmos.DrawLine(new Vector3(x1, y1), new Vector3(x2, y1));
Gizmos.DrawLine(new Vector3(x1, y2), new Vector3(x2, y2));
Gizmos.DrawLine(new Vector3(x1, y1), new Vector3(x1, y2));
Gizmos.DrawLine(new Vector3(x2, y1), new Vector3(x2, y2));
}
}
#endif
private static bool RayToPlane(Vector3 point, Vector3 normal, Ray ray, ref float distance)
{
var b = Vector3.Dot(ray.direction, normal);
if (Mathf.Approximately(b, 0.0f) == true)
{
return false;
}
var d = -Vector3.Dot(normal, point);
var a = -Vector3.Dot(ray.origin, normal) - d;
distance = a / b;
return distance > 0.0f;
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanPlane;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanPlane_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
Draw("ClampX", "Should the plane be clamped on the x axis?");
if (Any(tgts, t => t.ClampX == true))
{
BeginIndent();
Draw("MinX", "", "Min");
Draw("MaxX", "", "Max");
EndIndent();
Separator();
}
Draw("ClampY", "Should the plane be clamped on the y axis?");
if (Any(tgts, t => t.ClampX == true))
{
BeginIndent();
Draw("MinY", "", "Min");
Draw("MaxY", "", "Max");
EndIndent();
Separator();
}
Draw("SnapX", "The distance between each position snap on the x axis.");
Draw("SnapY", "The distance between each position snap on the y axis.");
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e167f5141e8fbbb429990fdd6dca01c5
timeCreated: 1512104139
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,117 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component rotates the current GameObject based on the current Angle value.
/// NOTE: This component overrides and takes over the rotation of this GameObject, so you can no longer externally influence it.</summary>
[ExecuteInEditMode]
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanRoll")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Roll")]
public class LeanRoll : MonoBehaviour
{
/// <summary>The current angle in degrees.</summary>
public float Angle { set { angle = value; } get { return angle; } } [SerializeField] private float angle;
/// <summary>Should the <b>Angle</b> value be clamped?</summary>
public bool Clamp { set { clamp = value; } get { return clamp; } } [SerializeField] private bool clamp;
/// <summary>The minimum <b>Angle</b> value.</summary>
public float ClampMin { set { clampMin = value; } get { return clampMin; } } [SerializeField] private float clampMin;
/// <summary>The maximum <b>Angle</b> value.</summary>
public float ClampMax { set { clampMax = value; } get { return clampMax; } } [SerializeField] private float clampMax;
/// <summary>If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.
/// -1 = Instantly change.
/// 1 = Slowly change.
/// 10 = Quickly change.</summary>
public float Damping { set { damping = value; } get { return damping; } } [SerializeField] private float damping = -1.0f;
[SerializeField]
private float currentAngle;
/// <summary>The <b>Angle</b> value will be incremented by the specified angle in degrees.</summary>
public void IncrementAngle(float delta)
{
angle += delta;
}
/// <summary>The <b>Angle</b> value will be decremented by the specified angle in degrees.</summary>
public void DecrementAngle(float delta)
{
angle -= delta;
}
/// <summary>This method will update the Angle value based on the specified vector.</summary>
public void RotateToDelta(Vector2 delta)
{
if (delta.sqrMagnitude > 0.0f)
{
angle = Mathf.Atan2(delta.x, delta.y) * Mathf.Rad2Deg;
}
}
/// <summary>This method will immediately snap the current angle to its target value.</summary>
[ContextMenu("Snap To Target")]
public void SnapToTarget()
{
currentAngle = angle;
}
protected virtual void Start()
{
currentAngle = angle;
}
protected virtual void Update()
{
// Get t value
var factor = CwHelper.DampenFactor(damping, Time.deltaTime);
if (clamp == true)
{
angle = Mathf.Clamp(angle, clampMin, clampMax);
}
// Lerp angle
currentAngle = Mathf.LerpAngle(currentAngle, angle, factor);
// Update rotation
transform.rotation = Quaternion.Euler(0.0f, 0.0f, -currentAngle);
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanRoll;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanRoll_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
Draw("angle", "The current angle in degrees.");
Draw("clamp", "Should the Angle value be clamped?");
if (Any(tgts, t => t.Clamp == true))
{
BeginIndent();
Draw("clampMin", "The minimum Angle value.", "Min");
Draw("clampMax", "The maximum Angle value.", "Max");
EndIndent();
Separator();
}
Draw("damping", "If you want this component to change smoothly over time, then this allows you to control how quick the changes reach their target value.\n\n-1 = Instantly change.\n\n1 = Slowly change.\n\n10 = Quickly change.");
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 44eecfd2f8136f94caff1a3e1238c023
timeCreated: 1523842237
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,362 @@
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using CW.Common;
namespace Lean.Common
{
/// <summary>This is the base class for all object selectors.</summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSelect")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Select")]
public class LeanSelect : MonoBehaviour
{
[System.Serializable] public class LeanSelectableEvent : UnityEvent<LeanSelectable> {}
public enum ReselectType
{
KeepSelected,
Deselect,
DeselectAndSelect,
SelectAgain
}
public enum LimitType
{
Unlimited,
StopAtMax,
DeselectFirst
}
public static LinkedList<LeanSelect> Instances = new LinkedList<LeanSelect>(); [System.NonSerialized] private LinkedListNode<LeanSelect> instancesNode;
/// <summary>If you attempt to select a point that has no objects underneath, should all currently selected objects be deselected?</summary>
public bool DeselectWithNothing { set { deselectWithNothing = value; } get { return deselectWithNothing; } } [SerializeField] private bool deselectWithNothing;
/// <summary>If you have selected the maximum number of objects, what should happen?
/// Unlimited = Always allow selection.
/// StopAtMax = Allow selection up to the <b>MaxSelectables</b> count, then do nothing.
/// DeselectFirst = Always allow selection, but deselect the first object when the <b>MaxSelectables</b> count is reached.</summary>
public LimitType Limit { set { limit = value; } get { return limit; } } [SerializeField] private LimitType limit;
/// <summary>The maximum number of selectables that can be selected at the same time.
/// 0 = Unlimited.</summary>
public int MaxSelectables { set { maxSelectables = value; } get { return maxSelectables; } } [SerializeField] private int maxSelectables = 5;
/// <summary>If you select an already selected selectable, what should happen?</summary>
public ReselectType Reselect { set { reselect = value; } get { return reselect; } } [SerializeField] private ReselectType reselect = ReselectType.SelectAgain;
/// <summary>This stores all objects selected by this component.</summary>
public List<LeanSelectable> Selectables { get { if (selectables == null) selectables = new List<LeanSelectable>(); return selectables; } } [SerializeField] protected List<LeanSelectable> selectables;
/// <summary>This is invoked when an object is selected.</summary>
public LeanSelectableEvent OnSelected { get { if (onSelected == null) onSelected = new LeanSelectableEvent(); return onSelected; } } [SerializeField] private LeanSelectableEvent onSelected;
/// <summary>This is invoked when an object is deselected.</summary>
public LeanSelectableEvent OnDeselected { get { if (onDeselected == null) onDeselected = new LeanSelectableEvent(); return onDeselected; } } [SerializeField] private LeanSelectableEvent onDeselected;
/// <summary>This is invoked when you try to select, but nothing is found.</summary>
public UnityEvent OnNothing { get { if (onNothing == null) onNothing = new UnityEvent(); return onNothing; } } [SerializeField] private UnityEvent onNothing;
public static event System.Action<LeanSelect, LeanSelectable> OnAnySelected;
public static event System.Action<LeanSelect, LeanSelectable> OnAnyDeselected;
public bool IsSelected(LeanSelectable selectable)
{
return selectables != null && selectables.Contains(selectable);
}
/// <summary>This will select the specified object and add it to this component's <b>Selectables</b> list, if it isn't already there.</summary>
public void Select(LeanSelectable selectable)
{
TrySelect(selectable);
}
/// <summary>This remove the specified object from this component's <b>Selectables</b> list if present, and deselect it.</summary>
public void Deselect(LeanSelectable selectable)
{
if (selectable != null && selectables != null)
{
TryDeselect(selectable);
}
}
protected bool TrySelect(LeanSelectable selectable)
{
if (CwHelper.Enabled(selectable) == true)
{
if (TryReselect(selectable) == true)
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
switch (limit)
{
case LimitType.Unlimited:
{
}
break;
case LimitType.StopAtMax:
{
if (selectables.Count >= maxSelectables)
{
return false;
}
}
break;
case LimitType.DeselectFirst:
{
if (selectables.Count > 0 && selectables.Count >= maxSelectables)
{
TryDeselect(selectables[0]);
}
}
break;
}
}
selectables.Add(selectable);
if (onSelected != null) onSelected.Invoke(selectable);
if (OnAnySelected != null) OnAnySelected.Invoke(this, selectable);
selectable.InvokeOnSelected(this);
return true;
}
}
// Nothing was selected?
else
{
if (onNothing != null) onNothing.Invoke();
if (deselectWithNothing == true)
{
DeselectAll();
}
}
return false;
}
private bool TryReselect(LeanSelectable selectable)
{
switch (reselect)
{
case ReselectType.KeepSelected:
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
return true;
}
}
break;
case ReselectType.Deselect:
{
if (Selectables.Contains(selectable) == false) // NOTE: Property
{
return true;
}
else
{
Deselect(selectable);
}
}
break;
case ReselectType.DeselectAndSelect:
{
if (Selectables.Contains(selectable) == true) // NOTE: Property
{
Deselect(selectable);
}
}
return true;
case ReselectType.SelectAgain:
{
}
return true;
}
return false;
}
protected bool TryDeselect(LeanSelectable selectable)
{
if (selectables != null)
{
var index = selectables.IndexOf(selectable);
if (index >= 0)
{
return TryDeselect(index);
}
}
return false;
}
protected bool TryDeselect(int index)
{
var success = false;
if (selectables != null && index >= 0 && index < selectables.Count)
{
var selectable = selectables[index];
selectables.RemoveAt(index);
if (selectable != null)
{
selectable.InvokeOnDeslected(this);
if (onDeselected != null)
{
onDeselected.Invoke(selectable);
}
if (OnAnyDeselected != null)
{
OnAnyDeselected.Invoke(this, selectable);
}
}
success = true;
}
return success;
}
/// <summary>This will deselect all objects that were selected by this component.</summary>
[ContextMenu("Deselect All")]
public void DeselectAll()
{
if (selectables != null)
{
while (selectables.Count > 0)
{
var index = selectables.Count - 1;
var selectable = selectables[index];
selectables.RemoveAt(index);
selectable.InvokeOnDeslected(this);
}
}
}
/// <summary>This will deselect objects in chronological order until the selected object count reaches the specified amount.</summary>
public void Cull(int maxCount)
{
if (selectables != null)
{
while (selectables.Count > 0 && selectables.Count > maxCount)
{
var selectable = selectables[0];
selectables.RemoveAt(0);
if (selectable != null)
{
if (selectable != null)
{
Deselect(selectable);
}
}
}
}
}
protected virtual void OnEnable()
{
instancesNode = Instances.AddLast(this);
}
protected virtual void OnDisable()
{
Instances.Remove(instancesNode); instancesNode = null;
}
protected virtual void OnDestroy()
{
DeselectAll();
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSelect;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSelect_Editor : CwEditor
{
[System.NonSerialized] TARGET tgt; [System.NonSerialized] TARGET[] tgts;
protected override void OnInspector()
{
GetTargets(out tgt, out tgts);
Draw("deselectWithNothing", "If you attempt to select a point that has no objects underneath, should all currently selected objects be deselected?");
Draw("limit", "If you have selected the maximum number of objects, what should happen?\n\nUnlimited = Always allow selection.\n\nStopAtMax = Allow selection up to the <b>MaxSelectables</b> count, then do nothing.\n\nDeselectFirst = Always allow selection, but deselect the first object when the <b>MaxSelectables</b> count is reached.");
if (Any(tgts, t => t.Limit != LeanSelect.LimitType.Unlimited))
{
BeginIndent();
Draw("maxSelectables", "The maximum number of selectables that can be selected at the same time.\n\n0 = Unlimited.");
EndIndent();
}
Draw("reselect", "If you select an already selected selectable, what should happen?");
Separator();
var select = (LeanSelectable)UnityEditor.EditorGUILayout.ObjectField(new GUIContent("Select", "Drop a selectable object here to select it."), null, typeof(LeanSelectable), true);
var deselect = (LeanSelectable)UnityEditor.EditorGUILayout.ObjectField(new GUIContent("Deselect", "Drop a selectable object here to deselect it."), null, typeof(LeanSelectable), true);
BeginDisabled();
Draw("selectables", "This stores all objects selected by this component.");
EndDisabled();
Separator();
var showUnusedEvents = DrawFoldout("Show Unused Events", "Show all events?");
DrawEvents(showUnusedEvents);
if (select != null)
{
Each(tgts, t => t.Select(select), true);
}
if (deselect != null)
{
Each(tgts, t => t.Deselect(deselect), true);
}
}
protected virtual void DrawEvents(bool showUnusedEvents)
{
if (Any(tgts, t => t.OnSelected.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onSelected");
}
if (Any(tgts, t => t.OnDeselected.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onDeselected");
}
if (Any(tgts, t => t.OnNothing.GetPersistentEventCount() > 0) == true || showUnusedEvents == true)
{
Draw("onNothing");
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: d63d4be4bed5744479160ea1148b5821
timeCreated: 1480141605
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,255 @@
using UnityEngine;
using UnityEngine.Events;
using System.Collections.Generic;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you make the current GameObject selectable.</summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSelectable")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Selectable")]
public class LeanSelectable : MonoBehaviour
{
[System.Serializable] public class LeanSelectEvent : UnityEvent<LeanSelect> {}
public static LinkedList<LeanSelectable> Instances = new LinkedList<LeanSelectable>(); [System.NonSerialized] private LinkedListNode<LeanSelectable> instancesNode;
public bool SelfSelected { set { if (selfSelected != value) { selfSelected = value; if (value == true) InvokeOnSelected(null); else InvokeOnDeslected(null); } } get { return selfSelected; } } [SerializeField] private bool selfSelected;
/// <summary>This is invoked every time this object is selected.
/// LeanSelect = The component that caused the selection (null = self selection).
/// NOTE: This may occur multiple times.</summary>
public LeanSelectEvent OnSelected { get { if (onSelected == null) onSelected = new LeanSelectEvent(); return onSelected; } } [SerializeField] private LeanSelectEvent onSelected;
/// <summary>This is invoked every time this object is deselected.
/// LeanSelect = The component that caused the deselection (null = self deselection).
/// NOTE: This may occur multiple times.</summary>
public LeanSelectEvent OnDeselected { get { if (onDeselected == null) onDeselected = new LeanSelectEvent(); return onDeselected; } } [SerializeField] private LeanSelectEvent onDeselected;
public static event System.Action<LeanSelectable> OnAnyEnabled;
public static event System.Action<LeanSelectable> OnAnyDisabled;
public static event System.Action<LeanSelect, LeanSelectable> OnAnySelected;
public static event System.Action<LeanSelect, LeanSelectable> OnAnyDeselected;
protected static List<LeanSelectable> tempSelectables = new List<LeanSelectable>();
/// <summary>This will tell you how many <b>LeanSelect</b> components in the scene currently have this object selected.</summary>
public int SelectedCount
{
get
{
var count = 0;
if (selfSelected == true)
{
count += 1;
}
foreach (var select in LeanSelect.Instances)
{
if (select.IsSelected(this) == true)
{
count += 1;
}
}
return count;
}
}
/// <summary>This will tell you if this object is self selected, or selected by any <b>LeanSelect</b> components in the scene.</summary>
public bool IsSelected
{
get
{
if (selfSelected == true)
{
return true;
}
foreach (var select in LeanSelect.Instances)
{
if (select.IsSelected(this) == true)
{
return true;
}
}
return false;
}
}
public static int IsSelectedCount
{
get
{
var count = 0;
foreach (var selectable in Instances)
{
if (selectable.IsSelected == true)
{
count += 1;
}
}
return count;
}
}
[ContextMenu("Deselect")]
public void Deselect()
{
SelfSelected = false;
foreach (var select in LeanSelect.Instances)
{
select.Deselect(this);
}
}
/// <summary>This deselects all objects in the scene.</summary>
public static void DeselectAll()
{
foreach (var select in LeanSelect.Instances)
{
select.DeselectAll();
}
foreach (var selectable in LeanSelectable.Instances)
{
selectable.SelfSelected = false;
}
}
public void InvokeOnSelected(LeanSelect select)
{
if (onSelected != null)
{
onSelected.Invoke(select);
}
if (OnAnySelected != null)
{
OnAnySelected.Invoke(select, this);
}
}
public void InvokeOnDeslected(LeanSelect select)
{
if (onDeselected != null)
{
onDeselected.Invoke(select);
}
if (OnAnyDeselected != null)
{
OnAnyDeselected.Invoke(select, this);
}
}
protected virtual void OnEnable()
{
instancesNode = Instances.AddLast(this);
if (OnAnyEnabled != null)
{
OnAnyEnabled.Invoke(this);
}
}
protected virtual void OnDisable()
{
Instances.Remove(instancesNode); instancesNode = null;
if (OnAnyDisabled != null)
{
OnAnyDisabled.Invoke(this);
}
}
protected virtual void OnDestroy()
{
Deselect();
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSelectable;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSelectable_Editor : CwEditor
{
[System.NonSerialized] TARGET tgt; [System.NonSerialized] TARGET[] tgts;
protected override void OnInspector()
{
GetTargets(out tgt, out tgts);
DrawSelected();
Separator();
var showUnusedEvents = DrawFoldout("Show Unused Events", "Show all events?");
DrawEvents(showUnusedEvents);
}
private void DrawSelected()
{
BeginDisabled();
EditorGUILayout.Toggle(new GUIContent("Is Selected", "This will tell you if this object is self selected, or selected by any LeanSelect components in the scene."), tgt.IsSelected);
EndDisabled();
BeginIndent();
if (Draw("selfSelected") == true)
{
Each(tgts, t => t.SelfSelected = serializedObject.FindProperty("selfSelected").boolValue, true);
}
BeginDisabled();
foreach (var select in LeanSelect.Instances)
{
if (IsSelectedByAnyTgt(select) == true)
{
EditorGUILayout.ObjectField(new GUIContent("selectedBy"), select, typeof(LeanSelect), true);
}
}
EndDisabled();
EndIndent();
}
private bool IsSelectedByAnyTgt(LeanSelect select)
{
foreach (var tgt in tgts)
{
if (select.IsSelected(tgt) == true)
{
return true;
}
}
return false;
}
protected virtual void DrawEvents(bool showUnusedEvents)
{
if (showUnusedEvents == true || Any(tgts, t => t.OnSelected.GetPersistentEventCount() > 0))
{
Draw("onSelected");
}
if (showUnusedEvents == true || Any(tgts, t => t.OnDeselected.GetPersistentEventCount() > 0))
{
Draw("onDeselected");
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 02e37bf9d23704849b6d4bb2fae198be
timeCreated: 1480141605
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,93 @@
using UnityEngine;
namespace Lean.Common
{
/// <summary>This is the base class for all components that need to implement some kind of special logic when selected. You can do this manually without this class, but this makes it much easier.
/// NOTE: This component will register and unregister the associated LeanSelectable in OnEnable and OnDisable.</summary>
public abstract class LeanSelectableBehaviour : MonoBehaviour
{
[System.NonSerialized]
private LeanSelectable selectable;
/// <summary>This tells you which LeanSelectable is currently associated with this component.</summary>
public LeanSelectable Selectable
{
get
{
if (selectable == null)
{
Register();
}
return selectable;
}
}
/// <summary>This method allows you to manually register the LeanSelectable this component is associated with. This is useful if you're manually spawning and attaching children from code.</summary>
[ContextMenu("Register")]
public void Register()
{
Register(GetComponentInParent<LeanSelectable>());
}
/// <summary>This method allows you to manually register the LeanSelectable this component is associated with.</summary>
public void Register(LeanSelectable newSelectable)
{
if (newSelectable != selectable)
{
// Unregister existing
Unregister();
// Register a new one?
if (newSelectable != null)
{
selectable = newSelectable;
selectable.OnSelected.AddListener(OnSelected);
selectable.OnDeselected.AddListener(OnDeselected);
}
}
}
/// <summary>This method allows you to manually register the LeanSelectable this component is associated with. This is useful if you're changing the associated LeanSelectable.</summary>
[ContextMenu("Unregister")]
public void Unregister()
{
if (selectable != null)
{
selectable.OnSelected.RemoveListener(OnSelected);
selectable.OnDeselected.RemoveListener(OnDeselected);
selectable = null;
}
}
protected virtual void OnEnable()
{
Register();
}
protected virtual void Start()
{
if (selectable == null)
{
Register();
}
}
protected virtual void OnDisable()
{
Unregister();
}
/// <summary>Called when selection begins.</summary>
protected virtual void OnSelected(LeanSelect select)
{
}
/// <summary>Called when this is deselected.</summary>
protected virtual void OnDeselected(LeanSelect select)
{
}
}
}

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: e79fdb2e0edc7c244ae6ee18c7f7cba7
timeCreated: 1480394982
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,70 @@
using UnityEngine;
using UnityEngine.UI;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to change the color of the Graphic (e.g. Image) attached to the current GameObject when selected.</summary>
[ExecuteInEditMode]
[RequireComponent(typeof(Graphic))]
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSelectableGraphicColor")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Selectable Graphic Color")]
public class LeanSelectableGraphicColor : LeanSelectableBehaviour
{
/// <summary>The default color given to the SpriteRenderer.</summary>
public Color DefaultColor { set { defaultColor = value; UpdateColor(); } get { return defaultColor; } } [SerializeField] private Color defaultColor = Color.white;
/// <summary>The color given to the SpriteRenderer when selected.</summary>
public Color SelectedColor { set { selectedColor = value; UpdateColor(); } get { return selectedColor; } } [SerializeField] private Color selectedColor = Color.green;
[System.NonSerialized]
private Graphic cachedGraphic;
protected override void OnSelected(LeanSelect select)
{
UpdateColor();
}
protected override void OnDeselected(LeanSelect select)
{
UpdateColor();
}
public void UpdateColor()
{
if (cachedGraphic == null) cachedGraphic = GetComponent<Graphic>();
var color = Selectable != null && Selectable.IsSelected == true ? selectedColor : defaultColor;
cachedGraphic.color = color;
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSelectableGraphicColor;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSelectableGraphicColor_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
var updateColor = false;
Draw("defaultColor", ref updateColor, "The default color given to the SpriteRenderer.");
Draw("selectedColor", ref updateColor, "The color given to the SpriteRenderer when selected.");
if (updateColor == true)
{
Each(tgts, t => t.UpdateColor(), true);
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 0e3837cd44482a8459c76be5c56a41d3
timeCreated: 1511189620
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,88 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to change the color of the Renderer (e.g. MeshRenderer) attached to the current GameObject when selected.</summary>
[ExecuteInEditMode]
[RequireComponent(typeof(Renderer))]
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSelectableRendererColor")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Selectable Renderer Color")]
public class LeanSelectableRendererColor : LeanSelectableBehaviour
{
/// <summary>The default color given to the SpriteRenderer.</summary>
public Color DefaultColor { set { defaultColor = value; UpdateColor(); } get { return defaultColor; } } [SerializeField] private Color defaultColor = Color.white;
/// <summary>The color given to the SpriteRenderer when selected.</summary>
public Color SelectedColor { set { selectedColor = value; UpdateColor(); } get { return selectedColor; } } [SerializeField] private Color selectedColor = Color.green;
[System.NonSerialized]
private Renderer cachedRenderer;
[System.NonSerialized]
private MaterialPropertyBlock properties;
protected override void OnSelected(LeanSelect select)
{
UpdateColor();
}
protected override void OnDeselected(LeanSelect select)
{
UpdateColor();
}
protected override void Start()
{
base.Start();
UpdateColor();
}
public void UpdateColor()
{
if (cachedRenderer == null) cachedRenderer = GetComponent<Renderer>();
var color = Selectable != null && Selectable.IsSelected == true ? selectedColor : defaultColor;
if (properties == null)
{
properties = new MaterialPropertyBlock();
}
cachedRenderer.GetPropertyBlock(properties);
properties.SetColor("_Color", color);
cachedRenderer.SetPropertyBlock(properties);
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSelectableRendererColor;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSelectableRendererColor_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
var updateColor = false;
Draw("defaultColor", ref updateColor, "The default color given to the SpriteRenderer.");
Draw("selectedColor", ref updateColor, "The color given to the SpriteRenderer when selected.");
if (updateColor == true)
{
Each(tgts, t => t.UpdateColor(), true);
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 2baa936f016a9a14e86ac8ea42211f4a
timeCreated: 1480381136
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,71 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to change the color of the SpriteRenderer attached to the current GameObject when selected.</summary>
[ExecuteInEditMode]
[RequireComponent(typeof(SpriteRenderer))]
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSelectableSpriteRendererColor")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Selectable SpriteRenderer Color")]
public class LeanSelectableSpriteRendererColor : LeanSelectableBehaviour
{
/// <summary>The default color given to the SpriteRenderer.</summary>
public Color DefaultColor { set { defaultColor = value; UpdateColor(); } get { return defaultColor; } } [SerializeField] private Color defaultColor = Color.white;
/// <summary>The color given to the SpriteRenderer when selected.</summary>
public Color SelectedColor { set { selectedColor = value; UpdateColor(); } get { return selectedColor; } } [SerializeField] private Color selectedColor = Color.green;
[System.NonSerialized]
private SpriteRenderer cachedSpriteRenderer;
protected override void OnSelected(LeanSelect select)
{
UpdateColor();
}
protected override void OnDeselected(LeanSelect select)
{
UpdateColor();
}
public void UpdateColor()
{
if (cachedSpriteRenderer == null) cachedSpriteRenderer = GetComponent<SpriteRenderer>();
var color = Selectable != null && Selectable.IsSelected == true ? selectedColor : defaultColor;
cachedSpriteRenderer.color = color;
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSelectableSpriteRendererColor;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSelectableSpriteRendererColor_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
var updateColor = false;
Draw("defaultColor", ref updateColor, "The default color given to the SpriteRenderer.");
Draw("selectedColor", ref updateColor, "The color given to the SpriteRenderer when selected.");
if (updateColor == true)
{
serializedObject.ApplyModifiedProperties();
Each(tgts, t => t.UpdateColor(), true);
}
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: a45146ec471801c4dbce44f8dd62608f
timeCreated: 1480379101
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,76 @@
using UnityEngine;
using CW.Common;
namespace Lean.Common
{
/// <summary>This component allows you to spawn a prefab at the specified world point.
/// NOTE: For this component to work you must manually call the <b>Spawn</b> method from somewhere.</summary>
[HelpURL(LeanCommon.HelpUrlPrefix + "LeanSpawn")]
[AddComponentMenu(LeanCommon.ComponentPathPrefix + "Spawn")]
public class LeanSpawn : MonoBehaviour
{
public enum SourceType
{
ThisTransform,
Prefab
}
/// <summary>The prefab that this component can spawn.</summary>
public Transform Prefab { set { prefab = value; } get { return prefab; } } [SerializeField] private Transform prefab;
/// <summary>If you call <b>Spawn()</b>, where should the position come from?</summary>
public SourceType DefaultPosition { set { defaultPosition = value; } get { return defaultPosition; } } [SerializeField] private SourceType defaultPosition;
/// <summary>If you call <b>Spawn()</b>, where should the rotation come from?</summary>
public SourceType DefaultRotation { set { defaultRotation = value; } get { return defaultRotation; } } [SerializeField] private SourceType defaultRotation;
/// <summary>This will spawn <b>Prefab</b> at the current <b>Transform.position</b>.</summary>
public void Spawn()
{
if (prefab != null)
{
var position = defaultPosition == SourceType.Prefab ? prefab.position : transform.position;
var rotation = defaultRotation == SourceType.Prefab ? prefab.rotation : transform.rotation;
var clone = Instantiate(prefab, position, rotation);
clone.gameObject.SetActive(true);
}
}
/// <summary>This will spawn <b>Prefab</b> at the specified position in world space.</summary>
public void Spawn(Vector3 position)
{
if (prefab != null)
{
var rotation = defaultRotation == SourceType.Prefab ? prefab.rotation : transform.rotation;
var clone = Instantiate(prefab, position, rotation);
clone.gameObject.SetActive(true);
}
}
}
}
#if UNITY_EDITOR
namespace Lean.Common.Editor
{
using UnityEditor;
using TARGET = LeanSpawn;
[CanEditMultipleObjects]
[CustomEditor(typeof(TARGET))]
public class LeanSpawn_Editor : CwEditor
{
protected override void OnInspector()
{
TARGET tgt; TARGET[] tgts; GetTargets(out tgt, out tgts);
BeginError(Any(tgts, t => t.Prefab == null));
Draw("prefab", "The prefab that this component can spawn.");
EndError();
Draw("defaultPosition", "If you call Spawn(), where should the position come from?");
Draw("defaultRotation", "If you call Spawn(), where should the rotation come from?");
}
}
}
#endif

View File

@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 3bb775feca211c748b48cd3f9b59a4cf
timeCreated: 1480141605
licenseType: Store
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: