• Text Rendering Performance Check for a ReactJS ASCII Roguelike

  • Other · 2019-03-19 · nightblade
  • Tags: ReactJS, Roguelike

    image

    Recently, I was tasked with learning ReactJS (or just "React"). So, I made the most obvious choice possible: I decided to write an ASCII roguelike, which uses React as the front-end.

    Why an ASCII Roguelike?

    Why? For several, obvious reasons:

    • I have very limited time to work on this; perhaps as little as one hour a day, or less.
    • ASCII roguelikes require little beyond a good monospace font -- no complex graphics, very simple animations
    • I enjoy creating roguelikes. It's fun. Fun motivates me to work on this, which fuels my learning.
    • Having created several (failed/incomplete) roguelikes in the past, I know how to code them. I don't have to think too hard about level generation algorithms, line-of-sight (or area-of-sight) lighting, etc. so I can focus on the technology.

    I set out to create, in my mind, the first step of any text roguelike: draw text on the screen really fast and see how it performs. HTML should render at lightning-speed in my modern browser, right?

    Wrong.

    The Simplest Possible Performance Test

    React is just a front-end technology; it doesn't dictate how I should structure my HTML. With that in mind, I set out to answer two questions:

    • What kind of performance can I expect with my roguelike?
    • What factors affect performance? Does changing the font, applying colour (or not), using nested div tags vs. span tags, etc. make any difference?

    In brief, the answer to question #2 ended up being "no." Performance bottlenecks on something else entirely; none of those changes make any significant difference.

    I wrote a little Javascript that tests exactly what I want:

    • Define dimensions of the screen. In this case, 50x16 tiles.
    • Create a div tag for each tile. Style it appropriately (eg. width/height big enough to fit any character)
    • Very frequently (like, 60 times per second), update each tile's display to a random character with a random colour.

    You can see the code, more or less (with FPS counting), here. It's simple, and to the point.

    The Results

    In a word: abysmal! On my fairly beefy dev machine (lots of RAM, good CPU, mediocre GPU), it renders at a measly 6-7FPS. You can see the results in this tweet.

    As I mentioned earlier, I tried several variations; none of them improve performance, at all. The core of it comes down to a call to set the character itself. Pseudocode:

    var character = ...
    var colour = ...
    var tile = window.tiles[i];
    tile.innerText = character
    

    It turns out that browsers, even modern ones, even on beefy hardware, are really, really slow when you set innerText. The only alternative - using innerHTML -- is slower, and probably broken on Internet Explorer.

    The Conclusion

    For an ASCII roguelike without too much going on, 6FPS is probably enough. If I really cared about performance, I could switch to canvas-rendering and a bitmap font (lots of work and not sure how it works with React), or using images - either images of text, or real images.

    For me, the goal is to learn ReactJS, so I plan to continue forward with this as-is, without major surgery.

  • Creating Tilemaps with Tile Entities in Godot

  • Other · 2018-10-02 · nightblade
  • Tags: Godot

    Godot's tile editor provides you with the ability to quickly make 2D tile-maps, similar to RPG Maker. This makes it easy to design and tweak maps. But can we take it a step further? Can we, in fact, use the tilemap editor to create functioning entities -- making it a domain-specific editor for our specific game?

    We can. This post walks through a lightning-quick method you can use.

    The Core Idea

    Godot's Tilemap entities are simple. They're essentially a reference to a tileset (graphic with all your tiles on it), and one or more layers of those tiles drawn on a grid. In Godot, tilemaps are all about presentation.

    What if you want to add functionality though? Imagine you're creating a 2D adventure or RPG (like, hmm, Eid Island maybe?), and you want to be able to draw enemies on the map. Or trees you can chop down, coins you can pick up, or anything else.

    By itself, Godot doesn't provide a way to do this. However, I asked around on Discord, and checked some issues on GitHub about this; a definitive, simple approach appeared, something like a best-practice. How? Easy.

    • Define your tilesets as usual, including objects
    • Draw them in as many layers as you like, as usual
    • At runtime, iterate over the tiles, replacing the object ones with real entities (Scene instances)

    The Secet Sauce: Swapping Tiles for Entities

    Godot makes this process quite easy. As a pre-requisite, I would recommend:

    • All your tiles have proper, uniquely-identifying names (like Tree or Plain Water)
    • You create entities for everything you want to replace, such as a Tree scene (subclasses Node2D, has a Sprite, script, etc.)
    • You map the two. I use a dictionary such as: var entity_tiles = { "Tree": preload("res://Scenes/Entities/Tree.tscn") }

    With this in place, we simply iterate over the tiles and -- if the name appears in our dictionary -- replace the tile with its corresponding entity. Here's an early reference implementation in Eid Island.

    # Find entities on the map (eg. trees). Remove them and replace them with
    # real entities (scenes) so that we can have logic (attach scripts) to them.
    func _populate_entities(map):
        var possible_tilemaps = map.get_children()
        for tile_map in possible_tilemaps:
            if tile_map is TileMap:
                var tile_set = tile_map.tile_set
                for cell in tile_map.get_used_cells():
                    var tile_id = tile_map.get_cellv(cell)
                    var tile_name = tile_set.tile_get_name(tile_id)
                    if entity_tiles.has(tile_name):
    
                        # Spawn + replace with entity of the same name
                        var scene = entity_tiles[tile_name]
                        var instance = scene.instance()
                        map.add_child(instance)
                        instance.position.x = cell.x * Globals.TILE_WIDTH
                        instance.position.y = cell.y * Globals.TILE_HEIGHT
    
                        # Remove tile
                        tile_map.set_cellv(cell, -1)
    

    Globals.TILE_WIDTH and TILE_HEIGHT refer to the (fixed) tile size; you can, alternatively, use the tileset/tilemap to get the cell size, or use the entity size as a reference (although the entity should be exactly one tile size for that to work).

    With this in place, you can quickly and easily create levels with real functionality from simple tilesets.

    Next Steps

    One obvious missing piece of functionality is customization. What if my tile is, say, a door that warps somewhere, and I want to specify the destination properties when I draw the tile? How can I do that?

    Unfortunately, I don't know the answer yet. As far as I know, Godot doesn't allow you to add additional variables/properties to the tile itself. Perhaps you could store the data elsewhere, such as a separate dictionary mapping entity/coordinates to custom data.

    If you know how to solve this latter problem, drop us a note on Twitter or in the Godot Discord chat so we can use it too!

  • Eman Quest Restrospective: A Procedural RPG in a Month

  • Devlog · 2018-09-28 · nightblade
  • screenshot of a hero facing a boss

    This month, I set out with a very specific goal: create a 2D procedurally-generated RPG; something I've never heard of (or done before). Although I ran out of time, I plan to devote another month to the game. This retrospective discusses the good, the bad, and the work ahead of us.

    Goal: What is a Procedurally-Generated RPG?

    I grew up playing Secret of Mana, Final Fantasy, Chrono Trigger, and other great titles. One day, I thought to myself, what if I can only create one more game before I die? What would I create?

    The answer? Something akin to those favourites; a game that generates a brand new, quality RPG, every time you plan. I spent some weeks brainstorming what I needed and didn't need, plans to create content such as monsters, weapons, etc. Some things clearly need to be procedurally-generated (such as dungeons and maps), while others don't (such as enemy sprites and player skills).

    A couple of false-starts later, I finally opened up the Godot editor and started the third (or fourth) attempt.

    The Good: What Went Well

    Initially, I focued on the technical side of procedural generation: creating stuff! As of writing, the game:

    • Generates a new, persistent world map each time; this includes a river that runs into a small lake, and randomly-positioned dungeon entrances
    • A procedurally-generated forest dungeon; it's a single, large map, with trees, dirt paths, monsters, and a boss.
    • Procedurally-generated equipment (weapons and armour), each with unique stats modifiers
    • Loot in the form of treasure chests in the forest dungeon.
    • The story. While very basic, the game generates the intro plot text, including the town name and final enemy type

    The forest dungeon took longer than expected. On the technical side, I figured out how to make Godot generate and persist my maps/game; initially with packed scenes (which got garbage-collected when I changed scenes, causing crashes), and later by separating the generation of game data from the presentation (populating scenes/tilemaps/etc. from content).

    Solving technical issues aside, I also spent too much time on what didn't go so well: the battle engine.

    The Bad: What Could've Been Better

    I find the concept of memory games fascinating (as an ex-Lumosity player). An Extra Credits video on puzzle vs. reflex mechanics challenged me to introduce something new into my game.

    At the heart of every RPG, in terms of fun gameplay, lies a fun and challenging battle system. I decided to design a memory-based battle system:

    battle engine animation

    This probably proved to be my undoing. I spent several days designing, balancing, and tweaking the battle engine; adding new features, action types, and skills. Five beta testers provided feedback after trying it out:

    • One really liked it
    • One said it's okay and could be improved
    • Three others didn't like it

    In the end, after several days, I forced myself to stop tweaking/changing it and decided to focus on fulfilling the rest of the game.

    Last, and perhaps worst, the game feels incomplete. Sure, you get a randomly-generated forest; but even with the intended scope for the end of the month (two dungeons with two different variations, including unique monsters), it might not be enough to give players that experience of a procedurally-generated RPG.

    What's Next

    I don't intend to give up; I plan to fill out and ship a version of this game (although with what functionality/content remains to be seen). My current plan of action looks like:

    • Finish the end-game sequence so players can actually complete the game
    • Create two variants on the forest (frost forest and death forest) each with unique enemies
    • Create a second dungeon (cave) with two variants (one is a volcano)

    Finally, I plan to create a proper RPG world. A good RPG, like a good novel, contains elements that all harmonize and work together to communicate a singular world/message. Right now, we have a patch of hastily-named enemies, skills, etc. and these need to be brought together.

    More specifically, I didn't yet start the other hard/fun part of a procedural RPG: creating a procedural world (and story) that feels unique. This includes a unique/different protagonist for each story.

    Finally, another round of beta testing should reveal whether the game is interesting/fun or not, and I should either finish it as-is or if I need to rewrite/rethink battles entirely. Time will tell. September may be almost gone, but October brings another month brimming with potential for me to see this game through to its potential.

    If you're interested in following my development on this project, feel free to follow me on Twitter.

  • Instance vs. New in Godot

  • Other · 2018-09-25 · nightblade
  • Tags: Godot

    godot logo

    Summary

    Godot provides a way to separate the presentation part of game entities from their underlying data. You can think of this as something akin to the Model-View-Controller design pattern, where scripts (classes/code) respresent the model and instanced scenes (sprite, animation, etc.) represent the presentation and control (eg. collision resolution).

    During the development of Eman Quest, we continuously ran into a problem around instancing scenes that are strongly typed and have methods from their corresponding scripts. In fact, there's even an open bug about it as of Godot 3.0.6.

    You can find a description of the problem below, along with the solution, and recommendations.

    Why This Became a Problem

    We created Eman Quest as a procedurally-generated RPG; the idea is to generate a persistent, complete world (maps, enemies, etc.) By itself, this proves to be an interesting and troublesome problem to solve, because:

    • In Godot, everything is a scene. Your world map, forest map, etc. are scenes.
    • You can't easily persist scenes or their entities.
    • When you change scenes, the old scene -- and all entities -- get garbage-collected.
    • Fundamentally, you need to separate data from the view/presentation.

    To solve this, we started initially by generating scenes and saving those; when the GC reared it's dragonish head, we instead moved to saving data as arbitrary JSON (dictionaries) of key/value pairs; when this required maintaining a set of properties twice (eg. monster strength in JSON vs. strength attribute in the instanced scene), we finally moved to creating scenes and matching scripts/classes.

    Then we broke Godot.

    The Problem and Symptoms

    Near the middle of development, while creating a TreasureChest class, I ran into an interesting problem: although my script file defined several methods and properties (like .open() or .contents), I couldn't call any of them; Godot complained:

    Invalid set index 'contents' (on base: StaticBody2D') with value of type 'Nil'

    Godot identified/typed the instance as StaticBody2D, not TreasureChest. Why?

    It turns out the answer lies in the two methods Godot offers to create objects: .instance and .new

    Scenes vs. Scripts as Classes

    Naively, Godot offers two ways to create an entity: instance and new.

    If it's a Scene with sub-objects (such as a Sprite), you load/preload the .tscn, and call instance. For example: var treasure_chest = load("res://Entities/TreasureChest.tscn").instance().

    This creates the entire hierarchy of sub-scenes. When you call add_child to add it to the scene, Godot calls the _ready function; this is where you put constructor-like code.

    Alternatively, if you just have a "class" defined in a script, you load/preload the .gd file and call new on it. For example: var treasure_chest = load("res://Entities/TreasureChest.gd").new(contents, coordinates, etc.).

    In the latter case, you define an _init function for a constructor, and specify whatever parameters you like. This conveniently allows you to specify state and guarantee invariants, such as "treasure chests always have non-null contents."

    However, Godot breaks when you cross these two together. If you load a .tscn definition and then call instance, and the class/script back-end has an _init function, the instanced scene doesn't have any of the scripts defined methods/properties.

    Solution and Combining the Two Approaches

    This could be because of timing (when in the life-cycle we're creating the script), I don't know. What I do know, is that removing _init fixes the problem. But then, how do we re-add initialization of the entity's state?

    For entities where we use both the instanced scene (in the place where we populate the tilemaps, etc. on-screen) and the script only (as the generated back-end data/object), we decided on the following approach:

    • Don't use _init. It could lead to this obscure bug again later, and suck up a ton of time to figure out.
    • To construct a new object class/script instance, create an initialize method and use that as the constructor. This is where we can set state/invariants, etc.
    • Keep a single scene for the presentation, and use a script to keep all the back-end class code (properties/methods)
    • When we need to instance the actual scene tree for that object -- such as with monsters or treasure chests -- keep the data object separate, and create an initialize_from instance.
    • initialize_from sets all the properties on the scene (such as presentation: is the treasure chest already opened or not?) based on the properties of the class/data object underlying.

    This approach also solved other problems, such as keeping references to instances across scenes. Godot doesn't like this, because it tries to GC everything. Storing the data separately neatly (sometimes in a singleton script) solves this problem.

    Conclusion and Future Improvements

    This solution solves our problem neatly. It keeps our code DRY, by using one definition of an entity's properties. It allows us to separate presentation from model/data, which is useful to prevent coupling.

    But, it's not ideal. At best, we can use _init judiciously for entities that we know we'll never show in a scene. We can always resort to using data (dictionaries/JSON) instead of classes; we miss out on code completion, but this better represents entity-component systems with data-driven design.

    Did you ever run into this problem? Do you see a better way of solving it? Drop a comment and let us know, we would really love to hear about better solutions to this problem.

  • A Month in Review: July 2018

  • Devlog · 2018-08-03 · nightblade
  • July brought a lot of challenges. @Chemical_Ink's internet went down for several days. I ran into a very busy work schedule. All of this meant less time for Abu Hamid X; but walhamdulillah, we still managed to ship a lot of changes.

    Perhaps most importantly, we decided to drop the adventure/open-world concept and stick to what we prototyped and proved as fun: the arena combat challenge. This introduced a lot of story/worldbuilding challenges, but we managed to work something out.

    screenshot

    New features include:

    • Half-hidden assassins who lunge and stab from out-of-sight
    • Lava eruptions that mean instant death
    • Proximity mines that explode
    • Jumping enemies
    • Spike traps
    • And more!

    For our Patreon supporters, you can download the prototype here. If you're not a Patreon supporter, consider joining us; your feedback shapes the direction of our games.

    Once we get feedback, our plan is to start the actual production version of the game. You can follow us on Twitter for updates.