Tags: Methodology
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.
Tags: Godot
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).
Tags: Godot, Testing
(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.
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.
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:
Overall, the benefits heavily outweigh the costs. There's really no reason not to write automated tests!
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 targetPlayer.hurt(damage)
decreases healthPlayer.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:
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 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.)
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
player.damage(selected_monster)
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.
We covered a lot of ground in this article. In summary:
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.
Tags: Godot, Android
I recently rebuilt a small app/game using Godot. For reference, the game contained one core loop, and took about four hours to build (from scratch). I reused all of the assets from the previous version of the game (audio and graphics), which took the bulk of development time on the original.
The game built and ran flawlessly in Godot on Windows. As soon as I ran it on Android, the game briefly showed the Godot loading logo for ~100ms, then it disappeared, then reappeared/disappeared around four times (you can see the original issue I opened about on GitHub, here).
The root cause of all this was multiple resources preloading the same script. I had a scene that preloaded HelperScript.gd
and then Second.tscn
; but Second.tscn
also preloaded HelperScript.gd
, causing the crash.
I found three different ways to address this problem:
HelperScript.gd
everywhere, I simply added it to the list of auto-loaded singletons in Godot. Problem solved. (This solution also makes sense, because I needed the same common method from HelperScript.gd
.)load
instead of preload
. This makes me think preload
is bugged; calling load
succeeds without any issues. Obviously, this has performance implications - you may want to preload your entire game at startup time.HelperScript.gd
into two different scripts, and just load what I need instead.If you find your Godot game crashes on startup without explanation, maybe this is something you can investigate. (Failing that, go for a git bisect
and try to isolate the commit that broke everything.)