405 lines
14 KiB
C#
405 lines
14 KiB
C#
using UnityEngine;
|
|
using UnityEditor;
|
|
using UnityEngine.Rendering.Universal;
|
|
using Unity.Tutorials.Core.Editor;
|
|
using UnityEngine.SceneManagement;
|
|
using System.Reflection;
|
|
|
|
/// <summary>
|
|
/// Implement your Tutorial callbacks here.
|
|
/// </summary>
|
|
[CreateAssetMenu(fileName = DefaultFileName, menuName = "Tutorials/" + DefaultFileName + " Instance")]
|
|
public class TutorialCallbacks : ScriptableObject
|
|
{
|
|
/// <summary>
|
|
/// The default file name used to create asset of this class type.
|
|
/// </summary>
|
|
public const string DefaultFileName = "TutorialCallbacks";
|
|
|
|
/// <summary>
|
|
/// Creates a TutorialCallbacks asset and shows it in the Project window.
|
|
/// </summary>
|
|
/// <param name="assetPath">
|
|
/// A relative path to the project's root. If not provided, the Project window's currently active folder path is used.
|
|
/// </param>
|
|
/// <returns>The created asset</returns>
|
|
public static ScriptableObject CreateAndShowAsset(string assetPath = null)
|
|
{
|
|
assetPath = assetPath ?? $"{TutorialEditorUtils.GetActiveFolderPath()}/{DefaultFileName}.asset";
|
|
var asset = CreateInstance<TutorialCallbacks>();
|
|
AssetDatabase.CreateAsset(asset, AssetDatabase.GenerateUniqueAssetPath(assetPath));
|
|
EditorUtility.FocusProjectWindow(); // needed in order to make the selection of newly created asset to really work
|
|
Selection.activeObject = asset;
|
|
return asset;
|
|
}
|
|
|
|
// ****************************
|
|
// Added by VB with help from ClaudeAI
|
|
// Used in early tutorials to select GameObject for the user.
|
|
public void SelectGameObjectInScene(string gameObjectName)
|
|
{
|
|
// Find the GameObject in the current scene
|
|
GameObject targetObject = GameObject.Find(gameObjectName);
|
|
|
|
if (targetObject != null)
|
|
{
|
|
// Select the GameObject
|
|
Selection.activeGameObject = targetObject;
|
|
|
|
// Optionally, you can also focus the Scene view on this object
|
|
if (SceneView.lastActiveSceneView != null)
|
|
{
|
|
//SceneView.lastActiveSceneView.FrameSelected();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"GameObject not found: {gameObjectName}");
|
|
}
|
|
}
|
|
// ****************************
|
|
// Added by VB with help from ClaudeAI
|
|
// Used in early tutorials to select asset in project for the user.
|
|
public void SelectAssetinProject(string assetName)
|
|
{
|
|
string[] guids = AssetDatabase.FindAssets(assetName);
|
|
if (guids.Length == 0)
|
|
{
|
|
Debug.LogWarning($"No asset found with name: {assetName}");
|
|
return;
|
|
}
|
|
|
|
string path = AssetDatabase.GUIDToAssetPath(guids[0]);
|
|
Object asset = AssetDatabase.LoadAssetAtPath<Object>(path);
|
|
|
|
if (asset != null)
|
|
{
|
|
Selection.activeObject = asset;
|
|
EditorUtility.FocusProjectWindow();
|
|
EditorGUIUtility.PingObject(asset);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError($"Failed to load asset at path: {path}");
|
|
}
|
|
}
|
|
|
|
// ****************************
|
|
// Added by VB with help from UAI
|
|
public void SelectFolderInProject(string folderPath)
|
|
{
|
|
Object folderObject = AssetDatabase.LoadAssetAtPath<Object>(folderPath);
|
|
|
|
if (folderObject != null)
|
|
{
|
|
EditorUtility.FocusProjectWindow();
|
|
Selection.activeObject = folderObject;
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Folder not found: " + folderPath);
|
|
}
|
|
}
|
|
|
|
// ****************************
|
|
// Added by VB with help from Guillaume
|
|
// Used in Welcome Dialog to start tutorials from buttons.
|
|
public void StartTutorial(Tutorial tutorial)
|
|
{
|
|
TutorialWindowUtils.StartTutorial(tutorial);
|
|
}
|
|
|
|
// ****************************
|
|
// Added by VB with help from ClaudeAI
|
|
public void DisableGameObject(string objectName)
|
|
{
|
|
GameObject objectToDisable = GameObject.Find(objectName);
|
|
|
|
if (objectToDisable != null)
|
|
{
|
|
objectToDisable.SetActive(false);
|
|
Debug.Log($"GameObject '{objectName}' has been disabled.");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"GameObject '{objectName}' not found. Unable to disable.");
|
|
}
|
|
}
|
|
// ****************************
|
|
// Added by VB with help from ClaudeAI
|
|
public void EnableGameObject(string objectName)
|
|
{
|
|
Debug.Log("EnableGameObject ran. Looking for: " + objectName);
|
|
GameObject objectToEnable = GameObject.FindGameObjectWithTag(objectName);
|
|
|
|
if (objectToEnable != null)
|
|
{
|
|
objectToEnable.SetActive(false);
|
|
Debug.Log($"GameObject '{objectName}' has been enabled.");
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"GameObject '{objectName}' not found. Unable to enable.");
|
|
}
|
|
}
|
|
|
|
public void EnableChildObjects(string objectTag)
|
|
{
|
|
// Find the object with the specified tag
|
|
GameObject objectToEnable = GameObject.FindGameObjectWithTag(objectTag);
|
|
|
|
if (objectToEnable != null)
|
|
{
|
|
// Iterate through all the child objects and enable them
|
|
foreach (Transform child in objectToEnable.transform)
|
|
{
|
|
child.gameObject.SetActive(true);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
public void FrameObjectWithTag(string objectTag)
|
|
{
|
|
// Find the object with the specified tag
|
|
GameObject objectToFrame = GameObject.FindGameObjectWithTag(objectTag);
|
|
|
|
if (objectToFrame != null)
|
|
{
|
|
Selection.activeGameObject = objectToFrame;
|
|
|
|
// Get the active Scene view
|
|
SceneView sceneView = SceneView.lastActiveSceneView;
|
|
if (sceneView != null)
|
|
{
|
|
// Focus on the selected object in the Scene view
|
|
sceneView.FrameSelected();
|
|
//sceneView.Repaint();
|
|
Selection.activeGameObject = null;
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("No active Scene view found.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"GameObject with tag '{objectTag}' not found. Unable to frame object.");
|
|
}
|
|
}
|
|
|
|
// Camera Enable/Disable functions using Transform to find child camera and child listener
|
|
// Added by VB with help from UAI
|
|
private GameObject mainCamera;
|
|
private GameObject robotCamera;
|
|
private GameObject robotListener;
|
|
public void UseMainCamera(bool useMain)
|
|
{
|
|
// Find the cameras by their names within the scene if they haven't been found already
|
|
if (mainCamera == null)
|
|
{
|
|
mainCamera = FindGameObjectByName("Main Camera");
|
|
}
|
|
|
|
if (robotCamera == null)
|
|
{
|
|
robotCamera = FindGameObjectByName("RobotCamera");
|
|
robotListener = FindGameObjectByName("RobotListener");
|
|
}
|
|
|
|
if (mainCamera == null && useMain)
|
|
{
|
|
Debug.LogError("Main Camera not found, and can't be enabled.");
|
|
return;
|
|
}
|
|
|
|
// Enable the target camera and disable the other one.
|
|
// If there is no robotCamera or robotListener, use Main Camera (with its listener) so that there is always a camera
|
|
// No error or warning if robotCamera or robotListener is null -- sometimes the robot isn't in the scene
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Functions to find a GameObject by name in the Hierarchy (even if a child)
|
|
// Added by VB with help from UAI
|
|
public static GameObject FindGameObjectByName(string name)
|
|
{
|
|
GameObject[] rootObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
|
|
|
|
foreach (GameObject rootObject in rootObjects)
|
|
{
|
|
GameObject foundObject = FindGameObjectInChildren(rootObject.transform, name);
|
|
if (foundObject != null)
|
|
{
|
|
return foundObject;
|
|
}
|
|
}
|
|
return null; // if not found
|
|
}
|
|
// Recursive helper method to search through the children of a given Transform
|
|
private static GameObject FindGameObjectInChildren(Transform parent, string name)
|
|
{
|
|
if (parent.name == name)
|
|
{
|
|
return parent.gameObject;
|
|
}
|
|
foreach (Transform child in parent)
|
|
{
|
|
GameObject foundObject = FindGameObjectInChildren(child, name);
|
|
if (foundObject != null)
|
|
{
|
|
return foundObject;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// ****************************
|
|
// Added by VB with help from ClaudeAI
|
|
// Used to set up the layout of the Project window to match the tutorial.
|
|
// ClaudeAI warned about Internal API calls.
|
|
// NOT CALLED -- Layout appears to handle this now
|
|
public void CustomizeProjectWindowLayout()
|
|
{
|
|
try
|
|
{
|
|
// Get the ProjectBrowser type
|
|
var projectBrowserType = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
|
|
if (projectBrowserType == null)
|
|
{
|
|
Debug.LogError("Failed to find ProjectBrowser type");
|
|
return;
|
|
}
|
|
|
|
// Find all ProjectBrowser windows
|
|
var projectBrowsers = Resources.FindObjectsOfTypeAll(projectBrowserType);
|
|
if (projectBrowsers.Length == 0)
|
|
{
|
|
Debug.LogWarning("No ProjectBrowser windows found");
|
|
return;
|
|
}
|
|
|
|
foreach (EditorWindow projectBrowser in projectBrowsers)
|
|
{
|
|
if (projectBrowser == null)
|
|
{
|
|
Debug.LogWarning("Found a null ProjectBrowser window, skipping");
|
|
continue;
|
|
}
|
|
|
|
// Set to two column layout
|
|
SetProjectBrowserToTwoColumnMode();
|
|
|
|
// Set the slider value to show titles
|
|
ShowTextAndHideIcons();
|
|
|
|
// Force the window to repaint
|
|
projectBrowser.Repaint();
|
|
}
|
|
}
|
|
catch (System.Exception e)
|
|
{
|
|
Debug.LogError($"Error in CustomizeProjectWindowLayout: {e.Message}\nStack Trace: {e.StackTrace}");
|
|
}
|
|
}
|
|
|
|
public void SetProjectBrowserToTwoColumnMode()
|
|
{
|
|
System.Type projectBrowserType = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
|
|
var projectBrowsers = Resources.FindObjectsOfTypeAll(projectBrowserType);
|
|
foreach (var projectBrowser in projectBrowsers)
|
|
{
|
|
MethodInfo setTwoColumnsMethod = projectBrowserType.GetMethod("SetTwoColumns",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
setTwoColumnsMethod?.Invoke(projectBrowser, null);
|
|
}
|
|
}
|
|
|
|
public void ShowTextAndHideIcons()
|
|
{
|
|
System.Type projectBrowserType = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser");
|
|
var projectBrowsers = Resources.FindObjectsOfTypeAll(projectBrowserType);
|
|
foreach (var projectBrowser in projectBrowsers)
|
|
{
|
|
FieldInfo listAreaField = projectBrowserType.GetField("m_ListArea",
|
|
BindingFlags.Instance | BindingFlags.NonPublic);
|
|
var listAreaState = listAreaField?.GetValue(projectBrowser);
|
|
if (listAreaState != null)
|
|
{
|
|
System.Type listAreaType = listAreaState.GetType();
|
|
|
|
var gridSizeProperty =
|
|
listAreaType.GetProperty("gridSize", BindingFlags.Instance | BindingFlags.Public);
|
|
|
|
gridSizeProperty.SetValue(listAreaState, 16);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Added by VB with help from UAI
|
|
// Closes the AI Navigation window if it opens by default
|
|
// NOT CALLED -- Layout appears to handle this now
|
|
public void CloseNavWindowOnStart()
|
|
{
|
|
// Find the Navigation window
|
|
var navigationWindowType = typeof(EditorWindow).Assembly.GetType("UnityEditor.NavMeshEditorWindow");
|
|
if (navigationWindowType != null)
|
|
{
|
|
var navigationWindow = EditorWindow.GetWindow(navigationWindowType, false, "Navigation");
|
|
if (navigationWindow != null)
|
|
{
|
|
// Close the Navigation window
|
|
navigationWindow.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Configure Main Camera to look at the Environment.
|
|
// Necessary because the user may create a new Scena and save over SampleScene in T1, using the Main Camer'as default config instead of the one we want, as set in SampleScene.
|
|
// 10/30/2024 AI-Tag
|
|
// This was created with assistance from Muse, a Unity Artificial Intelligence product
|
|
// Edited to use the mainCamera GameObject defined above for UseMainCamera()
|
|
public void ConfigureMainCamera()
|
|
{
|
|
if (mainCamera == null)
|
|
{
|
|
mainCamera = FindGameObjectByName("Main Camera");
|
|
}
|
|
|
|
if (mainCamera != null)
|
|
{
|
|
// Set the Transform Position and Rotation
|
|
mainCamera.transform.position = new Vector3(-18.0f, 26.0f, 0.0f);
|
|
mainCamera.transform.rotation = Quaternion.Euler(56f, 90f, 0f);
|
|
|
|
// Access the Camera component
|
|
Camera mainCameraCam = mainCamera.GetComponent<Camera>();
|
|
|
|
if (mainCamera != null)
|
|
{
|
|
// Basic Camera Settings
|
|
mainCameraCam.clearFlags = CameraClearFlags.Skybox;
|
|
mainCameraCam.backgroundColor = new Color(0.192f, 0.302f, 0.475f, 0.000f);
|
|
mainCameraCam.orthographic = false;
|
|
mainCameraCam.fieldOfView = 60f;
|
|
mainCameraCam.nearClipPlane = 0.3f;
|
|
mainCameraCam.farClipPlane = 1000f;
|
|
mainCameraCam.depth = -1;
|
|
mainCameraCam.allowHDR = true;
|
|
mainCameraCam.allowMSAA = true;
|
|
mainCameraCam.allowDynamicResolution = false;
|
|
mainCameraCam.useOcclusionCulling = true;
|
|
mainCameraCam.stereoConvergence = 10f;
|
|
mainCameraCam.stereoSeparation = 0.022f;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Debug.LogError("Main Camera GameObject not found!");
|
|
}
|
|
}
|
|
}
|