Making a Slick User Interface in Godot Part 1

In the time that I have been making games, I have come to realize that the one of the largest contributing factors to making a game “feel” polished is the menus and the user interface. That is not to say that juice with the mechanics isn’t also important, but if you were to go and play some of your favourite games you will see that a lot more work went into the menus and the user interface than you realize.

I will be the first to admit that I am not a professional UI/UX designer, but I think that I have picked up a couple of tricks that can help. Most of the tricks that I have learned can be achieved with the Control Nodes, and an Animation Player Node. I am confident that a Tween Node can achieve the same result, but I personally find it faster to do with an Animation Player.

The easiest way you can tell how much effort has gone into an indie game is to look at how the game handles transitions from one scene to another. To look at an example of how to do this wrong, take a look at my older games Pixel Kitchen or Endless Abyss. Pixel Kitchen has a main menu and when you press the play button, the game jumps immediately to game play and it can be quite jarring, and Endless Abyss behaves in a very similar manner but I tried to be a little bit more clever.

Adding a very simple fade to black animation not only fixes how jarring the transition is, but it also obfuscates the game engine changing backgrounds and initializing objects and interface elements from the player. The polish to effort ratio on this transition animation is quite high. To do this every scene that you can transition to will need to have the same couple of nodes added for the effect.

We will need to add a Control Node and nested in that is a Colour Rect Node. Both of these nodes need to have their layout anchors set to “Full Rect” so that they take up the entire space of any Camera or Viewport that is active in the scene. The Colour Rect will need to be opaque by default, otherwise the animation will not look correct. An Animation Player Node will also need to be added to the scene, and a reference to it will need to be added in code.

onready var anim = $AnimationPlayer;

With that basic set up out of the way, the next step is to set up the transition animations. Each scene is going to need 2 animations, a fade in and a fade out. We can create the animations by selecting the Animation Player Node, and then at the bottom of the screen click on the “Animation” button and select “New Animation”.

The Animation Player in Godot is a lot like the Animation Player in Unity3D. You can animate everything that has a property that is accessible from the editor. The fade to black transition is going to “Animate” the Modulate property of the Colour Rect Node. When we fade in, we will animate the alpha channel from 255 to 0, making the black disappear, and when we fade out we will do the opposite.

When we fade out of the room, the box will turn black over 1 second, and we do the opposite when we fade in.

What we have done so far is create the animations, but we are not switching rooms yet. This is where the magic of the Godot Animation Player comes in, you are able to execute code and scripts from the Animation Player. Knowing that, we now need to create a function to switch to a new scene that can be called from the Animation Player.

func _update_Scene():
     var _scene = get_tree().change_scene("res://Path To Scene");

Once the script is finished we need to add it to the “Fade Out” animation. This block of code is not required on the fade in animation, as you are not switching rooms when you fade in.

To add the script to the Fade Out animation, click on the “Add Track” button (as seen in the screenshot below) and add a “Call Method Track”. Once the track has been added, we move the animation head to the final frame and add our function as the key on the track. Now on the final frame of the animation, the engine will switch rooms from one room to another and it should be completely transparent to our players.

Now that the animation is finished, we need something to trigger it. We can have this set up to be triggered by whatever we want. Probably the easiest way to do this is to have some sort of button that emits a signal when its pushed connected to a function that plays the animation.

func _fadeOut():
    anim.play("Fade Out");

That is only one way to trigger this animation. We can have this function connected to a player death signal, or a collision box that is at the border of a scene, or any number of other situations that would require transitioning the player from one screen to another.

What you may have noticed if you try to run this in your game, right now, is the game will fade out and will not fade back up. If you are wondering why that is, it is because we currently do not have anything calling the fade in animation.

What we need to do now, is move the contents of our _Ready() function into a different function and have the _Ready() event play the fade in animation. Finally, the fade in animation will need to call the new ready function on the final frame of the fade in just like with the Fade Out.

func _Ready():
    anim.play("Fade In");

func _New_Ready():
    Your Ready Code Goes Here Now

And that is it, a very basic scene transition animation. Having the default animation time of 1 second may be too long because you have 2 animations playing back to back. We can tweak the time to make sure that it feels just fright.

To have a little bit more flexibility with the type of wipe / transition, that is playing, we could replace the Colour Rect node with a Texture Rect node to have whatever custom type of transition / wipe that we want.

To do that we would need to draw the custom transition in the graphics editor of our choice, export the animation so that each frame is its own png file (Texture Rect doesn’t support Sprite Sheets) and key each frame of the animation.

I was planning on explaining more of how the menus work in Captain Skyrunner and my new game Hexes, but this post is already over 1000 words, so I think that I am going to need to do a part 2 and probably a part 3 to this series.

Until Next Time
– Steven