Created
September 25, 2021 22:16
-
-
Save alankent/a8df5d39eff57b7db2afcead1185bee0 to your computer and use it in GitHub Desktop.
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 UnityEngine; | |
using UnityEditor; | |
using UnityEngine.Timeline; | |
using UnityEngine.Playables; | |
using System.Collections.Generic; | |
using UnityEditor.Timeline; | |
public class MyAssemblyWindow : EditorWindow | |
{ | |
[MenuItem("Window/Alan Tools/Assembly")] | |
public static void ShowWindow() | |
{ | |
// Find or create window so it appears on the screen. | |
EditorWindow.GetWindow<MyAssemblyWindow>("Assembly"); | |
} | |
private enum Positioning { InScene, AtCamera } | |
private enum Rotation { FacingCamera, SameAsCamera, AsIs } | |
private enum Gravity { Float, OnGround } | |
void OnGUI() | |
{ | |
AddCategory("Characters", Positioning.InScene, Rotation.FacingCamera, Gravity.OnGround); | |
AddCategory("Props", Positioning.InScene, Rotation.AsIs, Gravity.OnGround); | |
AddCategory("Lighting", Positioning.InScene, Rotation.AsIs, Gravity.Float); | |
AddCategory("Photography", Positioning.AtCamera, Rotation.SameAsCamera, Gravity.Float); | |
} | |
private void AddCategory(string name, Positioning positioning, Rotation rotation, Gravity gravity) | |
{ | |
var assets = AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/_SHARED/Assembly Prefabs/" + name, "Assets/_LOCAL/Assembly Prefabs/" + name }); | |
if (assets.Length == 0) | |
{ | |
return; | |
} | |
var buttonStyle = EditorStyles.miniButton; | |
GUILayout.Label(name, EditorStyles.boldLabel); | |
var inHoriz = false; | |
int i = 0; | |
foreach (var guid in assets) | |
{ | |
var path = AssetDatabase.GUIDToAssetPath(guid); | |
// Get a clean name for the button (removing "Prefab Variant" style endings). | |
var prefabName = path.Substring(path.LastIndexOf('/') + 1); | |
prefabName = prefabName.Remove(prefabName.LastIndexOf('.')); | |
if (prefabName.EndsWith(" Variant")) | |
{ | |
prefabName = prefabName.Remove(prefabName.LastIndexOf(' ')); | |
} | |
if (prefabName.EndsWith(" Prefab")) | |
{ | |
prefabName = prefabName.Remove(prefabName.LastIndexOf(' ')); | |
} | |
// Putting 4 per line because cannot work out how to flow them nicely to pack as many as will fit per line. | |
// TODO: try https://docs.unity3d.com/ScriptReference/EditorGUIUtility.GetFlowLayoutedRects.html | |
if (!inHoriz) | |
{ | |
GUILayout.BeginHorizontal("box"); | |
inHoriz = true; | |
} | |
if (GUILayout.Button(prefabName, buttonStyle)) | |
{ | |
AddPrefabInstance(prefabName, path, positioning, rotation, gravity); | |
} | |
if ((i % 4) == 3) | |
{ | |
GUILayout.EndHorizontal(); | |
inHoriz = false; | |
} | |
i++; | |
} | |
if (inHoriz) | |
{ | |
GUILayout.EndHorizontal(); | |
} | |
} | |
/* | |
* Add prefab instance | |
*/ | |
private void AddPrefabInstance(string name, string path, Positioning positioning, Rotation rotation, Gravity gravity) | |
{ | |
bool refreshNeeded = false; | |
// Normally only one sequence would be selected, but just in case... | |
foreach (GameObject obj in Selection.gameObjects) | |
{ | |
// Don't add if a sequence not currently selected | |
// (User can end up dropping things in wrong spots by accident) | |
PlayableDirector director = obj.GetComponent<PlayableDirector>(); | |
if (director == null) | |
{ | |
Debug.LogWarning("Not dropping instance as current selection is not a sequence node"); | |
} | |
else | |
{ | |
// Don't add the same character multiple times | |
// (This might be a bad idea - e.g. might want multiple instances of a prop) | |
//var child = obj.transform.Find(name); | |
//if (child == null) | |
{ | |
// Does not already exist, lets try and add it. | |
var prefab = (GameObject)AssetDatabase.LoadAssetAtPath(path, typeof(GameObject)); | |
if (prefab == null) | |
{ | |
Debug.LogError("Could not load '" + name + "' at path '" + path + "'."); | |
} | |
else | |
{ | |
// Add prefab game object as child. | |
var inst = PrefabUtility.InstantiatePrefab(prefab) as GameObject; | |
Undo.RecordObject(obj.transform, "Add " + name + " to sequence"); | |
inst.transform.parent = obj.transform; | |
refreshNeeded = true; | |
// Move new instance to where camera is (rather than starting at 0,0) | |
var sceneView = UnityEditor.SceneView.lastActiveSceneView; | |
if (sceneView != null) | |
{ | |
if (positioning == Positioning.InScene) | |
{ | |
//inst.transform.position = sceneView.pivot; | |
inst.transform.position = sceneView.camera.transform.position + 4f * sceneView.camera.transform.forward; | |
} | |
else | |
{ | |
inst.transform.position = sceneView.camera.transform.position; | |
} | |
if (rotation != Rotation.AsIs) | |
{ | |
// Default it to look at the scene camera | |
var offset = (rotation == Rotation.FacingCamera) ? 180f : 0f; | |
inst.transform.rotation = Quaternion.Euler(0f, sceneView.rotation.eulerAngles.y + offset, 0f); | |
} | |
// Try to make it land on ground so don't have to get the Y value correct manually | |
// https://answers.unity.com/questions/39203/instantiate-object-and-align-with-object-surface.html | |
if (gravity == Gravity.OnGround) | |
{ | |
RaycastHit hit; | |
var down = new Vector3(0, -1, 0); | |
if (Physics.Raycast(inst.transform.position, down, out hit)) | |
{ | |
// if too far away, its probably best to leave at middle of scene view | |
var distanceToGround = hit.distance; | |
if (distanceToGround < 3f) | |
{ | |
var currentPos = inst.transform.position; | |
var newY = currentPos.y - distanceToGround; | |
inst.transform.position = new Vector3(currentPos.x, newY, currentPos.z); | |
} | |
} | |
} | |
} | |
// If we find the Timeline, add an animation track to the timeline as well | |
// if the new object has an Animator component (otherwise we cannot animate it) | |
TimelineAsset timeline = (TimelineAsset) director.playableAsset; | |
if (timeline != null && inst.GetComponent<Animator>() != null) | |
{ | |
AnimationTrack track = timeline.CreateTrack<AnimationTrack>(null, name); | |
director.SetGenericBinding(track, inst); | |
} | |
} | |
} | |
} | |
} | |
if (refreshNeeded) | |
{ | |
TimelineEditor.Refresh(RefreshReason.ContentsAddedOrRemoved); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment