Crash, Replace, Repeat — Porting Resolutiion to Godot 3steemCreated with Sketch.

in games •  8 months ago  (edited)

Resoluttion with the Godot logo and the number 3

Resolutiion is an indie action-adventure game in the making, with a modern pixel style and plenty of fat, yellow worms. My brother and I have been working on this passion project for the better part over the last three years.

Initially “The Game” needed an engine for the heavy lifting. We are both big open source enthusiasts and I really wanted to do all the development on my Linux machine. Unity and Unreal both support development and exporting projects to Linux platforms and allow access to parts of their source code. Still, they don’t feel truly open-source in the sense of community engagement and direction.

Searching for alternatives I stumbled upon Godot. The young game engine looked very promising, following a holistic open source approach and all the features I needed for our little 2D game. It felt similar to Blender and I didn’t expect to outgrow its capabilities in the future, being my first game development project — ever.

Port Royal

July 9th, 2015.
I committed the first lines of code to our repository, based on Godot 1.0.

Since the API never changed dramatically during the following months, I jumped from version to version like nothing could stop me, taking “The Game” along.

When Godot 3.0 was announced in 2017, mayor API breaks appeared on the horizon. By then we had called the project “Resolutiion” and it had grown to a beast, collecting over 1,700 files and resources. Porting all our scripts and scenes to a new system would require tons of work. But the new features like audio effects, sprite tessellation and plenty of editor improvements were just too tempting to pass up.

I waited until early August when Godot 2.1.5 had reached the “stable” mark. It included an exporter to help transfer any project from the 2.x to the 3.x version. The rest of the team was on vacation, so no one could distract me.

This is how porting our game to Godot 3.1 went down.

Step 1 — The Magic Button

I created a new Git-branch, called it “PortTo3.0”, and fired up Godot 2.1.5. This version included a promising button, labeled “Export to Godot 3.0 (WIP)”.

Click!

Export-progress at 6 percent

A nice loading screen appeared and started to count percentages up. Be patient I told myself, this was going to take a while... Eventually the counter hit 99%, and then disaster:

Couldn't load resource: res://scenes/tileset.xscn::40
Method/Function failed, returning: ERR_BUG

Ok, that’s easy. Godot used to support three internal file-types: scn (binary), xscn (xml) and tscn (readable text). The xscn-format is now deprecated, in favor of scn and tscn. No big deal. I changed the corresponding files and restarted the routine. This time no errors occurred and the 100% export was completed safe and sound.

Importing the converted project into Godot 3.0.6 resulted in another progress bar. Plenty of warnings were thrown on the console, but I ignored them for the time being. Since the whole game is made up of individual scenes and sub-scenes, I had to take a look at the important ones anyway. Let’s see what works and what breaks.

Lessons learned

  • Don’t forget to branch (Git)
  • Convert xscn files to tscn format

Step 2 — Global Scene

Like most games, we run a global scene. Ours is called global.scn. It acts as a singleton and handles all the boring stuff like saving and loading, switching levels, input controls, the audio system and a couple of globally used shaders. It's the foundation for every other scene that runs on top of it.

Opening global.scn, I noticed that all our shaders were missing. The shaders had to be rewritten.
Here’s a simple example:

Old

uniform float size_pixel=0.008;
uv-=mod(uv,vec2(size_pixel,size_pixel));
COLOR.rgb = texscreen(uv);

New

shader_type canvas_item;
uniform float size_pixel=0.008;
void fragment() {
  vec2 uv = SCREEN_UV;
  uv-=mod(uv,vec2(size_pixel,size_pixel));
  COLOR.rgb= textureLoad(SCREEN_TEXTURE,uv,0.0).rgb;
}

Resolutiion’s pixel aesthetic does not require many shaders, so the workload was manageable, but this might have been a different story for any other game.

Resolutiion screenshots

Let’s start the scene.
Crash!

The debugger tells me Identifier not found: Globals.
That right. The global variables Globals are now called ProjectSettings.

We use the Globals system everywhere in the game. It would be a pain to change all respective scripts by hand, but there are great tools available to help with bulk processing. I use Atom due to its powerful and extensively used “find-all-occurrences-of-a-string-in-the-whole-project-folder-and-rename-them-accordingly” feature. Nothing to do with Atom being open source software ;-)

Let’s start the scene again.
Crash!
get_data_dir for loading save-files is now called get_user_data_dir.
A quick fix.

Restart.
Crash!
Something was wrong with the audio system.
Finally, we’re onto the fun stuff.

Since Godot’s audio system had been completely reworked, we needed to do the same to our internal pipeline. I set up some busses for music and effects streams, and adapted the configuration of those according to the new bus API. The volume scale in Godot 2.x was defined from 0 to 1. Godot 3’s new bus system makes use of the decibel scale instead, ranging from -80 to 0.

Audio busses

Ok, restart scene.
Crash!
Dammit, it’s the input-event-system-types.
All right, that one was also redesigned and event-types don’t exist anymore. Instead, input-events are now resources and checking them looks something like:

func _input(ev):
    if ev is InputEventMouseButton or ev is InputEventKey: #mouse/keyboard
        cur_input_type = 0
    elif ev is InputEventJoypadButton or ev is InputEventJoypadMotion: #joypad
        cur_input_type = 1

Restart scene.
Wait …
By Marty’s Boot: it’s working!

Marty’s Boot

Committing all those audio related changes to the global scene, I realized that my trusty, sound-handling buddy SamplePlayer2D was no more. Instead there was a new AudioStream2D node, connecting to the audio busses. This fella can hold no more than a single sample, instead of a variable array, like my old friend.
What a wuss.

Also, looping and loop times are now decoupled from the audio player and set directly on the audio resource (wav or ogg). So we have to do some more coding.

Play music in a loop:

func music_play(newsong, offset):
    #load audio players, set loop & offset
    #newsong is an audio file like ogg or wav
    if newsong != null:
        newsong.loop = true    
        newsong.loop_offset = offset
        get_node("MusicPlayer").stream = newsong

At this point it became obvious to me that Godot 3’s audio system was not mature enough for our project. Most of the player and NPC characters trigger their many audio effects by performing their respective animations. Due to the new AudioStreamPlayer’s limitations, juggling the streams at the time as the animations would be very cumbersome.

All was not lost. After some frantic Googling, I discovered that Godot 3.1 will have an improved audio workflow. At that point, I realised I had to leave safe developer space and dive into the unstable and deep waters of Godot 3.1-some-weird-development-build-from-a-shady-website. I downloaded the latest nightly build and went on with my business, slightly more anxious.

Lessons learned

  • Audio system has to be set up from scratch
  • AudioServer.get_stream_global_volume_scale() becomes AudioServer.get_bus_volume_db(0)
  • Shaders need to be rewritten
  • Global becomes ProjectSettings
  • OS.get_data_dir() becomes OS.get_user_data_dir()
  • Input Event types become Input Event resources

Step 3 — Start Menu

The menu with floating particles

Enter Resolutiion. An atmospheric tune fades in. The logo appears. Buttons emerge and the background graphics slide up — parallaxing (naturally). Some particles float...
Wait, there are no particles!
Probably because the particle system was also completely refactored in Godot 3.x.
Does that mean I have to dive into all scenes where particles are used, and set them up properly again?

Yes. Yes it does.
And there are plenty.

Particle effects

Another problem: buttons and other UI elements no longer reacted to my clicks. This was down to some control nodes in the top layer that stopped the mouse event. The solution was to set all control nodes that could intercept the mouse click events to mouse → ignore.

Some asset positions in the timelines are messed up, too. So is the UI theme in the settings menu.
More renaming work.
Fixing asset positions.
Starting the scene.
Success!

Lessons learned

  • All particle systems have to be refactored
  • Set control nodes to mouse → ignore
  • Animation player signal finished becomes animation_finished
  • Tween signal tween_complete becomes tween_completed

Step 4 — Characters

With all the main control scenes running again, continuing the port got more colorful: levels, our player character Valor, NPCs and enemies. The latter didn’t get very far: trying to start the first level caused the NPC class to crash.

Let’s fix those buggers first, shall we?
collision_layer and collision_mask are key words now. Variables with the same name have to be renamed.
Also get_layer_mask becomes get_collision_layer, which makes it more consistent with the old and new get_collision_mask.
And for helper systems within the editor, we have to change get_tree().is_editor_hint() to Engine.editor_hint == true.
Again, Atom did that work for me in bulk.

Another major change for Godot 3.x is movement and collision handling of kinematic characters. There are two new methods for moving characters: move_and_slide or move_and_collide, depending on what you want to accomplish. The first will do for most applications and clearly reduces the amount of code needed:

Old

func _fixed_process(delta):
    if state in move_states:
        move( vec.normalized() * speed * delta )
        if (is_colliding()):
        var n = get_collision_normal()
        move( n.slide(vec).normalized() * speed * delta )

New

move_and_slide( vec.normalized() * speed )

Don’t forget to remove the delta when calculating the motion, or your character will just crawl very slowly across the screen.

Developer-humor:
In Godot 2.1 line 19 said trailpath.remove(0), where trailpath is an array. Somehow the exporter thought re-move should do some collisions and renamed that line to trailpath.remove_and_collide().
The auto-exporter clearly showed some character here.

With the enemies fixed, I moved along to our big, bad, black and deadly player character.

Various spawn-items

Valor is a toolbox of his own, spawning guns, bombs, and various animals to wreak ruin to the world of Resolutiion. When these spawned objects “die”, I usually removed their collision with the clear_shapes()-function.

Well, this function doesn’t exist anymore in Godot 3.x, instead we have the very cool disable-variable for all shapes. It lets us toggle the collision of a single shape at runtime.

$CollisionShape2D.disabled = true

With enemies and our player character back in place, they may resume beating the crap out of each other.

Lessons learned

  • Character movement needs to be refactored to use the new move_and_slide and move_and_collide functions
  • Variable names that are keywords now need to be changed, for example collision_layer
  • get_layer_mask() becomes get_collision_layer()
  • get_tree().is_editor_hint() becomes Engine.editor_hint == true
  • clear_shapes() becomes $CollisionShape2D.disabled = true
  • Rotations are now calculated clockwise instead of counterclockwise

Step 5 — Levels

In Resolutiion every level is its own scene and embeds some logic for the save and audio systems, as well as the scripts for all cutscenes. There’s a new helper to yield a timer timeout, reducing six lines of code to one:

Old

var t = Timer.new()
t.set_wait_time(3)
t.set_one_shot(true)
add_child(t)
t.start()
yield(t, "timeout")

New

yield(get_tree().create_timer(3), "timeout")

After hitting Atom’s “Replace all” button for what felt like days, I got a very unsettling message when opening one of the levels:

Alert! Error while parsing 'Stadium02.tscn'.

Another one gave me:

Alert! Unexpected end of file 'Grass.scn'.

Probably a bug in the exporter?
Usually the terminal gives a hint on what went wrong and in which line the problem occurred. In this case it was mostly audio related stuff: SampleLibrary’s sub-resources were used, which Godot 3.x has forgotten all about.

Parse Error: Can't create sub-resource of type: SampleLibrary.

Here, the problem with easily editable scene files became obvious. Occasionally I had to remove a whole sub-resource block and its corresponding links — the sub-resource’s ID comes in handy. Saving the tscn-file solves the error.

Then, files that were saved as scn can not be opened with a text editor. So I had to fire up Godot 2.1, open the particular scene, save it as a tscn file, move it to the new project folder, and reopen it in Godot 3.1.
Tedious.

Lessons learned

  • Open all scenes and check if they were converted properly. If not, save them as tscn files in Godot 2.x and fix them manually.

Final thoughts

As I write these lines, Resolutiion might easily be twelve months away from being released. But over the last few weeks, we got much, much closer. Taking our not-so-little project from Godot 2.1.5 to 3.1 gave us plenty of exciting new options and set us up nicely for more to come.

It took me about twenty hours to refactor all the changes in logic and scripts, and twenty more to grind through the boring tasks. The former was fine, because I used the opportunity to fix some bugs that had been around for a while. The latter one, though, was just repetitive and annoying: opening every single scene and object, updating each animation in respect to the new audio system and then rebuilding all particle systems we had distributed in our heavily connected world.

An agony.
But it was worth it.

Investing a week of pain to gain access to amazing features like the new audio pipeline and others can easily be justified. Being able to quickly toggle collision shapes, yielding a Timer, and the improved helper systems $ and get_node will be used extensively in my future development.

Godot 3 is a wonderful game engine. Using and seeing it mature month by month is certainly a great inspiration. It helps us to stay motivated as we progress with Resolutiion.

Creating Resolutiion with Godot 3


Richi and Günther Beyer from Germany are developing their first video game. Inspired by early Zelda and fueled by the do-it-yourself spirit of the open-source movement, Resolutiion will ship in 2019, with plenty of pixels, cats and the mystery of the Cradles. Subscribe to our newsletter or tag along at Twitter.

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

Make real ether while destroying your opponents.
Chibi Fighters is a brutally fun and addicting game.

Come check out Chibi Fighters
https://chibifighters.io/

That is something that will be developing quickly creating of programs for computer games. That is a great market and have more and more users, especially considering that young generation know computer games already from their almost first steps. Everyone who play games have his own thought what is good and what needs to be better and that is the best stimulus to create it better and having such big market it brings a competition and again that is engine to improve yourselves.

It is nice to see how much time and efforts you put in your work and I like the way you mentioned what you have learnt from it. This is sometimes we learn on our mistakes and improve ourselves. I wish you a lot of success!

  ·  8 months ago (edited)

Hey thanks so much for that, I'll make a port for my game, Moon Cheeser as well since 3.1 supports GLES2 again. This will for sure help a lot

Congratulations, this post was rewarded with a SteemGC Upvote!

Want to meet fellow Steemit gamers and earn upvotes yourself? Join the SteemGC Discord channel!

Hi cloudif,

This post has been upvoted by the Curie community curation project and associated vote trail as exceptional content (human curated and reviewed). Have a great day :)

Visit curiesteem.com or join the Curie Discord community to learn more.

Congratulations @cloudif!
Your post was mentioned in the Steemit Hit Parade for newcomers in the following categories:

  • Upvotes - Ranked 8 with 530 upvotes
  • Pending payout - Ranked 1 with $ 36

I also upvoted your post to increase its reward
If you like my work to promote newcomers and give them more visibility on Steemit, consider to vote for my witness!

Thank you for sharing you game development post with us! You have received an upvote! Please visit our page @steemgg to learn more about how you can use our platform Steemgg, the first html5 gaming platform built on the Steem Blockchain to get more out of game development.

Congratulations @cloudif! You have completed the following achievement on the Steem blockchain and have been rewarded with new badge(s) :

Award for the total payout received

Click on the badge to view your Board of Honor.
If you no longer want to receive notifications, reply to this comment with the word STOP

Do not miss the last post from @steemitboard:

Presentamos el Ranking de SteemitBoard

Support SteemitBoard's project! Vote for its witness and get one more award!