Fireside Hero - a turn-based strategy / RPG (Devlog)

Makelee

  • Posts: 14
Nice looking game and also an Ian Livingstone endorsement! Him and Steve Jackson are legends. My personal favourite was Vault of the Vampire in their series of books.

dtishin

  • *
  • Posts: 83
Thanks! Not sure the endorsement makes any difference commercially but it was definitely a big encouragement.

dtishin

  • *
  • Posts: 83
Wrote a little devlog on Gamejpolt about the original concept from a year ago. There's a 30-second video of the first prototype gameplay and a poll. I'll appreciate your opinion! https://gamejolt.com/games/firesidehero/489536/devlog/how-it-started-minesweeper-on-parchment-nu9fvhhh

dtishin

  • *
  • Posts: 83
No programming experience + First solo project = Horrible code, guaranteed.

Fireside Hero turned 1 in early May 2020 and got a Steam page. A good milestone to look under the hood, I thought, and pulled a Hydra from the engine compartment. The Hydra told me my framerate sucked. It could drop to an average of 3 frames per second when a hero was travelling over the map - picking items, gathering resources, boosting skills and fighting monsters. Basically, it dropped to 3 FPS when anything exciting was happening! Not good.

6 weeks and 14 build versions later, job done. Kind of. The new average is somewhere between 20 and 30 FPS, which is not ideal - but I like the results and hope they'll get me through the year (and maybe to release?) Also note most of the drops in performance happen when there's little or no movement on screen (e.g. an item has been released into an inventory slot, hero has moved onto a map location, etc) which will go unnoticed. And, to be honest, the thought of more debugging makes me sick, so I'll pass for now and swing back to gameplay, features, UI and other fun stuff :)
Some optimisation observations I made:

1. Unless you're making a physics-based game, absolutely do disable collisions.
You can do it via game settings and via settings for individual actor types. Since I wanted to be absolutely sure I disabled everything, I set the global physics settings from "Normal" (Box2D) to "Simple" and individual actor type settings from "Normal" to either "Simple" (for actors that need to receive updates and execute behaviours) or "Minimal" (for purely cosmetic actors e.g. rivers, landscape transitions and such). Unfortunately, most of my pointer interactions (clicking buttons, selecting heroes, managing inventory etc) and hero interactions (visiting map locations etc) were based on collision events... Replacing map collisions with coordinate checks posed few challenges, as FH is tile-based and everything fits nicely into a grid. Pointer collisions, on the other hand, turned out trickier, as windows (and buttons) can overlap on different layers. Such as when you open a "hero details" window then click an item, which opens the item details window with its own "Sell" or "Use" button. Checking a-z layer order inside the coordinate check behaviour generally solved this issue.
Of course, the precision of irregular collision shapes was sacrificed in the process - all buttons effectively turned into rectangles, even if their sprites were diamonds, circles or whatnot. But from UX perspective I found it doesn't make a difference.

2. Replace "Always" events with "Custom" (one-off) events if possible.
Not only it saves the engine from unnecessary calculations, but it also helps you structure the logic and better control what is triggered when. I'm really puzzled why official Stencyl documentation claims "Always" events to be the meat of any game. In my opinion, they are more like the nerve system - listening to inputs and relaying commands to other parts of the body (i.e. actual "meat"). So instead of a few dozen events in separate behaviours all checking same boolean conditions and listening to the same mouse releases, I made one master event for all mouse clicks in the game, which triggers respective custom events in various scene and actor behaviours. Same goes for triggering hero collisions with map objects or programming combat logic. If making a one-off event is not an option, at least consider switching to "Do every N sec." That's how text is now refreshed for hero stats (prowess, charm, knowledge etc). It creates a small lag but you get used to it pretty quick.

To be continued...

Bombini

  • *
  • Posts: 1381
Unofrtunately thats part of the development :)
And getting better FPS can also be fun if you see the result.

I wrote down some thoughts a while a go which might be helpful:
http://community.stencyl.com/index.php/index.php?topic=28359.msg301552#msg301552
Cheers!

dtishin

  • *
  • Posts: 83
Unofrtunately thats part of the development :)
And getting better FPS can also be fun if you see the result.

I wrote down some thoughts a while a go which might be helpful:
http://community.stencyl.com/index.php/index.php?topic=28359.msg301552#msg301552
Cheers!

True story, thanks for the link!
Actually, your more recent post about Goldspace optimisation nudged me to write this devlog ;)

dtishin

  • *
  • Posts: 83
3. Use handler events for updating animations, font colour, sound etc.
Basically, instead of "Switch animation for actor..." you may want to use a custom setter "Set animation requested to...". The separate handler will then check if the requested new state is different from the current state, maybe remember the current state as "previous", check additional conditions and only then do the hauling part and actually switch the animation. Besides improving performance, it allows to easily tweak the code in one place without sifting through your whole project.
4. Pre-load sounds on separate channels if the files are large and you switch them often.
FH has a lot of sounds. Some are very short (clicks, thumps, horse neighs and such) or only play occasionally (e. g. "new day" jingle). These don't really affect performance, so I can group and play them on the same channel (e.g. all UI sounds on Channel #4, all jingles on channel #3 etc). Other sounds, specifically ambience loops for the map (forest, swamp, desert etc) are switched every time the hero moves to a different tile. In this case, consecutively looping and stopping them on Channel #0 created visible lags in movement of hero avatars on the map. Investigation with hxScout proved that the engine was retrieving the sounds from files every time, which was creating the lags. So instead of two sound channels (one main, second for fade-out of a previous loop) I introduced as many Channels as there were ambient loops (currently around 15). All these sounds are looped on individual channels while the scene initialises, are immediately paused and remain in RAM for the whole game onwards. Yes, it permanently occupies 30 MB of RAM instead of 3 MB, but the difference in UX is amazing.
(And because I had a separate "sound handler" event, changing this logic was quite quick ;).
5. Use procedural animation if possible.
I have a simple dying animation for my monsters and heroes. It erases the character image (sort of "the Infinity Gauntlet" wind effect). I initially stored these animations as actor animations (roughly 20 frames per character x 18 characters = 360 frames, 240 x 240 pixels each, with 5 default scaling variants Stencyl includes). The way Stencyl works, all of these are loaded into RAM at the start of the scene (I couldn't exclude some characters via memory atlases, because my game has procedural generation and I need all the monsters in my scene). What I did instead was: create 1 animated Eraser actor (left-to-right + right-to-left variants, 17 frames each, 120 x 240 pixels), leave only 1 static image for each of the 18 characters, and use the "clear image using image" block "every [duration of the eraser frame]". This is almost 350 sprites less than the original solution and uses 10x less RAM and disc space (at least 20 MB saved immediately).
6. Crop transparent borders from your sprites.
Texture Packer can do this really quickly and efficiently.
7. Use the native Stencyl functions to draw text (If possible).
Stencyl's text engine is extremely basic. No kerning, no grid fitting, no alignment, no text wrapping (list goes on). When it comes to Serif fonts like my main font, Eagle Lake, the look is horrible. I mean, playtesters started complaining and all. On the upside: it's fast, the glow / offset effects look great, and you can apply styles (bolding / italics etc) even when the original font file doesn't support this style.
I'm using a custom Stencyl build that can display text using the "Open Flash Textfield" blocks. It makes perfect spacing between characters, and the result looks crisp. Unfortunately it seems to come with a tiny extra tax on performance. So I ended up using the "Open Flash Textfield" blocks for big chunks of text (narrative, item descriptions, tutorial prompts etc) and use the native "Draw" blocks for UI button labels.
8. Use hxScout to monitor performance.
Detailed guidelines in Merrak's thread here. Side note: the telemetry / hxScout monitoring process taxes your memory too - so the framerate it shows is not exactly the framerate you'll have with telemetry disabled (though it's probably pretty close). In other words, you cannot measure framerate without affecting the framerate!
9. Use Visual Studio to debug runtime crashes if the native Debug mode doesn't help.
Guidelines by Justin here. If I read this thread when I started developing my game I could save myself at least a hundred hours (likely much more). I'm really perplexed why Stencylpedia doesn't have a section on properly debugging runtime crashes.
10. Avoid creating important actors in scene editor if you continue adding behaviours to your game.
While refactoring the code I ran into a bad Stencyl bug. If you add a new behaviour to an actor type, it may not get added to the actor instance you had previously placed in the scene via scene editor! It took me a whole day to figure out why my pointer stopped working, until I found that the pointer instance didn't show a new behaviour I recently attached to the pointer actor type. As soon as I removed the pointer from the scene and created it procedurally at scene start, the issue was fixed. Note it's a confirmed issue planned for fixing in future Stencyl releases.

dtishin

  • *
  • Posts: 83
Adding backgrounds for the combat mode in Fireside Hero.
What do you think?

Luyren

  • *
  • Posts: 2375
They look good. My only issue is with the lava one. If you google lava, you'll see a bunch of images. Lava is more of an orangy-red with a lot of yellow in the center (more like fire-color), so I think you could try something more akin to that, instead of the pink-ish red you got. I'm also not a fan of the blur effect you added to what I think are pools of lava.
My Stencyl resources are available here: https://luyren.itch.io/
Cutscenes, RPG Elements, Particles, Map System and many more.
Twitter

Bombini

  • *
  • Posts: 1381
Very good stuff!

3. Use handler events for updating animations, font colour, sound etc.
Basically, instead of "Switch animation for actor..." you may want to use a custom setter "Set animation requested to...". The separate handler will then check if the requested new state is different from the current state, maybe remember the current state as "previous", check additional conditions and only then do the hauling part and actually switch the animation. Besides improving performance, it allows to easily tweak the code in one place without sifting through your whole project.
I find this interesting! Is this improving the performance? wow. Only if in always?

5. Use procedural animation if possible.
I have a simple dying animation for my monsters and heroes. It erases the character image (sort of "the Infinity Gauntlet" wind effect). I initially stored these animations as actor animations (roughly 20 frames per character x 18 characters = 360 frames, 240 x 240 pixels each, with 5 default scaling variants Stencyl includes). The way Stencyl works, all of these are loaded into RAM at the start of the scene (I couldn't exclude some characters via memory atlases, because my game has procedural generation and I need all the monsters in my scene). What I did instead was: create 1 animated Eraser actor (left-to-right + right-to-left variants, 17 frames each, 120 x 240 pixels), leave only 1 static image for each of the 18 characters, and use the "clear image using image" block "every [duration of the eraser frame]". This is almost 350 sprites less than the original solution and uses 10x less RAM and disc space (at least 20 MB saved immediately).
You can expand this to other situations. Just ask yourself what is used again and again. I have for example one explosion actor for the whole game which changes depending on the situation.

« Last Edit: July 08, 2020, 03:18:19 am by Bombini »

dtishin

  • *
  • Posts: 83
I find this interesting! Is this improving the performance? wow. Only if in always?

It seems to somewhat improve performance but I'm not 100% sure.
I'm not a programmer, so I imagine operations like "send actor to layer with name" involve extra memory for rendering if you do them "always" but I may be totally wrong. I'm sure Justin can clarify this... 

dtishin

  • *
  • Posts: 83
They look good. My only issue is with the lava one. If you google lava, you'll see a bunch of images. Lava is more of an orangy-red with a lot of yellow in the center (more like fire-color), so I think you could try something more akin to that, instead of the pink-ish red you got. I'm also not a fan of the blur effect you added to what I think are pools of lava.

Thanks Luyren. The reason for the pinkish colour was, I wanted the hue of the background to roughly match the hue of lava terrain on my adventure map, so I coloured the mountains accordingly then proceeded to experiment with the ground. And then found that the orange / yellowish doesn't go well with with the mountains, so I harmonised it all to roughly one hue (red) :)

But you absolutely have a fair point there, I'll tinker with colours and see what works better!

dtishin

  • *
  • Posts: 83
It seems to somewhat improve performance but I'm not 100% sure.
I'm not a programmer, so I imagine operations like "send actor to layer with name" involve extra memory for rendering if you do them "always" but I may be totally wrong. I'm sure Justin can clarify this... 

- Chatted with Justin; it seems that most functions such as switch animation, send actor to layer etc, have a built-in check to avoid unnecessary memory load. So most likely it was a miss on my part...

- Still, even if not for performance reasons, handler events improve code structure, because you have all object changes of a certain type in one place. E.g. my sound handler always remembers the previous music track when the music is switched; this way when I close the main menu the game plays "the previous" track (whatever that may be). Implementing this without the handler event would be messy.

- Also Justin mentioned something I did notice in Fireside Hero: Moving a lot of actors forward / back a layer may slow down performance, since in this case the engine cycles through all the scene layers to find which one is the target. 

dtishin

  • *
  • Posts: 83
Do you like this lava more Luyren?

deepshock

  • *
  • Posts: 42
This is extremely valuable information. Thanks for your whole thread!