When player feedback is not enough, data will come to the rescue

Let’s assume you are developing a game: You put your heart into it, make a nice-looking, working prototype and play it by yourself, with your team, with your friends. Everything seems to work well, so it’s time to get your game out there. You manage to release it, maybe on itch.io or even as an alpha on Steam.

So, people are finally playing your game, yay! …but soon you realize that receiving feedback is hard. It has always been, and it will probably always be. Comments are scarce, ratings are too opaque… how are you supposed to make your game better?

You have to keep looking for playtesters and observe them play, and you will do it for sure. But there’s another important thing you can do: Collect data from your game.

Players make lots of choices while playing. These choices/actions can be transformed into extremely valuable data for you: Which character did they choose? How much time did they spend on a level? How many times did they fail before completing the game? How many enemies did they kill? How many shots did they fire?

Example of data coming from your game

You can formulate many questions like these, derive the answers from the data inside your game, then use those insights to inform your design.

COLLECTING DATA FROM YOUR GAME

So… how can we, as penniless indie devs, collect this data?
Before running through data, let’s take a step back and present an example:

Superstarfighter: a Use Case

SuperStarfighter is a local multiplayer party game for 1 to 4 players, in which you play by dashing and shooting bombs with just one button. Controls are that simple to appeal to a broad audience, but mastering the game requires skill. Fine tuning is extremely important in a party game like this, because of the many different play styles and skill levels involved in targeting such a diverse user base.

Superstarfighter gameplay screenshot

After the initial release we realized that just a handful of users gave us feedback, and there was no sign that would improve anytime soon. So we decided to bring our game to a convention. It is a very good way to receive feedback, you meet a lot of people, and it’s a really cool and extremely fun experience …

… but it could be very expensive! Too expensive for two spare time devs like us.

Thus, we decided to leverage the power of the Internet, and politely ask users to help us gather data from their play sessions.

Game Analytics: Our analytics tool

In order to actually gather data, we use GameAnalytics (GA), a free analytics tool platform that allow you to setup a free account, which lets you collect anonymous data from your game. Even if it is principally focused on mobile gaming, GA gives you extremely easy-to-use and nice tools to get the most out of your data, without any hassle from the players’ side.

Important ask the player first! We use a disclaimer that pops up the first time you open the game (with the option to opt-out anytime).

The GA dashboard is really nice and gives you a great overview of what data you can collect, and how you can extract important info, along with interactive graphs.

Screenshot 2020-03-26 at 16.48.22
Example of GameAnalytics dashboard

You just have to add some code to your game, or use already available features for GA, according to your engine of choice (as explained in the Integration Guide in the GA docs – There is even a step-by-step integration guide for Unity if interested). 

This would allow your game to send data to GA, along with an identification key they provided you when you registered.

Customize data to be collected

GameAnalytics has different so-called types available for tracking different events in your game: user, business, resource, progression, design and error (check out here to know more). 

We will focus on design events, since they can be customized to collect anything you want. To be as generic as possible, we will use the GA REST API, that allows you to send JSON data through the internet.

I’ll give you an example:

In SuperStarfighter there is an entire section in which you can personalize the games you want to play, selecting them from a map:

The map where you can choose which games to play


You can send a “design” event, putting in the event_id field something like this:

{"category": "design", "event_id": "selection:gamemode:{gamemode_name}"}

Where the field event_id is made with the following format:

A 1 to 5 part event id.
[part1]:[part2]:[part3]:[part4]:[part5]

Another example is when you choose which character to play with. We, as developers, could be interested into knowing which one is the most loved by players.

The species you can choose in Superstarfighter

So this would become something like this:

{"category": "design", "event_id": "selection:species:{species_name}"}

For more info about how to use the event_id and the design customization field, and how checkout the GA API’s doc.

Going the extra mile

We had a first overview of the data we collected directly from the GameAnalytics dashboard. But we can do more: analyze the data with our own tools. 

We extracted all the data from GA (thanks to the Export button you can find under Settings->Export choosing your preferred data range), performed some aggregation (we used python) and uploaded everything into the Elasticsearch/Kibana stack (that you can install on your local machine). With Kibana, then, we create some visualization dashboards to find out more.

Here is what we found out about our players:

In the first month of the current release (v0.6) we had 37 players who were kind enough to opt-in in our analytics campaign, and we had 72 play sessions.

The country where most of our player are from is USA, then Italy (the country we’re in), following Argentina and Spain. Most of these players play with Windows, runner-up Linux. Most of these players play alone and in two, whilst some of them played in 3 but no-one in 4.

22,849 bombs have been shot, and 2,238 kills have been done. Deathmatch seems to be the most selected minigame,people seems to love Roborlords

… and much more. Ask in the comment below, if you want to know more! 🙂

This analytics are yet preliminary but they are already effective for us, and we will take in consideration in order to tune our game.

Conclusion

Of course, the specific data we showed makes way more sense to us than you. But you can have the same analytics system set up for your own game, and this might inform and speed up lots of strategic choices you are going to make as you are developing it.

GameAnalytics manages all collected data in a GDPR-compliant mode, giving you a way to interface with their API and a wonderful overview with some cool visualizations.

As we showed above, after having exporting the data, We used python and Elasticsearch as well-behaved Data Scientists, but you can use any tool you like (comment below if you have any suggestion).

Your users will do the rest, they are playing your game, so they are already rooting for your success! Just ask for a little extra help, and someone will answer.

Best of luck.

Super Starfighter – frantic local-multiplayer game

Today I want to show you guys what we have been working on in the last months.

After a few game jams we have been joined, we wanted to, starting from the #devtober challenge to complete a full game. So we wanted to focus on one of our game and make it leave the prototype zone.

The game who chose would be the one is: Superstarfighter. A free game, developed by notapixelstudio with GodotEngine downloadable on https://notapixel.itch.io/superstarfighter

Superstarfighter logo

Superstarfighter is a league in which 5 species (currently), bring their own view to the galaxy. It is a “sportive competition”. Every game mode is a different “sport” as it was Olympic Games. The idea is that every species bring a “sport” they are passionate about it. The first species who  win 3 matches win.

Single or multiplayer

This game was born as a couch local-multiplayer game, but we added the option to play single player – with a bot, that is not really smart but goes directly to the point. But we suggest you gather some friends and play with the gamepad.

The game supports 1 to 4 players, so even if you can play alone, finding some friends to play with and defeat is funny.
Each player starts by choosing an alien pilot of their liking to enter the Superstarfighter league.

There are different Game-modes. All of them spin around two principal mechanics: The driving and the feeling of driving a ship in which you cannot stop and its physics (drifting, collision, speed) and the fact that your only way to defend is shooting bomb at your back. The bomb generates an explosions that might kill you .

Different Game modes

Currently 4 game modes are present in this version (v0.3.1). Every game-mode has their field as it was a Sport.

Deathmatch mode
Take the crown mode
Capture the flags mode

Session and match feeling. What we need to test


We have in mind looots of game modes (and lot of game modes have been throwed away). We are experimenting right now arenas (so .. level design) and the “session feeling”.
I’ll tell you more about it. Every combat is an arena with its game mode: you have a purpose – the game mode – and a time (or points) to reach that goal. With this comes lots of design questions:
This is the “combat feeling” : how long do you want that to last? how much you’re enjoying it? Does the goal of the game mode is clear enough? is it funny? is it enjoyable? How frustrating is the death? How long till you respawn? too short? too long? Is it fair? How is the arena? the obstacle in it?


Then there is the “session feeling”: Once a combat is finished how much the player(s) want to carry on with the game? would it like to keep going with the same game mode? Would it like to change it? would it like to choose? How many matches would you be able to do it to make more fun?

What do you guys think ? Would you give superstarfighter a try ? 

Animated tiles… more or less

Tonight I wanted to make something simple and fun: animate the water tiles for DeadBox. I created a 16×16 image for each frame of the animation, eight in total. Each image is simply a translation of the previous one by two pixels to the left, in order to give the illusion of waves when the animation is looping. Fortunately for me, Tiled has support for infinite looping animations, so I simply selected the first image from my tileset, opened the animation editor in the tileset menu, double-clicked on each frame, and set the duration of each frame to 150 milliseconds. The result was simple but pleasing:

I know, I know… This should have been a screen recording

But, when I tried to load a tilemap with animated tiles in Godot, I discovered that animation is not supported in Godot tilemaps. There are feature request for supporting animated tiles both in godot-tiled-importer and in Godot itself (here and here). The second link suggests some kind of workaround that maybe I will try in the future, but as for tonight, no animated tiles… 😦

Adding water and buoyancy to a platform game with Godot and Tiled

We definitely think that failed results are worth to be shared as the successful ones. That’s what this post is about.

DeadBox is a platform game that @notapixelstudio is working on. As stated in the article, we want to practice our knowledge and our ideas in game design/development with this funny game. So we are adding new features and new elements to the gameplay step by step.

We started with the collision box for the platform, the harmful tiles (spikes) and the one-way collision box (link).

We are working with Tiled, since it is easier for our purpose making maps with Tiled that with the Tilemap tool from Godot. Thanks to the Tiled automapping and the Godot plugin  we can import the tilemap and all the element with collision in Godot Engine, after applying the automapping (more in the Tiled docs). But importing the tilemap in the standard way in Godot, will create StaticBody2D with a ShapeCollision2D for the collision tiles, and for the background, no collisions.

With the water tile we needed something else, it is a tile different from the other ones. The best idea would be having an Area2D because we want to apply the buoyancy when entering in the water. So this is what we did:

Screenshot from 2018-03-10 00-05-06
Example of a tilemap with water

  1. Created the tiles on Tiled
  2. Updated the automapping with an Object Layer and not as a Tile Layer (important – see picture below)
  3. adding type “area” to water layer (in order to let the plugin godot-tiled-importer know that has to create an Area2D instead of a StaticBody2D)
  4. Create the level with a tilemap
  5. Import the tilemap in godot thanks to the above mentioned plugin
  6. Create a signal connection between the Area2D and the Hero.

Screen Shot 2018-03-10 at 11.25.10
Godot and Tiled recognise map TileMap type with collision or background

We need to get the water-tile type as a node from Godot and connect to the signal.

# World.tscn

func load_level(level):
[...]

# enable signals from water layer 
  var wl = map.get_node('water') 
  if wl: for water_area in wl.get_children():
     water_area.connect('body_entered', self, '_on_water_entered') 
     water_area.connect('body_exited', self, '_on_water_exited') 

  [...]

  func _on_water_entered(body): 
    if body.name == 'Hero':
      $Hero.enter_water() 

  func _on_water_exited(body): 
    if body.name == 'Hero': 
      $Hero.exit_water()

Now that we have the signal we need to write the buoyancy in the Hero.gd

var floating = 0

func _physics_process(delta):
   if floating:
      motion.y -= WATER_K * (position.y - sea_level)
 
func enter_water():
 if not floating:
   sea_level = position.y
   floating += 1
 
func exit_water():
 floating -= 1

The buoyancy is a mess right now, with a trial and error policy, here is a few examples:

The problem here is the oscillation, the idea was to treat the movement as a elasting/spring impulse so we have to figure it out how to do the trick.

and

Right now we are, kind of satisfied with the movement, but we will tweak when we will have more time.

Do you guys know a good way to implement buoyancy and dampening? Let us know.

One way collision tile with Tiled and Godot

This is deadbox icon. With it we want to explore the design and the development of a platform game in GodotEngine.

Today we are going to talk about One-way Collision box. Here is an example of our little hero with a one way collision box.

one_way_collision
Example of Deadbox jumping on and off one-way collision tiles

I see lot of tutorial that explains how to add manually a box and CollisionShape2D directly from Godot. But I haven’t seen anything on Tiled.

The maps in our games are made in Tiled. This allows us to build them in a easy way, and use all the fantastic features that Tiled has to offer. In another article we’d post something about our tileset setup, check it out here.

Below there is an image that shows our rules.tmx file. You can see the top left image showing the mapping layers:

  • harmful: layer for the elements of the map that hurt the player (or anything actually)
  • solid: layer for the collision elements, like wall, floor, ceiling, platform
  • bouncy: layer that allows the player to bounce when step on it (To be implemented yet)
  • water: layer object for the water tile (more in a future post)
  • jumpthrough: layer that will identify the one-way platform
  • regions: layer that allows the automapping for Tiled
  • art: layer that identifies our own tileset

In order to understand better the automapping, I suggest to read the docs.

In the top right picure we have the properties set of the above mentioned tile. We need to know its ID, in order to let Godot know what is the tile to which apply the function tile_set.tile_set_shape_one_way. Function that is not documented, that allows us to set, via code,  CollisionShape2D to One way collision (the same as checking the box “One Way Collision” in Godot).

After having the tilemap imported we need to find the proper layer and put the right CollisionShape to it. Unfortunately for us, the script doesn’t allow,  yet, handle types of tiles in a better way than we did it. A big shout out to the developer of this incredible plugin and hopefully the 3.1 version will be released soon enough, as stated here in this issue.

We adopted this workaround: on the gdscript of the root node World we add the following script

# World.gd
 # enable one-ways collision in jumpthrough layer
 var jtl = map.get_node('jumpthrough')
 if jtl:
 # workaround - waiting for a better implementation in godot-tiled-importer
 # reference: https://github.com/vnen/godot-tiled-importer/issues/37
   jtl.tile_set.tile_set_shape_one_way(123, 0, true)

#123 here is the index of the current tile into the tileset. For some reason the imported tileset starts the index from 1 instead of 0.

#0 is the collisionShape associated to that tile in the tileset.

This workaround is not stable because there is no way to find out the index of the CollisionShape that the plugin gives to the tile when importing the tilemap. That might change if you reimport it with a new map.

We might be wrong about the automapping and setting the right properties, let us know with a comment down below. We would like to know if there is anoter or a better way.

Fail and learn. 🙂

See you next time!

DeadBox tileset III

Screenshot from 2018-03-09 22-24-11

These are tests for the third version of a tileset for DeadBox. I believe this has a better polish than the previous ones. The fake “perspective” is a bit weird though, and the player seems to be floating above the floor rather than standing on it.

Screenshot from 2018-03-09 23-43-52

Because of the weird projection, spike tiles had to be drawn in lots of variants. The use of an additional layer helped in lowering the variations needed to interface spikes with left-facing walls.

Screenshot from 2018-03-10 00-05-06

Water also needed some special tiles for connecting to left-facing walls, and another layer to place it on top of spikes.

I hope to be able to tell Tiled to help me place the correct combinations of tiles, but I suspect that would only be possible with a nightmare of an automapping rule file.

I wonder if there are better, more easy to maintain techniques…

Celeste: open-source Player class

celeste

Being a huge fan of TowerFall, I was bound to love Celeste. They share a lot of similarities in platforming mechanisms, especially the use of dash. It’s by mastering dash techniques while playing TowerFall with your friends that you become so skilled to perform seemingly superhuman feats. To see an entire single-player game develop around twisting and turning that very mechanic is awesome.

Even more awesome is the fact that Matt Thorson and Noel Berry, the two developers of Celeste, recently decided to share the code of the Player Class:

Then, some days later:

A link to a very interesting post about the physics of TowerFall is also included in the FAQ document above.

DeadBox tileset II

blocks

This is the current “art” for both the tiles and the nameless character of DeadBox, in all its amateur glory. I tried to make it more streamlined than the previous one. It is also 16×16 pixels instead of 32×32, because I wanted to give a more pixelated feel when upscaled.

I am not sure about the blocks, but I find the black outline of the character quite useful.

In the lower-left corner you can see the metatiles we use to define solid and harmful tiles. They are automatically added by Tiled into separate layers whenever a regular tile is added. So, for example, whenever we draw spikes onto the art layer, the corresponding metatile is automatically added to the harmful layer (this is called automapping). We then export the tileset and tilemaps to the Godot engine, using a dedicated plugin. At runtime, Godot is able to distinguish how to handle a collision by telling apart the different layers.

I don’t know if this is a best practice or not, but I find it suitable for our current setup.

DeadBox

Several years ago, I was watching a trailer of the then-upcoming game Limbo. I honestly don’t know why, but I heavily misunderstood what the game was about. I remember running to one of my friends, and describing him what I believed was the central mechanism of the game:

  1. You try to beat the level;
  2. Whenever you die, you leave a corpse there;
  3. While replaying the level, you can use the corpse by jumping over it, thus avoiding whatever hazard killed you the first time.

I was very disappointed when I found out that the game was not about that (the game turned out very good, by the way). But that random event was the moment the idea behind DeadBox was born.

Screen Shot 2018-03-09 at 15.52.47
A screenshot from the current prototype

It is not an original idea of course, there is now at least one game out that is based on that very premise. But some days ago, when we were looking for an idea for our first attempt at developing a full game, I remembered about that concept and pitched it to the group.

It turned out they liked it a lot, way more than I do! Let’s see how it develops… 🙂