I've been working on network services since I was 15 years old. I started doing some HTML pages, than simple web sites with a single form for an order or a feedback, then more complicated web sites backed by some CMS like Joomla, after I switched to web frameworks and started making fully customized services, then just some network services which communicate over different protocols (sometimes even custom internal ones). However, initially I wanted to do games as probably most of the programmers of that epoch. I liked playing video games, and programming seemed like an ultimate game. Unfortunately, I couldn't get a chance go get a job in the field, so took the way I took. However, the desire to work with graphics never left me. This post is about me creating a tribute to Space Invaders.
I had a few attempts to the field in the past. I tried to go from bottom to top, trying to create my own game engine. I tried to go from top to bottom, taking Unreal Engine 4. I tried to start from somewhere in the middle, taking OGRE. However, I couldn't last for long time with any of them for various reasons. Feel free skipping the details until here, because those details have more emotions rather than technical value.
Creating own 3D game engine is tough task for a regular student programmer. However, I had a lot of the enthusiasm and a great (in my opinion) idea. I knew that I needed OpenGL for graphics, OpenAL for sound, GLUT to create the window and handle the input devices. I'm locked and loaded, ready to engage. However, I mostly worked on how to render the scene rather than on the game itself. It was a pretty dark age, I had did something like glEnable(GL_LIGHT1);
. Eventually, I found that I spend a lot of time just to render some simple things and changed the tactics.
For some reason, I remember that OGRE was defined as a graphics framework. Now I see that the proper definition of it is Graphics Rendering Engine. In fact, the abbreviation OGRE stands for Object-Oriented Graphics Rendering Engine. Anyway, with OGRE things were a bit better. The examples of the project inspired me enough to approach a game project again. However, I just couldn't understand why something doesn't work within my code, but does work within an example. By that time I already had some experience in web development. In fact, I had a little success at that field. That success also made me thinking I'm mature enough to approach a game project. I was fixing websites a lot. When a web site doesn't work, it's pretty straightforward to debug it. However, when I was dealing with a game and I saw some black spikes out of a mesh, and I had no idea what's wrong. Also, I didn't want to admit some things like the necessity to compile a dependency with debug symbols to be able what's under the hood of the dependency. We don't do in Python, why would we need it in C++? Nowadays, the answer clear as a day. The outcome wasn't good enough, so I tried to find something else.
Eventually, I approached Unreal Engine 4 when it became open source and available under GNU/Linux. However, there were two issues which demotivated me:
UCLASS
or USTRUCT
was incomprehensible. In addition, as it's macro code, the debugger can't really help you to walk through it..git
directory grows too fast. The moment with Git disappointed a lot, because I rely on Git diffs a lot. Even, when I learn something I want to have a tool to spot the thing which breaks everything.There was some positive outcome, though. I created is the plugin for inverse leg kinematics. I even supported it for the newer versions of Unreal Engine 4. At certain moment I couldn't understand the changes in the API and I gave up. Eventually, the functionality became the part of the engine.
After the attempt to make a game with an engine, I realized miss something. Despite me being a competitive programmer in web development I felt like I'm far from being ready to make something else.
There is a saying: "Every programmer has to create a game, an operating system, a music player". At that moment I had a music player and failed to create a game. Therefore, I decided to approach an operating system. I hoped that it would teach me something to make games. I found the website OSDev.org which has the list of books required to create an OS. I showed the list to my CTO at that moment, who told me to take something else. He advised to start from:
It's been years since those days. I read those book and a couple more from the references. In particular, it worth to mention the series of articles What every programmer should know about memory. Eventually, all these materials helped me to understand what the capabilities of Linux, how to write code considering CPU caches, purposes of some data structures.
I came across the article Challenging projects every programmer should try. One of the challenges from the list is making a clone of Space invaders. After all these attempts, I decided that makes so much sense to make the first game as simple as possible. My first website had just 3 HTML pages with no interaction. Why did I expect to make an amazing game from the beginning? Let's make a simple game!
This is a magic moment when you are choose the available solutions on the internet to start your new project. What do I need?
As the picture is the most important when you do graphics, I started looking for some software for rendering. Based on the previous experience, I decided that I'm not ready to create my own engine. Therefore, I'm not taking graphics API again. Also, I don't want to take a monster like Unreal Engine either. What do I take?
In web development things are clear. There are the following sets:
Therefore, the relations between the software you take and possible application look this way
Software | Possible application |
---|---|
TCP socket library | Network service, HTTP service, Website, Blog |
HTTP server implementation | HTTP service, Website, Blog |
Web framework | Website, Blog |
Blog engine | Blog |
The relations in game development was totally unclear to me. Intuitively I saw the following kinds of applications:
However, the gap between 3D graphical application and a game seemed huge. Because, we might have a 3D graphical application, but with sound and other facilities. Also, I considered to create 2D game. Given that, I expected to have something which would allow me to make 2D graphics, but also allows me fairly simply transfer to 3D if feel like that. In addition, I remember an old thread somewhere with the message, saying that making games with DirectX is much easier than with OpenGL. Given all that, I had a strong that there is some kind of libraries which covers the gap.
Apparently, I've never considered DirectX, because I wanted to make a game for GNU/Linux. All in all, it's nice to have a tool to make a cross-platform application rather than for a single-platform. On the way, I did a little research about Vulkan, because I had heard that it's cross-platform. However, "hello world" in Vulkan is verbose enough to show that it's not an option. And still, Vulkan is only API.
I don't remember how, but eventually I found SDL. I mean that I knew about SDL, but I had an impression that it's a minimal 2D graphical library. However, I found this video of the talk from Steam Dev Days 2014. In that video, Ryan Gordon (@icculus) actually compared a "hello world" in SDL2 and DirectX. The video showed that it has support of everything I need: sound, user input, window creation etc. I felt like it's exactly what I need. However, I still wanted to have an ability to make 3D graphics. I found Raylib, but I read that it has some font rendering issues. I had been researching how to include 3D into SDL. A miracle happened after a couple of days! SDL3 (actually 3.2.0) had been released.
I'm still not sure about the classification, though. However, I have got an impression that the groups should be this way from the lowest level to the highest. I see the highest a level as a real-time strategy game engine as an example of a specific game genre. I brought a blog-engine as the highest level of software, but there are other kinds of websites and they have their engines. Anyway, here is the list:
Level | Hardware setup | Windows and user input | OS interaction | Graphics primitives | Model loading | Graphical user interface | Sound playing |
---|---|---|---|---|---|---|---|
Graphics API | NO | NO | NO | NO | NO | NO | NO |
Graphics toolkit | YES | YES | YES | NO | NO | NO | YES* |
3D Graphics engine | YES | YES | YES | YES | YES | NO | NO |
Game library | YES | YES | YES | YES | YES | YES | YES |
// lib.addObjectFile(b.path(sdldependencies[2] ++ "/build/build-emscripten/libSDL3mixer.a")); wasm-ld said that libSDL3mixer.a is not a WASM object. Probably I missed the platform when I was building SDLmixer.
wasm-ld: error: /home/antlord/Nest/study/sdllearn/current/dependencies/SDLmixer/build/build-emscripten/libSDL3mixer.a(musicwavpack.c.o): undefined symbol: WavpackSeekSample64 wasm-ld: error: /home/antlord/Nest/study/sdllearn/current/dependencies/SDLmixer/build/build-emscripten/libSDL3mixer.a(musicopus.c.o): undefined symbol: opseekable
found that the missed dependencies are in build/build-emscripten/external. I add everything this way
emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/libSDL3_mixer.a")); emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/external/libxmp-build/libxmp.a")); emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/external/ogg-build/libogg.a")); emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/external/opus-build/libopus.a")); emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/external/opusfile-build/libopusfile.a")); emcc.addFileArg(b.path(sdl_dependencies[2] ++ "/build/build-emscripten/external/wavpack-build/libwavpack.a"));
Nevertheless, I have to disable the unused dependencies -DSDLMIXEROPUS=OFF -DSDLMIXERWAVE=OFF -DSDLMIXERMP3=OFF
I had the option -sUSE_SDL_TTF=1
added to emcc
The errors I got are
error: undefined symbol: TTFCreateRendererTextEngine
I tried to build SDLTTF with emscripten
I tried to build with emmake ninja
I tried to disable freetype, but SDLTTF requires it. It says that HarfBuzz is for better text-shaping, PlutoSVG for emojis
path_open
Can be fixed linking libc to the object file of the game, but it leads to another error: undefined symbol errno
. I gave up on wasi and built the project for emscripten.
An exception occurs in the browser console log when I allocate some memory. I decided to go back and fix the compilation error which occurs when I build in Debug mode
/home/home/wantlord/Apps/zig-linux-x86_64-0.14.0/lib/std/debug.zig:870:24: error: no field named 'base_address' in struct 'debug.SelfInfo.Module__struct_13803' module.base_address, ^~~~~~~~~~~~ /home/home/wantlord/Apps/zig-linux-x86_64-0.14.0/lib/std/debug/SelfInfo.zig:798:27: note: struct declared here .wasi, .emscripten => struct { ^~~~~~ error: the following command failed with 1 compilation errors: /home/home/wantlord/Apps/zig-linux-x86_64-0.14.0/zig build-obj -ODebug -target wasm32-emscripten -mcpu baseline -I /home/antlord/Nest/study/sdllearn/current/dependencies/release-3.2.4/SDL-release-3.2.12/include -I /home/antlord/Nest/study/sdllearn/current/dependencies/SDL_image/include -I /home/antlord/Nest/study/sdllearn/current/dependencies/SDL_mixer/include -I /home/antlord/Nest/study/sdllearn/current/dependencies/SDL_ttf/include -I /home/antlord/Nest/study/sdllearn/current/emcc/cache/sysroot/include --dep errors --dep config -Mroot=/home/antlord/Nest/study/sdllearn/current/src/main.zig -ODebug -target wasm32-emscripten -mcpu baseline -Merrors=/home/antlord/Nest/study/sdllearn/current/libs/errors/errors.zig -Mconfig=/home/antlord/Nest/study/sdllearn/current/.zig-cache/c/273c9fa14190b77ebfd0ed345b547773/options.zig -lc --cache-dir /home/antlord/Nest/study/sdllearn/current/.zig-cache --global-cache-dir /home/antlord/.cache/zig --name sdllearn --zig-lib-dir /home/home/wantlord/Apps/zig-linux-x86_64-0.14.0/lib/ --listen=-