Recommended Posts

Newer versions of KSP support Unity’s updated UI system (technically available since KSP 1.0, but only really practical since 1.1), which is a major improvement over the old OnGUI system. Rather than rebuilding the UI every frame using the esoteric GUILayout and GUI system, the new Unity UI is built using standard Unity objects and hierarchies. This significantly improves performance, reduces the garbage created, and allows for some fancy effects that weren’t practical with OnGUI. The only problem is that building the new UI entirely through code is extremely tricky, to the point of being impractical in all but the simplest cases.

So there are basically two options for using the new UI. One is built into KSP and is perfect for creating relatively simple windows that don’t require much customization. This is the PopupDialog system, it uses notations similar to the old GUILayout system, but generates a new Unity UI object. There is a thread with more details on PopupDialogs and some examples.

The other option is to build your UI in the Unity editor, export it as an AssetBundle, and load it into KSP. There are also two options for how to handle this. You can create the UI in Unity without importing any assemblies into the Unity editor. When you load this UI into KSP you will need to manually hook up all of the elements through code. Things like the function called by a button, or the string of a text element will all have to be manually assigned. This is OK for simpler UIs, but can become prohibitively tedious for more complex UIs. Sarbian’s GC Monitor is an example of this type of UI.

For a more complex UI it can be simpler to import an assembly directly into the Unity editor, allowing you to set button, or toggle listeners, and to store references to UI elements where needed. The only problem here is that you cannot import an assembly that refers to KSP’s Assembly-CSharp.dll, the primary KSP assembly. This means that any mod using a Unity UI will need two assemblies, one to handle the KSP side of things, and another that can be imported into Unity and will handle the UI side of things. The KSP assembly can keep a reference to the UI assembly, but the UI assembly can’t refer to the KSP assembly, since that will refer to Assembly-CSharp. This means that communication between the two assemblies will be difficult.

But before we get to that we can go over some of the basics of Unity UI creation (generic UI creation is also broadly similar to KSPedia creation, which is covered in its own tutorial). I won’t go into too much detail, since there are a number of very thorough tutorials and examples available. Check out some of these; pay particular attention to the RectTransform as that is the core positioning component of all UI elements, and it can be quite tricky to get a handle on.

Using Basic Orbit as an example project we’ll go over several different areas of UI creation, starting with making a simple, static window, one that can’t be re-sized and has a fixed amount of UI elements. I’ll be using the same formatting as in the KSPedia tutorial, with Unity Editor screen elements Bolded and UI objects and elements Bolded and Italicized. The code for Basic Orbit is available on GitHub. Much of the methods used here come from Kerbal Engineer Redux (which uses the Unity UI only for its toolbar button), its source and Unity project can be found on its GitHub page.

This is the window we’ll be creating and hooking up to KSP today. It controls the visibility and some options for other screen objects.

KSP Part Tools – The current package includes what is needed for generating Asset Bundles and the legacy Part Tools files

It should be noted that anyone familiar with Unity who can write their own asset bundle script doesn't need to worry about the Part Tools, just write the script for building an asset bundle and you can use the version of Unity that matches KSP

If you do so make sure that any scripts you add to the project are placed in a folder called Editor somewhere within your Unity project's Assets folder

To start with we need a new Unity project (I’m using Unity 5.2.4f1, since this is the version supported by the KSP Part Tools - KSP 1.4.x uses Unity 2017, but earlier versions should still work). I would suggest making a separate project folder for each UI, you can create a new project in the Unity startup window, you can then copy the KSP Part Tools files into that project’s Asset folder, or just import them into Unity in the normal way. We only need the Part Tools for the Asset Compiler function, which we’ll get to later.

Unity Project Setup:

The first step is to install Unity and add the Part Tools package.

Once Unity is installed open it and create a project for KSP (there are probably other tutorials that cover setting up Unity in more detail; that isn’t really covered here).

Go to the Assets Tab along the top -> Import Package -> Custom Package -> Select the PartTools_AssetBundles.unitypackage file

This will load all of the bits necessary for KSPedia creation

Now you want a new scene; the Hierarchy Window should only have the Camera, which we want because it allows us to view the UI as it will be in-game

In the Main Window you’ll want to be in the #Scene Tab, in 2D mode, using regular Shaded mode

Section 2: Creating the UI:

All Unity UI windows must be the child of a Canvas element, so we’ll need to add one here. In general, we don’t need to export this Canvas, since our UI can be added directly to one of KSP’s Canvases, but we need it in the editor to actually see anything we create.

Add a Canvas: GameObject Tab -> UI -> Canvas

This adds the Canvas element and the EventSystem

The default properties should be fine, you’ll want to set Pixel Perfect on, since most KSP canvas’ seem to use this, it simply makes sure that all UI elements are snapped to pixel edges

Render Mode should be on Screen Space – Overlay, this is for basic UI styles, a simple window on the screen

To this Canvas we then add a Panel: Right-click on the Canvas -> UI sub-folder -> Panel

The Panel is the basic window object, this is where we add buttons, labels, images, and so on

The Panel has a RectTransform, a Canvas Renderer, and an Image element

The RectTransform controls the window size and position

The Canvas Renderer is a required component for any UI element that will actually be drawn on-screen

The Image is the window background image

By default, the Panel is the same size as the Canvas and uses the Stretch Anchor, meaning it will always stretch to the size of the Canvas, we obviously don’t want this

Click on the Anchor image (the blue arrows, or red lines) in the RectTransform element and select the Middle-Center Option, with red lines crossing in the center

You may end up wanting the Anchor to be Top-Left and the Pivot to be 0, 1, but you can leave them as they are for now

This will set the Anchor to the center of the Panel, and will change the available RectTransform Fields, we can now directly set the size of the Panel, and they will be fixed

Once we set the Panel size it will look like this:

The Pivot (blue circle) and Anchor (white arrows) are in the center and the borders are marked in the corners with the blue dots

Adding Elements to the Window:

We’ll go over the background images used for the window, and more complex anchoring and pivot functions later, but for now, we can start adding the UI elements to this simple window.

Basic UI elements can be added by right-clicking on any object in the Hierarchy Window and selecting a UI object

The Add Component button can be used to add those same elements, or any other Unity object or script

To start with we’ll add a few Text labels and some Toggles that look like buttons

For Text elements just right-click the Panel and select a Text object under the UI tab

To position the element we can drag it to the desired location, it should snap to the center line

The width and height can be changed by dragging the edges, or by changing their values in the RectTransform properties

Dragging the edges will make the element un-centered, whereas editing the height and width in the RectTransform will adjust the element’s size based on its pivot position (centered by default)

Since this window is fixed we don’t need to worry about the anchor position, but if you want to make sure that an element stays at the top of the window you can change the anchor to Top-Center, or Top-Right if you want it stay in that corner, this can help when making a UI if you are frequently adjusting the window size

For fixed Text elements we can just type in the Text Field whatever we want, for dynamic elements (which are set by something in-game) we’ll cover them later

The Text properties: size, style, alignment, color, etc… can all be adjusted in the Text element’s properties

This means that child elements are drawn over their parents, and siblings are drawn in order from last to first, with last on top

Now to add some Toggle Buttons, since this a group of three related Toggles we can put them all under one parent object

Right-Click on the Panel and select Create Empty, this will add a simple RectTransform with no UI elements

We can adjust its position, size and anchor so that it can hold the Toggles and be fixed relative to the top of the window

Now add a Toggle element directly to the new empty object (all Unity objects can be renamed by double-clicking on the in the Hierarchy window)

Unity doesn’t have native Toggle Buttons, so the element created here is a standard toggle with a label and checkbox

We can adjust these elements to work as a Toggle Button just by changing their size and positions (and some code that we’ll get to later)

We need to go from this:

To this (yes it looks odd, but we’ll get to specifics in a bit):

Creating a Toggle Button and Adjusting the RectTransform:

To make what we need we basically only have to adjust the RectTransform component for each UI element in the Toggle element. The Toggle itself is made up of a Toggle Script, which controls actually activating and deactivating it, the Background Image, which by default is the empty checkbox, the Checkmark Image, which is only shown when the Toggle is put into the “on” state, and a Text Label.

We want this Toggle to look like a standard KSP button, so the images should fill the entire object and the text should be centered

The Background Image will be set to standard KSP button styles (the regular button, a brighter button for when the mouse is over the element, and a darker button for when the mouse is actually clicking on it) and the CheckmarkImage will be set to the darker, pressed KSP button; since the Checkmark Image is drawn over the Background Image, it will hide the standard button images when the Toggle is on

To do this we need to adjust the Background and Checkmark Image RectTransforms to fill the entire object

We set the Anchor to Stretch-Stretch (four blue arrows), this will make the element stretch to fit the size of its parent, it also replaces the size and position fields with offsets, so if we want an element to stretch with its parent, but always have 10 pixel borders around the edge, we can set each field to 10, here we want them all set to 0, the same size as its parent

We do the same with the Checkmark Image

For the Label, we generally want some padding around the edges, and the text should be changed to centered alignment

We’ll get into actually assigning images to these elements later on, for now these are all using the default Unity UI element sprites (which can be seen in KSP in the debug window and a few other places)

We can also replace the Text label with an Image element, this will simply draw an icon over the Toggle Button rather that a label

Images can be imported into Unity by simply copying them into a folder in the Unity Project’s Asset folder, or by dragging them directly into the Unity Editor’s Project Window

Images should be imported as Sprite (2D and UI) and the Generate Mip Maps toggle should be off

Filling in the other UI elements:

I’m not going to go over how to add all of the different UI elements. There are numerous UI tutorials that thoroughly cover different element types; sliders, standard buttons, and text labels are all fairly straightforward to add. For static windows it isn’t too complicated, the RectTransform can get quite complicated, but the best way to understand it is to simply play around with it and change values to see what happens. More complicated UI layouts, and variable size windows will be covered later on.

Exporting the Asset Bundle:

To load anything into KSP we’ll need to actually export all of our prefabs as an AssetBundle using the Asset Compiler from the KSP Part Tools. The method for this is similar to that described in the KSPedia tutorial, just without the KSPedia Database steps.

Drag any prefabs into the Assets folder in your Project Window (this would be the Panel that we added to the Canvas at the start for our window)

This will export a file into the AssetBundles folder in your Unity Project, it should have the .ksp file extension

Section 3: Hooking up the UI:

Now to get to the fun part. By making use of the Unity Editor we can assign methods to all of the toggles and buttons, store references to text elements so they can be updated later, spawn new windows, and much more. To do this we need to make a new assembly that can be imported into the Unity Editor. Any MonoBehaviours defined in this assembly can then be added as scripts to our UI objects. These can be scripts that control specific behaviors, like switching text colors when mousing-over an element, or that control the various window functions.

I’ll be referring to the assembly that is imported into the Unity editor as the Unity Assembly, and the assembly that uses KSP code as the KSP Assembly.

Unity Assembly:

We create our Unity Assembly the same as any other, it should use .Net 3.5, but it should only add references to UnityEngine and UnityEngine.UI, it should not have any references to KSP assemblies. For our KSP Assembly we create that as always, and we add a reference to our new Unity Assembly. This means that the KSP Assembly can call any public methods from the Unity Assembly, modify any public fields, and implement interfaces. But the Unity Assembly can’t directly communicate with the upstream KSP Assembly, or directly use any KSP code. There are a few ways around this, we could use some sort of listener and events system to trigger methods in the KSP Assembly or use interfaces in the Unity Assembly. We’ll be using the interface method.

To simplify importing your assembly into Unity you might want to add a post-build event to your VS project that copies the assembly into the Unity folder:

copy /y "$(TargetPath)" "C:\YourUnityProjectFolder\Assets\Plugins\"

Creating Scripts for Unity:

Any class that inherits from a Unity MonoBehaviour can be imported into Unity and added as a component to any other Unity object.

To import an assembly into the Unity Editor just drag the .dll into your Unity project’s Asset folder, there should be a separate Plugins folder

Once it is imported you can add the script to any Unity object: Add Component -> Scripts -> Your.Assembly.NameSpace -> YourScript

Now the script is added to that object and a new instance will be instantiated whenever that object, or its parent object is created

These scripts can accomplish several things

They can store references to elements of the UI that need to modified by the script

They can add behaviors to elements, such as controlling text color, or for replacing standard Unity Text elements with KSP’s new vector font Text Mesh Pro elements

They can be used to assign listeners to buttons, toggles, sliders, etc…

For the simple window that we’ve already created we have a Text element that needs to be updated in-game (the little mod version label), and several toggles. Because we want to set the initial state of some of these toggles (one controls whether a separate window is open or closed, so if it’s already open that toggle should be set to the on state), and because we want some of the toggles to affect others, we need to store references to the Toggle elements. And we need to assign listeners to the Toggle scripts.

Any field with Primitive Types, or Unity Object Types can be set in the script then assigned to in the Unity Editor

Note the RequireComponent attribute at the top, this simply means that the specified types must also be present on the same GameObject

All UI elements that actually draw something on the screen (images, text, etc…) require a CanvasRenderer, for example

If that component isn’t present on the object it will be added when you add the script

Public fields will automatically be added to the script’s Inspector tab

You can set attach the [NonSerialized] attribute to public fields to prevent them from being shown in the editor or serialized

Private fields can be added by attaching the [SerializeField] attribute (unity chops off the m_ part of the field's name in the Inspector window)

These fields can be filled in by simply dragging the desired elements into their respective fields, or by selecting the little circle to right of the field and selecting the element from a list of all valid elements in the project, or by filling in the desired value for primitive types

Now we can access these fields from any instance of the script, though it is still a good idea to check if they are null, in case of errors made when setting up the UI, or exporting your prefabs

One thing to note about setting Toggle states like this, whenever you change a Toggle’s isOn field, it will trigger that Toggle’s Listener Events, so anything you or anyone else has attached to this Toggle will be triggered. One way of getting around this is to have a Bool set to False while you are doing the initial setup. Then set your Toggle Listener to not do anything when the Loaded Flag is False, after the setup is complete you can set the Flag to True.

Now to add listeners to Toggles and Buttons. The Unity UI attaches listeners to Unity Events triggered by a Button, Toggle, Slider, etc… Any public method that meets the requirement of that particular event can be added as a listener.

public void OrbitPanelToggle(bool isOn)
{
if (!loaded)
return;
if (m_OrbitToggle == null)
return;
//Turn on Orbit Panel
}
public void OrbitDragToggle(bool isOn)
{
if (m_OrbitDragToggle == null)
return;
//Turn on Orbit Panel drag state
}
public void OrbitSettingsToggle(bool isOn)
{
if (m_OrbitSettingsToggle == null)
return;
//Spawn Orbit Panel settings window
}
public void myButtonListener()
{
//Methods with no arguments can be added to any button or to any other element if the argument does not need to be specified
}
public void mySliderAlpha(float alpha)
{
if (!loaded)
return;
if (m_AlphaText != null)
m_AlphaText.text = alpha.ToString("P0");
//Change panel background alpha
}

Inside the Inspector tab for any Unity element with a Unity Event there is a section for adding listeners, you can add more by selecting the plus button on the bottom.

Add a listener by first selecting an object for the little box below the “Runtime Only” box

This will be the object that contains the script which has your listener

You can either drag the object into the box, or select it from the list using the little circle

In this case we select the parent Panel object, which has our example script

The specific method is then selected in the box on the right

This box has a list of all components attached to the selected object

Select the Example script which will then show a list of all public methods that can be chosen

Now we can just basically repeat these steps wherever needed. If you need access to an object somewhere, just add a reference to it in your script and assign it in the editor. If you need more listeners, just add them. More complicated behaviors will be explained later.

One thing to note about Buttons, Toggles, etc, is the Transition and Navigation elements in their Inspector tabs.

Transition refers to how the element behaves in its different states

Sprite Swap transitions mean that different sprites are used for the normal state, when the mouse is over the object, or for when the mouse is clicking on the object

Color Tint just adjusts the color tint for the attached Image element in those same states

Animation uses Unity Animations to design more complex behaviors

This is something that will be covered more later, but KSP generally uses Sprite Swap transitions, and for this example these states will all be setup in-game

Navigation refers to keyboard navigation and is generally something that should be deactivated

When you click on an object it will become the “active” object until you click somewhere else unless Navigation is disabled

This means that the element will remain in the Highlighted state

Section 4: Assembly Communication:

Now that we have our UI hooked up to the Unity Assembly we need to get it communicating with the KSP assembly. There are probably several ways of handling this, but I’ve been using Interfaces in the Unity Assembly to handle it. The basic idea is to create one or more interfaces with the methods and fields needed to send information between the two assemblies, then we add those interfaces to objects in our KSP Assembly. This basically serves two purposes, the interface us used to set the initial state of the UI when it is created, using information from KSP, this could be persistent data, or just anything that can be altered at run time, like the name of a vessel. And it allows for the UI elements to make changes on the KSP side, by setting persistent data, or triggering a KSP-related function.

In our last example we had a window with several Toggle elements, a Text field, and a method for the alpha Slider, so the interface contains what is needed to setup those elements, and to transfer data to the KSP Assembly for persistent storage.

Note that we store a reference to the interface for use by the listener methods

Make sure to set the Loaded Flag to true if needed

The code for actually turning on the separate panel, or changing the background image’s alpha channel can all be handled within the Unity Assembly

Section 5: Turning it On:

Now we have to be able to actually turn on the UI. To do this we need a reference to the UI Prefab and a button somewhere to trigger the UI. We can let KSP load the AssetBundle that was exported from Unity, anything with a .ksp file extension should be loaded, or we can load it ourselves (KSP won’t load it twice, so there is no duplication of resources doing it this way; you can also just remove the .ksp extension to hide it from KSP’s asset loader). I’ve been loading it myself, it works find, and it allows me to open it immediately upon starting KSP. If we need to process or update all of the prefabs it can be useful to load in all of the prefabs, but to generate a window all you really need is the primary prefab (anything that will be created directly by the KSP Assembly). We can store prefabs as references in our Unity scripts by adding a serializable GameObject.

A simple KSPAddon can be used to manually load and store a reference to the prefab:

The arguments in the Instantiate method are used to set the window’s position and rotation

The rotation is set to zero

Here the position is set using the Toolbar Button’s GetAnchor method

There are several canvases that could be used, but in general the MainCanvas will probably be best

We can get a reference to the UI script since it is attached to the newly instantiated object

The window can be closed by either hiding it or destroying it

You can hide the window by setting its gameObject.SetActive(false)

This can be used if the window has a complicated initial setup and you don’t want to keep repeating that

This should be enough to get a basic window into KSP. Future sections will go over dynamic UI generation and UI layouts elements, specific UI features, KSP-style UI elements, and using TextMeshPro for all text elements. They should also, hopefully, be much shorter, since they won’t have to cover so much information.

If you want to setup your UI with the legacy Unity GUI style elements, they are available for free on the Asset Store. You will need to import them into Unity and use the Sprite Editor to set the Splicing lines properly (so that the images stretch to fit whatever size is needed). After that you can simply drag the sprites into your Image elements wherever needed.

Share this post

Link to post

Share on other sites

In this section we’ll go over how to use layout groups, how to make your window adjust its size to the content within, and how to attach a variable number of elements using prefabs.

In principle Unity UI layout groups are simple, there are Horizontal and Vertical Layout groups. By adding child elements, with Layout Elements attached, to a GameObject with a Layout Group the children will be organized according to their order, with their size adjusted according to the Layout Element settings.

Section 1: Layout Groups and Elements:

Basic Orbit has two panels that use Layout Groups to create a variable-sized window. These two windows (the background image transparency is turned down very low) use the same prefab, but a different number of readout modules.

There are two components that drive windows like this; the Layout Group and the Content Size Fitter.

Layout Groups:

Vertical and Horizontal Layout Groups are standard components that can be added to any UI element through the Add Component button in the Unity Editor Inspector Panel. Only one Layout Group can be placed on a GameObject, but as many as is necessary can be placed on child objects. For this example we are only using Vertical Layout Groups, Horizontal Layout Groups work essentially the same way.

To make a window like those shown above we need to create a new Panel and add a Vertical Layout Group element

The script has several properties

Padding allows for a border to be placed around the edges of all child Layout Elements

Padding is applied after all child elements are placed

Spacing places the specified number of pixels between each child Layout Element

Child Alignment doesn’t seem to do anything; I think it may be used for cases where your Layout Elements don’t fill out all of the available space

Child Force Expand can be used to make the child elements expand to fill out available space in the window; this would usually be used for Width with a Vertical Layout Group

By itself the Layout Group won’t do very much, so we need to add child Layout Elements

In this case, each element will need an empty GameObject and two child Text elements

We could use the Text elements as Layout Elements (perhaps within a parent Horizontal Layout Group), but it is generally best to minimize the number of Layout Elements

The Layout Element has several fields to specify its size and flexibility

Min Width and Height are used to set the minimum allowed size, they also set the baseline for the Preferred Size

Preferred Width and Height can be used for some more advanced behavior, such as variable sized objects based on the length of the text content, which we’ll get to later

This is also used frequently used in conjunction with the Content Size Fitter, which we’ll also get to later

Flexible Width and Height can be used to limit how much the size of the element can be changed

A value of 0 means no change is allowed; I believe 0 is implied if the option is unchecked

Note how most of the RectTransform values cannot be adjusted, since they are being controlled by the parent Layout Group

Now we have a window with several Layout Elements under a parent Vertical LayoutGroup. To get the window to adjust automatically to these, and any additional elements, we’ll need to tell the window how to fit everything.

Content Size Fitter:

At the very top-level Layout Group we need to add a Content Size Fitter component. Only one should be added to window (which can sometimes make things tricky in Unity, since we are usually creating all the different UI elements as separate objects that will be combined later in-game) and it should be added alongside a Layout Group element.

These are the scripts attached to the top-level object in this hierarchy. It’s important to note that the Image element is included here; this is used as the window’s background image and will be re-sized along the window according to how many Layout Elements are present (the Canvas Group is used to control the fade in and out behavior that we will get to much later). Also, note how the RectTransform’s Height field cannot be altered, since it is under the control of the Content Size Fitter.

There is a Vertical Layout Group added here; this is the direct parent of the Vertical Layout Group described above

Since this is a simple window none of its properties need to be changed, more complex windows may need adjustments to the Padding or Spacing

The Content Size Fitter is added and its Vertical Fit field is set to Preferred Size

The Horizontal Fit does not need to be changed, since the window is intended to be fixed-width

This will force the window to adjust its size properties to fit the Preferred Height values of all of the child Layout Elements

It also takes into account all of the Spacing and Padding specified in any included Layout Groups

Each time a new Layout Element is added or removed the window will be re-sized to adjust

This also happens when a child GameObject is set to active or inactive

The Layout Groups and Content Size Fitter are simple in principle, but can get quite complicated to deal with when working with, and adjusting Layout Elements.

Layout Element Prefabs:

When we want to dynamically add Layout Elements to a Layout Group it is sometimes necessary to make separate prefabs that can be instantiated multiple times depending on your needs. Basic Orbit is constructed this way, with each readout module consisting of a Layout Element and two Text Elements. The prefab is then instantiated as many times as is necessary and added to the readout panel.

For the parent panel we just need to store a reference to the Prefab GameObject, and to the Transform where we want to add the prefabs, this transform should have a Layout Group that will control the size and positioning of the elements. The picture with the Content Size Fitter above demonstrates this in its Basic Orbit_Panel script.

Section 2: Adding Prefab Elements to a Layout Group:

Now that we have our Layout Groups and Prefabs setup we need to actually add them to our window in-game. First off we need a script attached to the window with references to the Prefabs and to the Transform where they will be added (see part I for how to generate these scripts; prefabs can be dragged into a Serializable GameObject field the same as any other object).

Generate a list of all the elements that need to be added to the window

For Basic Orbit these are readout modules defined in the KSP Assembly

This could be anything, for instance, a list of vessels with their names and locations

Enumerate through the list and instantiate a new instance of the prefab for each element

This process is generally the same as that defined in part I

The only difference is that the new UI Element’s Transform parent is set to the Window Transform described above

You can hide the new element, or use some logic for dynamically turning elements on and off if necessary

Using gameObject.SetActive(true/false) will change the visibility of the element, and make the Layout Group readjust the window to compensate

In general, you should try to limit SetActive calls

Use gameObject.activeSelf or activeInHierarchy to check if an element is active or not before using SetActive

Using a list of the layout elements from the KSP Assembly (using the interface method described in part I) we can instantiate and add our new objects to the Layout Group one at a time. Since not all elements will necessarily be active when they are first added we set their active status based on information from the KSP Assembly. It is sometimes useful to keep references to the UI elements that we are adding, that way they can be updated, removed, or monitored in some other way whenever we need them.

There is a special case when dealing with Layout Elements where you may want an object to fit the size of its text content, for instance, you have a button that might have a long text string. Layout Elements and Layout Groups can be used to handle this. There is also a section in the Unity tutorials that deals specifically with this.

A specific setup is required for this

The Text Element should be a direct child of the element that needs its size to be adjusted

For instance, you have a Button Script with an Image on the same GameObject, then the Text Element should be a child of that GameObject

The parent object (the Button) should have a Layout Group Element (either Vertical or Horizontal should work)

The Text Element should have a Layout Element

Define a Min Width and Height, but leave the Preferred values unchecked

The Pivot point (the blue circle, not to be confused with the anchor point) for the Text Element should be put at the origin of where you want it to expand from

Top-center if you want the Button to have a fixed width, variable height and fixed top position

Somewhere upstream needs to be a Content Size Fitter with its Vertical Fit set to Preferred Size

When all of these requirements are met the UI element should adjust its size based on the length of the text content

Note here the Layout Element on the Text Element (left), with its Preferred Height field left blank, the Pivot position, and the Layout Group on the parent object (right) with the Toggle Script and Image. The Contracts Window + source and Unity project files are also available on GitHub, though in general I would recommend not using it as a reference, it is an extremely complicated UI and has many elements that are adapted specifically to its needs.

In the next section we’ll go over how to apply KSP style UI elements to our creations.

Share this post

Link to post

Share on other sites

In this section we’ll go over how to implement style elements for your UI so that your windows, buttons, sliders, etc… can match the KSP style.

KSP defines a UISkinDef that contains all of the sprites used for the different states of all the common UI elements. Using this we can assign those sprites to our UI. You could also, theoretically define your own UISkinDef, using the old Unity Smoke UI elements, for instance, and possibly allow for switching between the two styles (if you only want to use the Unity UI style it would probably be easier to just assign all of the sprites within Unity.

The basic method used here, like much of the material covered in this tutorial comes from Kerbal Engineer Redux.

Section 1: Tagging UI Elements:

The first step is to create some kind of tag for all of the different UI elements that we are using, this will be used to identify those elements and to determine which KSP style to apply to them.

Create a simple script in your Unity Assembly

Define an Enum with options for each type of UI element

Window, box, slider, button, toggle button, regular toggle, etc…

Text will be covered in a later section

Add a serializable field for that Enum

Add the script to each UI Element in your Unity project and choose the matching option for the EnumField

You want the script to be attached to the same GameObject that needs styling applied

The window or box option should be added to a GameObject with an Image element

The button should be added to a GameObject with the Button script, etc…

If you add in new Enum options later on make sure to them to the end of the list, otherwise you will need to update the selection for each existing GameObject that the script has been added to

Once you’ve added the script to each UI Element that needs styling you’ll need to create a method for processing each UI Element from in-game. Engineer processes each element whenever it is created, but I think it’s better to work at the prefab level, loading and processing each prefab as soon as possible so that it only needs to be done once.

Create a script in your KSP Assembly for loading and processing the prefabs

The UISkinDefs can be accessed through UISkinManager

defaultSkin contains the definitions that will be of interest in most cases

These are only available once the Main Menu has been loaded, so prefab processing can’t occur during the initial loading scene

I’ve been manually loading the assetbundles and then loading the prefabs from there

It’s also possible to keep the .ksp file extension on the assetbundle and let KSP load it

Make sure that all prefabs in your assetbundle use the same tag name

Store a reference to the list of all prefabs so they can be processed when ready

Simple UI Elements like the Window background, or the content Box are only an Image

Only the sprite, and possibly the sprite color and Image.Type need to be accessed

Image.Type is an Enum that defines the different Sprite types possible, in most cases UI elements use Sliced sprites, where the edges are defined and the center is stretched to fill in the space

Many KSP UI elements (though not generally the ones found in the default UISkinDef) are actually white and use a color overlay to give the final result

For Selectable UI Elements we need the sprites for the different states, normal, highlight (mouse over the element) active or pressed (mouse clicking on the element), inactive or disabled (when the element can’t be interacted with)

The Selectable’s Image Component is used to define the normal state

The SpriteState component is used to define the transition style (SpriteSwap) for standard KSP UI Elements and the sprites for each state

The Toggle UI Elements have an additional component, the Checkmark Image, which is activated only when the Toggle is in the On state

This could be an actual check mark, or a circle to fill in the Toggle radio button, or a Toggle Button sprite

The Toggle.graphic component is the Checkmark Image

For Toggle Buttons we can use the pressed/active sprite so that it appears as a button that has been pressed down when active, this basically draws the pressed sprite over the standard sprite

Other components can be handled in the same manner

Some don’t directly store their image components, so you’ll need to make note of how the UI Element is arranged in Unity and manually find each Image

The same process can applied to non-standard UI components by picking apart their prefab

In the next section we’ll go over how to use TextMeshPro elements in your UI. These are the new vector font text elements used by KSP that make text much cleaner and more readable. The overall method is similar to how UI style elements are applied, but there are some special concerns that need to be covered to make it work properly.

Share this post

Link to post

Share on other sites

This section will cover how to replace the standard Unity Text elements with the fancy new TextMeshPro elements included with the latest version of KSP.

Since we can’t add the KSP assemblies to Unity we won’t be able to directly add TMP elements to out Unity project, unless you happen to own the $95 TMP Asset. So instead we’ll have to use standard Text elements as placeholders then do all of the replacements from in-game. This turns out to be fairly simple, the only real issue is that we need some method for dynamically updating the text, or other properties from in-game.

Section 1: Tagging Text Elements:

Much like in the last part of the tutorial, the first step is to create a simple script that can be used to tag all of the Text elements that we wish to replace.

Create a script in your Unity Assembly

This script will also be used for updating the Text Field of the element whenever it needs changing

A simple UnityEvent can be used to trigger then text change when needed

Listeners can be added to this event to watch for text changes and update the Text Field

The only tricky part to all of this is how we take the placeholder Text Element and replace it with the Text Mesh Pro Element. The method for this works similar to assigning style elements, covered in the last part.

See the last part for how to load the Asset Bundle and process the prefabs during loading

Once we have the prefabs we use the TextHandler tag to search for all of the Text Elements that need replacing

We cache the properties from the placeholder Text Element to be used for creating the TMP Element

Since a GameObject can only contain one UI element at a time we need to immediately destroy the Text Element after caching its properties

That’s basically all there is to it. The result is much cleaner looking text that scales well and is readable in much smaller fonts. The only real problem areas are UI elements that require a standard Text Element, such as a Text Input Field. A custom input field will be required to handle these, or you can just leave them as standard Text, since they generally aren’t used as much.

The full source code for the most complete version of the TextHandler and Text Mesh Pro extension (from Contracts Window +) are included below. Feel free to use the code directly.

Share this post

Link to post

Share on other sites

I have several updates made for SCANsat that I'll add here eventually, but for now I'll just add a section on how to get a Text Mesh Pro Input Field into KSP using a method similar to that used for the TextMeshProUGUI.

This method follows the same basic idea as the previous entry: tag any standard Unity Input Fields that you want to replace with a small script, create a TMP_InputField extension class in the KSP Assembly, then convert the Unity Input Field into a TMP_InputField when processing the UI components.

Section 1: Tagging Input Fields:

Make a script for tagging of the Unity Input Fields that need replacing and to handle a few things.

The standard TMP_InputField onValueChanged event is used to update the text in the InputHandler and to trigger its onValueChanged event

It also updates the InputHandler IsFocused field

It adds a small method to the InputHandler's OnTextUpdate event to update the input field's text value.

Section 3: Unity setup:

The Input Field needs to be setup in Unity in a certain way to properly replace it with a TMP_InputField.

First we have to make sure to add the Text-to-TextMeshProUGUI tag to the Text and the Placeholder text components

And make sure to process the Text fields before processing any Input Fields; the TMP_InputField will only work with TMP_Text components

Text Mesh Pro uses a Rect Mask to handle masking text that overflows the text box, rather than the string methods used by the standard Unity Input Field

Add a Rect Mask 2Das a direct child of the Input Field, then add the two Text components to that object

I'm using an Event Trigger and the OnInputClick event to handle control locks whenever an Input Field is clicked on, as you can see in the bottom of the Inspector Tab.

Section 4: Replacing Input Field Elements:

As before, we process the TMP_InputField during loading to replace all standard Input Fields. We do this by caching a few values, generating a new TMP_InputField, and assigning the required values, and assigning the TextMeshProUGUI components.

A conversion method is required for the ContentType field, but the available types for Input Fields and TMP_InputFields are the same

Then we have to find the Rect Mask and the two TextMeshProUGUI components and cache these

Then we Destroy the old Input Field and create a new TMP_InputField

Then assign the cached values

It is important to assign the TextMeshProUGUI components first, since assigning some of the others values relies on these

After all of this you'll end up with Input Fields that use the much nicer Text Mesh Pro text, providing better scaling and better support for other input languages. I'll add links to the full code for this once it is online.

Share this post

Link to post

Share on other sites

Apparently this managed to slide by without notice. It would seem the Text Mesh Pro is now free and will eventually be integrated into Unity

I did not mention it for one simple reason: the free version does not uses the same script id as the paid one. So you should not be able to import Asset made with the free version in KSP (feel free to prove me wrong, it would be better for the modding community). And this will be a mess to migrate into when it is integrated (unless they add a tool later, or the KSP's dev write a script to do it)

Share this post

Link to post

Share on other sites

@DMagic Nice tutorial, but it's a little confusing. For example, how do you make the scripts? Do you make them in one big cs file or create them separately? (in either situation, Unity complains about not being able to find the IExample interface)

And some feedback: I prefer the tutorials that provide base code, which you can modify later to your needs

Share this post

Link to post

Share on other sites

Personally, I made it to the first screenshot of Unity. Have you seen the other API?

Yes, thanks, that's exactly what I did. It's not what I wanted but in 10 lines of code I achieved what was taking me hours and costing me money in pain killers. I was hoping for a set of 4 buttons with arrow icons to direct movement. After seeing exactly what it was going to take to achieve that... I mean I honestly tried. I got to the point I was adding the code into the KSP Asset as he calls it, and ended up throwing my hands up in disgust because it made no sense... all the references and callbacks. All this work for 4 buttons with arrows? It should not be this difficult. My end users will get stupid looking text buttons.

Share this post

Link to post

Share on other sites

@EricL A comprehensive tutorial would be nice, but it would take far more time than I'm willing to put into it. This tutorial assumes some experience with C# (you can put all of your classes and scripts into a single file, I think, maybe Unity requires separate files, but in general that just makes it very difficult to keep track of things), and Unity. There are a million resources, tutorials and answers to specific questions available for both C# and Unity (in particular Unity has some really good video series available for UI content), so there's no need to go over all of that here.

For complete code you can check the source for Basic Orbit (it has changed a bit since this tutorial has been made, but most of the added complexity is in picking apart the stock KSP UI assets to make a matching style). There is also some more complex code for SCANsat's UI, and some simpler code for Maneuver Node Evolved. The Unity project files are also included in those GitHub repos.

Overall I don't think the Unity UI system is that complicated. It is based on standard Unity objects, so most of the UI related work also relates to how most things work in Unity. The Rect Transform and Layout Elements are probably the trickiest aspects to figure out, there are lots of tutorials covering them, but the best thing to do is probably just play around with different values and see what they do, being able to work within the Unity Editor makes this simple since you don't have to launch KSP to test things.

Hooking things up with KSP is tedious, but not really difficult. You either have to just manually assign functions and listeners, or go through interfaces or some kind of event system. It takes some time to get your head around how to set things up, but once you manage that it just becomes tedious.

Edited September 18, 2017 by DMagic

Share this post

Link to post

Share on other sites

I'm having difficulties understanding how the two assemblies are speaking with each other. I'm missing something in the initial post and its confusing me. I assume that we have the KSP assembly and the Unity assembly.

The Unity assembly does not need to discuss jack with the KSP assembly. The KSP assembly has an include of the Unity assembly? Which allows it to access the classes and variables? If this is so, then are we dealing with .dll assemblies or simply .cs files contained within the same project or folder? If the Unity assembly is turning into a .dll assembly... how is that possible?

Please ignore me. I found the exact line which says exactly what I was asking. Sorry.

Edited March 14, 2018 by nightstalker101sRevision

Share this post

Link to post

Share on other sites

I'm working on designing a UI and when I update and build using the part tool assembly compiler I can't find A) Asset Bundle file or B) anything with a .ksp extension. I know I'm doing something wrong, but I don't know what it is.

You can make a C# script in Unity (this is not something to add to your plugin, it is just for the Unity editor), you just have to make sure it's in a folder called "Editor" (so Unity project folder/Assets/Editor/script.cs).

This adds a toolbar button along the top row in the editor, then it builds any asset bundles you have defined, and in this case, attaches a custom file extension. The target directory appears alongside your Assets folder.

.ksp files will be loaded automatically by KSP during loading. But I just handle the asset loading myself, so you can use any extension, or none, for that:

Share this post

Link to post

Share on other sites

I understand your example code. I'm not tracking how you are loading them yourself into KSP. I'm assuming KSP loads all it's assets, goes to main menu, then when appropriate your plugin starts calling on the private asset bundle? I really don't "need" to know this, but it is very interesting as I may decide to use this later for some unknown reason.

The newest Unity made several functions obsolete which are used by the parts tool. I installed the version you listed in your OP and had no problems building the bundle. It took me a minute to understand that literally I am building a UI bundle (I don't even need textures or anything actually in the Unity project) and then using Visual Studio to build two separate libraries (.dll). I had a serious oh duh moment when I realized what you meant in the OP. You did a great job at explaining it, but it really doesn't make sense (no fault of yours) until you are actually doing it. Even worse, if someone isn't familiar with Unity or other game engines it makes it even more difficult to understand. It seems silly to have to build a library (I understand why) in VS instead of just having the part tool asset bundler compile a .dll for any scripts attached to the asset bundle, but again I get why. Thank you for your assistance.

Side Note: I am working on what I'm calling K.O.P.C.O. ("K.A.S.A. Operation Chief Campaign Overhaul"). I'm going to try to build more of a story line or story line feel without causing restrictions to gameplay into KSP. The player starts out being selected as the Operational Chief Director by 11 chairmen of the Kerbal Aeronautics and Space Administration. This was step one because the player will need to participate in interviews later and I needed a UI which is easier than trying to figure out how to program and animate a whole scene (maybe some day). Thank you again.

Share this post

Link to post

Share on other sites

KSP's loading system loads all of its own resources first (I assume before it even gets to the loading screen) then goes through the GameData folder looking for certain file types. .ksp files are treated as asset bundles. I only use .ksp for KSPedia entries, because I don't really know what KSP does with them and I've never really needed to find out.

For most of my mods I just wait until the main menu screen then manually load the asset bundle (which is really just a collection of GameObjects) and doing whatever needs to be done from there, in most cases that is either adding stock KSP UI style sprites to the UI, saving prefabs for use later on, or replacing standard Unity Text components with TextMeshPro components.

And yes, you don't really have to group things together for export in Unity, just label the objects you want with the asset bundle name and it will package together any sprites, textures, or whatever that those objects refer to. Which reminds me, now that KSP is using Unity 2017 I need to add another section here about using the new sprite atlas system.

Share this post

Link to post

Share on other sites

I am sorry but I am following by the letter your instructions and it just does not work.

You are explaining it as if everyone here knows exactly what it should do, and thats not the case, I am an expert on c# and know my deal in Unity, but was unable to follow all you said, even reading it 10 times and doing it at the same time.

Mostly because some stuff is totally out of context. And some parts are not explained at all.

of course this is my first mod so i do not know much of ksp, and thats mostly the problem.

Is there any truly basic UI to see this?, or can you say in which file to set each step

My main problem is, after have done the Unity part, doing the rest (the hook).

because from the ksp section It is unable to see what it should.

Also, I need this as soon as possible. will be really gratefull for a fast answer.

my main issue is, how do you do that, if you are in ksp and m_versionText.text, does not exist there. And if you are not in ksp, how you reference it?

This is the relevant section covering what I think is the problem. Your KSP Assembly needs to have a reference to your Unity Assembly. Then your KSP Assembly can call any method and use any interface defined in the Unity Assembly.

The reverse isn't true because you wouldn't be able to import your Unity Assembly into Unity if it had a reference to any KSP code.

On 11/2/2016 at 3:08 PM, DMagic said:

For our KSP Assembly we create that as always, and we add a reference to our new Unity Assembly.﻿ This means that the KSP Assembly can call any public methods from the Unity Assembly, modify any public fields, and implement interfaces. But the Unity Assembly can’t directly communicate with the upstream KSP Assembly, or directly use any KSP code.

Share this post

Link to post

Share on other sites

Your KSP Assembly needs to have a reference to your Unity Assembly. Then your KSP Assembly can call any method and use any interface defined in the Unity Assembly.

I'm currently taking a look at this too and also had some confusion at this part. Should I reference the generated Asembly-CSharp generated by the Unity project? Do I also need to bundle this with my mode afterwards? Or should I force all this code into an external .csproj to the Unity generated project and bundle this?

Share this post

Link to post

Share on other sites

I'm currently taking a look at this too and also had some confusion at this part. Should I reference the generated Asembly-CSharp generated by the Unity project? Do I also need to bundle this with my mode afterwards? Or should I force all this code into an external .csproj to the Unity generated project and bundle this?

To be clear, the Unity Assembly is one that can be imported into a Unity project and used by assets within that project. It only required references to the UnityEngine.dll and (probably) the UnityEngine.UI.dll. This is imported into Unity as a compiled dll, not as un-compiled .cs files, then all of its classes are accessible by Game Objects within Unity.

Whenever you create a Unity project it also generates an Assembly-CSharp.dll (and several others). This is not related to KSP's Assembly-CSharp.dll (well, technically this is how KSP's .dll is generated, through Squad's original KSP Unity Project).

The new Assembly-CSharp.dll is the default assembly file where any code added through Unity is compiled. This happens when you create a new .cs script somewhere in your project's Asset folder. That code will be compiled into the Assembly-CSharp.dll.

In general you should never have to do anything or worry about the Assembly-CSharp.dll generated by your Unity project. The Unity Assembly that you create is an already compiled .dll file that you import directly into Unity.

So the answer is no, you should not reference the Assembly-CSharp.dll generated by the Unity project, and you should not include that file with your mod.

When you make the KSP Assembly you do so basically the same way as you would for any other mod. You add references to the UnityEngine.dll and UnityEngine.UI.dll, and to KSP's Assembly-CSharp.dll. Then, in this case, you would also add a reference to your Unity Assembly .dll, so that it can be accessed directly from the KSP Assembly.

UnityUIAssembly.dll -> this is the Unity Assembly that is imported into Unity
|
|
---> UnityEngine.dll
---> UnityEngine.UI.dll
KSPAssembly.dll -> this is the KSP Assembly
|
|
---> Assembly-CSharp.dll -> this is from the KSP folder, not from your Unity project folder
---> UnityEngine.dll
---> UnityEngine.UI.dll
---> UnityUIAssembly.dll -> this is your Unity Assembly file

Both the UnityUIAssembly.dll and the KSPAssembly.dll files are placed in a folder in KSP's GameData folder and loaded into KSP.

The basic problem, and the reason for this whole run-around method, is that you can't import an assembly into Unity that has a reference to KSP's Assembly-CSharp.dll. Unity gets confused, or thinks there is some loop of references, or something, and it will fail to import. So if you want code that you can access from within the Unity Editor, then you have to make a separate assembly and go through some indirect method of tying that back into KSP's code.

It isn't absolutely necessary to do this, Fengist has a great tutorial for how to make a UI with a single, standard KSP Assembly. It works well for simpler UI's, but I would have a hard time doing many of the things that I have in my UI's without having code that is directly accessible from within Unity. You can also use this same method for doing other interesting things, like adding custom behavior into KSPedia entries.

Share this post

Link to post

Share on other sites

To be clear, the Unity Assembly is one that can be imported into a Unity project and used by assets within that project. It only required references to the UnityEngine.dll and (probably) the UnityEngine.UI.dll. This is imported into Unity as a compiled dll, not as un-compiled .cs files, then all of its classes are accessible by Game Objects within Unity.

Okay, that's the part I wasn't too clear about. That makes a lot more sense actually. I've been working a lot with the new object based UI lately on personal projects and yeah I do agree it's probably gonna be a lot more handy to have an assembly directly into Unity. Thanks!

Other than that, is there any particular reason it seems to be recommended to anchor in the top left? Just as a convenience or does KSP handle it's Canvas in a specific way that makes this more convenient?