Last active
June 17, 2022 23:42
-
-
Save thebeardphantom/38c7317af90a353fd5a3dcc5702aed24 to your computer and use it in GitHub Desktop.
AttenuationCurve
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using Unity.Mathematics; | |
using UnityEngine; | |
[Serializable] | |
public struct AttenuationCurve | |
{ | |
#region Properties | |
[field: SerializeField] | |
public float RangeMin { get; private set; } | |
[field: SerializeField] | |
public float RangeMax { get; private set; } | |
[field: Min(0f)] | |
[field: SerializeField] | |
public float Power { get; private set; } | |
#endregion | |
#region Methods | |
public float GetValue(float input) | |
{ | |
var t = math.unlerp(RangeMin, RangeMax, input); | |
t = math.saturate(t); | |
return 1 - math.pow(t, Power); | |
} | |
#endregion | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Unity.Mathematics; | |
using UnityEditor; | |
using UnityEditorInternal; | |
using UnityEngine; | |
using UnityEngine.Pool; | |
[CustomPropertyDrawer(typeof(AttenuationCurve))] | |
public class AttenuationCurvePropertyDrawer : PropertyDrawer | |
{ | |
#region Fields | |
private const float CURVE_BOX_HEIGHT = 50f; | |
private static readonly float _valueBoxHeight = EditorGUIUtility.singleLineHeight; | |
private static readonly float _curveAreaTotalHeight = CURVE_BOX_HEIGHT + _valueBoxHeight; | |
#endregion | |
#region Methods | |
public override void OnGUI( | |
Rect position, | |
SerializedProperty property, | |
GUIContent label) | |
{ | |
var propRect = new Rect(position); | |
propRect.yMax -= CURVE_BOX_HEIGHT; | |
EditorGUI.PropertyField(position, property, label, true); | |
if (!property.isExpanded) | |
{ | |
return; | |
} | |
using (new EditorGUI.IndentLevelScope(1)) | |
{ | |
var curveRect = new Rect(position); | |
curveRect.yMin = curveRect.yMax - _curveAreaTotalHeight; | |
DrawCurve(curveRect, property); | |
} | |
} | |
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) | |
{ | |
return EditorGUI.GetPropertyHeight(property, label, true) | |
+ (property.isExpanded | |
? _curveAreaTotalHeight | |
: 0f); | |
} | |
private void DrawCurve(Rect rect, SerializedProperty property) | |
{ | |
var owner = property.serializedObject.targetObject; | |
var attenuationCurve = (AttenuationCurve)fieldInfo.GetValue(owner); | |
var valueBoxRect = new Rect(rect) | |
{ | |
height = _valueBoxHeight | |
}; | |
var curveBoxRect = new Rect(rect); | |
curveBoxRect = EditorGUI.IndentedRect(curveBoxRect); | |
curveBoxRect.yMin += _valueBoxHeight; | |
GUI.enabled = false; | |
var isMouseInCurveBox = curveBoxRect.Contains(Event.current.mousePosition); | |
if (isMouseInCurveBox) | |
{ | |
// [0, 1] for mouse X inside of gui rect | |
var t = math.unlerp(curveBoxRect.xMin, curveBoxRect.xMax, Event.current.mousePosition.x); | |
// [0, attenuationCurve.RangeMax] to calculate accuracy at distance | |
var distance = math.lerp(0f, attenuationCurve.RangeMax, t); | |
var value = attenuationCurve.GetValue(distance); | |
EditorGUI.LabelField(valueBoxRect, $"D: {distance:0.00}", $"Value: {value:0.00}"); | |
} | |
else | |
{ | |
EditorGUI.LabelField(valueBoxRect, "D: N/A", "Value: N/A"); | |
} | |
GUI.enabled = true; | |
EditorGUI.DrawRect(curveBoxRect, new Color(0.34f, 0.34f, 0.34f)); | |
// Draw X axis lines | |
var step = curveBoxRect.width / 10f; | |
Handles.color = Color.gray; | |
for (var x = curveBoxRect.xMin; x <= curveBoxRect.xMax; x += step) | |
{ | |
Handles.DrawLine(new Vector3(x, curveBoxRect.yMin), new Vector3(x, curveBoxRect.yMax)); | |
} | |
// Draw Y axis lines | |
step = curveBoxRect.height / 10f; | |
Handles.color = Color.gray; | |
for (var y = curveBoxRect.yMin; y <= curveBoxRect.yMax; y += step) | |
{ | |
Handles.DrawLine(new Vector3(curveBoxRect.xMin, y), new Vector3(curveBoxRect.xMax, y)); | |
} | |
// Get line points for curve | |
const float PADDING = 2f; | |
var insetRect = new Rect(curveBoxRect); | |
insetRect.xMin += PADDING; | |
insetRect.yMin += PADDING; | |
insetRect.yMax -= PADDING; | |
insetRect.xMax -= PADDING; | |
step = insetRect.width / 1000f; | |
using var _ = ListPool<Vector3>.Get(out var points); | |
for (var x = insetRect.xMin; x < insetRect.xMax; x += step) | |
{ | |
var t = math.unlerp(insetRect.xMin, insetRect.xMax, x); | |
var distance = math.lerp(0f, attenuationCurve.RangeMax, t); | |
var accuracyAtDistance = math.lerp(insetRect.yMax, insetRect.yMin, attenuationCurve.GetValue(distance)); | |
points.Add(new Vector3(x, accuracyAtDistance)); | |
} | |
// Draw curve | |
Handles.color = Color.white; | |
Handles.DrawAAPolyLine(2.5f, points.ToArray()); | |
if (isMouseInCurveBox) | |
{ | |
// Draw mouse crosshair | |
InternalEditorUtility.RepaintAllViews(); | |
var gui = Event.current.mousePosition; | |
Handles.color = Color.black; | |
Handles.DrawLine(new Vector3(gui.x, curveBoxRect.yMin), new Vector3(gui.x, curveBoxRect.yMax)); | |
Handles.DrawLine(new Vector3(curveBoxRect.xMin, gui.y), new Vector3(curveBoxRect.xMax, gui.y)); | |
} | |
} | |
#endregion | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment