Skip to content

Instantly share code, notes, and snippets.

@thebeardphantom
Last active June 17, 2022 23:42
Show Gist options
  • Save thebeardphantom/38c7317af90a353fd5a3dcc5702aed24 to your computer and use it in GitHub Desktop.
Save thebeardphantom/38c7317af90a353fd5a3dcc5702aed24 to your computer and use it in GitHub Desktop.
AttenuationCurve
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
}
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