[Dev] Godot Tips and Tricks

Quintillus

Restoring Civ3 Content
Super Moderator
Supporter
Joined
Mar 17, 2007
Messages
8,972
Location
Ohio
Creating a thread for sharing tips and tricks relating to Godot, so they don't get lost in various non-Godot-focused threads that become 20,000 posts long over time.

Reminder to self: Extract (and gradually update) Signals info from 0.1 or 0.0 thread.
 
Signals

Todo: Extract what I've already written.

Problem: My new signal that I added in VSCode doesn't show up in the Node view in Godot
Info: This one vexed me for some time. Closing and reopening scenes didn't help, nor did closing and restarting Godot, at least not reliably. For awhile I thought the Node might have to be of a certain type, but...
Answer: Click the "Build" button in the upper right of Godot, just below the red "x" on Windows, and to the right of where it says "GLES3". Once it rebuilds the project, it ought to find your new signal.

Problem: I tried to send MyCoolCSharpObject via a Signal and it arrived as null! What gives?
Answer: You can only send objects that extend Godot.Object (and some primitives) across Signals. For now, we're using a working called ParameterWrapper; see its documentation for details. Longer term, we may move to other options as we learn.
 
Last edited:
My lessons learned from Godot so far (off the top of my head):
  • If you want it to move or scale with the interface window, use a Control node (or its many descendants)
  • Put a CanvasLayer between the root Node2D and Control hierarchy. I forget all the reasons why, but there were more than one, and most of it is due to making positioning easier. I think it may be the target for any draw commands, too, but can't recall for sure.
  • Anything you can do in the UI (scene trees, node properties, signals, anchors, etc.), you can do in code. I think it's possible to have no tscn files at all, but it will be a lot easier to have at least a root node with a script attached. Whether we add and configure stuff in the UI versus code is probably a matter of style and clarity. My bias is to use the visual interface as a UI skeleton and code for much else.
  • An exported project is literally the packed project folder (with mono assemblies compiled) alongside a prebuilt Godot binary that has the UI and presumably Mono SDK left out.
  • You can distribute patches and DLC as PCK files (and maybe zip?)
  • PCK files are just uncompressed packages of files.
  • Godot is in C++, and Mono is sort-of bolted on as an alternative to the Python-like GDScript. In most cases this shouldn't be a problem, but there may be some edge cases with object references and memory management.
  • Godot's Mono documentation has come a long way recently, but sometimes it's hard to find the non-GDScript documentation, and using C# is more rare than GDScript
  • Read differences between GDScript and C#. Generally a gdscript_name will be GdscriptName in C# but otherwise be very similar. Oh, I was going to call out a challenged I've had with Enums, but that's in the link, too! SOME_ENUMTYPE is Some.Enumtype , often needing namespace qualification.
 
Some more jumping to mind:

  • Draw order is last item to first. In the scene tree, the bottom-most node draws over prior nodes. In code, the first child is drawn last and the last child is drawn first. (Control nodes in CanvasLayers are on top of everything else, though)
  • In code, when you AddChild() it's always the last, but you can move it with MoveChild(referenceToAddedNode, int) .
 
Centering/Component Alignment

Problem
: My CenterContainer isn't centering my component! Am I going crazy?
Answer: Yes, you probably are. However, CenterContainer doesn't center just any Node. It will center TextureRect, and perhaps any type of Control node, but not all nodes. If you set the CenterContainer to AnchorLeft and AnchorTop at 0, and AnchorRight and AnchorBottom at 1, and add a node that's a TextureRect to it, and set the TextureRect's Texture to an image of the appropriate size for what needs to be centered, it should center all right.

Problem: Okay, I'm using a TextureRect, but my VBox/HBox only works if I put the TextureRect at the beginning. If I put it at the end, it appears off the edge of the screen. Why
Answer: The TextureRect must have a texture applied; that is where the VBox/HBox grabs its dimensions from. If you only added children, then even if those children have dimensions, the VBox/HBox will not be able to figure out the size of your TextureRect, and it will not appear properly unless it's beginning aligned.

If you don't have one uniform texture for the background, but instead are composing it of several children, a solution is to create an ImageTexture programmatically, set that to be 100% transparent, and use that as the texture for your TextureRect. See the example below:

Code:
        ImageTexture thisTexture = new ImageTexture();
        Image image = new Image();
        image.Create(540, 320, false, Image.Format.Rgba8);
        image.Fill(Color.Color8(0, 0, 0, 0));
        thisTexture.CreateFromImage(image);
        this.Texture = thisTexture;

----------

This is probably a corollary of @Puppeteer 's "If you want it to move or scale with the interface window, use a Control node (or its many descendants)". And I believe it's probably related to why he made LowerRightInfoBox a TextureRect. I recently went through this with the Domestic Advisor (which should roll over to all advisors), and I must admit I still don't entirely follow why CenterContainer doesn't center a plain-old Node2D which has descendants with dimensions of their own (including TextureRect descendants).

@Puppeteer - If you can explain or know a blog post that can explain why this is, I'd love to add it to the documentation. I probably spent an hour trying to get the Advisor to be centered in a CenterContainer the "proper" way so the CenterContainer would cover the whole window and it would be nicely centered without hard-coding the margins, but 45 minutes of that was trying to figure out why seemingly sensible values didn't work, and trying every possible combination I could find in the Node view of the CenterContainer to try to get it to center, and 13 minutes of it was figuring out how MainMenu did it and why that seemingly didn't translate to the advisor area. It feels like one of the bigger "gotchas" I've run across with Godot.
 
Last edited:
Oh yeah, it took me a while to figure that, too. I actually just commented on the commit where you had similar notes:

Node2Ds have no size, no edges. They are infinite planes. So their children have no reference to anchor to. Control objects have size, and the top one knows what the current window dimensions are (but see below), and therefore one inside another can anchor/scale responsively as its parent does.

If the root node is a Node2D–and as far as I know it usually should be–a direct control child of it won't anchor properly unless you put a CanvasLayer between them. Bonus: a CanvasLayer has a Layer setting so you can place what's under them in explicit drawing layers instead of relying on the hierarchy order. I re-discovered that in the past day or two.

So the levers are all there, but if there is not a Control as the scene root node or a CanvasLayer between them, it won't anchor and therefore can't center.

Editing in a bit more: Clearly the nodes don't all talk to the window. I don't think a Node2D has any concept of where it is. Because there is literally no size or edge to reference.

And maybe there is confusion between OO children and node children. OO children of Node2D have no size, so scene tree children of Node2D can't reference the edges of their scene tree parents. (They can transform relative to parent scale, origin position and rotation, but that requires only knowledge of 0,0.) "Center" is meaningless without edges to reference.
 
Last edited:
That helps explain it. I had missed/not fully processed that a Node2D was an infinite, sizeless plane. I think the fact that I could add child TextureButtons and Labels to a Node2D, and position them based on coordinates (different parts of the Domestic Advisor screen), was making me think it had size since those children seemed to have some awareness. But they were just referencing their parent offset in absolute terms.

Still sort of weird, but I know what to expect now. I should also look at my GitHub settings and see how to enable comment notification, particularly for commits I authored. I'll have to bring it up in another browser though, since my Luna + Clippy theme on GitHub is unfortunately not compatible with changing GitHub settings.
 
I've updated post #5 to reflect increased knowledge I've gained from working on the Disband Popup (which is still in a branch). It turns out that if you want a TextureRect (and probably any Control) to be aligned properly in a VBox/HBox, to Center or especially End position, you need to give it a texture with appropriate dimensions, so the Godot engine knows how much space it takes up. If it only has child components, Godot won't add up the size of those to figure out the TextureRect size.

This might sound silly (why weren't you just setting the appropriate TextureRect background, Quintillus?), but popups don't have texture backgrounds in Civ3, or at least not directly. Popups can be of different size, and rather than create a bunch of background PCX files, Civ III just tiles the graphics shown in Art/popupborders.pcx. So, I was programmatically tiling those as children of the TextureRect, and not realizing that meant the TextureRect didn't know its own size.

It probably makes more sense in the long run to compose the PCX components into one Image and use that as the TextureRect's texture, rather than adding the PCX components as individual child components multiple times. But that solution didn't come to mind until I'd already figured out why my popup had size 0 to the Godot engine.
 
From a Godot point of view, and if I am not wrong, a TextureRec can definitely have a size independent of its texture size. The texture can stretch, tile, or center. There are flags/property (StretchMode) for how to handle texture scaling.

Size is not on the document page for TextureRec, but since TextureRec is an OO descendant of Control it inherits all its methods and properties including RectSize. The inheritance path is at the top of every class API doc page, and I often have to go up the OO tree to look for properties and methods.

The PCX image data is the texture in our use case, and from Godot's point of view.

I'm not yet following exactly what challenges you had, but as far as adapting Civ3 UI graphics to C7 I think our options are to leave it at pixel scale, scale up with window, or try to procedurally turn the old texture into a tilemap and then responsively paint the larger UI area with the generated tilemap.

That last one is not trivial, and it may not work at all. At the very least it will mean having to layout-map every UI screen and figure out how to turn the borders and into tilemaps.

Huh, a fourth option just occurred to me: I guess we could break each UI screen down into layout rectangles and selectively scale the rectangles to make a more responsive UI that didn't stretch uniformly and fatten the borders. That might be an only slightly hideous compromise. But now I'm off-topic for the thread.
 
It's probably best to illustrate the challenge with a PCX:

upload_2021-11-11_16-22-8.png

This is the file Civ uses to create pop-ups (well, and perhaps PopupAndMenuBackground.pcx, but that doesn't include borders). Popups can be of different sizes, and rather than shipping a PCX for each popup size, there are ones that can be tiled to create popups of various sizes.

To add an extra wrinkle, the popup sizes need not be multiples of the tiles that are pictured above. Which might complicate treating the components of the popup background as a tilemap?

I'll have to see if RectSize would have fixed the problem I was running into. That might have been a significantly easier way to solve the same problem.

Scaling is an interesting problem. For now, I've mostly been treating things as pixel size, though making sure they're anchored to the proper area to scale. But it reminds me of when a friend was playing Civ III on a 15" Retina MBP, at native res, and is was almost unplayable due to the UI being so tiny. I'm not sure why he didn't just change the resolution to something lower. But I can see the case for having interface elements that scale. Not sure it's quite time to tackle that, although a proof of concept would be interesting to see.
 
I'm working on my laptop, and had noticed last time that the battery life was not great. This time I realized that's because Godot was using my dedicated GPU. I have figured out that if you set the Windows battery saver settings all the way to "power saving" and restart Godot, it'll switch to integrated graphics. Happily, those are working just fine for C7 as well (which they really should be, but always good to know they are).

I'm not sure what the equivalent settings would be on Mac/Linux, but if you've also noticed Godot using battery life, check the command prompt it brings up when it starts and see which GPU it's using.
 
There are a few interesting tabs such as "Profiler" and "Video RAM" under the Godot Debugger, but I haven't looked into how to practically make use of them.

Switching to the iGPU hasn't helped too much with battery life, either. Godot seems to be using 1/12th of my CPU all the time that C7 is running, which would be one thread of the hex-core system, and 1/6th of the iGPU, so it seems to be preventing the system from going to idle. Which I guess means (1) battery life can be used by closing C7 when I'm in VSCode, and (2) for some reason C7 is doing stuff all the time. No idea whether that's inherent to Godot, or if we have something causing that. Seems to happen on both the game scene and the main menu scene, so it isn't map rendering.
 
My MacBook fan certainly comes on and the base really warms up if I leave a Godot project running. I keep intending to look into whether anything can be done about it. I saw somewhere that running in non-Retina mode might help. But I'm typically on power, so it hasn't been a big deal until I go somewhere else and put it in my lap.
 
Tip: If Godot keeps telling you it can't find needed dependencies and flat out doesn't show you the files in the res:// file browser, and you know the repo is working on another computer, it's because I'm a dummy and downloaded the non-mono version of Godot which pretends .cs files don't even exist.
 
Has anyone else noticed that if you have a file open in Godot, and you modify it externally, then every time you launch C7 until you restart Godot, you get a "Save Resource As" dialog?

This often happens to me if I'm opening files in Godot when tracing through a debug stack, and then fixing things in VSCode. Kind of annoying, but haven't found a way to prevent it other than restarting Godot.
 
I recall usually getting a prompt to reload the file from disk if changed which I always do. You should be able to close tabs in Godot to minimize this. There are sort-of extra hidden tabs in the script editor where one scene tab's script tab may have several files open, and you can close superfluous ones there.
 
https://itch.io/jam/godot-wild-jam-41

Oh, a Godot game jam is starting. A game jam is kind of a little contest where you typically make a game within a time period with a particular theme and/or restrictions. Rules may be optional. This one is Godot-specific, so I may try to make something for it. It's starting today and submissions are due in about a week.
 
Top Bottom