• A Primer on AABB Collision Resolution

  • Other · 2020-01-30 · nightblade
  • Tags: Physics

    video demo

    This blog post includes a discussion about AABB collision resolution: what it is and isn't, it's strengths and weaknesses, some common pitfalls, and how you can (hopefully) implement it in your low-level gaming tool of choice, if needed.

    I learned all this (the second time) as part of adding fast/stable collision resolution to Puffin, a fast, lightweight 2D game engine built on top of MonoGame.

    AABB, Collision Detection, and Collision Resolution

    Some quick definitions to start:

    • AABB (Axis Aligned Bounding Boxes) means two non-rotated boxes, that are aligned on one axis. In 2D, everything on the same "layer" or "z" is on the same axis; in 3D, it means your boxes are on the same plane.
    • Collsiion detection means detecting if there is a collision (two AABBs overlapping) or will be a collision (eg. in 0.3s these two will start colliding).
    • Collision resolution means resolving the collision. Broadly, there are two approaches to this: prevention or pre-collision resolution (stop just at the onset of collision when the two AABBs touch), and post-collision resolution (once two AABBs overlap, move them backward in time until they no longer overlap).

    My approach to AABB uses pre-collision resolution, because it tends to be less complex and more stable.

    Pros and Cons of AABB

    Why should you use AABB collision resolution? There are many other options, such as collision points, sphere/line collsion algorithms, polygons, etc.

    The strengths of AABB include:

    • It works well in most cases. Most games can do well enough with just bounding boxes on their entities.
    • It's relatively simple to code (math-wise), because it's just boxes.
    • It's quite cheap computationally (eg. doesn't have an expensive square-root calculation, unlike spherical checks)

    However, it includes some drawbacks:

    • It doesn't work with rotated boxes
    • It doesn't work well with non-box shapes
    • It requires extra work for it to work well with multi-entity collisions
    • It's succeptible to "tunnelling" (high-speed objects move through solid objects because of their velocity)

    If you can live with those limitations, I recommend AABB, primarily because it is computationally cheap (works well with a high number of colliding entities).

    Collision Resolution is Complex, like Physics

    While AABB collision resolution is relatively easier to code, it doesn't mean it's easy to code. Many game frameworks don't include collision resolution, because this is part of the physics engine.

    Read that again: it's often part of the physics engine. Physics engines are notoriously difficult to get right, and require lots of fiddling and corner-case evaluation. Even high-quality physics engines have limitations, such as tunneling.

    It took me around 10 hours to discover all the caveats and get this to work right. And it works well, including with multi-entity collisions. Test thoroughly.

    That said, my implementation includes a few bonus features:

    • It's resistant to collision tunneling (but not impervious)
    • It works with multiple objects colliding at the same time
    • It allows an object to optionally "collide and slide" along the object it collides with

    With that out of the way, let's dive into the actual theory of how to make a stable AABB resolution, and then some code.

    High-Level Description of AABB

    AABB collision resolution works by looking at the X and Y component resolutions of your velocity. Simply put:

    • Consider the intended destination of your moving entity (where it will be after updating its position, not where it is now)
    • Look at the distance dx to travel before we collide on the X-axis and dy for the Y-axis
    • Divide these by your component X-velocity and Y-velocity respectively (vx and vy) to figure out how long before each axis collision takes place (tx and ty)
    • Resolve the collision on the axis that collides first

    AABB sweep test with velocity

    This excellent diagram (credit: LaroLaro on GameDev.SE) shows a moving object (A) that will collide with a second object (B). Based on the component velocities, you can see from the projected A box that the faster collision will be on the Y-axis first.

    Because collision resolution takes place on a single axis at a time, you may end up having to resolve the same collision multiple times to get a stable resolution. I find that running the collision resolution twice suffices.

    And Now, the Code

    Below, I discuss some pseudocode that's almost the same as the actual (C#) code from Puffin. The same code can apply to any programming language or framework.

    One unorthodox implementation detail I used: when each entity moves, I make a note of their "intended" destination X/Y. If that location would cause it to collide inside an object, I instead update it so it stops just at the point of collision. In my pseudocode below, you'll see this as intendedX and intendedY.

    For every collidable entity, you're going to compare it to every other collidable entity. Since we're using AABBs, this is pretty simple: just compare the coordinates plus intended movement of the moving entity, against the entity that isn't moving:

    private static bool isAabbCollision(float x1, float y1, int w1, int h1, float x2, float y2, int w2, int h2)
        // Adapted from https://tutorialedge.net/gamedev/aabb-collision-detection-tutorial/
        return x1 < x2 + w2 &&
            x1 + w1 > x2 &&
            y1 < y2 + h2 &&
            y1 + h1 > y2;

    You simply call this with e1.x + e1.velocity.x, e1.y + e1.velocity.y, e1.width, e1.height, e2.x, e2.y, e2.width, e2.height and it will return if they collide or not.

    However, to stop at the point of collision, we need to consider our entity's velocity: if it's moving right, then the distance to collide on the X-axis is the right edge of e1 compared to the left-edge of e2. If it's moving left, then vice-versa (left edge of e1 vs. the right edge of e2). The same thing applies when we resolve on the Y-axis.

    // Assuming we have two AABBs, what's the actual distance between them?
    // eg. if `e1` is on the left of `e2`, we want `dx` to be `e2.left - e1.right`.
    private static (float, float) CalculateAabbDistanceTo(Entity e1, Entity e2)
        float dx = 0;
        float dy = 0;
        if (e1.X < e2.X)
            dx = e2.X - (e1.X + e1.Width);
        else if (e1.X > e2.X)
            dx = e1.X - (e2.X + e2.Width);
        if (e1.Y < e2.Y)
            dy = e2.Y - (e1.Y + e1.Height);
        else if (e1.Y > e2.Y)
            dy = e1.Y - (e2.Y + e2.Height);
        return (dx, dy);

    Then, for every collidable entity, if it results in an AABB collision with another collidable entity, we figure out which axis collides first, based on which one collides first time-wise:

    // Another entity occupies that space. Use separating axis theorem (SAT)
    // to see how much we can move, and then move accordingly, resolving at whichever
    // axis collides first by time (not whichever one is the smallest diff).
    (float xDistance, float yDistance) = CalculateAabbDistanceTo(entity, target);
    (float xVelocity, float yVelocity) = (entity.VelocityX, entity.VelocityY);
    float xAxisTimeToCollide = xVelocity != 0 ? Math.Abs(xDistance / xVelocity) : 0;
    float yAxisTimeToCollide = yVelocity != 0 ? Math.Abs(yDistance / yVelocity) : 0;

    Resolving collision based on collision time solves some corner-cases where an object is very close to collision on one axis, but moving much faster on the other axis (eg. a player falling off a tall building moves into it, and instead of colliding against the side, he collides with the top).

    Once we know which collision is first, it's easy to resolve if the collision is only on one axis:

    float shortestTime = 0;
    if (xVelocity != 0 && yVelocity == 0)
        // Colliison on X-axis only
        shortestTime = xAxisTimeToCollide;
        entity.IntendedMoveDeltaX = shortestTime * xVelocity;
    else if (xVelocity == 0 && yVelocity != 0)
        // Collision on Y-axis only
        shortestTime = yAxisTimeToCollide;
        entity.IntendedMoveDeltaY = shortestTime * yVelocity;

    Finally, the most complex case: what do we do if the object would collide on both X- and Y-axes? We resolve the fastest collision first:

        // Collision on X and Y axis (eg. slide up against a wall)
        shortestTime = Math.Min(Math.Abs(xAxisTimeToCollide), Math.Abs(yAxisTimeToCollide));
        entity.IntendedMoveDeltaX = shortestTime * xVelocity;
        entity.IntendedMoveDeltaY = shortestTime * yVelocity;

    Easy! If it would take 0.1s to collide on the X-axis, and 0.2 on the Y-axis, we increment the entity's X and Y by their velocity times 0.1 (the faster collision time).

    Finally, for stable resolutions, make sure you run the collision resolution twice per update. Since MonoGame-based frameworks give you the update time, simply run the update twice, with half of the elapsed time, each update:

    var halfElapsed = TimeSpan.FromMilliseconds(elapsed.TotalMilliseconds / 2);
    // Resolve collisions twice to stabilize multi-collisions.
    this.ProcessMovement(halfElapsed, entity);
    this.ProcessMovement(halfElapsed, entity);

    That's it, you're done!

    Slide on Collide

    With basic collision resolution out of the way, you might ask "how do I slide up against the target object instead of simply stopping abruptly?"

    The answer to that caused about 50% of my development time. The answer is "you collide as usual but then move on the other axis as much as is reasonable," where reasonable means "don't move so much you collide with something else." In this case, don't slide if doing so would land you in another AABB collision.

    Another complication I can't explain well is my need to refer to the "old" intended X/Y distances; I'm not 100% sure at this moment why I needed those, but those are needed for a proper resolution.

    Some code:

        if (entity.SlideOnCollide)
            // Setting oldIntendedX/oldIntendedY might put us directly inside another solid thing.
            // No worries, we resolve collisions twice, so the second iteration will catch it.
            // Resolved collision on the X-axis first
            if (shortestTime == xAxisTimeToCollide)
                // Slide vertically
                entity.IntendedMoveDeltaX = 0;
                // If we're in a corner, don't resolve incorrectly; move only if we're clear on the Y-axis.
                // Fixes a bug where you  move a lot in the corner (left/right/left/right) and suddenly go through the wall. 
                if (!isAabbCollision(entity.X, entity.Y + oldIntendedY, entity.Width, entity.Height,
                    collideAgainst.X, collideAgainst.Y, target.Width, target.Height))
                        entity.IntendedMoveDeltaY = oldIntendedY;
            // Resolved collision on the Y-axis first
            if (shortestTime == yAxisTimeToCollide)
                // Slide horizontally
                entity.IntendedMoveDeltaY = 0;
                // If we're in a corner, don't resolve incorrectly; move only if we're clear on the X-axis.
                // Fixes a bug where you  move a lot in the corner (left/right/left/right) and suddenly go through the wall.
                if (!isAabbCollision(entity.X + oldIntendedX, entity.Y, entity.Width, entity.Height,
                    collideAgainst.X, collideAgainst.Y, target.Width, target.Height))
                        entity.IntendedMoveDeltaX = oldIntendedX;

    Wrapping it All Up

    That concludes a somewhat whirlwind tour of AABB collision detection. I hope you came out of it understanding the pros and cons, how to assess when you need it or not, and enough pseudo-code to actually get it working for you.

    I would love to hear from you! If you have any feedback, please drop me a message/tweet on Twitter.

  • Integrating Religion Into Games

  • Game Development · 2019-10-30 · nightblade
  • screenshot of a verse of Qur'an in Eman Quest

    I recently engaged in a conversation with someone about religion and games; specifically, about me showing, explaining, and playing the audio of verses of the Qur'an in my games. This discussion lead me to think about, question, evaluate, and eventually understand (at a deeper level) why I do this, and why I included that section in that game.

    To summarize: I think the use of religion (specifically, elements of Islam and Muslim culture) in games is essential for many reasons, including breaking stereotypes and building a deeper understanding of what Islam's canonical texts really mean. In fact, the word "deen" in "Deen Games" means religion, but it also means "a way of life."

    Why Integrate Religion Into Games?

    So, the core issue: why bother mixing religion with games? Certainly, this is not something people ask for (barring a very narrow definition of "game" and focusing on educational aspects, usually in the form of quizzes).

    Some thoughts that come to mind: - Religion, in general, is something that is often looked down upon as "backward," "violent," and not useful. - Religion in games, is seen as almost a taboo; something people just don't do. - In the past, some people have made thinly-veiled religious propaganda as games

    In particular, if you look at the demographic of Muslims, it paints a similarly bleak picture: - Muslims are often portrayed as terrorists, oppressors, and backward in media - Games are no better - Muslims commonly appear as the antagonists in first-person shooters, supplanting Nazis

    Knowing all this, a not-so-obvious solution becomes obvious: why not use games to counter the negativity and stereotypes around religion and Islam in particular? Games reach around the world, but can uniquely engage players interactively; can create fantsatic scenarios that don't exist outside; and can cast players in the role of those they wouldn't normally choose to be. Games can show an alternative view, and potentially build empathy for marginalized groups. (In fact, some ideologies already use games to achieve all these goals.)

    That brings us back to Deen Games. Our mission is not to create religious propaganda or poorly-disguised quizzes. We actively work to create fun, innovative, unique, accessible games that include Muslim culture, history, and beliefs as an integral part of the game universe.

    As one person aptly tweeted:

    If your beliefs do not bleed into your creative process on some level, they are not your beliefs. I expect a game made by Muslims to have some aspects of their beliefs even if they're not overt. Scrubbing games of every hint of a belief system to avoid offense is imo offensive.

    What we can Learn from Islamic Games

    data cube in Ali the Android which references a verse of Qur'an

    My specific interest within Islamic games is to create games that leave the player with an understanding and practical application of canonical texts of Islam through games. Because games allow us to create arbitrary fantastical scenarios and situations (fantasy and sci-fi in particular), this provides us a rich, fertile ground for creative expression and building a real understanding.

    Creating Islamic games with visibly Muslim characters not only breaks stereotypes, but it also normalizes us in popular culture, contrary to how we're often portrayed in media.

    It also confers an additional benefit: it allows us to easily break the common tropes/stereotypes of games. For example, a fantasy game like Eman Quest might contain slimes, bats, sentient rocks; but also jinns and other creatures/elements drawn from our Islamic theology and history.

    It's a Trade-Off Though

    Like choosing a pixel-art or low-poly aesthetic to your game, choosing to include Islamic elements comes with some down-sides. I expect the benefits to (greatly) overwhelm the downsides, which include:

    • This can break immersion. In particular, the Islamic content must be well-integrated into world lore.
    • It doesn't appeal to everyone. A rather vocal group exists decrying religion in games. Even Nintendo has a history of removing religious elements from various games.
    • It takes practice to integrate religious identity and iconography and beliefs in games - especially if you don't want a very superficial integration.
    • Not every religious quote, source, message, symbol, etc. can or should be integrated into games.

    Closing Thoughts

    Thanks for taking the time to read this! I highly encourage you to drop me some comments on Twitter and let me know your thoughts.

    Or, if you feel up to it, why not include a visibly Islamic element/character/outfit in your next game? I would be happy to work with you on this to define something you feel happy about including in your games.

  • Eman Quest Retrospective

  • Devlog · Godot · 2019-08-22 · nightblade
  • screenshot of the protagonist in a cave map

    Welcome to the rather large retrospective on Eman Quest. Eman Quest, if you haven't heard about it, is a "procedurally-generated mini-RPG with memory mechanics." You can try the full game, for free, on Itch.

    This retrospective covers two parts: first, the overall idea (what did I plan to achieve? What did I actually achieve? Reflections), and the key lessons learned (mostly specific to Godot).

    The Overall Idea: A Procedurally-Generated RPG

    I really like procedural content generation (in general), although it's deceptively difficult to implement correctly (corner cases really get you). I always wanted to make a "procedurally-generated Chrono Trigger-like RPG," although that's a huge undertaking; Eman Quest was the first step: creating a small, "lightweight" or "mini" RPG.

    What, exactly, did I include in Eman Quest?

    • A fixed story about life, faith, and family
    • Seven different areas; these are three geographies (forest, cave, and dungeon) each with multiple biomes (frost forest, death forest, desert dungeon, etc.)
    • A unique world: each time you play a new game, it picks three out of seven biomes, and generates the required maps for each.
    • Unique enemies, two per area, statically-generated and balanced with careful trade-offs (eg. glass cannon, tank), and a unique boss per biome.
    • Procedurally-generated equipment (weapons, armour), including their stats
    • A battle system that relies on not puzzle/thinking or skills/reflexes, but memory: remember the selected tiles, pick them to get action points, chain them to get tech points, and then use them for your turn.
    • Two fixed skills in battle
    • A progression system with experience, levels, and stats points you can distribute to your liking (respeccing is free).

    one round of battle

    I didn't include many things in Eman Quest; specifically, I analyzed Bastion, how they cut corners to cut down on the amount of content they needed to complete the game, and applied it to my game. Specificially:

    • I scrapped procedural story-, world-, event-, and character-creation, and wrote a fixed story instead
    • I scrapped the world map, because it adds little or no value
    • I didn't incude any NPCs or shops, because those require a lot of art/coding. (You always find better equipment in chests than what you're using.)
    • I cut a few things in content: avatars, some biome variants (crystal caves), variant bosses, and unique final-boss skills.

    What Went Well

    Overall, I am very happy with the result, and thankful that I could finish this project, although it doesn't come quite close to my initial vision (due to scope cuts and resource/time constraints).

    Things I really like about Eman Quest:

    • It "feels" like a procedurally-generated RPG.
    • It's balanced (monsters seem quite distinct/different to fight) even though it's on the easy side
    • Quite a few people completed the game
    • The memory mechanics received some compliments (more on the design below)
    • I shipped. Especially considering I tend to abandon long-running projects, this is especially important to me
    • I polished the game considerably, including audio (background and sound-effects)
    • I received some fan-art, and several questions about my protagonist; which prompted me to create an elaborate background, and a character representing many minorities: a strong woman, a Muslim, and an African
    • I represented Muslims and Islam positively, and communicated one of our values (good treatment of parents)
    • I learned a lot about game accessibility, and added a few accessibility options into my game

    Below, you can see the fan-art of the protagonist, Aisha.

    As my first full Godot project, I'm not really proud of the code quality; as I joked on my Discord server, code quality decreases as you get closer to production!

    Memory Mechanics

    memory mechanics screenshot

    Aside from the technical challenge of creating a procedural RPG, I challenged myself to create a fun battle mechanic based on memory instead of reflexes or puzzles. I also received lots of good feedback about this from users, who praised the memory mechanic as interesting.

    The core mechanic works simply: a 5x5 grid appears, some squares highlight for a fractional second, and then disappear. You need to click on those highlighted tiles to accumulate "action points," which you can use for different actions (attack costs 2, critical costs 3, and defend/heal costs 1).

    In initial prototypes, I experimented with requiring players to pick both energy (action poinst) and actions they wanted to play. This proved to be quite "stressful," because you have a fractional second to look for both required energy tiles and action tiles; midway through development, I streamlined it into what it is now. I also tried several variations (incluiding a "simon says" type mini-game and a stream of "which of these items did you never see before"), both of which didn't seem fun enough to include in the final.

    I also found that battles become somewhat rote and mechanical/deterministic after a while: you pick the five specified tiles, then pick crit, attack, and repeat, healing as necessary. To change it up, and to reward skillful play, I added techniques/skills and technical points.

    Players who pick three or more tiles correctly in a row (with no mistakes) acquire tech points. If you select all five tiles correctly, you get a total of three tech points. You can save these up and use either five or seven for stun/vampire skills respectively. This adds an element of strategy and non-determinism. Tech poinst also persist across battles, adding another dimension of planning.

    What Didn't Go Well + Key Insights

    I initially planned one month to complete the project; it ended up taking around nine months. Why? Many reasons, including:

    • I realized early on that different forests just didn't cut it, and needed both variation (styles of forests) and map types (cave, dungeon, etc.)
    • Creating art was not my strong-suite, and I needed three distinct tilesets, each with two unique variations
    • Finding, drawing and animating 14 monsters, and their walk cycles, took a lot of effort; even though I found many of the base sprites and received lots of help on the art side.
    • I added the story about five months in, and required implementing an entire message dialog system, key-item system, final-game events, and lots of unexpected things.
    • Technical struggles with, and crashes in, Godot (more on that below).

    I ran into several difficulties along the way. These include:

    • Learning Godot's API, which seemed quite foreign to me (anything is a scene and can contain sub-scenes). Also, Godot is quite a wide framework, and it takes time to discover/learn/use things (like UI components).
    • Learning about Godot's automatic garbage-collection (free and queue_free) and how they destroy all objects when changing scenes. This caused me to rewrite my early version to completely separate data about maps (tiles, treasure, etc.) from the visual presentation of those, which got GCed.
    • I wrote garbage, prototype-quality code throughout. This lead to numerous bugs (some difficult to diagnose and fix), and a cycle of "fix something but break something else" late in development. You can see tweets with some bugs, including a few hilarious ones, here.
    • I didn't write any unit or integration tests. This meant I could break things without noticing for days/weeks. I remedied this quite late in development by using GUT (Godot Unit Testing).
    • Lack of CI. Unlike other, C#-based projects, I couldn't get Travis to run my tests; so if I forgot to run tests, broken things stay unnoticed.
    • The final game crashed a lot, which caused a lot of stress and resulted in a lot of lessons; so I wrote a section just on that.
    • Close to the end of development, I lost my GPU on my main development machine, and my main Windows installed contained drivers that didn't run with Godot 3.0.6. I ended up switching to Linux (which included up-to-date drivers), but lost of a lot of time; I also upgraded Godot to 3.1.1, which initially showed a problem with cave maps running at 2-3FPS, but works fine on Linux.
    • Serialization for saving games. As the Godot docs on saving games suggests, you need to manually serialize all your (nested, hierarchial) data structures into JSON. This requires a lot of boilerplate, and it's very tedious (you may miss a field and not realize unless you test thoroughly).

    The Game Crashed on Release

    When I released the game, it crashed. A lot. This undoubtedly resulted in a terrible first-impression, although I received several supportive comments such as "it crashed once and then I reloaded and it was fine" or "it keeps crashing after battles but I completed the game."

    Why did it crash, why didn't I notice, and how did I fix the crashes? Well, I'm glad you asked.

    Godot runs the game in the editor, somewhat differently from when you export the final platform-specific release. This includes differences such as caring about case-sensitivity (which the Editor doesn't, on Windows), and - critically - crashing when you do Bad Things (like waiting for an event and disposing the scene before it comes back).

    I always tested Eman Quest from within the editor, so I never noticed (or cared to notice?) the errors. The game also crashed on Linux and (troublingly) MacOS (which I don't have hardware to test with), but not on Windows, so I failed to catch it in my pre-release testing.

    In the end, I upgraded Godot from 3.0.6 to 3.1.1 (someone commented that it didn't work with their graphics card, and 3.1.1 works with OpenGLES 3.1 and earlier), and the crashes disappeared. I also:

    • Tested on Linux (after switching to Linux) and found I could reproduce, and fix, one crash
    • Fixed sporadically-appearing errors that Godot printed out, which fixed at least one crash
    • Received great offers of help from people on Twitter, specifically running MacOS, who found workflows in-game that reproduced the crash, and verified after I fixed them.

    Action Items

    I learned several key lessons out of this. Learning about Godot itself proved invaluable; beyond that:

    • Write clean code. (Clean meaning, as good as you can make it.) This results in less bugs and less painful troubleshooting later on when things turn bad.
    • Export and test your project often. This catches bugs, crashes, and all kinds of badness that you don't want players to find first.
    • Fix all runtime errors, and any errors/warnings Godot generates. These often foreshadow crashes when the game runs (sometimes, only on select platforms - Windows seems fairly resilient)
    • Unit test everything. Unit testing is cheap, runs quickly, and pays for itself in spades; you can quickly catch issues without manually retesting everything.
    • Set up a continuous integration pipeline to run your tests. With GitHub and Travis, you can check in code, and get an email whenever you broke some tests (but didn't notice by running them manually). That can be invaluable to avoid breaking things that work.
    • The Godot community is awesome. Ask on Discord, open a GitHub issue, post a tweet - there are experienced developers who will get back to you, promptly, with solid solutions. Just be careful what you ask for! (I upgraded to Godot 3.1.1 and needed to redo all my UI to fix a tiny bug with one slider.)
    • Always update to the latest version of Godot. It really works better, and often clears up hard-to-fix bugs.
    • Make friends on Twitter. You never know who will like, follow, comment, download your game, and - critically - help often comes from those who we least expect it from.

    Finally, I want to personally thank everyone who helped me with the project - you know who you are. Without your help, coaching, support, and mentorship, I would never actually finished the game.

  • Capture In-Game Screenshots in Godot

  • Other · 2019-06-11 · nightblade
  • Tags: Godot

    Animation of a game saving, with a screenshot appearing on the save screen

    Godot allows you to capture in-game screen-shots, without the use of any plugins/addons. However, I couldn't find a complete, step-by-step guide to do this, without relying on any specific nodes being instantiated in your scene.

    You can follow these steps. I tested these on Godot 3.0.6. Some of the code originated in a thread that mentioned that "you need this prior to Godot 3.1," but through testing, I eventually removed all unnecessary code. I can confirm that this sample works, because I implemented it in Eman Quest.

    Saving Screenshots

    Godot provides a rather straight-forward API for saving a screenshot:

    var screenshot_path = "user://screenshot-test.png"
    # Retrieve the captured image
    var image = get_tree().get_root().get_texture().get_data()
    # Flip it on the y-axis (because it's flipped)

    This saves a screenshot into a file called screenshot-test.png, under the user space; on Windows, that's something like C:\Users\CURRENTLY_LOGGED_IN_USER\AppData\Roaming\Godot\app_userdata\Eman Quest\screenshot-save0.png, where CURRENTLY_LOGGED_IN_USER is your user name (eg. nightblade).

    Loading Screenshots

    Loading screenshots proves more complicated:

    • To load the data, you can use image.load_png_from_buffer, but it takes a PoolByteArray - not something you can load with a call to preload("res://..."). This requires using the File API.
    • To set the texture onto a sprite, you cannot simply assign it to an Image instance; instead, you need to create and initialize an ImageTexture instance.

    After we sort through these issues, we end up with code like this:

    var file = File.new()
    file.open("user://screenshot-test.png", File.READ)
    var buffer = file.get_buffer(file.get_len())
    var image = Image.new()
    var image_texture = ImageTexture.new()
    sprite.texture = image_texture

    This loads the PNG file screenshot-test.png from the user-space into an Image, wraps it into an ImageTexture, and assigns it to some Sprite instance

    Crop and Scale

    Since we loaded our PNG into a Sprite, we can execute other operations on it:

    • You can set the region_rect properties (through code or through the editor) to crop the image
    • You can set the scale to create a thumbnail of the image
    • You can apply effects, colourization, etc.

    This affords a lot of interesting use-cases, such as creating a thumbnail of the in-game screen per save-game (my personal use case).

  • Getting Started with Unit and Integration Testing in Godot

  • Game Development · 2019-05-13 · nightblade
  • screenshot with code on top (Image credit: Godot blog)

    Godot affords the possibility of unit and integration testing gdscript code via the GUT extension. If you're getting started with unit/integrated testing (collectively "automated testing") or Godot, and interested in what automated testing can do for you, read on.

    What is Automated Testing and Why do I Care?

    Your game contains lots of logic. When you make changes, you need to re-test that everything that worked before, still works (nothing broke). As you find and fix bugs, and get into corner-cases of your game (eg. testing the entire play-through), manually re-testing everything becomes more and more time-consuming.

    Automated testing provides a solution to these problems: what if you could write code that would test your game code? What if that code could run through all the necessary scenarios of your game in a matter of minutes, or seconds? What if you could run the tests after every single change, and be sure that you never broke anything?

    Automated testing provides exactly this benefit. You write test-code that tests your "production" code. Unit tests, which test very small levels of functionality, run in in a matter of milliseconds; integration tests, which typically test workflows (fight monster, get experience, level up, spend skill points) take longer, but allow you to automate workflows.

    Finally, any experienced developer with automated testing will agree: the mindset change you get from automated testing is unparalleled. Not only can you test every single line of code you change, making sure it works, but you can also make bigger, riskier changes, because of your safety-net of unit tests. You can actually go faster, and make bigger changes, with your safety net of automated tests.

    The Downside of Automated Testing

    Like many things, automated testing is a trade-off. Unit testing is the de-facto standard now in the software industry, so we happily accept the downsides in exchange for the wealth of benefits we receive. Regardless, here they are:

    • Automated tests take time to write. You could be writing production code instead! For very tiny projects, proof-of-concepts, prototypes, and simple applications, you may not care about testing at all.
    • Writing testable code changes your code. Your code ends up being more decoupled, more isolated, and more testable. While these are all good software engineering practices, some communities look down on this.
    • Flaky tests. Poorly-written integration tests (and sometimes unit tests) can fail on and off, sometimes for reasons not related to your change. (As you become more experienced with testing, you learn how to write less-flaky tests.)
    • Major refactoring is expensive. If you decide to change a significant amount of code - like moving a method from one place to another, or significantly changing functionality - you now need to pay the cost of updating lots of broken tests.

    Overall, the benefits heavily outweigh the costs. There's really no reason not to write automated tests!

    Unit vs. Integration Testing

    We touched earlier on the difference between unit and integration tests. Typically, unit tests test a single method or class. The hallmark of unit tests, is that they don't depend on anything external - they don't touch the database, the file-system, they don't make web-service calls, etc. Because of this, unit tests tend to be extremely stable and robust, and run in sub-millisecond time. A large suite of thousands of unit tests may run in just a few seconds.

    However, unit tests, by nature, test only a very limited set of functionality. Integration tests, in contrast, typically test more complex scenarios, and/or workflows.

    Imagine we wrote an RPG-style game. The user can battle monsters; on victory, he receives experience points, and can use those to invest in learning new skills. If we structured this as typical domain-driven classes (such as Player, Monster, etc.) we might write unit tests such as:

    • Player.attack(target) damages the target
    • Player.hurt(damage) decreases health
    • Player.hurt(damage) throws an error if damage is less than zero (programmer bug)

    These are all class/object/method-level tests. In contrast, we might write integration tests such as:

    • When a player fights an enemy and defeats it, the player receives experience points
    • When the player receives enough XP, they level up and gain skill points
    • When monsters defeat the player, the game reaches a "game over" state

    These tests, by nature, work with a larger set of code - so you can test more things. But, they can also be more fragile - if something breaks in the workflow (damage calculation broken? Level-up calculation broken? Skill-points UI broken?), the test can fail.

    Godot with Mono vs. GDscript

    Godot provides two options for unit testing, depending on whether you prefer C# or GDscript.

    If you prefer C#, create your project as a Godot + Mono project, and write as much logic as possible in the C# code. This way, you can use the "usual" set of C# testing tools (NUnit, Moq, etc.) and everything Just Works.

    If you can't, won't, or prefer GDscript, you can use the GUT extension. (GUT stands for Godot Unit Test.)

    Getting Started Testing (with Gut)

    The GUT project page on GitHub provides a detailed explanation of getting started, which I won't repeat here. Install GUT in your project, create a new scene with the Gut object, and you're ready to go.

    Instead, I'll focus on getting started testing: what do you test? Assuming you have an existing project with an extensive code-base and zero unit tests, what do you do?

    Fundamentally, you need to start doing two things:

    1) Refactor your code to make it more testable 2) Write tests for any new code

    We didn't really touch on testable code in any depth. The short version is: write simple classes with simple methods, and avoid dependencies on any external "things." For example, if your Player.damage class depends on a Globals.battle_difficulty multiplier, accept it as a passed-in value instead of referencing the Globals script. Change this:

    // player.gd
    class damage(target):
      var total_defense = target.defense * (1 + Globals.battle_difficulty)
      target.current_health -= self.strength - total_defense
    // Elsewhere

    To this:

    // player.gd
    class damage(target, battle_difficulty):
      var total_defense = target.defense * (1 + battle_difficulty)
      target.current_health -= self.strength - total_defense
    // Elsewhere
    player.damage(selected_monster, Globals.battle_difficulty)

    While the code is functionally equivalent, the latter is far more testable: you can pass in whatever value of battle_difficutly you want in your tests, even invalid values like -3! In fact, this is the kind of functionality you want to unit-test: in addition to making sure it works with a correct difficulty, you can test that it fails appropriately (eg. throws an error or returns 0) with an invalid value - something you can't easily test through the actual game.

    In Conclusion

    We covered a lot of ground in this article. In summary:

    • We talked about automated testing, and why we care about it at all
    • We looked at some of the benefits and advantages of unit-testing
    • We touched on some of the downsides, most of which are not really disadvantageous
    • We looked at unit vs. integration tests, and one way to define integration tests as workflows
    • We covered testing with C# (in brief) vs. testing GDscript with Godot
    • We looked at writing a few simple unit tests on a player class, and making code more testable

    I hope this post benefited you, and convinced you to at least try unit testing! Please let me know your feedback on Twitter - whether you decide to try testing, what you didn't understand, and what kind of topics you would like me to cover in future articles on Godot and testing.