HUD is fixed to left edge of screen

Godot Version

4.3, 4.4 beta 1

Question

I want my game to have a HUD/panel to show score-like information with options to show it as a ribbon at the top or a panel on the right. Ideally with options to use the bottom or left instead, but it doesn’t look as if that’s going to be possible.

I prefer to do as much as possible in code instead of in the scene editor, because the latter is laborious and inflexible. In hindsight I would have used the editor more from the start if I’d known how much Godot would fight back against code-centric development.

The main game scene extends Control and has a node derived from SubViewportContainer displaying the game, which is added to the tree after construction but before _ready (I’ve also tried moving it to _ready but it makes no difference). The HUD is added in _ready.

If it’s a ribbon HUD it’s OK, even though that took ages to get right, because the UI layout system is so flaky. I tried designing the HUD in the editor instead, while I was struggling with that, but although it changed the behaviour it was still just as broken, so I went back to pure code.

Both types of HUD have the same base class which extends Control. In _enter_tree the HUD base class adds this node hierarchy (where > means ‘contains’: CanvasLayer > ColorRect > MarginContainer > BoxContainer > [HudItem]).

To implement the panel HUD on the right, I tried adding an HBoxContainer to the game screen’s top-level, with the SubViewportContainer and HUD panel in the HBoxContainer. The HUD rendered over the top of the game viewport instead of to the right of it. I wasn’t really surprised, after the struggle getting the ribbon HUD to work, and decided to try manual positioning, but that doesn’t work either. Neither offset_left nor position have any effect.

Getting desperate, I wondered if offsets only work if you also set anchors, so I set the HUD’s anchors preset to PRESET_TOP_LEFT (there is a reason I would prefer to use an offset than PRESET_TOP_RIGHT/RIGHT_WIDE, and the RIGHT presets don’t work either anyway). That didn’t make any difference to the HUD’s position, but then it gets really insane. If I set the preset after adding the HUD to the tree, it has no effect at all. But if I set the preset before adding it, it affects its (future) sibling - the game viewport - instead! Normally there’s a blank space on its right where the HUD should go, but setting the HUD’s anchors before adding it causes the game viewport to be stretched into that space, filling the window!

What the hell is going on?

1 Like

You might need to show some screenshots of your current scene setups and your code snippets that change the layout. Otherwise it’ll be difficult to understand and imagine accurately what you mean.

2 Likes

Could it also possibly be a scaling issue if your going full screen?

I made this reply earlier to someone else who might be having a similar problem to you

1 Like

I’ve set the window stretch mode to disabled and choose a scaling factor at run-time which aims to choose a scale factor with the best compromise between showing the right amount of the map and avoiding scaled pixels not lining up with physical pixels and looking fuzzy.

I suspect it would be just as hard to reproduce this issue in a minimalist example as it would be to fix it in the real project. I think I’m going to scrap my current approach and try to make the game screen 100% Node2D, or at least avoid Containers. My current approach has some efficiency concerns:

  1. The game is based on a large TileMap rendered into a SubViewport, because I wanted to avoid wastefully rendering parts of the map which would be under the HUD, and SubViewport seemed to be the only straightforward way I could clip a TileMap, because Godot doesn’t give me access to glViewport or equivalent. But SubViewport forces it to be rendered to a texture, then the texture is rendered to the screen, which is probably even more inefficient than rendering invisible parts of the tilemap.
    However, maybe it’s possible to mix Node2D and Control nodes in the same hierarchy and I could use a Control with a Node2D child and enable clip_contents in the Control. Can Node2D and Control be mixed this way? There’s also CanvasItem.clip_children, but I suppose I would have to use a Control node with a size or a Node2D with a texture for that to work. Why have clip_contents when clip_children exists? They both seem to have the same aim, but could also conflict with each other.

  2. The HUD uses Labels to display values of the score etc. The docs say CanvasItem._draw only gets called once, so things aren’t as bad as performing font rendering on every frame, but they still have to be rendered every time a value is updated. The values are all numerical, so I could make it more efficient by pre-rendering an atlas of the digits 0-9. AFAICT I should use Font.draw_string with the RID of an ImageTexture.

Related to 2, but also about CanvasItem custom drawing in general, can the draw* methods be used at any time or only during a call to _draw?

When reimplementing my HUD with Node2D instead of Control I ran into a similar problem. It turned out to be caused by using a CanvasLayer. It seems to make its child’s position get ignored and treated as if it’s (0, 0) relative to the viewport. When I removed the CanvasLayer and used z_index instead, things started getting positioned correctly. I haven’t checked, but I think that must have been what was breaking the Control layout too.