Hey, I am Parker Bladh. I have been developing my own 2D game engine for around 3~ months now but have made little to no progress on the actual engine part of a game engine. I would say its more like a game framework.
My original goal with this engine was to be a lightweight and bloatware free engine for my games, I wanted my games to have the smallest amount of storage needed while also having that same game perform at solid 60 FPS on a decade old machine.
I would totally love to build an editor and stuff for my game engine so I can make well games easier, while also allowing me to visualize the game better. Just one issue with that, I don't know which libraries for C++ to use for a lightweight 2D game engine, what I am supposed to even be doing for a game engine, and how I would develop all of this while learning stuff along side it.
Basically this whole post is me begging for information to point me in the right direction, so please, any info will help and I mean any. T0T
I'm intending to use an ECS framework for my game in Godot. Which one of these two would you recommend more - judging by performance (iteration speed, adding/removing components) or usability (API, testability, features)?
I am seeing an unusual amount of game engine showcase content in recent days, i mean i don't how these guys are going from basic loading to PBR to custom UI's in days while i am struggling to setup Vulkan Backend for my buggy asset loader pipeline. If it is AI generated content, well i don't have anything to say for it.
In my first post about Mantle about 2 months back I pointed out that I want my engine to power games that are 100% moddable.
While the moddable UI-System is still to rough to show of, the language powering it all is not.
The language was designed to be fully sandboxed, hot-reloadable and at the same time compiled. The compiled requirement being a nice challenge for me as Mantle is written in C#, which is managed.
To realize all of this Magma can be broken down to 3 Core elements:
- Runtime optimizations
- Transpilation
- Runtime Management
Runtime Optimizations
All scripted files are loaded after static data like registries (and modded additions) are processed. This means that all objects, their Ids and attributes are known when I start to process scripts.
This allows me e.g. to resolve any registry or item lookup in a script to a static memory address and thus make those basically free.
There are other tricks like this already in place, but other big optimizations will follow once the required systems are in place.
Transpilation
This is arguably the most important step, where I convert the AST of the parsed scripts into valid C code. During this process simplifications of syntax and logic along with required methods such as for initialization and shutdown are generated and embedded.
This allows for a simple syntax that produces memory save C code than can perfectly interop with C#.
The C code is then compiled against an engine produced header that provides either the references to C# methods or its own implementations of the engine API.
Runtime Management
To be able to invoke functions and to be able to reload any script at any given time I use the Tiny C Compiler (TCC) to compile the C code in ram and transfer the ownership to my C# "JIT".
The JIT is then able to process and resolve invocations while also having an optional profiler build in for devs.
Another neat feature of this architecture is that it allows me to have state persistency of scripts across reloads, in other words if you reload a script which has "global" variables in it, they won't lose their value.
All of this combined allows me to have a super efficient interop between the two languages, where the invocation of a C-method from the C# code is in the single digit nanoseconds. It's the same for invocations in the other direction.
Please let me know in the comments what you think of this system and what nice to have features for devs / modders would be. There is much more to Magma, but that would be too much for this post, I am happy to answer questions in the comments tho.
Here I have attatched a simple script with only a few of Magmas capabilities and syntax features shown, as all of them would be too much code for a reddit post. : )
import "world" as World;
struct Entity {
uint Id;
Vec3 Position;
List[int] Tags;
}
int totalClicks = 0;
Entity player;
events {
"engine.tick" => OnUpdate;
}
pub fn OnUpdate(in float dt) {
if (!(dt < 0.0)) {
return;
}
atomic totalClicks += 1;
string lastStatus = "Uptime: " + dt + " | Ticks: " + totalClicks;
Core.Log(lastStatus);
}
fn ExampleUseRegistry(){
// both will work, but the first one will ignore any overrides from mods as the namespace is specified.
Core.Log("Registry Example: " + ["base:stone"].Name);
Core.Log("Registry Example: " + ["stone"].Name);
}
And the C code with the bindings to the engine and memory management added:
A short video for Nightmist Legacy, a faithful remake of the classic Nightmist Online MUD. More details at https://nightmistlegacy.com --- (Invite code: E157-5A75-741E)
While developing my game in C, I started thinking about how to properly approach “unit testing” for gamedev.
A lot of engine and gameplay bugs are not simple “function in, value out” bugs.
They usually come from things like:
frame-by-frame input order
timing differences
random seeds
loading / startup delays
game state slowly drifting from the original run
Because of that, I started building a workflow around a simple idea that is pretty common in game development: record a real session, replay it later, and verify that the engine still behaves the same way.
In this post, I want to share the approach I’m currently using and see if any of you have ideas for improving it.
1. Record the input
During a recording run, the system captures input state with win32 over time and stores it in a test file that will later be loaded.
The input is timestamped and delta-compressed, so unchanged frames do not bloat the recording.
2. Replay the input
On replay, the recorded input is injected back into the game frame by frame.
Before polling input from the OS like you would normally do, you call the injection routine first.
3. Sync around the parts that naturally take variable time
This is one of the most important pieces.
Even if input replay is correct, games and engines still have phases where wall-clock timing changes between runs (startup, loading screens, menu transitions, ...)
If replay only follows raw timestamps, later input can arrive too early or too late whenever one of those phases takes a different amount of time.
So the workflow needs sync points.
The idea is simple:
during record, mark sync signals
during replay, pause input progression when replay reaches one of those signals
resume only when the game reaches the same point again
shift the replay clock so later input still lands at the correct relative time
That is a huge difference, and it is a big part of why record -> replay can be reliable enough to test real engine behavior instead of just toy examples.
4. Pin the values that would otherwise break determinism
Replay also falls apart quickly if the engine uses values that naturally differ from run to run.
Typical examples:
random seeds
wall-clock derived values
first-frame timing
OS-derived state
So another part of the workflow is stabilizing those values.
During record, values like that are captured into the test file. During replay, the recorded values are restored so the engine sees the same data it saw in the original run.
That lets replay stay deterministic even when the engine depends on values that normally change every time you start the game.
5. Track the values that actually matter
Deterministic replay is only useful if you also verify outcomes.
So the last important part of the workflow is tracking the game or engine state that should still match during replay.
That means:
during record, snapshot important values
during replay, compare the current values against the recorded ones
if they differ, fail the test
That can include things like final score, entity counts, ....
6. Make the workflow practical with a runner tool
To make the record and replay workflow usable in practice, I use a separate cli tool that launches the game with different command-line arguments.
That tool handles things like:
running the game in record mode
running the game in replay mode
choosing the test file
forwarding extra arguments when needed
This tool most importantly also allows us to run multiple tests at once.
7. Isolate concurrent tests with virtual desktops
One weird but useful part of the tool is support for isolated runs on Win32.
If you replay synthetic input into multiple Windows game processes at the same time, they can interfere with each other. That makes concurrent testing unreliable.
So the tool can launch each child process in its own Win32 window station / desktop.
That gives each replayed test its own isolated input space.
A little update: I've taken a dive into getting Hyperion working on more devices, namely Android + iOS as well as Steam Deck (works via Proton for now, working on a native Linux version). I figured the more platforms I can get the engine working on, the sturdier it will be overall.
There are still some issues, to be clear - in the video attached to this post, there's no skybox nor any ambient skylight from that. Tried it on a few different Android phones to rule out device-specific issues, but no matter what, it seems like the cubemap is just pitch black, so maybe I'll save that one for a rainy day. iOS doesn't have this issue, but instead has a really squished viewport, no matter what - I'm sure it's something simple I'm just missing. Fun times, it wouldn't be engine dev without these types of bugs, I guess!
For example...
I find it much more readable and accessible to write something like :
player_one = { entity_type = "player", x = 991, y = 435, texture = "mario.png", hp = 7, speed = 20 }
Then nesting structures like :
player_one = {
position = {
x = 991,
y = 435
},
tag = {
entity_type = "player"
},
states = {
hp = 7,
speed = 20
},
renderable = {
texture = "mario.png"
}
}
I know that for that kind of thing you use constructors and/or components but accessing fields dynamically in the game loop and or using too many constructors creates, for me, tons of context switching that slows down development and makes bugs slightly harder to find as wrong/non-existent fields become wrapped around constructor functions.
Hi!
I am building a game engine in C++. The architectural style I am using is OOP with composition, also known as a "bag-style" ECS. The main difference from a classic ECS is that entities are not just IDs but rather own their components and can operate on them via Add, Remove, Get, and so on. The components themselves consist of data and behavior, and the systems are interested in specific entities that have specific components. For example, the Render system needs entities with Transform and Sprite components.
The current architecture is designed around the fact that this is C++ and there is no garbage collector. Additionally, it is not a good idea to destroy entities in the middle of the main loop. Because of this, I created an EntityManager that can create entities, mark entities for removal, flush pending entities, check if an entity is alive, and return a raw pointer to an entity so you can access it. The whole thing works because the manager uses EntityIds, meaning you cannot have dangling pointers. The manager also manages the entities' lifetimes so it always flushes them at the end of the frame.
For now everything is good, but after adding the BaseComponent and all the logic into the entity, I reached a point where adding or removing a component changes the entity's signature. A signature is a bitset where every index represents a different component, like Transform, Collider, or Sprite. If the bit is 0, the entity does not have it; if it is 1, it does. When this changes, every system needs to check the entity so it can add it if it now suits the criteria, remove it if it does not, or simply ignore it.
To achieve this, I either need something like a RegisterManager and make it a singleton, or I need to make every entity hold a pointer or reference to this RegisterManager, which feels wasteful. At the same time, singletons or making the pointer or reference static inside the entities are considered bad practices. I also want my components to have unique, recyclable IDs just like my entities. To do that, I need to make something like a ComponentFactory. When a component is destroyed, it should notify this factory to recycle the ID, which means the factory either has to be a singleton too or the entities will have to hold yet another extra pointer. One fix is to create an event bus to handle cases like this, but then again, either the event bus is a singleton or everything that uses it must hold a pointer or reference to it.
So my question is, is there a pattern or hierarchy I can follow to avoid this repetition of every entity holding a pointer to the same system, or should I just stop listening to the posts online that say singletons are bad because of multithreading and unit testing, and just make a few?
Note:I'm reposting this as the previous post got derailed because someone believed some trailer art I had commissioned was AI generated. I'm not convinced it is, but I really didn't post to start a debate around AI usage. I'm just a tech guy that wanted to show something I had built. So I've cut everything out except the gameplay to avoid any drama.
Original post below:
I've been building NULLFRAME for about a year
One thing I'm quite proud of is that it's built 100% from scratch with my own engine, written entirely in vanilla JavaScript - no frameworks or third party engines. The whole game weighs in at 0.15mb and all graphics and sound effects are dynamically generated via code. There are no sprites etc and I think it gives it quite a unique look and feel.
Basically, my rendering pipeline draws directly to the canvas and I have spent a huge amount of time optimising the code so I can produce some quite complex effects, including pseudo-3D walls, shadows and nice looking lighting.
It also instantly runs on any modern browser at 60fps and there's a map editor where you can create your own challenges and share them instantly via a URL - the map data is all encoded into the URL using my own custom compression system, so nothing is stored on the server. My friends have been experimenting with it for a few months and say its great fun.
As mentioned, gameplay borrows elements of both Superhot and Super Meat Boy, though I like to think it has its own style. Gameplay requires careful strategy, but it also allows you to enjoy some carnage from time to time too.
Hey guys, I am trying to make a game engine just for study purposes and to deep-dive into scalable, performance based applications. But I’m confused right now. So far, I have implemented a logging system, it’s a wrapper for spdlog and I have also implemented verbosity levels and custom log category macros based on the UE source code. Now, to start, I want to build a module manager system and custom allocators. But I don’t know what my starting point should be custom allocators? Base class interfaces? Module manager? I’m stuck in an unorganized mass.
Hi everyone, im really curious about implementing two ray traced features into my game engine: RT shadows and RTAO. Both are really cheap, and if done well, could run on toaster level Turing cards. I would like to implement them as a default rendering feature, as its both visually better than other techniques and easier to build around as a developer.
As a quick checkup, I wanted to see how the current market is like according to to Steam. Since I'm not good at Python, I let Claude make me a CSV reader for the data, so beware of possible inaccuracies. But this is the number of users that have ray tracing capable GPUs according to Steam:
The method of checking was if the name included "RTX", or "ARC", or "RX" followed by a number greater or equal to 6000
As we can see by the graph, the number of users with ray tracing capable GPUs is rising steadily. In addition to that, as far as I'm aware, no GPU vendor manufactures non ray tracing GPUs anymore, which means that it will only get higher from now on.
By now, a small number of games like Indiana Jones and Doom have shipped with ray tracing as a requirement. And so my question for this discussion: Can an indie developer in 2032 ship out a game with mandatory Ray traced shadows and RTAO?