Last active
February 4, 2025 09:08
-
-
Save havchr/3fcbee65a8c3a7fe1391708ddda7e7e0 to your computer and use it in GitHub Desktop.
Unity3DS editor script, context menu for adjusting pivot of a parent object that have children placed around.
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 UnityEditor; | |
using UnityEngine; | |
namespace Agens | |
{ | |
#if UNITY_EDITOR | |
public enum PivotAlignment | |
{ | |
Min,Mid,Max, | |
} | |
public class PivotAdjuster | |
{ | |
/* | |
* PivotAdjuster adjusts the pivot based on childrens geometry/positions | |
* Good if you have a group of objects where someone has moved them locally instead of using the parent pivot, | |
* or to just tidy up. | |
* | |
* say hello @[email protected] (preffered) | |
* or @havchr.bsky.social on BlueSky | |
* | |
* License MIT, use as you please | |
* | |
* | |
* | |
* Usage click context menu for a game object in the hierarchy, - it generates a context menu | |
* | |
* like [MenuItem("GameObject/PivotAdjuster/X-Max/Y-Max/Z-Max", false, 30001)] | |
* Select the Pivot you want to have, and hopefully it will work. | |
* See bottom part of this file to see all permuations | |
*/ | |
private static void AdjustPivot(MenuCommand command, PivotAlignment xAxis, PivotAlignment yAxis, PivotAlignment zAxis,bool preferMeshBounds=true) | |
{ | |
var gob = command.context as GameObject; | |
Undo.SetCurrentGroupName("PivotAdjuster"); | |
int undoGroupIndex = Undo.GetCurrentGroup(); | |
Undo.RegisterFullObjectHierarchyUndo(gob,$"PivotAdjuster Undo {gob.name}"); | |
var bounds = GetBoundsFromPositions(gob.transform); | |
if (preferMeshBounds) | |
{ | |
GetBoundsFromMeshes(gob.transform,ref bounds); | |
} | |
//grab all children | |
var children = new Transform[gob.transform.childCount]; | |
if (children.Length == 0) | |
{ | |
//if we are childless - create a wrap, burrito. | |
GameObject wrap = new GameObject($"{gob.transform.name}_Wrap"); | |
Undo.RegisterCreatedObjectUndo(wrap,$"PivotAdjuster Create Object Undo {wrap.name}"); | |
wrap.transform.parent = gob.transform.parent; | |
gob.transform.parent = wrap.transform; | |
gob = wrap; | |
children = new Transform[gob.transform.childCount]; | |
} | |
for (int childIndex = 0; childIndex < children.Length; childIndex++) | |
{ | |
children[childIndex] = gob.transform.GetChild(childIndex); | |
} | |
//unparent all children. | |
for (int childIndex = 0; childIndex < children.Length; childIndex++) | |
{ | |
var child = children[childIndex]; | |
child.parent = null; | |
} | |
gob.transform.position = GetPosition(bounds, xAxis, yAxis, zAxis); | |
//reparent | |
for (int childIndex = 0; childIndex < children.Length; childIndex++) | |
{ | |
var child = children[childIndex]; | |
child.parent = gob.transform; | |
} | |
Selection.activeTransform = gob.transform; | |
Undo.CollapseUndoOperations(undoGroupIndex); | |
} | |
private static Vector3 GetPosition(Bounds bounds, PivotAlignment xAxis, PivotAlignment yAxis, PivotAlignment zAxis) | |
{ | |
Vector3 answer = Vector3.zero; | |
if (xAxis == PivotAlignment.Min) | |
{ | |
answer.x = bounds.min.x; | |
} | |
if (yAxis == PivotAlignment.Min) | |
{ | |
answer.y = bounds.min.y; | |
} | |
if (zAxis == PivotAlignment.Min) | |
{ | |
answer.z = bounds.min.z; | |
} | |
if (xAxis == PivotAlignment.Max) | |
{ | |
answer.x = bounds.max.x; | |
} | |
if (yAxis == PivotAlignment.Max) | |
{ | |
answer.y = bounds.max.y; | |
} | |
if (zAxis == PivotAlignment.Max) | |
{ | |
answer.z = bounds.max.z; | |
} | |
if (xAxis == PivotAlignment.Mid) | |
{ | |
answer.x = Mathf.Lerp(bounds.min.x,bounds.max.x,0.5f); | |
} | |
if (yAxis == PivotAlignment.Mid) | |
{ | |
answer.y = Mathf.Lerp(bounds.min.y,bounds.max.y,0.5f); | |
} | |
if (zAxis == PivotAlignment.Mid) | |
{ | |
answer.z = Mathf.Lerp(bounds.min.z,bounds.max.z,0.5f); | |
} | |
return answer; | |
} | |
public static Bounds GetBoundsFromPositions(Transform node) | |
{ | |
Vector3 negative = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); //lower left corner | |
Vector3 positive = new Vector3(float.MinValue,float.MinValue,float.MinValue); //upper right corner | |
for (int childIndex = 0; childIndex < node.childCount; childIndex++) | |
{ | |
var child = node.GetChild(childIndex); | |
//m.bounds | |
negative.x = child.position.x < negative.x ? child.position.x : negative.x; | |
negative.y = child.position.y < negative.y ? child.position.y : negative.y; | |
negative.z = child.position.z < negative.z ? child.position.z : negative.z; | |
positive.x = child.position.x > positive.x ? child.position.x : positive.x; | |
positive.y = child.position.y > positive.y ? child.position.y : positive.y; | |
positive.z = child.position.z > positive.z ? child.position.z : positive.z; | |
} | |
return new Bounds(Vector3.Lerp(negative,positive,0.5f), positive-negative); | |
} | |
public static bool GetBoundsFromMeshes(Transform node,ref Bounds bounds) | |
{ | |
Vector3 negative = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); //lower left corner | |
Vector3 positive = new Vector3(float.MinValue,float.MinValue,float.MinValue); //upper right corner | |
var rendies = node.GetComponentsInChildren<Renderer>(); | |
if (rendies.Length == 0) | |
{ | |
return false; | |
} | |
foreach (var rendy in rendies) | |
{ | |
negative.x = rendy.bounds.min.x < negative.x ? rendy.bounds.min.x : negative.x; | |
negative.y = rendy.bounds.min.y < negative.y ? rendy.bounds.min.y : negative.y; | |
negative.z = rendy.bounds.min.z < negative.z ? rendy.bounds.min.z : negative.z; | |
positive.x = rendy.bounds.max.x > positive.x ? rendy.bounds.max.x : positive.x; | |
positive.y = rendy.bounds.max.y > positive.y ? rendy.bounds.max.y : positive.y; | |
positive.z = rendy.bounds.max.z > positive.z ? rendy.bounds.max.z : positive.z; | |
} | |
bounds = new Bounds(Vector3.Lerp(negative, positive, 0.5f), positive - negative); | |
return true; | |
} | |
/* | |
* This lists all the menu options that allow us to select our pivots | |
*/ | |
//feel free to hide this region | |
#region different pivot options | |
[MenuItem("GameObject/PivotAdjuster/Bottom Left Corner Back", false, 30001)] | |
static void AdjustPivotBottomLeftNearestBack(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Bottom Right Corner Back", false, 30001)] | |
static void AdjustPivotBottomRightNearestBack(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Bottom Left Corner Front", false, 30001)] | |
static void AdjustPivotBottomLeftNearestFront(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Max); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Bottom Right Corner Front", false, 30001)] | |
static void AdjustPivotBottomRightNearestFront(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Max); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Center All", false, 30001)] | |
static void AdjustPivotCenterAll(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Center Top", false, 30001)] | |
static void AdjustPivotCenterTop(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/Center Bottom", false, 30001)] | |
static void AdjustPivotCenterBottom(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Mid); | |
} | |
#region X-MIN | |
#region Y-MIN Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Min/Z-Min", false, 30001)] | |
static void AdjustPivotXMinYMinZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Min/Z-Mid", false, 30001)] | |
static void AdjustPivotXMinYMinZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Min/Z-Max", false, 30001)] | |
static void AdjustPivotXMinYMinZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MID Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Mid/Z-Min", false, 30001)] | |
static void AdjustPivotXMinYMidZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Mid/Z-Mid", false, 30001)] | |
static void AdjustPivotXMinYMidZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Mid/Z-Max", false, 30001)] | |
static void AdjustPivotXMinYMidZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MAX Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Max/Z-Min", false, 30001)] | |
static void AdjustPivotXMinYMaxZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Max/Z-Mid", false, 30001)] | |
static void AdjustPivotXMinYMaxZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Min/Y-Max/Z-Max", false, 30001)] | |
static void AdjustPivotXMinYMaxZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Min, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#endregion | |
#region X-MID | |
#region Y-MIN Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Min/Z-Min", false, 30001)] | |
static void AdjustPivotXMidYMinZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Min/Z-Mid", false, 30001)] | |
static void AdjustPivotXMidYMinZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Min/Z-Max", false, 30001)] | |
static void AdjustPivotXMidYMinZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MID Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Mid/Z-Min", false, 30001)] | |
static void AdjustPivotXMidYMidZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Mid/Z-Mid", false, 30001)] | |
static void AdjustPivotXMidYMidZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Mid/Z-Max", false, 30001)] | |
static void AdjustPivotXMidYMidZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MAX Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Max/Z-Min", false, 30001)] | |
static void AdjustPivotXMidYMaxZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Max/Z-Mid", false, 30001)] | |
static void AdjustPivotXMidYMaxZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Mid/Y-Max/Z-Max", false, 30001)] | |
static void AdjustPivotXMidYMaxZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Mid, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#endregion | |
#region X-MAX | |
#region Y-MIN Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Min/Z-Min", false, 30001)] | |
static void AdjustPivotXMaxYMinZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Min/Z-Mid", false, 30001)] | |
static void AdjustPivotXMaxYMinZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Min/Z-Max", false, 30001)] | |
static void AdjustPivotXMaxYMinZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Min, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MID Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Mid/Z-Min", false, 30001)] | |
static void AdjustPivotXMaxYMidZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Mid/Z-Mid", false, 30001)] | |
static void AdjustPivotXMaxYMidZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Mid/Z-Max", false, 30001)] | |
static void AdjustPivotXMaxYMidZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Mid, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#region Y-MAX Z-MIN to Z-MAX | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Max/Z-Min", false, 30001)] | |
static void AdjustPivotXMaxYMaxZMin(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Min); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Max/Z-Mid", false, 30001)] | |
static void AdjustPivotXMaxYMaxZMid(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Mid); | |
} | |
[MenuItem("GameObject/PivotAdjuster/X-Max/Y-Max/Z-Max", false, 30001)] | |
static void AdjustPivotXMaxYMaxZMax(MenuCommand command) | |
{ | |
AdjustPivot(command, xAxis: PivotAlignment.Max, yAxis: PivotAlignment.Max, zAxis: PivotAlignment.Max); | |
} | |
#endregion | |
#endregion | |
#endregion | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment