Basically, it teleports the selection to the first collision encountered by the mouse. Very useful when moving stuff around. Invoked with CTRL+T.
There’s a small utility used to get the mouse position outside a SceneView context;
/// <summary>
/// This is used to find the mouse position when it's over a SceneView.
/// Used by tools that are menu invoked.
/// </summary>
[InitializeOnLoad]
public class MouseHelper : Editor
{
private static Vector2 position;
public static Vector2 Position
{
get { return position; }
}
static MouseHelper()
{
SceneView.onSceneGUIDelegate += UpdateView;
}
private static void UpdateView(SceneView sceneView)
{
if (Event.current != null)
position = new Vector2(Event.current.mousePosition.x + sceneView.position.x, Event.current.mousePosition.y + sceneView.position.y);
}
}
Finally, the code to teleport stuff around;
/// <summary>
/// Teleport the selection to the first collider found by the mouse.
/// </summary>
public class Teleport
{
[MenuItem("Tool/Teleport %t")]
public static void TeleportTool()
{
if (Selection.gameObjects.Length == 0)
return;
SceneView view = EditorWindow.GetWindow<SceneView>();
Vector2 position = MouseHelper.Position;
if (position.x < view.position.x || position.x > view.position.x + view.position.width)
return;
if (position.y < view.position.y + SceneView.kToolbarHeight || position .y > view.position.y + view.position.height)
return;
Vector3 offset = new Vector3((position.x - view.position.x) / view.position.width, ((view.position.height + view.position.y) - position.y) / view.position.height, 0);
Ray ray = view.camera.ViewportPointToRay(offset);
RaycastHit[] hits = Physics.RaycastAll(ray);
if (hits.Length == 0)
return;
RaycastHit? best = null;
foreach (RaycastHit hit in hits)
{
if (best == null !Selected(hit))
best = hit;
else if (best != null hit.distance < ((RaycastHit)best).distance !Selected(hit))
best = hit;
}
if (best != null)
{
Undo.RegisterUndo(Selection.gameObjects, "Teleport");
foreach (GameObject o in Selection.gameObjects)
{
o.transform.position = ((RaycastHit)best).point;
}
}
}
private static bool Selected(RaycastHit hit)
{
foreach (GameObject o in Selection.gameObjects)
{
if ((hit.collider != null o == hit.collider.gameObject) ||
(hit.rigidbody != null o == hit.rigidbody.gameObject))
return true;
}
return false;
}
}
Well… the script doesnt work with unity 2018.3.6f1, someone now why?
Unity tell that he doesn’t know “SceneView.kToolbarHeight” and some “if” miss “&&” or “||” (but i don’t know one or other)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// This is used to find the mouse position when it's over a SceneView.
/// Used by tools that are menu invoked.
/// </summary>
[InitializeOnLoad]
public class MouseHelper : Editor
{
private static Vector2 position;
public static Vector2 Position
{
get { return position; }
}
static MouseHelper()
{
SceneView.duringSceneGui += UpdateView;
}
private static void UpdateView(SceneView sceneView)
{
if (Event.current != null)
position = new Vector2(Event.current.mousePosition.x + sceneView.position.x, Event.current.mousePosition.y + sceneView.position.y);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
/// <summary>
/// Teleport the selection to the first collider found by the mouse.
/// </summary>
public class EditorTeleport
{
[MenuItem("MyTools/Teleport %²")] //ctrl + ² shortcut in Unity Shortcuts
public static void TeleportTool()
{
if (Selection.gameObjects.Length == 0)
return;
SceneView view = EditorWindow.GetWindow<SceneView>();
Vector2 position = MouseHelper.Position;
if (position.x < view.position.x || position.x > view.position.x + view.position.width)
return;
if (position.y < view.position.y || position.y > view.position.y + view.position.height)
return;
Vector3 offset = new Vector3((position.x - view.position.x) / view.position.width, ((view.position.height + view.position.y) - position.y) / view.position.height, 0);
Ray ray = view.camera.ViewportPointToRay(offset);
RaycastHit[] hits = Physics.RaycastAll(ray);
if (hits.Length == 0)
return;
RaycastHit? best = null;
foreach(RaycastHit hit in hits)
{
if (best == null || Selected(hit))
best = hit;
else if (best != null && hit.distance < ((RaycastHit)best).distance && !Selected(hit))
best = hit;
}
if(best != null)
{
Undo.RegisterCompleteObjectUndo(Selection.gameObjects, "Teleport");
foreach (GameObject o in Selection.gameObjects)
{
o.transform.position = ((RaycastHit)best).point;
}
}
}
private static bool Selected(RaycastHit hit)
{
foreach (GameObject o in Selection.gameObjects)
{
if((hit.collider != null && o == hit.collider.gameObject) || (hit.rigidbody != null && o == hit.rigidbody.gameObject))
return true;
}
return false;
}
}
I changed the shortcut to be ctrl + ².
Make sure to add the shortcut to Unity Shortcuts in editor as it is how it is handled now since the original post.
Unity Shortcuts are really easy to add and edit and check conflicts.
Once the script has compiled, you will easily find the shortcut by searching “MyTools/Teleport”.
I will customize the script to make it so it teleports my player (without selection) if there is one in the scene on pressing ‘²’.