Last active
May 5, 2024 05:32
-
-
Save dvddarias/dd4206dec54a3f48aa536126de2594b0 to your computer and use it in GitHub Desktop.
Unity editor script to precisely move, rotate and scale GameObjects on a 2D scene, using the arrow keys.
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
/* | |
Unity editor script to precisely move, rotate and scale GameObjects on 2D scenes, using the arrow keys. | |
Notes: | |
- To use it just include it on an "Assets/Editor" folder on your project. | |
- The action depends on the selected tool and the size of the movement depends on the Scene view zoom. | |
- The more "zoomed in" is the scene view the smaller is the movement step. | |
- It will work when the current Scene tab is in 2D mode and there is at least one gameObject selected, | |
otherwise the scene camera will move as usual :) | |
David Darias. Lic: MIT. Dec 2017. | |
*/ | |
using System; | |
using System.Collections; | |
using UnityEngine; | |
using UnityEditor; | |
[InitializeOnLoad] | |
public class Arrows2DMovement { | |
private static float viewportPercentRotation = 0.1f; | |
private static float viewportPercentMovement = 0.01f; | |
private static float viewportPercentScale = 0.001f; | |
private static int rotationSnapAngle = 15; | |
static Arrows2DMovement(){ | |
//avoid registering twice to the SceneGUI delegate | |
SceneView.onSceneGUIDelegate -= OnSceneView; | |
SceneView.onSceneGUIDelegate += OnSceneView; | |
} | |
static void OnSceneView(SceneView sceneView){ | |
Event currentEvent = Event.current; | |
//if the event is a keyDown on an orthographic camera | |
if( currentEvent.isKey | |
&& currentEvent.type == EventType.KeyDown | |
&& (currentEvent.modifiers == EventModifiers.None || currentEvent.modifiers == EventModifiers.FunctionKey) //arrow keys are function keys | |
&& sceneView.camera.orthographic){ | |
//choose the right direction to move | |
switch (currentEvent.keyCode) | |
{ | |
case KeyCode.RightArrow: | |
moveSelectedObjects(Vector3.right, sceneView); | |
break; | |
case KeyCode.LeftArrow: | |
moveSelectedObjects(Vector3.left, sceneView); | |
break; | |
case KeyCode.UpArrow: | |
moveSelectedObjects(Vector3.up, sceneView); | |
break; | |
case KeyCode.DownArrow: | |
moveSelectedObjects(Vector3.down, sceneView); | |
break; | |
} | |
} | |
} | |
private static void moveSelectedObjects(Vector3 direction, SceneView sceneView){ | |
//the step size is a percent of the scene viewport | |
Vector2 cameraSize = getCameraSize(sceneView.camera); | |
Vector3 step = Vector3.Scale(direction, cameraSize); | |
//choose the transformation based on the selected tool | |
Action<Transform,Vector3> transform; | |
switch (Tools.current) | |
{ | |
case Tool.Rotate: | |
transform = rotateObject; | |
break; | |
case Tool.Scale: | |
transform = scaleObject; | |
break; | |
default: | |
transform = moveObject; | |
break; | |
} | |
//get the current scene selection and move them | |
var selection = Selection.GetFiltered(typeof(GameObject), SelectionMode.Editable | SelectionMode.ExcludePrefab); | |
//apply the transformation to every selected gameObject | |
for (int i = 0; i < selection.Length; i++) transform( (selection[i] as GameObject).transform, step); | |
//only consume the event if there was at least one gameObject selected, otherwise the camera will move as usual :) | |
if(selection.Length>0) Event.current.Use(); | |
} | |
private static Vector2 getCameraSize(Camera sceneCamera){ | |
Vector3 topRightCorner = sceneCamera.ViewportToWorldPoint(new Vector2(1,1)); | |
Vector3 bottomLeftCorner = sceneCamera.ViewportToWorldPoint(new Vector2(0, 0)); | |
return (topRightCorner - bottomLeftCorner); | |
} | |
private static void rotateObject(Transform t, Vector3 rotation) | |
{ | |
//allow undo of the rotation | |
Undo.RecordObject(t, "Rotation Step"); | |
if(rotation.y!=0){ | |
//move the rotation to the nearest multiple of the snap angle. | |
Vector3 currentRotation = t.rotation.eulerAngles; | |
if(rotation.y>0){ | |
currentRotation.z = (Mathf.RoundToInt(currentRotation.z) / rotationSnapAngle) * rotationSnapAngle; | |
currentRotation.z += rotationSnapAngle; | |
} | |
else{ | |
int current = Mathf.RoundToInt(currentRotation.z); | |
if(current%rotationSnapAngle == 0) currentRotation.z -= rotationSnapAngle; | |
else currentRotation.z = (current / rotationSnapAngle) * rotationSnapAngle; | |
} | |
//set the new angle | |
t.rotation = Quaternion.Euler(currentRotation); | |
} | |
else{ | |
t.Rotate(new Vector3(0,0, -rotation.x) * viewportPercentRotation); | |
} | |
} | |
private static void scaleObject(Transform t, Vector3 scale) | |
{ | |
//allow undo of the scale | |
Undo.RecordObject(t, "Scale Step"); | |
t.localScale = t.localScale + scale * viewportPercentScale; | |
} | |
private static void moveObject(Transform t, Vector3 movement){ | |
//allow undo of the movements | |
Undo.RecordObject(t, "Move Step"); | |
t.position = t.position + movement * viewportPercentMovement; | |
} | |
} |
Great script, thank you! In case there's need of a fixed precise step, one should modify moveSelectedObjects method:
var fixedSize = new Vector2(1f, 1f); //needed values
Vector3 step = Vector3.Scale(direction, fixedSize);
Niiiice!! Such a shame that you have to add every small basic stuff yourself.
This helps a bit with Unity 2021.2.1xx versions as the scene tools are partially broken.
On this version it just complained that "onSceneGUIDelegate" is obsolete. It seems it can just be replaced with the recommendation like this, if you want to get rid of the warnings:
28 SceneView.duringSceneGui -= OnSceneView;
29 SceneView.duringSceneGui += OnSceneView;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Any contribution is of course welcome : )