Home Creators Posts Import Register Favorites Logout

Content

So lately, I've been playing quite a few small incremental games... And I thought it might be a nice idea to make one just for fun.

The Prototype

I wanted to experiment more with the finishing steps of making games, and incremental games are generally faster to get you into that stage of development.

On another plus side, upgrade maps that are very common in these games can be re-used for future projects.

So I came into this with the mind of making an upgrade system as re-usable as possible.

After a short while, I had the basics of a game. Nothing too concrete. As I want to focus on the upgrade system at first.

But here is what I have. A character that mines a node when it goes near it.

At this point, I was considering whether to make the player control the character, or going into a more automated route for the game, where you buy upgrades that create entities that does the mining for you.

(In the video, I'm also spawning the resources manually with a hotkey.)

In case it becomes a more player-controlled game, I've added a direction to the "attacks".

But that's the last we see of game related stuff.

The Upgrade System Design

What I really wanted to focus was making a very generic and re-usable upgrade system for this game, that I can keep it and take over to other games too.

So I started with what I know. I structured everything using custom Resources, so its easy to create them inside the engine.

I create a Resource to store the "money", the Energy type, name and icon.

And for the UpgradeData resource, Instead of creating variables for each type of energy the game might have. I created an array the stores the type of energy and its cost.

And to keep things very customizable, I also had to create a TierData that hold what an upgrade tier cost's and gives the player.

Instead of making a system that increases the cost and effect by the same amount each upgrade. So we can add different increments and cost for the upgrades, if we ever want to. The con is that if we don't, its a bit more manual work when creating the same incremental values for the next tier.

And while this makes sense from a data point of view..... its a horrible experience to create and edit those inside the UpgradeData resource.

Array inside arrays, inside arrays.... And this is even using the new Godot 4.6.dev which is more compact. Not very good.

So I have a few options here.

I can accept that managing this from resources inside native Godot is not worth it. And make a main screen addon to better show, create and edit those types of data.

Or ditch the resource approach and create a json loaders that creates those resources with a script.

Even if the data is structured the same, seeing it inside a json text file is already much better than the Godot's interface.

The Upgrade UI

But before we go back to the data, let's take a look at how the UI is looking for now.

One of the things I have very little experience is making UI systems look game-ready. So that's also another thing I want to experiment with.

The few examples in the past where I made more complex UI systems went horribly wrong. I once had to completely refactor what I made in the next day, and even so I didn't feel all that good about the new code.

So here's what we have:

I made a very simple UI and used tool scripts to make the lines draw at runtime inside the editor.

This led to a few complications and discoveries.

I made the lines using Godot's _draw function. But to make sure they updated as I'm moving the nodes in the editor, I needed to add a few extra steps.

Basically, I enabled transform notifications to alert when the node is moved. And the script now emits a signal when it is moved.

Then a top level node that controls the upgrade ui decides what nodes needs to redraw by calling queue_redraw() on them.

I also made a hacky solution to auto-create the upgrade lines when duplicating nodes for faster prototyping.

I used a @export_storage var that starts as null as is set to the node's name on the ready function.

So when you duplicate the node, it contains the name of the node it comes from. Then I use that to find the original node, and add this new copy to that one's unlock array, which creates the lines. (first image)

Its not the final solution, but it makes it much nicer to work on the editor.

And of course, I used a bunch of signals to make the description box properly show the upgrade data when hovering the node.

The Data Problem

But back to our data problem.

While I think it would be nice to have a main screen manager screen to manage the upgrade data. I also don't think I could make something good to be re-usable in a short amount of time.

So, at least for now, I decided to go with another approach.

Instead of storing the energy type and the cost. I'm just using an int array. And I'm going to assume the array index is the energy type index.

So while not very ideal, its much nicer to quickly add an change the resource cost for each upgrade tier. I can just keep zero on the cost types the upgrade doesn't need.

But I'm leaning on changing this towards using a json database in the future.

And I still haven't decided on how to tell which upgrades do what.

For now, I'm using an id for each UpgradeData and each tier just stores an array of floats. And the game's logic is responsible to know what to do with that random float.

It's easy to setup, but horrible to maintain. So I'll change that later on, after I flush out how to create and edit the upgrade data properly.

Buying and Using the Data

Next I changed the UI a bit.

And finally implemented the ability to buy the upgrades and unlock things.

Which made me realize a couple of things.

Currently, I'm setting the unlocks purely in the UI node class while creating the upgrade map.

It has a reference for the UpgradeData reference, and a GameManager holds all player data.But the UpgradeData itself so far doesn't have any knowledge of what it unlocks. It's all decided by the UI.

The reason for that is mainly because I thought I would end up manually arranging the upgrades inside the upgrade tree anyways, so it seemed a bit pointless to have to set up the unlocks in two places.

But thinking a bit more about this. It would be nice to have a way to quickly change what upgrade unlocks what for balancing the game later without messing around with the UI.

But then again, that would need system that arranges the upgrades nodes automatically so the UI upgrade tree correctly displays the unlocks.

Saving and Storing Data

Now that the basic system are in place, I went to the next feature all games need. Saves.

I don't think I ever used this yet in any of my many small prototype games with Godot. Bigger games I made previously that had save systems were in other or custom engines I made, but none in Godot so far. So that's a good opportunity to experiment with that too.

For saving things, Godot has a very easy to use FileAccess class with all the things you can except. So I tried the simplest thing possible. Just save the data.

upgrades here is a Dictionary[StringName, UpgradeData].It holds the UpgradeData id for quick-access, and the UpgradeData itself.

By using JSON.stringify, Godot can take your class and convert it to a string in json format, which is easier to load later. But we have a problem. Here we get something like this:

Basically, it saved the resources reference instead of the actual results.

Which is completely useless for loading new objects.

So next I tried the file.store_var function. That can be used to store whole objects in binary format.

But make sure you don't forget to set full_objects to true, or it will save only references like the JSON.stringfy.

This saves the whole variable in binary format:

You can even later use FileAccess.open_encrypted if you don't want the saves to be easily modifiable.

But that whole giant file made me realize that there is a whole lot of things that that don't need to be saved. The only thing we need for the upgrade saves is what tier upgrade is already bought.

A single int per upgrade.

So I did the smart thing and made the UpgradeData create a dict of what it needs saved.

Which in this case, is only the current_tier variable.

Making a very compact savefile that is easy to modify for testing later. We can later encrypt it, or even save this as a binary with store_var.

Considerations

One thing that I realized is that often times I just want to just duplicate and upgrade.

For example, to create another upgrade that increases attack. But that turns out is quite a paint to do with this system. First I need to completely duplicate the UpgradeData and all its subresources resource just to change its id and values. Then I need to change the game's code to know what this new upgrade does, or set a self-rule for upgrade id prefixes to know what the upgrade does.

At the start using the id to decide in game code what the upgrade does seemed fine. But now its starting to feel like that was a horrible idea for making things more re-usable.

I think I can improve this if I create a way to define what an upgrade does.I might change the UpgradeData to hold UpgradeAttributes or something. Making what an upgrade does decided by whats inside it, and not uniquely based on its ID like it is now.

That way, its easier to duplicate resources, since I won't need ids anymore.

Conclusions

Re-usable systems are harder to make than quick prototype games. A lot more things needs to be considered for future usage, and even the ease of create and edit things in a larger scale.

There is quite a bit that needs to change in this yet. I just wanted to make a quick post about it before I forget how this felt what what problems I encountered at this time.

Fun fact: What made me want to make an incremental game was actually an interview from Jona's podcast with Cory Darby (Click and Conquer game), and seeing him talk about his custom system he made exclusively to help him balance all sorts of incremental games independent of the game itself.

So I thought I'd make an incremental game and try to make something like that too.

End

But that's it. Just a quick post about what I played around with yesterday.

Files

Comments

No comments found for this post.