Snapping to vertex on one axis only (while holding V in scene editor)?

In the Unity scene editor, I’d like to position an object in relation to another one nearby. In a 3d software, I could move it once in one or two axis’ horizontally first and then move it again to align it vertically.

To do this, I would need to be able to lock one or two axis’. For example, to move an object down to snap in the Z axis, you can temporarily disable the X and Y movement.

In Blender you would press Z to lock an object you’re moving to the Z axis or shift-Z to lock it to X and Y.

Is there a way to do this with a Unity editor script?

Could it be scripted? Is there an asset in the asset store that allows this?

Are you trying to move the object via an editor script, or via your mouse?

If it’s your mouse, you can just use the individual arrows on the position gizmo to move the object on only a particular axis, or use one of the 3 small “squares” at the center of the gizmo to move the object along a particular plane (along 2 axes).

“Snapping to vertex”. It’s in the title :wink:

And I am looking for the same functionality… Holding v to snap vertices is great and all, but one would normally also have the functionality to constrain the vertex-snap along a chosen axis. I haven’t found this to be possible in Unity. But I will code it myself now

Ok, I have made a thing. It might have been made before by others, but at least I never found it.
This recreate some functionality I have been missing from 3Ds Max, and which the OP also seem to need, where you can constrain movement in the editor to only one axis, even when snapping to vertices and such. It is an Overlay script with a new element that goes along. And so it NEEDS to be placed in folder called “Editor” (Filename: AxisConstrainOverlay.cs).

To activate it you as you do with other scene overlay tools: Three dots->Overlay Menu->Axis Constrain
It’s a toggle and dropdown in one, and can be activated and deactived by clicking the name, and the axis chosen by clicking the arrow. Also F5, F6 and F7 changes the axis (And if held down while the tool is deactive, it will activate until key up)

Anyways. Fun to make, learned something new. Some hacks in it (Don’t mention the icon creation!!!), but otherwise pretty robust. Probably did something backwards, but it is what it is :smile:

using System.Collections.Generic;
using UnityEngine;
using UnityEditor.Toolbars;
using UnityEditor.Overlays;
using UnityEngine.UIElements;
using UnityEditor;

[Overlay(typeof(SceneView), "AxisConstrain", "Axis Constrain", true)]
public class AxisConstrainOverlay : ToolbarOverlay
{
    public static Dictionary<Transform, Vector3> initPositions = new Dictionary<Transform, Vector3>();
    public static Texture2D icon;
    AxisConstrainOverlay() : base(
        AxisDropDown.id
        )
    {
        icon = Icon();
        collapsedIcon = icon;
    }

    public static Texture2D Icon ()
    {
        //Build the icon to prevent needing an asset for it. Oh well...
        Color[] txColor = new Color[256];
        txColor[0] = new Color(1f, 1f, 1f, 0f);
        txColor[1] = new Color(1f, 1f, 1f, 0f);
        txColor[2] = new Color(1f, 1f, 1f, 0f);
        txColor[3] = new Color(1f, 1f, 1f, 0f);
        txColor[4] = new Color(1f, 1f, 1f, 0f);
        txColor[5] = new Color(1f, 1f, 1f, 0f);
        txColor[6] = new Color(1f, 1f, 1f, 0f);
        txColor[7] = new Color(1f, 1f, 1f, 0f);
        txColor[8] = new Color(1f, 1f, 1f, 0f);
        txColor[9] = new Color(1f, 1f, 1f, 0f);
        txColor[10] = new Color(1f, 1f, 1f, 0f);
        txColor[11] = new Color(1f, 1f, 1f, 0f);
        txColor[12] = new Color(1f, 1f, 1f, 0f);
        txColor[13] = new Color(1f, 1f, 1f, 0f);
        txColor[14] = new Color(1f, 1f, 1f, 0f);
        txColor[15] = new Color(1f, 1f, 1f, 0f);
        txColor[16] = new Color(1f, 1f, 1f, 0f);
        txColor[17] = new Color(1f, 1f, 1f, 0f);
        txColor[18] = new Color(1f, 1f, 1f, 0f);
        txColor[19] = new Color(1f, 1f, 1f, 0f);
        txColor[20] = new Color(1f, 1f, 1f, 0f);
        txColor[21] = new Color(1f, 1f, 1f, 0f);
        txColor[22] = new Color(1f, 1f, 1f, 0f);
        txColor[23] = new Color(1f, 1f, 1f, 0f);
        txColor[24] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[25] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[26] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[27] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[28] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[29] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[30] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[31] = new Color(1f, 1f, 1f, 0f);
        txColor[32] = new Color(1f, 1f, 1f, 0f);
        txColor[33] = new Color(1f, 1f, 1f, 0f);
        txColor[34] = new Color(1f, 1f, 1f, 0f);
        txColor[35] = new Color(1f, 1f, 1f, 0f);
        txColor[36] = new Color(1f, 1f, 1f, 0f);
        txColor[37] = new Color(1f, 1f, 1f, 0f);
        txColor[38] = new Color(1f, 1f, 1f, 0f);
        txColor[39] = new Color(1f, 1f, 1f, 0f);
        txColor[40] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[41] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[42] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[43] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[44] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[45] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[46] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[47] = new Color(1f, 1f, 1f, 0f);
        txColor[48] = new Color(1f, 1f, 1f, 0f);
        txColor[49] = new Color(1f, 1f, 1f, 0f);
        txColor[50] = new Color(1f, 1f, 1f, 0f);
        txColor[51] = new Color(1f, 1f, 1f, 0f);
        txColor[52] = new Color(1f, 1f, 1f, 0f);
        txColor[53] = new Color(1f, 1f, 1f, 0f);
        txColor[54] = new Color(1f, 1f, 1f, 0f);
        txColor[55] = new Color(1f, 1f, 1f, 0f);
        txColor[56] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[57] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[58] = new Color(1f, 1f, 1f, 0.03921569f);
        txColor[59] = new Color(1f, 1f, 1f, 0.6078432f);
        txColor[60] = new Color(1f, 1f, 1f, 0.8352942f);
        txColor[61] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[62] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[63] = new Color(1f, 1f, 1f, 0f);
        txColor[64] = new Color(1f, 1f, 1f, 0f);
        txColor[65] = new Color(1f, 1f, 1f, 0.2941177f);
        txColor[66] = new Color(1f, 1f, 1f, 0.1960784f);
        txColor[67] = new Color(1f, 1f, 1f, 0f);
        txColor[68] = new Color(1f, 1f, 1f, 0f);
        txColor[69] = new Color(1f, 1f, 1f, 0f);
        txColor[70] = new Color(1f, 1f, 1f, 0f);
        txColor[71] = new Color(1f, 1f, 1f, 0f);
        txColor[72] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[73] = new Color(1f, 1f, 1f, 0.1960784f);
        txColor[74] = new Color(1f, 1f, 1f, 0.8470589f);
        txColor[75] = new Color(1f, 1f, 1f, 0.5882353f);
        txColor[76] = new Color(1f, 1f, 1f, 0.03921569f);
        txColor[77] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[78] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[79] = new Color(1f, 1f, 1f, 0f);
        txColor[80] = new Color(1f, 1f, 1f, 0f);
        txColor[81] = new Color(1f, 1f, 1f, 0.282353f);
        txColor[82] = new Color(1f, 1f, 1f, 0.854902f);
        txColor[83] = new Color(1f, 1f, 1f, 0.7607844f);
        txColor[84] = new Color(1f, 1f, 1f, 0.3333333f);
        txColor[85] = new Color(1f, 1f, 1f, 0.01568628f);
        txColor[86] = new Color(1f, 1f, 1f, 0f);
        txColor[87] = new Color(1f, 1f, 1f, 0.003921569f);
        txColor[88] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[89] = new Color(1f, 1f, 1f, 0.8980393f);
        txColor[90] = new Color(1f, 1f, 1f, 0.3058824f);
        txColor[91] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[92] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[93] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[94] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[95] = new Color(1f, 1f, 1f, 0f);
        txColor[96] = new Color(1f, 1f, 1f, 0f);
        txColor[97] = new Color(1f, 1f, 1f, 0f);
        txColor[98] = new Color(1f, 1f, 1f, 0.007843138f);
        txColor[99] = new Color(1f, 1f, 1f, 0.2941177f);
        txColor[100] = new Color(1f, 1f, 1f, 0.7254902f);
        txColor[101] = new Color(1f, 1f, 1f, 0.8862746f);
        txColor[102] = new Color(1f, 1f, 1f, 0.4784314f);
        txColor[103] = new Color(1f, 1f, 1f, 0.7333333f);
        txColor[104] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[105] = new Color(1f, 1f, 1f, 0.1019608f);
        txColor[106] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[107] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[108] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[109] = new Color(1f, 0.4313726f, 0.2509804f, 0f);
        txColor[110] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[111] = new Color(1f, 1f, 1f, 0f);
        txColor[112] = new Color(1f, 1f, 1f, 0f);
        txColor[113] = new Color(1f, 1f, 1f, 0f);
        txColor[114] = new Color(1f, 1f, 1f, 0f);
        txColor[115] = new Color(1f, 1f, 1f, 0f);
        txColor[116] = new Color(1f, 1f, 1f, 0f);
        txColor[117] = new Color(1f, 1f, 1f, 0.1568628f);
        txColor[118] = new Color(1f, 1f, 1f, 0.5490196f);
        txColor[119] = new Color(1f, 1f, 1f, 0.937255f);
        txColor[120] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[121] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[122] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[123] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[124] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[125] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[126] = new Color(1f, 0.4313726f, 0.2509804f, 1f);
        txColor[127] = new Color(1f, 1f, 1f, 0f);
        txColor[128] = new Color(1f, 1f, 1f, 0f);
        txColor[129] = new Color(1f, 1f, 1f, 0f);
        txColor[130] = new Color(1f, 1f, 1f, 0f);
        txColor[131] = new Color(1f, 1f, 1f, 0f);
        txColor[132] = new Color(1f, 1f, 1f, 0f);
        txColor[133] = new Color(1f, 1f, 1f, 0f);
        txColor[134] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[135] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[136] = new Color(1f, 1f, 1f, 0f);
        txColor[137] = new Color(1f, 1f, 1f, 0f);
        txColor[138] = new Color(1f, 1f, 1f, 0f);
        txColor[139] = new Color(1f, 1f, 1f, 0f);
        txColor[140] = new Color(1f, 1f, 1f, 0f);
        txColor[141] = new Color(1f, 1f, 1f, 0f);
        txColor[142] = new Color(1f, 1f, 1f, 0f);
        txColor[143] = new Color(1f, 1f, 1f, 0f);
        txColor[144] = new Color(1f, 1f, 1f, 0f);
        txColor[145] = new Color(1f, 1f, 1f, 0f);
        txColor[146] = new Color(1f, 1f, 1f, 0f);
        txColor[147] = new Color(1f, 1f, 1f, 0f);
        txColor[148] = new Color(1f, 1f, 1f, 0f);
        txColor[149] = new Color(1f, 1f, 1f, 0f);
        txColor[150] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[151] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[152] = new Color(1f, 1f, 1f, 0f);
        txColor[153] = new Color(1f, 1f, 1f, 0f);
        txColor[154] = new Color(1f, 1f, 1f, 0f);
        txColor[155] = new Color(1f, 1f, 1f, 0f);
        txColor[156] = new Color(1f, 1f, 1f, 0f);
        txColor[157] = new Color(1f, 1f, 1f, 0f);
        txColor[158] = new Color(1f, 1f, 1f, 0f);
        txColor[159] = new Color(1f, 1f, 1f, 0f);
        txColor[160] = new Color(1f, 1f, 1f, 0f);
        txColor[161] = new Color(1f, 1f, 1f, 0f);
        txColor[162] = new Color(1f, 1f, 1f, 0f);
        txColor[163] = new Color(1f, 1f, 1f, 0f);
        txColor[164] = new Color(1f, 1f, 1f, 0f);
        txColor[165] = new Color(1f, 1f, 1f, 0f);
        txColor[166] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[167] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[168] = new Color(1f, 1f, 1f, 0f);
        txColor[169] = new Color(1f, 1f, 1f, 0f);
        txColor[170] = new Color(1f, 1f, 1f, 0f);
        txColor[171] = new Color(1f, 1f, 1f, 0f);
        txColor[172] = new Color(1f, 1f, 1f, 0f);
        txColor[173] = new Color(1f, 1f, 1f, 0f);
        txColor[174] = new Color(1f, 1f, 1f, 0f);
        txColor[175] = new Color(1f, 1f, 1f, 0f);
        txColor[176] = new Color(1f, 1f, 1f, 0f);
        txColor[177] = new Color(1f, 1f, 1f, 0f);
        txColor[178] = new Color(1f, 1f, 1f, 0f);
        txColor[179] = new Color(1f, 1f, 1f, 0f);
        txColor[180] = new Color(1f, 1f, 1f, 0f);
        txColor[181] = new Color(1f, 1f, 1f, 0f);
        txColor[182] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[183] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[184] = new Color(1f, 1f, 1f, 0f);
        txColor[185] = new Color(1f, 1f, 1f, 0f);
        txColor[186] = new Color(1f, 1f, 1f, 0f);
        txColor[187] = new Color(1f, 1f, 1f, 0f);
        txColor[188] = new Color(1f, 1f, 1f, 0f);
        txColor[189] = new Color(1f, 1f, 1f, 0f);
        txColor[190] = new Color(1f, 1f, 1f, 0f);
        txColor[191] = new Color(1f, 1f, 1f, 0f);
        txColor[192] = new Color(1f, 1f, 1f, 0f);
        txColor[193] = new Color(1f, 1f, 1f, 0f);
        txColor[194] = new Color(1f, 1f, 1f, 0f);
        txColor[195] = new Color(1f, 1f, 1f, 0f);
        txColor[196] = new Color(1f, 1f, 1f, 0f);
        txColor[197] = new Color(1f, 1f, 1f, 0f);
        txColor[198] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[199] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[200] = new Color(1f, 1f, 1f, 0f);
        txColor[201] = new Color(1f, 1f, 1f, 0f);
        txColor[202] = new Color(1f, 1f, 1f, 0f);
        txColor[203] = new Color(1f, 1f, 1f, 0f);
        txColor[204] = new Color(1f, 1f, 1f, 0f);
        txColor[205] = new Color(1f, 1f, 1f, 0f);
        txColor[206] = new Color(1f, 1f, 1f, 0f);
        txColor[207] = new Color(1f, 1f, 1f, 0f);
        txColor[208] = new Color(1f, 1f, 1f, 0f);
        txColor[209] = new Color(1f, 1f, 1f, 0f);
        txColor[210] = new Color(1f, 1f, 1f, 0f);
        txColor[211] = new Color(1f, 1f, 1f, 0f);
        txColor[212] = new Color(1f, 1f, 1f, 0f);
        txColor[213] = new Color(1f, 1f, 1f, 0f);
        txColor[214] = new Color(1f, 1f, 1f, 0.05882353f);
        txColor[215] = new Color(1f, 1f, 1f, 0.9411765f);
        txColor[216] = new Color(1f, 1f, 1f, 0f);
        txColor[217] = new Color(1f, 1f, 1f, 0f);
        txColor[218] = new Color(1f, 1f, 1f, 0f);
        txColor[219] = new Color(1f, 1f, 1f, 0f);
        txColor[220] = new Color(1f, 1f, 1f, 0f);
        txColor[221] = new Color(1f, 1f, 1f, 0f);
        txColor[222] = new Color(1f, 1f, 1f, 0f);
        txColor[223] = new Color(1f, 1f, 1f, 0f);
        txColor[224] = new Color(1f, 1f, 1f, 0f);
        txColor[225] = new Color(1f, 1f, 1f, 0f);
        txColor[226] = new Color(1f, 1f, 1f, 0f);
        txColor[227] = new Color(1f, 1f, 1f, 0f);
        txColor[228] = new Color(1f, 1f, 1f, 0f);
        txColor[229] = new Color(1f, 1f, 1f, 0f);
        txColor[230] = new Color(1f, 1f, 1f, 0.03137255f);
        txColor[231] = new Color(1f, 1f, 1f, 0.4705883f);
        txColor[232] = new Color(1f, 1f, 1f, 0f);
        txColor[233] = new Color(1f, 1f, 1f, 0f);
        txColor[234] = new Color(1f, 1f, 1f, 0f);
        txColor[235] = new Color(1f, 1f, 1f, 0f);
        txColor[236] = new Color(1f, 1f, 1f, 0f);
        txColor[237] = new Color(1f, 1f, 1f, 0f);
        txColor[238] = new Color(1f, 1f, 1f, 0f);
        txColor[239] = new Color(1f, 1f, 1f, 0f);
        txColor[240] = new Color(1f, 1f, 1f, 0f);
        txColor[241] = new Color(1f, 1f, 1f, 0f);
        txColor[242] = new Color(1f, 1f, 1f, 0f);
        txColor[243] = new Color(1f, 1f, 1f, 0f);
        txColor[244] = new Color(1f, 1f, 1f, 0f);
        txColor[245] = new Color(1f, 1f, 1f, 0f);
        txColor[246] = new Color(1f, 1f, 1f, 0f);
        txColor[247] = new Color(1f, 1f, 1f, 0f);
        txColor[248] = new Color(1f, 1f, 1f, 0f);
        txColor[249] = new Color(1f, 1f, 1f, 0f);
        txColor[250] = new Color(1f, 1f, 1f, 0f);
        txColor[251] = new Color(1f, 1f, 1f, 0f);
        txColor[252] = new Color(1f, 1f, 1f, 0f);
        txColor[253] = new Color(1f, 1f, 1f, 0f);
        txColor[254] = new Color(1f, 1f, 1f, 0f);
        txColor[255] = new Color(1f, 1f, 1f, 0f);
        Texture2D iconTx = new Texture2D(16, 16, TextureFormat.ARGB32, false);
        iconTx.filterMode = FilterMode.Point;
        iconTx.wrapMode = TextureWrapMode.Clamp;
        iconTx.SetPixels(txColor);
        iconTx.Apply();

        return iconTx;
    }
}

[EditorToolbarElement(id, typeof(SceneView))]
class AxisDropDown : EditorToolbarDropdownToggle, IAccessContainerWindow
{

    public const string id = "AxisConstrain/Axis";
    public EditorWindow containerWindow { get; set; }
    public bool constrainActive;
    static int axisIndex = 0;
    static readonly string[] axisName = new string[] { "x", "y", "z" };
 
    UndoPropertyModification[] changedMods = null;
    static readonly Color[] btnColors = new Color[] { new Color(0.3686275f, 0.1254902f, 0.1341253f), new Color(0.162876f, 0.3679245f, 0.126691f), new Color(0.1460929f, 0.19f, 0.4622641f) };
 
    bool preConstrainActive;
    bool down;
    public AxisDropDown()
    {
        //Load some values from registry to make tool persistent in editor load instances.
        SetActive(EditorPrefs.GetBool("AxisConstrainActive", false), EditorPrefs.GetInt("AxisConstrainAxis", 0));

        //Subscribe the dropdown function
        dropdownClicked += ShowMenu;
        //Subscribe the toggle click
        this.RegisterValueChangedCallback(SetConstrain);
        //Subscribe to scene view changes
        SceneView.duringSceneGui -= ConstrainPositions;
        SceneView.duringSceneGui += ConstrainPositions;

        icon = AxisConstrainOverlay.icon;
    }

    void ConstrainPositions(SceneView view)
    {
        //Only run when visible
        if (containerWindow != null && containerWindow.TryGetOverlay("AxisConstrain", out Overlay result))
        {
            if (!result.displayed)
            {
                SceneView.duringSceneGui -= ConstrainPositions;
                ObjectChangeEvents.changesPublished -= ObjectChangeEventsHandler;
                Undo.postprocessModifications -= PostprocessModifications;
                return;
            }
        }

        if (view != containerWindow)
        {
            return;
        }

        if (constrainActive)
        {
            //Convert curretn rotation handle to its matrix form, so we can grab an axis
            Matrix4x4 handleOrientation = Matrix4x4.TRS(Vector3.zero, Tools.handleRotation, Vector3.one);
            Vector3 compareAxis = handleOrientation.GetColumn(axisIndex).normalized;

            //Do to rounding errors in floats, constraining with initial value before moving is best.
            foreach (KeyValuePair<Transform, Vector3> entry in AxisConstrainOverlay.initPositions)
            {
                //Delta vector of current local position vs initial local position when action started
                Vector3 moveVector = entry.Key.localPosition - entry.Value;

                if (entry.Key.parent != null)
                {
                    //The changed property is in local coordinates. Transform to world using parent
                    moveVector = entry.Key.parent.localToWorldMatrix.MultiplyPoint3x4(moveVector) - entry.Key.parent.position;
                }
                //Project the delta vector on to the constrain axis (In world space)
                Vector3 modMoveVector = Vector3.Project(moveVector, compareAxis);

                //Offset the transform by how the projected vector differs from the delta vector
                entry.Key.position += modMoveVector - moveVector;
            }
        }

        if (Event.current != null)
        {
            if (Event.current.type == EventType.MouseUp)
            {
                //Clear all init positions on MouseUp, to prepare for next move action
                AxisConstrainOverlay.initPositions.Clear();
            }
            else if (Event.current.type == EventType.KeyDown && !down)
            {
                preConstrainActive = constrainActive;
            
                if (Event.current.keyCode == KeyCode.F5)
                {
                    down = true;
                    SetActive(true, 0);
                }
                else if (Event.current.keyCode == KeyCode.F6)
                {
                    down = true;
                    SetActive(true, 1);
                }
                else if (Event.current.keyCode == KeyCode.F7)
                {
                    down = true;
                    SetActive(true, 2);
                }
            }
            else if (Event.current.type == EventType.KeyUp && down)
            {
                if (Event.current.keyCode == KeyCode.F5)
                {
                    down = false;
                    SetActive(preConstrainActive, 0);
                }
                else if (Event.current.keyCode == KeyCode.F6)
                {
                    down = false;
                    SetActive(preConstrainActive, 1);
                }
                else if (Event.current.keyCode == KeyCode.F7)
                {
                    down = false;
                    SetActive(preConstrainActive, 2);
                }
            }
        }
    }

    void SetConstrain(ChangeEvent<bool> toggle)
    {
        //Save toggle state in both var and registry and appearance
        SetActive(toggle.newValue, axisIndex);
    }

    public UndoPropertyModification[] PostprocessModifications(UndoPropertyModification[] modifications)
    {
        //Save this undo event to make sure we have the initial values of the objects being moved
        changedMods = modifications;
        return modifications;
    }

    public void ObjectChangeEventsHandler(ref ObjectChangeEventStream stream)
    {
        int changes = stream.length;

        for (int i = 0; i < changes && changedMods != null && changedMods.Length > 0; i++)
        {
            //Find the "kind" of change i
            ObjectChangeKind changeKind = stream.GetEventType(i);
            if (changeKind == ObjectChangeKind.ChangeGameObjectOrComponentProperties)
            {
                //"Kind" is the one we are looking for: Component changes.
                //Grab the data of this change
                stream.GetChangeGameObjectOrComponentPropertiesEvent(i, out ChangeGameObjectOrComponentPropertiesEventArgs data);

                //Get the Object of this change based on instanceID
                Object instance = EditorUtility.InstanceIDToObject(data.instanceId);

                if (instance is Transform)
                {
                    //Changed object is of type: Transform
                    Transform changedTransform = instance as Transform;

                    //Check if we have this object stored already!
                    if (!AxisConstrainOverlay.initPositions.ContainsKey(changedTransform))
                    {
                        //Create and initialize initPos with local positions of changed transform (So that components of the Vector3 localPosition that are NOT changed are still saved)
                        Vector3 initPos;
                        initPos = changedTransform.localPosition;

                        //Loop through undo ops and find one that matches this change
                        for (int j = 0; j < changedMods.Length; j++)
                        {
                            UndoPropertyModification currentMod = changedMods[j];

                            if (currentMod.previousValue.target == changedTransform)
                            {
                                if (float.TryParse(currentMod.previousValue.value, out float valueAsFloat))
                                {
                                    //Value is a float. Find the vector component by the property path
                                    if (currentMod.previousValue.propertyPath == "m_LocalPosition.x")
                                    {
                                        initPos.x = valueAsFloat;
                                    }
                                    else if (currentMod.previousValue.propertyPath == "m_LocalPosition.y")
                                    {
                                        initPos.y = valueAsFloat;
                                    }
                                    else if (currentMod.previousValue.propertyPath == "m_LocalPosition.z")
                                    {
                                        initPos.z = valueAsFloat;
                                    }
                                }
                            }
                        }
                        //Store init value in dict.
                        AxisConstrainOverlay.initPositions.Add(changedTransform, initPos);
                    }
                }
            }
        }
    }

    void ShowMenu()
    {
        //Create and show drop down of toggle element. Add actions on value change
        GenericMenu menu = new GenericMenu();
        menu.AddItem(new GUIContent("x-axis"), axisIndex == 0, () => { SetActive(true, 0); });
        menu.AddItem(new GUIContent("y-axis"), axisIndex == 1, () => { SetActive(true, 1); });
        menu.AddItem(new GUIContent("z-axis"), axisIndex == 2, () => { SetActive(true, 2); });
        menu.ShowAsContext();
    }

    void SetActive (bool active, int axis)
    {
        constrainActive = active;
        EditorPrefs.SetBool("AxisConstrainActive", constrainActive);
        SetValueWithoutNotify(constrainActive);
        axisIndex = axis;
        EditorPrefs.SetInt("AxisConstrainAxis", axisIndex);
        SetText();

        ObjectChangeEvents.changesPublished -= ObjectChangeEventsHandler;
        Undo.postprocessModifications -= PostprocessModifications;
        if (active)
        {
            ObjectChangeEvents.changesPublished += ObjectChangeEventsHandler;
            Undo.postprocessModifications += PostprocessModifications;
        }
    }

    void SetText ()
    {
        //Change text based on if active...
        if (constrainActive)
        {
            text = "<color=#" + ColorUtility.ToHtmlStringRGB(btnColors[axisIndex] * 4) + "><b>Constrain to " + axisName[axisIndex] + "-axis";
        }
        else
        {
            text = "<b>Axis Constrain Off";
        }
    }
}