Skip to content

Commit afa5292

Browse files
underscore95ItsNeil17
authored andcommitted
feat: add ui guide
Create ui.mdx
1 parent f5bb505 commit afa5292

1 file changed

Lines changed: 229 additions & 0 deletions

File tree

  • content/docs/en/guides/plugin
Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
---
2+
title: Custom UI
3+
description: Learn how to show custom UI to the player
4+
authors:
5+
- name: "underscore95"
6+
url: "https://github.com/underscore95"
7+
---
8+
9+
## Important Information
10+
- All .ui files must be included in your plugin resources folder, specifically they should be in resources/Common/UI/Custom
11+
- Ensure your manifest.json contains `"IncludesAssetPack": true`
12+
- The Hytale client has a "Diagnostic Mode" setting under General, this will give more detailed error messages.
13+
14+
## Useful Resources
15+
16+
Video Tutorials:
17+
- CustomUIHud Tutorial: https://www.youtube.com/watch?v=u4pGShklEKs
18+
- InteractiveCustomUIPage Tutorial: https://www.youtube.com/watch?v=NOFWQt9wEbk
19+
20+
Examples:
21+
- Simple InteractiveCustomUIPage Example: https://github.com/underscore95/Hytale-Sandbox-Plugin/tree/ui-pages
22+
- Complicated InteractiveCustomUIPage Example: https://github.com/Buuz135/AdminUI/tree/main
23+
24+
## .ui files
25+
26+
Hytale currently uses .ui files to render UI, these are currently deprecated and Hytale is planning to move to NoesisGUI.
27+
The transition has not happened yet and .ui files are the only way to render UI.
28+
29+
UI is defined using a .ui file which functions similarly to HTML and CSS.
30+
31+
### UI Elements
32+
33+
A .ui file contains a tree of UI elements, declaring a UI element uses the following syntax:
34+
```
35+
Group {
36+
TextField #MyInput {
37+
Style: $Common.@DefaultInputFieldStyle;
38+
Background: $Common.@InputBoxBackground;
39+
Anchor: (Top: 10, Width: 200, Height: 50);
40+
}
41+
}
42+
```
43+
44+
`TextField` and `Group` are the types of UI elements.
45+
- A text field is an input where the user can enter text.
46+
- A group is an empty UI element, similar to a div in HTML.
47+
48+
`#MyInput` is the ID of the TextField UI element, this is required for Java code to access the element later.
49+
50+
### Variables
51+
52+
You can define variables using the following syntax:
53+
```
54+
@MyTex = PatchStyle(TexturePath: "MyBackground.png");
55+
```
56+
57+
### Textures
58+
59+
Textures can be loaded using
60+
```
61+
PatchStyle(TexturePath: "MyBackground.png");
62+
```
63+
64+
The path is relative to the .ui file.
65+
Your textures must be included in your resource folder.
66+
67+
If applying a texture as the background of a UI element, you do not need to match size, the texture will automatically be stretched.
68+
69+
### Including other .ui files
70+
71+
You can include other .ui files using `$Common = "Common.ui";`, this allows you to access variables like so: `Style: $Common.@DefaultInputFieldStyle;`
72+
73+
## HUDs
74+
75+
A HUD is an element of UI that stays on the screen all the time, for example the players hot bar or health bar.
76+
It cannot be interacted with.
77+
78+
### CustomUIHud
79+
80+
Create a Java class that extends `CustomUIHud` and overrides the `build` function.
81+
82+
The `build` function has a `UICommandBuilder` parameter, this allows you to add .ui files to the HUD.
83+
```
84+
uiCommandBuilder.append("MyUI.ui"); // This file must be located at resources/Common/UI/Custom/MyUI.ui
85+
```
86+
87+
### Showing & Hiding UI
88+
89+
You can get the HudManager using `Player#getHudManager`.
90+
- Use `HudManager#setCustomHud` to show UI.
91+
- Use `HudManager#hideHudComponent` to hide UI.
92+
- https://www.curseforge.com/hytale/mods/multiplehud allows showing multiple custom HUDs
93+
94+
## UI Pages
95+
96+
Pages are another type of UI, these prevent the player from interacting with the game and unlock the players mouse.
97+
Some examples of pages include: Crafting menu, pause menu
98+
99+
### CustomUIPage
100+
101+
If you do not need user input, you can make a class extending CustomUIPage, this is very similar to CustomUIHud.
102+
103+
### InteractiveCustomUIPage
104+
105+
You must extend InteractiveCustomUIPage to receive events and user input.
106+
107+
The following UI is used for the below code:
108+
```
109+
$Common = "Common.ui";
110+
111+
@MyTex = PatchStyle(TexturePath: "MyBackground.png");
112+
113+
Group {
114+
LayoutMode: Center;
115+
116+
Group #MyPanel {
117+
Background: @MyTex;
118+
Anchor: (Width: 800, Height: 1000);
119+
LayoutMode: Top;
120+
121+
Label #MyLabel {
122+
Style: (FontSize: 32, Alignment: Center);
123+
Anchor: (Top: 50);
124+
Text: "MyText";
125+
Padding: (Full: 10);
126+
}
127+
128+
TextField #MyInput {
129+
Style: $Common.@DefaultInputFieldStyle;
130+
Background: $Common.@InputBoxBackground;
131+
Anchor: (Top: 10, Width: 200, Height: 50);
132+
Padding: (Full: 10);
133+
}
134+
}
135+
}
136+
```
137+
138+
InteractiveCustomUIPage takes a generic argument, this is a class containing any UI data you want the client to send to the server.
139+
140+
```
141+
public static class Data {
142+
public static final BuilderCodec<Data> CODEC = BuilderCodec.builder(Data.class, Data::new)
143+
.append(new KeyedCodec<>("@MyInput", Codec.STRING), (data, value) -> data.value = value, data -> data.value).add()
144+
.build();
145+
146+
private String value; // Value of the TextField input
147+
}
148+
```
149+
150+
InteractiveCustomUIPage constructor takes a PlayerRef, CustomPageLifetime, and `BuilderCodec<Data>`.
151+
- PlayerRef is the player you want to show UI to.
152+
- CustomPageLifetime is an enum controlling if the player can close the UI or not
153+
154+
`BuilderCodec<Data>` tells the server how to create the Data object from the JSON the client sends.
155+
Example value in the Data class above.
156+
157+
This passes the Class and a lambda function that constructs the Data object, the server uses this to deserialize the JSON.:
158+
```
159+
BuilderCodec.builder(Data.class, Data::new)
160+
```
161+
162+
This provides a setter and getter lambda function to the field that should contain whatever @MyInput is set to:
163+
```
164+
.append(new KeyedCodec<>("@MyInput", Codec.STRING), (data, value) -> data.value = value, data -> data.value).add()
165+
```
166+
167+
The following build method is used:
168+
```
169+
@Override
170+
public void build(@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder uiCommandBuilder, @Nonnull UIEventBuilder uiEventBuilder, @Nonnull Store<EntityStore> store) {
171+
uiCommandBuilder.append("MyUI.ui");
172+
uiEventBuilder.addEventBinding(CustomUIEventBindingType.ValueChanged, "#MyInput", EventData.of("@MyInput", "#MyInput.Value"), false);
173+
}
174+
```
175+
176+
This says we are listening for whenever the value of #MyInput changes:
177+
```
178+
CustomUIEventBindingType.ValueChanged, "#MyInput"
179+
```
180+
181+
You may be wondering where `@MyInput` in the codec previously came from, it is defined here.
182+
This creates the codec value `@MyInput` and maps it to `#MyInput.Value`:
183+
```
184+
EventData.of("@MyInput", "#MyInput.Value")
185+
```
186+
187+
The following `handleDataEvent` will be overridden, take care to override the one that has a `Data` parameter rather than `String raw` (this is the raw json).
188+
```
189+
@Override
190+
public void handleDataEvent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, Data data) {
191+
super.handleDataEvent(ref, store, data);
192+
193+
System.out.println("EVENT: " + data.value);
194+
195+
sendUpdate();
196+
}
197+
```
198+
199+
You must always either switch to a new UI or call `sendUpdate();` otherwise the Hytale client will display "Loading..." forever and prevent the user from interacting with your UI.
200+
This will be familiar if you have ever made commands in a Discord bot, where you have to acknowledge interactions.
201+
202+
### Opening UI pages
203+
204+
```
205+
player.getPageManager().openCustomPage(ref, store, MyUI(playerRef));
206+
```
207+
208+
## Dynamically Updating UI
209+
210+
In many cases you want to update your UI at run time.
211+
212+
Here is an example of updating a Label's (with id MyLabel) text content, this method should be added to your UI Java class:
213+
214+
```
215+
public void updateText(String newText) {
216+
UICommandBuilder uiCommandBuilder = new UICommandBuilder();
217+
uiCommandBuilder.set("#MyLabel.TextSpans", Message.raw(newText));
218+
update(false, uiCommandBuilder); // false = don't clear existing UI
219+
}
220+
221+
## Common Issues
222+
223+
### Failed to apply custom ui hud commands
224+
225+
This means your .ui file has something wrong with it.
226+
227+
### Could not find document XXXXX for Custom UI Append command
228+
229+
This means your .ui file wasn't in the location your Java code said it was, double check that your path is correct.

0 commit comments

Comments
 (0)