- Use a
TextureRect for crosshair
Range such as
TextureProgress for health bars
RichTextLabel for digit cooldowns
TextureProgress for radial icon progression)
You probably are the only one to know which design suits best your project and your workflow.
What I mean by that is UI is not just a generic interface, it's part of the gameplay and should fit the artistic direction you're aiming toward.
That said godot built a nice abstraction with convenient classes for UI quick prototyping but also more complex and in-depth interfaces by instancing UI scenes.
Pretty much all UI nodes inherit from
Control which works a lot like
There is no "main" UI node as all nodes share the same capabilities thanks to the inheritance. Rather, your visual UI nodes will be shaped, controlled, contained via parent helper UI nodes.
For example in my projects, to import UI in the main scene, I instantiate a separate specific UI scene which is based on a generic
This allow to list all related UI visuals as children and to have a "root-level" script which controls the initialisations and can be used as a proxy in case the different UI parts ever need to interact with each other. But this is not required and could be confusing depending on your workflow/code logic.
An other possibility is having each game element have its own UI elements, for example enemies in a 2D shooter could have their own health bars above their sprites.
The main visuals element are summarized here and are basically:
- Text (
TextEdit): different way to display text
MenuButton): different ways to handle user input
- Sliders/Bars (
Range) : everything related to ranges whether it is to display or set a value/progression
TextureRect: Probably the most interesting one, a way to display a 2D image along with
NineRectPatch to easily display a scalable image (by tiling the centre but preserving the corners).
For further fine-tuned control such as placement, those can be instantiated as children of a
Container. There are many specific containers but to name a few,
VSplitContainer will offload the calculation to equally place (or stack) respectively horizontally and vertically your nodes.
ScrollContainer will offload the scrolling logic for your scrollable elements such as lists.
ViewportContainer will allow you to display a sub-view, a texture, shader, camera, its own 3D space, you name it.
- And the
MarginContainer you're naming is meant to add a margin (spacing toward the exterior of the element) to all children, instead of having to manually add a margin to all your children.
CanvasLayer you mentioned is used in case you don't want to have all the 2D related elements to draw on the same "layer", given the rendering order, some elements could be overwritten/superimposed. For example It allows you to draw a 2D background on the farthest plane, draw the 2D player over it on another layer above it, then draw the UI on yet another layer above the last, and yet a new layer above it for special effects, and so on...
Where you want to instantiate your crosshair is up to you. If it's closely linked to other UI then maybe a generic
Control node as a parent which holds the rest of the UI element makes sense. Or maybe it can be part of your player Node if you plan to have the crosshair react to player input or gameplay logic.
But I can only see 2 convenient solutions to draw your crosshair:
TextureRect: loading a image of a crosshair to which you can scale (make it bigger when player moves and have less accuracy or smaller when the player stops and crouch and have better accuracy), you can also change its tint/color depending on what you're aiming at (by overriding the
CanvasItem within the
TextureRect with a custom shader).
There is a crosshair in the godot 3D example demo. Here, it is used to aim where we shoot but also as a 2D origin point from which to shoot a ray projected in 3D space and spawn a projectile (same script, line 140).
When you aim, the visibility is controlled via an Animation. The
TextureRect "visibility" properties are controlled via curves for the given key in the
Animation node when either "far" or "shoot" animation is played .
- Draw it yourself. Whether for
Control, the Nodes inheriting from
CanvasItem let you use a whole set of 2D drawing primitives:
draw_line(): draws a line of specified color and width from point A to point B
draw_rect(): draws a rectangle at point A of size (x,y) with specified color, filled or not, etc..
draw_circle(): draws a circle of origin point C and radius R with specified color, filled or not, etc..
You could then manually draw the 4 characteristic rectangles that compose a regular popular crosshair of any size/color you want, solid color or fill color different from border color...
It depends how you want those to look like. Is there text next to a bar ? Is there a numerical value ? Is only filled/empty icons ? Is it a regular colored bar or should it be a texture ?
In your first version you could probably have a
ProgressBar, a gauge filled based on a value. You need to somehow connect the health value change in the gameplay to this UI element.
But you can make more detailed version with an image using a
But basically you can do pretty much any crazy look combining textures and shaders, for example, you could make a spiral or round texture with decreasing alpha along the spiral, and add an alpha cut-off in the shader based on a uniform which represent the health value. (Think Kingdom Hearts style rounded health bar.)
Again, how does the cool down look like ?
- Is it a simply a filling bar ?
- Is it a digit countdown ?
RichTextLabel which text value is constantly updated
- Is it a skill icon ?
TextureRect as a background "disabled" icon and either another
TextureRect or a
TextureProgress with the
FILL_CLOCKWISE flag set, on top for a "enabled" icon.