Last active
May 5, 2018 16:01
-
-
Save ByronMayne/a22d41179a610aa4a1a06779c0434858 to your computer and use it in GitHub Desktop.
A int version of a Vector2 in Unity. Supports explicit conversion between the two types as well as a few helpful helper functions. The second script is a property drawer that supports panning of values and right clicking to reset the value to zero (taking from 3Ds Max).
This file contains hidden or 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 UnityEngine; | |
using System; | |
[Serializable] | |
public struct Point | |
{ | |
[SerializeField] | |
public int x; | |
[SerializeField] | |
public int y; | |
public Point(int x, int y) | |
{ | |
this.x = x; | |
this.y = y; | |
} | |
/// <summary> | |
/// Gets the value at an index. | |
/// </summary> | |
/// <param name="index">The index you are trying to get.</param> | |
/// <returns>The value at that index.</returns> | |
public int this[int index] | |
{ | |
get | |
{ | |
int result; | |
if(index != 0) | |
{ | |
if(index != 1) | |
{ | |
throw new IndexOutOfRangeException("Index " + index.ToString() + " is out of range." ); | |
} | |
result = y; | |
} | |
else | |
{ | |
result = x; | |
} | |
return result; | |
} | |
set | |
{ | |
if (index != 0) | |
{ | |
if (index != 1) | |
{ | |
throw new IndexOutOfRangeException("Index " + index.ToString() + " is out of range."); | |
} | |
y = value; | |
} | |
else | |
{ | |
x = value; | |
} | |
} | |
} | |
/// <summary> | |
/// Sets the x and y components of an existing Point | |
/// </summary> | |
/// <param name="x">The new x value</param> | |
/// <param name="y">The new y value</param> | |
public void Set(int x, int y) | |
{ | |
this.x = x; | |
this.y = y; | |
} | |
/// <summary> | |
/// Shorthand for writing new Point(0,0). | |
/// </summary> | |
public static Point zero | |
{ | |
get | |
{ | |
return new Point(0, 0); | |
} | |
} | |
/// <summary> | |
/// Shorthand for writing new Point(1,1). | |
/// </summary> | |
public static Point one | |
{ | |
get | |
{ | |
return new Point(1, 1); | |
} | |
} | |
public static explicit operator Vector2(Point point) | |
{ | |
return new Vector2((float)point.x, (float)point.y); | |
} | |
public static explicit operator Point(Vector2 vector2) | |
{ | |
return new Point((int)vector2.x, (int)vector2.y); | |
} | |
public static Point operator +(Point lhs, Point rhs) | |
{ | |
lhs.x += rhs.x; | |
lhs.y += rhs.y; | |
return lhs; | |
} | |
public static Point operator -(Point lhs, Point rhs) | |
{ | |
lhs.x -= rhs.x; | |
lhs.y -= rhs.y; | |
return lhs; | |
} | |
public static Point operator *(Point lhs, Point rhs) | |
{ | |
lhs.x *= rhs.x; | |
lhs.y *= rhs.y; | |
return lhs; | |
} | |
public static Point operator /(Point lhs, Point rhs) | |
{ | |
lhs.x /= rhs.x; | |
lhs.y /= rhs.y; | |
return lhs; | |
} | |
public static bool operator ==(Point lhs, Point rhs) | |
{ | |
return lhs.x == rhs.x && lhs.y == rhs.x; | |
} | |
public static bool operator !=(Point lhs, Point rhs) | |
{ | |
return lhs.x != rhs.x || lhs.y != rhs.x; | |
} | |
public override int GetHashCode() | |
{ | |
unchecked // Overflow is fine, just wrap | |
{ | |
int hash = (int) 2166136261; | |
hash = (hash * 16777619) ^ x; | |
hash = (hash * 16777619) ^ y; | |
return hash; | |
} | |
} | |
public override bool Equals(object other) | |
{ | |
if(!(other is Point)) | |
{ | |
return false; | |
} | |
Point point = (Point)other; | |
return x == point.x && y == point.y; | |
} | |
public override string ToString() | |
{ | |
return string.Join(", ", new string[] { x.ToString(), y.ToString() }); | |
} | |
} |
This file contains hidden or 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 UnityEngine; | |
using UnityEditor; | |
/// <summary> | |
/// Draws a unique property drawer for <see cref="Point"/> which | |
/// draws the values in line. It also adds support for panning the values | |
/// and to right click the value to reset it to zero. | |
/// </summary> | |
[CustomPropertyDrawer(typeof(Point))] | |
public class PointPropertyDrawer : PropertyDrawer | |
{ | |
private const float FIELD_LABEL_WIDTH = 15f; | |
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) | |
{ | |
Rect labelRect = position; | |
labelRect.width = EditorGUIUtility.labelWidth; | |
GUI.Label(labelRect, label); | |
Rect contentRect = position; | |
contentRect.x += labelRect.width; | |
contentRect.width -= labelRect.width; | |
// Get our properties | |
SerializedProperty xValue = property.FindPropertyRelative("x"); | |
SerializedProperty yValue = property.FindPropertyRelative("y"); | |
Rect xContentRect = contentRect; | |
xContentRect.width /= 2f; | |
Rect yContentRect = xContentRect; | |
yContentRect.x += xContentRect.width; | |
float contentWidth = xContentRect.width; | |
DrawContent(xValue, xContentRect, contentWidth); | |
DrawContent(yValue, yContentRect, contentWidth); | |
HandleDrag(property); | |
} | |
/// <summary> | |
/// Used to allow the user to slide the values when clicking the label. | |
/// </summary> | |
private static void HandleDrag(SerializedProperty property) | |
{ | |
if (Event.current.type == EventType.MouseDrag) | |
{ | |
string propertyName = DragAndDrop.GetGenericData("PointEditorDrag") as string; | |
SerializedProperty modifiedProperty = property.FindPropertyRelative(propertyName); | |
// We could not find the property so something went wrong. Force quit it. | |
if (modifiedProperty == null) | |
{ | |
// Stops the drag events. | |
DragAndDrop.AcceptDrag(); | |
} | |
else | |
{ | |
// We modify our value by the mouses delta movement. | |
modifiedProperty.intValue += Event.current.delta.x > 0f ? 1 : -1; | |
} | |
Event.current.Use(); | |
} | |
// We have stopped dragging and dropping. | |
if (Event.current.type == EventType.MouseUp) | |
{ | |
// Clear our data. | |
DragAndDrop.PrepareStartDrag(); | |
} | |
} | |
private void DrawContent(SerializedProperty propertValue, Rect rect, float contentWidth) | |
{ | |
// We are about to draw a label we want to set our width. | |
rect.width = FIELD_LABEL_WIDTH; | |
// Changes the mouse icon to an left right arrow to show the user they can slide the values. | |
EditorGUIUtility.AddCursorRect(rect, MouseCursor.SplitResizeLeftRight); | |
// If we Context Click (right click) and are inside the rect of the label. | |
if (Event.current.type == EventType.ContextClick && rect.Contains(Event.current.mousePosition)) | |
{ | |
// Wipe the focus of the controls. | |
ClearFocus(); | |
// Force our value to zero. | |
propertValue.intValue = 0; | |
// Use the event so no one else can. | |
Event.current.Use(); | |
} | |
// We clicked on the label rect. | |
else if (Event.current.type == EventType.MouseDown && rect.Contains(Event.current.mousePosition)) | |
{ | |
// Wipe the focus of the controls. | |
ClearFocus(); | |
// Start the drag function | |
DragAndDrop.PrepareStartDrag(); | |
// We use the property name as our generic data so we can assign the property later. | |
DragAndDrop.SetGenericData("PointEditorDrag", propertValue.name); | |
// Use the current event. | |
Event.current.Use(); | |
} | |
// Draw the label | |
GUI.Label(rect, propertValue.displayName); | |
// Push the rect to the left by the labels width. | |
rect.x += FIELD_LABEL_WIDTH; | |
// Reset to the correct width. | |
rect.width = contentWidth - FIELD_LABEL_WIDTH; | |
// Draw our int value box. | |
propertValue.intValue = EditorGUI.IntField(rect, propertValue.intValue); | |
} | |
private void ClearFocus() | |
{ | |
// Clear the focus of the mouse (Unity will not let you change the value if it's in focus). | |
GUIUtility.hotControl = -1; | |
// Clear the keyboard focus. | |
GUIUtility.keyboardControl = -1; | |
} | |
} |
Updated the GetHashCode() function because it would have the following issue.
Point x = new Point(0, 1);
Point y = new Point(1, 0);
int xHash = x.GetHashCode();
int yHash = y.GetHashCode();
// Would be true.
bool sameHash = xHash == yHash;
Both hashes would be the same which is in correct since they have two different values.
// Typo in the == and != operators:
return lhs.x == rhs.x && lhs.y == rhs.x;
// Fix, braces for readability:
return (lhs.x == rhs.x) && (lhs.y == rhs.y);
Nice script. Just found a little mistake. Drag is not working if you have multiple Point fields in inspector. It's due to the "Event.current.Use();" in HandleDrag method. You should pass rect position to HandleDrag and check if it contains the mouse position before doing anything ;).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example Gif of the editor. The values being reset to zero is done by right clicking the label.
