version 0.23 / PolyLan aftermath
PolyLan: The Aftermath
You might remember from the last devlog: We’ve hosted a small Everblind tournament at the PolyLan during the easter break. We had 16 players come for 2h30 of mind-reading, sneaky ball-destructive action. Congrats to the winners, although it was a shame I didn’t get the chance to play against them.
Fun fact: So it seems that when you host a tournament, you need a way to rank players. I know, right? Who would’ve thought. So 10 minutes before the beginning of the contest, I finished coding a python script that extracts gameplay stats for that. I knew my EventStore would come in handy at some point!
The script parses the csv file that contains all events for the last match, and whenever it encounters a e.g. PlayerKilledEvent, it increments player X’s kill count. Super simple.
We ranked players according to points scored. We broke ties by counting number of kills. In case of ties at that point, we broke them by rewarding players that attacked less (because attacking makes you visible, and the goal should be to remain hidden).
Interestingly, in the last 20s of the game, when players had no chance to score a new point, and saw that there were ties on the score, they all entered a killing spree, trying to maximize their secondary winning stat. That was unexpected, although obvious when thinking about it, but most importantly: it was fun. Seing players all tacitly agree on not playing the primary objective and enter a killing madness was incredibly satisfying.
It worked out pretty well, apart from a few bugs that I’ll detail when they are fixed. An annoying aspect was that I had to alt-tab to the python console to extract the stats after each match.
So I’ve moved this in-game and dedicated the end-game view to that.
End game stats
Alt-tabbing between matches is fine, but being a lazy developer I prefer when I can just grab a beer and watch people play instead of having to do things.
So I took the python code that extracted my stats and ported it to C++. Bound that to the current end-game panel, and voilà ! Next tournament we’ll have more time to drink beer. After a game ends, each player will have her own view with personal stats.
Looking at it, I think I’ll replace “Time attacking” with “Telepathic score”, a value obtained as such:
What this represents is a score that will increase linearly with the game time (given that a player keeps the same “attack rate” no matter the duration of the game). Thus, longer games will yield greater Telepathic scores. The factor “a” is simply here to normalize things in nice-looking ranges. I think I’ll set it to something like 0.01. That way, a player that never attacks during a 5mn match will get a score of 900. Conversely, a player that attacks the whole time will get a score of 3. For a 10mn match, the scores will be in the [6, 3600] range.
Config → Save file
Part of working with a new engine (in my case, Unreal) is also making painful mistakes when misunderstanding a system. The latest “I’m an idiot” realization on my long, on-going list of things I messed up is misusing the Config system.
It seemed pretty cool at first: you just tag a variable as UPROPERTY(Config) and it gets saved/loaded from configuration files. Perfect for replacing Unity’s PlayerPrefs system that I was missing in Unreal, right?
I couldn’t be more wrong.
As it turns out, the syntax of the configuration files is very precise and you won’t get any errors in case you messed up. You can put a bunch of stuff in it, so you have to make sure every variable is properly identified with the full path to it. For instance, here’s how one of my config files looked like:
The header for each variables block must be the path to where the source file is that uses this variable. You can easily imagine the problems you can have with this. It breaks when you move or rename a file, when you extend a C++ object in a blueprint (from my observations, you then have to reference the blueprint class, and not the C++ class anymore). Even more fun: configs are only loaded when starting the game, or the editor. That is, whenever I wanted to try a new config value, I had to close and reopen the editor.
Even worse is the way that defaults are handled. The file above is the default configuration file; the one I have in my source code. When packaged, the game carries this file, but it’s now empty. That is because in a packaged game, this config file only represents the changes with respect to the original configuration. So if you played around with the config and used a default value for a variable, the line would disappear, together with the [/Game…] header! And you’d have to guess what the header and variable names were.
I needed all that to realize that I was using the Config system for something it wasn’t meant to. What I wanted were user profiles. I quickly stumbled upon unreal’s SaveGame system. This is the go-to way to save a player’s progress in a UE4 game and would solve my problem easily. My issue with that is that the .sav format, in which the files are saved, is binary. The reason I like Unity’s PlayerPrefs and -to a certain extent- Unreal’s Config files so much is because I can fiddle around with them in plaintext. When release time comes, I usually encrypt or binary-serialize these files to make sure nobody plays with them in unexpected ways.
So I came up with my own solution. Unreal bundles a module called “JsonUtilities” wich can convert USTRUCTs to json and vice versa. Perfect for me! My new profile system is called EverblindSave and simply reads/writes an EverblindSaveStruct struct in json.
This system writes and reads a “SaveFile.eb” file, which contains the json-based representation of my custom game settings. It is saved in the GameDir/Saved folder, to reuse Unreal’s conventions. And it is accessible anywhere from any Blueprint.
There we go, boys and girls, that was iteration 0.23. As a general info, we’re still looking for alpha testers, so if you’re into that sort of thing sign up here. Also, we’ve updated our presskit with new screenshots. Check it out!
Also, the initial feedbacks we got from our testers is that the game is a performance monster. First item on the list for version 0.24 will be to understand how 4 spheres and a static mesh can eat up so much power. As a teaser because I’ve already started working on it: never let an artist use cascade unsupervised.