Nice work! I’ve had a moment to try some things myself.
It looks like line 17 may serve us better as:
if (EditorWindow.mouseOverWindow != SceneView.lastActiveSceneView || SceneView.lastActiveSceneView == null) {
Note the Editor.mouseOverWindow, which adds some functionality that was missing: if focus is in the hierarchy window, but the mouse is hovering over the scene, pressing “F” should focus the object in the scene.
Also, after re-reading karl_jones’s post above, I noticed that encapsulation of children only happens when Tools.pivotMode == PivotMode.Center. I think that has been a major source of confusion for me as the ignorant user I am, as sometimes focus works nicely while other times it’s zoomed way out for seemingly no reason.
To that end, after playing around with different ideas, I think the built-in Frame works fairly well in pivotMode==PivotMode.Pivot (minus the zooming-out for lights and ParticleSystems), and I’m not a fan of child encapsulation at all. I’ve also found I enjoy having a “maxExtent” value, whereby even large objects have a limit to how far away the camera will be from them. (I also like zooming-in 30% closer than Unity’s default, so I added that as “zoomFactor = 0.7f”.)
Anyway, here’s how it’s looking:
using UnityEditor;
using UnityEngine;
public class FocusReplacer {
static float defaultExtent = 1f; // if no renderer or collider is found, zoom to an object of this size
static float maxExtent = 20f; // controls the maximum "zoom-out" when focusing large objects
static float zoomFactor = 0.7f; // zoom-in more than Unity would by default (1f)
[MenuItem("Edit/Focus Selected &f")]
static void Focus() {
if (Selection.activeObject == null)
return;
//Detect object is in project, so just ping
if (AssetDatabase.Contains(Selection.activeObject)) {
EditorGUIUtility.PingObject(Selection.activeInstanceID);
return;
}
//Detect hierarchy window is focused, so just ping
if (EditorWindow.mouseOverWindow != SceneView.lastActiveSceneView || SceneView.lastActiveSceneView == null) {
EditorGUIUtility.PingObject(Selection.activeInstanceID);
return;
}
Bounds bounds;
Bounds allBounds = new Bounds();
foreach (GameObject obj in Selection.gameObjects) {
Renderer renderer = obj.GetComponentInChildren<Renderer>();
Collider collider = obj.GetComponentInChildren<Collider>();
if (renderer && !(renderer is ParticleSystemRenderer)) {
bounds = renderer.bounds;
} else if (collider) {
bounds = collider.bounds;
} else {
bounds = new Bounds(obj.transform.position, Vector3.one * defaultExtent);
}
if (allBounds.extents == Vector3.zero)
allBounds = bounds;
else
allBounds.Encapsulate(bounds);
}
allBounds.extents *= zoomFactor;
if (allBounds.extents.magnitude > maxExtent)
allBounds.extents = allBounds.extents.normalized * maxExtent;
SceneView.lastActiveSceneView.Frame(allBounds, false);
}
}
5300517–532338–FocusReplacer.cs (1.97 KB)