Skip to content

Instantly share code, notes, and snippets.

@kirevdokimov
Created August 11, 2017 15:20
Show Gist options
  • Save kirevdokimov/49bac51825c9967f4df7ba9258d8882b to your computer and use it in GitHub Desktop.
Save kirevdokimov/49bac51825c9967f4df7ba9258d8882b to your computer and use it in GitHub Desktop.
using UnityEditor;
using UnityEngine;
//http://answers.unity3d.com/questions/463207/how-do-you-make-a-custom-handle-respond-to-the-mou.html
// Author http://answers.unity3d.com/users/57609/higekun.html
public class MyHandles{
public enum DragHandleResult{
none = 0,
LMBPress,
LMBClick,
LMBDoubleClick,
LMBDrag,
LMBRelease,
RMBPress,
RMBClick,
RMBDoubleClick,
RMBDrag,
RMBRelease
}
// Храним хеш строки DragHandleHash в качестве индивидуального ключа
private static readonly int s_DragHandleHash = "DragHandleHash".GetHashCode();
private static Vector2 s_DragHandleMouseStart;
private static Vector2 s_DragHandleMouseCurrent;
private static Vector3 s_DragHandleWorldStart;
private static float s_DragHandleClickTime;
private static int s_DragHandleClickID;
private static readonly float s_DragHandleDoubleClickInterval = 0.5f; // допустимый интервал между кликами, чтобы сработал doubleClick
private static bool s_DragHandleHasMoved;
// externally accessible to get the ID of the most resently processed DragHandle
public static int lastDragHandleID;
//
public static Vector3 DragHandle(Vector3 position, float handleSize, Handles.CapFunction capFunc, Color colorSelected,
out DragHandleResult result){
// Используем хеш ключ для получения индивидуального ключа контроля
var id = GUIUtility.GetControlID(s_DragHandleHash, FocusType.Passive);
// Переменная для хранения id, доступная извне (public)
lastDragHandleID = id;
//???//
var screenPosition = Handles.matrix.MultiplyPoint(position);
// Сохраняем действующую матрицу в отдельную переменную, чтобы после отработки всего кода вернуть Handles матрицу в прежнее состояние
var cachedMatrix = Handles.matrix;
result = DragHandleResult.none;
// Проверяем на события
switch(Event.current.GetTypeForControl(id)){
// Мышь нажата
case EventType.MouseDown:
/*
HandleUtility.nearestControl по видимому возвращает id ближайшего контроллера
public static int nearestControl{
get { return (double) HandleUtility.s_NearestDistance > 5.0 ? 0 : HandleUtility.s_NearestControl;}
set { HandleUtility.s_NearestControl = value; }
}
public static void AddControl(int controlId, float distance){
if ((double) distance < (double) HandleUtility.s_CustomPickDistance && (double) distance > 5.0)
distance = 5f;
if ((double) distance > (double) HandleUtility.s_NearestDistance)
return;
HandleUtility.s_NearestDistance = distance;
HandleUtility.s_NearestControl = controlId; <<< !
}
---
Event.current.button == 0 < left mouse button click
Event.current.button == 1 < right mouse button click
*/
if(HandleUtility.nearestControl == id && (Event.current.button == 0 || Event.current.button == 1)){
/*
Записывая текущий контроллер как hot мы позволяем только ему перехватывать события мыши
*/
GUIUtility.hotControl = id;
s_DragHandleMouseCurrent = s_DragHandleMouseStart = Event.current.mousePosition;
s_DragHandleWorldStart = position;
s_DragHandleHasMoved = false;
// использование Use позволяет как бы обнулить событие, что непозволит другим элементам среагировать на него
// http://answers.unity3d.com/questions/971262/when-to-actually-use-eventuse.html
Event.current.Use();
/* Когда ведешь контроллер к краю экрана эта строчка позволяет курсору мыши перескочить на другой край и продолжать двигаться
Типа как если взять rotation tool и двигать мышь постоянно влево
*/
EditorGUIUtility.SetWantsMouseJumping(1);
// Записываем состояние коонтроллера в DragHandleResult
if(Event.current.button == 0)
result = DragHandleResult.LMBPress;
else if(Event.current.button == 1)
result = DragHandleResult.RMBPress;
}
break;
// Мышь отпущена
case EventType.MouseUp:
if(GUIUtility.hotControl == id && (Event.current.button == 0 || Event.current.button == 1)){
// Отключаем горячесть контроллера
GUIUtility.hotControl = 0;
Event.current.Use();
// Отключаем фичу с перескоками
EditorGUIUtility.SetWantsMouseJumping(0);
if(Event.current.button == 0)
result = DragHandleResult.LMBRelease;
else if(Event.current.button == 1)
result = DragHandleResult.RMBRelease;
// Мышь не сдвинулась
if(Event.current.mousePosition == s_DragHandleMouseStart){
// Если текущий id клика идентичный предыдущему и интервал между кликами не высок то doubleClick = true;
var doubleClick = s_DragHandleClickID == id &&
Time.realtimeSinceStartup - s_DragHandleClickTime < s_DragHandleDoubleClickInterval;
s_DragHandleClickID = id; // Храним id клика
s_DragHandleClickTime = Time.realtimeSinceStartup; // Храним время клика
if(Event.current.button == 0)
result = doubleClick ? DragHandleResult.LMBDoubleClick : DragHandleResult.LMBClick;
else if(Event.current.button == 1)
result = doubleClick ? DragHandleResult.RMBDoubleClick : DragHandleResult.RMBClick;
}
}
break;
case EventType.MouseDrag:
// Если текущий контроллер активен
if(GUIUtility.hotControl == id){
// Рассчет новойц позиции мыши
s_DragHandleMouseCurrent += new Vector2(Event.current.delta.x, -Event.current.delta.y);
//???// Handles.matrix.MultiplyPoint
// Рассчет текущего смещения мыши относительно s_DragHandleMouseStart
var position2 = Camera.current.WorldToScreenPoint(Handles.matrix.MultiplyPoint(s_DragHandleWorldStart))
+ (Vector3) (s_DragHandleMouseCurrent - s_DragHandleMouseStart);
// Перевод в мировые координаты
position = Handles.matrix.inverse.MultiplyPoint(Camera.current.ScreenToWorldPoint(position2));
// Если камера смотрит ровно по направлению Vector3.forward или - Vector3.forward
if(Camera.current.transform.forward == Vector3.forward || Camera.current.transform.forward == -Vector3.forward)
position.z = s_DragHandleWorldStart.z; // То не изменяем позицию по z
// Такая же система
if(Camera.current.transform.forward == Vector3.up || Camera.current.transform.forward == -Vector3.up)
position.y = s_DragHandleWorldStart.y;
if(Camera.current.transform.forward == Vector3.right || Camera.current.transform.forward == -Vector3.right)
position.x = s_DragHandleWorldStart.x;
if(Event.current.button == 0)
result = DragHandleResult.LMBDrag;
else if(Event.current.button == 1)
result = DragHandleResult.RMBDrag;
s_DragHandleHasMoved = true;
// Сообщаем GUI, что контроллер изменил состояние входных данных
GUI.changed = true;
Event.current.Use();
}
break;
case EventType.Repaint:
var currentColour = Handles.color;
if(id == GUIUtility.hotControl && s_DragHandleHasMoved)
Handles.color = colorSelected;
Handles.matrix = Matrix4x4.identity;
capFunc(id, screenPosition, Quaternion.identity, handleSize, EventType.Repaint);
Handles.matrix = cachedMatrix;
Handles.color = currentColour;
break;
case EventType.Layout:
Handles.matrix = Matrix4x4.identity;
// Добавление контроллера
// Тут не берется во внимание какой cap стоит и тут всегда дистанция считается через DistanceToCircle
// Вообще такие дела с id и AddControl делаются в CapFunction
//HandleUtility.AddControl(id, HandleUtility.DistanceToCircle(screenPosition, handleSize));
capFunc(id, screenPosition, Quaternion.identity, handleSize, EventType.Layout);
Handles.matrix = cachedMatrix;
break;
}
return position;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment