Java程序辅导

C C++ Java Python Processing编程在线培训 程序编写 软件开发 视频讲解

客服在线QQ:2653320439 微信:ittutor Email:itutor@qq.com
wx: cjtutor
QQ: 2653320439
Creating a UIElements Custom Inspector in Unity | A²I² Artificial Intelligence at Deakin Skip to content About Research Publications Themes Grants Awards Industry Projects Careers Study with us News Blog Contact . Home About Research Themes Grants Awards Publications Industry Projects Study with us News Blog Contact Home/Blog Creating a UIElements Custom Inspector in Unity Blog / Matthew van Zyl / April 3, 2020 Introduction Have you ever worked on a Unity project wishing the inspectors could do more than just set the values of exposed variables? Have you ever wished there was a better way to interact with that data and even automate some of the construction of your scenes using that data inside of the editor? Well if you have, then you’ve no doubt come across the wonderful world of editor extension. The creation of editor extensions in Unity used to be done with the use of IMGUI, however Unity has made this process even more approachable and flexible with the release of UIElements, Unity’s retained-mode UI toolkit. In order to demonstrate some of the new workflows for creating editor extensions using UIElements, this tutorial will detail the implementation of a custom inspector for representing and manipulating nested scriptable objects. We’ll use the scenario of a star system containing multiple planets as an example for this tutorial and by the end of it you’ll have made a custom inspector. I’ve made the project files available if you’d like to play along. The project files also include a way to use the scriptable objects we’ll define in this tutorial to populate the scene with game objects, which is not covered in this post. Fig 1: Final Star System Game Object Why create a custom inspector? A default inspector in Unity will only give you a very basic way to interact with the variables a class has. It will not display any fields for properties and will also not allow you to edit the variables of any object references your class may have. For the most part, this isn’t an issue but sometimes you may want to represent the data your classes contain in a more visual and meaningful manner or have the values of some of your variables dynamically change the values of others, or perhaps you’d like to create buttons that can automate processes inside of the editor to simplify development in your project. These are the kinds of benefits a custom inspector can bring. In our scenario, we’ll create a custom inspector to enable us to edit all of our star system and planet data in a single centralised place. The default inspector for our StarSystem scriptable objects will look like this: Fig 2: Default Star System Inspector With a custom inspector, we can change how all of the data our objects contain are presented, but more than that we can also directly interact with that data and even define more complex functionality. Ultimately this tutorial will create a custom inspector that will look like this: Fig 3: Custom Star System Inspector In addition to presenting the planets that a star system has, this custom inspector will allow us to easily add and remove planets from our star system and directly change the values of variables those planets have from within the star system inspector. Why use Scriptable Objects? ScriptableObject is a serializable class that is great for storing data and is commonly used in inventory systems, config files and storing game object properties. Because ScriptableObjects can be saved as asset files in your project, they can be referenced and shared by game objects in any scene. This makes them an excellent candidate for storing our StarSystem and Planet data. The usefulness of ScriptableObjects doesn’t stop there, though; talks given by Richard Fine and Ryan Hipple cover more about how you can leverage the power of ScriptableObjects in your projects. Creating the Scriptable Objects The setup for our two Scriptable Objects is quite straightforward, requiring only two objects: A StarSystem object that has a sprite and a collection of planet objects. A Planet object that describes what a single planet looks like. Create the following two script files inside your scripts folder: StarSystem.cs [CreateAssetMenu(fileName = "New Star System", menuName = "Star System")] public class StarSystem : ScriptableObject { public Sprite sprite; public List planets; public float scale; private void OnEnable() { if (planets == null) planets = new List(); } } Planet.cs public class Planet : ScriptableObject { public Sprite sprite; public float scale; public float speed; public float distance; } The [CreateAssetMenu(...)] attribute allows us to easily create custom assets of our class from the create menu: Fig 4: Create a Star System Scriptable Object From this menu, create a new StarSystem scriptable object asset. Creating the Editor Files Note: All editor scripts must be placed in a specially named Editor folder in order for them to work correctly. For more details see: Unity Special Folder Names Now we will get to the meat of this tutorial – the creation of the custom inspector. Before we begin, let’s quickly get an idea about the structure of our custom inspector: Fig 5: Star System Inspector Structure When selecting a StarSystem Scriptable Object asset, we want our custom inspector to appear in the inspector window. This inspector will display the details of the star system but will also contain a list of sub-inspectors for each of the Planets the StarSystem has. Now with that out of the way, we can create all the editor files our StarSystem custom inspector will need. Right-click in your project window and select Create/UIElements Editor Window: Fig 6: Creating a new UIElements Editor After selecting this, you will be presented with a dialog box in which you can specify which editor files you wish to create and their associated names: Fig 7: UIElements Editor Window Creator – Star System Note: By default, this new UIElements editor will be an Editor Window, we will change this so that it will be a custom inspector later in this tutorial. Now we have all the files we will need for our main StarSystem custom inspector. We will repeat this process to create all the editor files required for our Planet custom sub-inspector. Fig 8: UIElements Editor Window Creator – Planet After this, we should have the following 6 files: Fig 9: Custom Editor Files So what are these files we have just created? Well in UIElements there are 3 types of files that each work together to define the look and functionality of your editor: The C# file is responsible for the logic of the editor and is also responsible for loading in the styles and templates from the other two editor files. The UXML file is responsible for the structure of the editor and can be used to define templates for different UI elements. The format of this file is inspired from HTML, XAML and XML. The USS file is responsible for the styling of the editor and will dictate the look and feel of the editor. The format of this file is very similar to CSS. StarSystemEditor UXML Open your StarSystemEditor.uxml file; this file will define the structure of our Star System editor. For the StarSystem custom inspector, we will be populating our StarSystemEditor.uxml file so that its elements fall into one of two different sections: Fig 10: Star System Inspector Sections Layout Edit your file until it mirrors the below: StarSystemEditor.uxml In UIElements, all of the existing elements are defined in one of two namespaces: UnityEngine.UIElements – Elements defined as part of the Unity Runtime. UnityEditor.UIElements – Elements defined as part of the Unity Editor. To simplify things we define these namespaces to the prefixes engine and editor respectively so that we can easily specify the elements we require. You’ll see that we’ve separated our elements into two container elements that correspond with our layout; both of these container elements are of the type VisualElement. Many of these elements have classes and names associated with them and these will be important later. The classes will be used to apply styling to the various elements and the names will be used to identify the elements when connecting up the actual functionality. USS Now open your StarSystemEditor.uss file. Here we’ll define the styles that will be used by our Star System custom inspector. Edit your file until it mirrors the below: StarSystemEditor.uss .container { background-color: #A9A9A9; margin: 5, 10, 5, 0; padding: 5; border-width: 3; border-radius: 10; border-color: #909090; } .spriteContainer { width: 120; height: 120; left: 150; margin-bottom: 2; } .spriteImage { position: absolute; -unity-background-scale-mode: scale-and-crop; margin: 10; width: 100; height: 100; border-radius: 10; } .spriteBackground { position: absolute; background-color: #000000; border-width: 3; border-color: #444444; width: 120; height: 120; border-radius: 10; } .heading{ margin: 5, 0, 10; font-size: 20; -unity-font-style: bold; -unity-text-align: middle-center; color: #4B4B4B; } For those of you familiar with CSS you should feel right at home. The classes defined in this file are used in our StarSystemEditor.uxml file but some of them will also be used in our Planet sub-editor later in this tutorial. Unity’s documentation on the USS-SupportedProperties offers information on all the currently available properties so we will not be going into any detail about what these do here. However, I will say that it’s worthwhile to make use of the UIElements Debugger when determining what property values to change. The debugger can be found within the dropdown list right above the scene view: Fig 11: UIElements Debugger CS Open your StarSystemEditor.cs file. This file defines the functionality of the editor. Clear out all the example code until your file mirrors the following: StarSystemEditor.cs using UnityEditor; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.UIElements; [CustomEditor(typeof(StarSystem))] public class StarSystemEditor : Editor { public void OnEnable() { starSystem = (StarSystem)target; rootElement = new VisualElement(); // Load in UXML template and USS styles, then apply them to the root element. VisualTreeAsset visualTree = AssetDatabase.LoadAssetAtPath("Assets/Scripts/Editor/Star System Editor/StarSystemEditor.uxml"); visualTree.CloneTree(rootElement); StyleSheet stylesheet = AssetDatabase.LoadAssetAtPath("Assets/Scripts/Editor/Star System Editor/StarSystemEditor.uss"); rootElement.styleSheets.Add(stylesheet); } ... There are a few things of particular importance to note in the changes: Firstly we’ve added the [CustomEditor(typeof(StarSystem))] attribute to the class, this informs Unity which object the class should act as an editor for. Secondly, the class no longer derives from EditorWindow but instead Editor, this change is important as we are creating a custom inspector and not an editor window. Within the OnEnable method, we define the root element; this visual element will contain all of our other elements. We’ll also load in our UXML template and USS styles and apply them to our root element. This will mean that all the children of our root element can have access to the styles that were defined in our StarSystemEditor.uss file. Next, append the following to your file: StarSystemEditor.cs ... public override VisualElement CreateInspectorGUI() { #region Fields // Find the visual element with the name "systemSprite" and make it display the star system sprite if it has one. VisualElement systemSprite = rootElement.Query("systemSprite").First(); systemSprite.style.backgroundImage = starSystem.sprite ? starSystem.sprite.texture : null; // Find an object field with the name "systemSpriteField", set that it only accepts objects of type Sprite, // set its initial value and register a callback that will occur if the value of the filed changes. ObjectField spriteField = rootElement.Query("systemSpriteField").First(); spriteField.objectType = typeof(Sprite); spriteField.value = starSystem.sprite; spriteField.RegisterCallback>( e => { starSystem.sprite = (Sprite)e.newValue; systemSprite.style.backgroundImage = starSystem.sprite.texture; // Set StarSystem as being dirty. This tells the editor that there have been changes made to the asset and that it requires a save. EditorUtility.SetDirty(starSystem); } ); FloatField scaleField = rootElement.Query("starScale").First(); scaleField.value = starSystem.scale; scaleField.RegisterCallback>( e => { starSystem.scale = e.newValue; EditorUtility.SetDirty(starSystem); } ); #endregion #region Display Planet Data // Store visual element that will contain the planet sub-inspectors. planetList = rootElement.Query("planetList").First(); UpdatePlanets(); #endregion #region Buttons // Assign methods to the click events of the two buttons. Button btnAddPlanet = rootElement.Query