Of all the fun, interesting, exciting game development topics I could write about, I chose: testing.
Why?
Because testing is essential to shipping your game without bugs and without hours upon hours of manual effort. As we'll see shortly, there are well-known patterns and practices you can apply; some generic to softwre testing, and some specific to game testing.
I discovered these practices after working more then a decade as a professional software developer, and applying them on and off for a decade as a hobbyist game developer. They take time, yes. They work, often and they're better (faster, more reliable, consistent) than manual testing.
Your goal with software testing is to release your game at the highest possible quality: that means NO BUGS! Everything should work the way the designers intended, without us spending tons of manual hours play-testing the same parts over and over and over (yawn).
Your overall strategy needs to include the following categories:
Without these fundamentals, be prepared for lots of bugs and poor game reviews! Above and beyond them, depending on your available time, skill, and interest, you may benefit from additional categories:
I can't reasonably explain all those topics in a suitable depth without writing an essay, but get in touch with me if you would like more information on these topics.
Regardless of how little or how much you utilize the above categories of testing, you need to properly test your game whenever you make a change. Properly means: sufficiently testing those areas that may be affected, to make sure they still work as expected.
After all, global variables are still a thing. Singletons can easily be accessed from anywhere. Highly-coupled code is common in (and out) of games. You need to make sure you didn't break stuff by mistake.
How? Simple; but it all depends on two things:
Let's start with baby steps. Whenever you make a code change:
This makes sure your code works (very granularly, at a class/scene/script method) and that you didn't break anything in an unrelated area. Good unit testing usually uncovers bugs (while you're writing the test code). They run fast (sub-second), so you can test lots of things quickly.
If you have integration/functional/system tests, they're usually slower (seconds each), but highly valuable, since they're like a mini version of your game in some scenario/situation. Run those as part of the build, too.
After the build runs, it generates a playable binary of some sort. Against this, you want to properly test the new feature/change/fix you just added. Like, properly, including things leading up to it and things after it should have happened.
With this done, you can be reasonably sure your game works, assuming you have good coverage of unit, regression, and system tests. You may want to run some exploratory tests: mess around and see if you can find anything or break anything.
Your game is done, hurray! Congratulations! Now, the fun part: testing your game before you ship it. Since you have a robust build system in place, take the binaries from a passing build, and test it.
The best way to know if your game is likely ready, is:
Repeat this until you can complete the game without finding anything to fix.
Also, when you find a bug, you don't need to re-test everything; you can re-test just the affected area (e.g. inventory management) and any other areas that might be affected because of shared/common/global code (e.g. item crafting).
If you write tests for balancing (make sure monsters, or skills, or levels, etc. are balanced with respect to each other), performance (make sure a certain part of the game runs fast enough), or crash (exercise a workflow and check it doesn't crash) testing, you can run them as little or as much as you like.
I recommend running them at least weekly or per-sprint, to make sure nothing broke unexpectedly. If you have a good CI system and don't mind longer test runs, you can run them as part of the CI.
Testing is expensive! It takes time and effort! Write tests, debug tests, update tests! Anyone who tried this process will likely tell you it's worth it. The pay-off of pressing a button or pushing a commit, and having confidence that your game works, in every way you tested, is well worth the effort.
Don't be daunted. Take baby steps. Practice. Ask questions. Try to add a bit more to each new project you start, so you can eventually master all these skills. They take time.
If you ever want a second opinion or sounding board, or have questions or comments, feel free to reach out to me on Twitter or Discord.
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.
Some quick definitions to start:
My approach to AABB uses pre-collision resolution, because it tends to be less complex and more stable.
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:
However, it includes some drawbacks:
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).
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:
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.
AABB collision resolution works by looking at the X and Y component resolutions of your velocity. Simply put:
dx
to travel before we collide on the X-axis and dy
for the Y-axisvx
and vy
) to figure out how long before each axis collision takes place (tx
and ty
)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.
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:
else
{
// 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!
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;
}
}
}
}
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.
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."
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.
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.
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:
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.
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).
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?
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:
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:
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!
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.
I initially planned one month to complete the project; it ended up taking around nine months. Why? Many reasons, including:
I ran into several difficulties along the way. These include:
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.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:
I learned several key lessons out of this. Learning about Godot itself proved invaluable; beyond that:
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.
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.
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)
image.flip_y()
image.save_png(screenshot_path)
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 proves more complicated:
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.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())
file.close()
var image = Image.new()
image.load_png_from_buffer(buffer)
var image_texture = ImageTexture.new()
image_texture.create_from_image(image)
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
Since we loaded our PNG into a Sprite
, we can execute other operations on it:
region_rect
properties (through code or through the editor) to crop the imagescale
to create a thumbnail of the imageThis affords a lot of interesting use-cases, such as creating a thumbnail of the in-game screen per save-game (my personal use case).