| name | add-ui-feature |
| description | Add a new UI panel, control, or overlay to CLOiSim's HUD. Use when: adding a new button or panel to UIController, creating a new camera mode, adding an info overlay. |
Add a New UI Feature
Procedure for adding UI elements to CLOiSim's HUD using Unity UI Toolkit or extending camera controls.
When to Use
- Adding a new button, toggle, or panel to the main HUD
- Creating a new info display or overlay
- Adding a new camera view mode
- Extending the following target list or model importer UI
UI Architecture
UI (scene root)
├── Main Canvas (UIDocument + UIController)
│ └── UI Toolkit visual tree (UXML)
├── InfoDisplay (legacy TextMeshPro overlay)
└── FollowingTargetList
Two UI systems in use:
- UI Toolkit — main HUD (
UIController.cs): buttons, toggles, text fields, labels
- Legacy UGUI/TextMeshPro — info overlay (
InfoDisplay.cs): FPS counter, status text
Do not mix UI Toolkit and UGUI in the same component.
Procedure
Option A: Add to Main HUD (UI Toolkit)
1. Add Element to UXML
Edit the UXML document (referenced by the UIDocument component on the Main Canvas object). Add your element:
<ui:Button name="MyFeature" text="My Feature" class="toolbar-button" />
2. Wire in UIController
Edit Assets/Scripts/UI/UIController.cs:
private Button _buttonMyFeature = null;
_buttonMyFeature = _rootVisualElement.Q<Button>("MyFeature");
_buttonMyFeature.clickable.clicked += () => OnMyFeatureClicked();
_buttonMyFeature.RegisterCallback<MouseEnterEvent>(
delegate { ChangeBackground(ref _buttonMyFeature, Color.gray); });
_buttonMyFeature.RegisterCallback<MouseLeaveEvent>(
delegate { ChangeBackground(ref _buttonMyFeature, Color.clear); });
3. Implement the Handler
private void OnMyFeatureClicked()
{
_buttonMyFeature.ToggleInClassList("selected");
Main.Instance.MyNewMethod();
}
UI Toolkit Query Patterns
_rootVisualElement.Q<Button>("CameraView")
_rootVisualElement.Q<Toggle>("LockVerticalMoving")
_rootVisualElement.Q<TextField>("ScaleField")
_rootVisualElement.Q<Label>("StatusMessage")
_rootVisualElement.Q<EnumField>("MyEnum")
_rootVisualElement.Q<ScrollView>("MyList")
_rootVisualElement.Q<VisualElement>("MyContainer")
Event Registration Patterns
button.clickable.clicked += () => { ... };
toggle.RegisterValueChangedCallback(x => DoSomething(x.newValue));
textField.RegisterCallback<FocusOutEvent>(OnFocusOut);
element.RegisterCallback<MouseEnterEvent>(delegate { ... });
element.RegisterCallback<MouseLeaveEvent>(delegate { ... });
Styling Patterns
element.style.display = DisplayStyle.None;
element.style.display = DisplayStyle.Flex;
element.style.visibility = Visibility.Hidden;
element.style.backgroundColor = new Color(0.5f, 0.5f, 0.5f, 1f);
element.ToggleInClassList("selected");
element.EnableInClassList("active", isActive);
element.AddToClassList("highlight");
element.RemoveFromClassList("highlight");
Option B: Add a New Camera Mode
1. Create CameraControl Subclass
using UnityEngine;
public class MyCameraControl : CameraControl
{
protected override void HandleMouseWheelScroll(in float value)
{
}
protected override void HandleKeyboardDirection(in float duration)
{
}
}
2. Register in Main.cs
Camera switching is done in Main.cs via SetCameraPerspective() / SetCameraOrthographic() pattern. Add your mode similarly.
Option C: Status Messages
Use the existing status message system:
Main.UIController?.SetStatusMessage("Loading model...");
Main.UIController?.SetWarningMessage("Warning text");
Main.UIController?.SetErrorMessage("Error text");
Main.UIController?.ClearMessage();
Input System
Use New Input System APIs only:
if (Keyboard.current[Key.Space].wasPressedThisFrame) { ... }
if (Keyboard.current[Key.LeftCtrl].isPressed) { ... }
var scrollDelta = Mouse.current.scroll.ReadValue().y;
var mousePosition = Mouse.current.position.ReadValue();
if (Mouse.current.leftButton.wasPressedThisFrame) { ... }
Never use legacy Input.GetKey(), Input.GetAxis(), etc.
Key Rules
- The
UI root object and Main Canvas child must not be renamed
- Camera controls set
_blockControl = true when UI text input has focus
UIController accesses singletons via Main.Instance, Main.ObjectSpawning, etc.
- Use
LateUpdate() for camera control input processing
Checklist