Writing a game engine in pure C: The Graphic Initialization
prdeving.wordpress.com
prdeving.wordpress.com
But if you yourself want to write your own game with just you or a small team of like-minded people, I would highly encourage using C++. As long as you don't have too many cooks arguing about which C++ features to throw into the pot, you can pick a subset of C++ that isn't much more complex than C (which is already more complex than most realize) and you'll get a much cleaner, safer language. C++ has saner rules for implicit type conversions, namespaces, overloading, and a cleaner notation for dynamic allocation. Those alone make it a sufficiently "better C" to be worth using in my book.
Going farther, even if you don't like "object-oriented programming", I think classes offer modularity and encapsulation features that make them worth using, even if you never once write the keyword "virtual" or use subclassing. (Fun fact: the first version of C++ did not have virtual methods!)
I like C and enjoy programming in it, which I've done for over 20 years. I've written a successful open source project in it and am writing a book that uses C as one of the implementation languages. Even so, I generally only use straight C if I'm writing a library that I want C users to be able to consume. Otherwise, I think C++ contains any number of "better C"'s within it, and it's mostly a matter of choosing which better C you want.
For a beginner that can make things more difficult: in addition to the actual thing you want to learn, you need to first become a capable curator of language features from the past 35 years.
JavaScript today has the same problem: you can’t just start writing a web app because two lines into a tutorial you’ll be barraged with “So this is actually an ES2017b.71 feature that we’re enabling using babel-ts-flooginator, therefore you also need TypeScript and that means you need to...”
C feels clunky today, but writing more code in exchange for not having to curate the language can be helpful for learning.
It makes me wonder if this can be solved technically by published curated language subsets. Racket does this by supporting a bunch of different dialects, specifically to make it easier to teach [0].
One good do something similar for other languages. Step one is probably just writing a doc that says "Here's a standard subset of C++ we call Blah. These are the features it uses and these are the ones it doesn't: ..."
Then you could add tooling so that it will warn you if you use a prohibited feature.
Of course, this just pushes the problem up a level: now a new user has to know which curated sublanguage to use. But that's arguably simpler than doing it on a per-feature basis. At least they can just order a combo instead of having to pick a la carte.
I'm biased, but I generally find the Google C++ Style Guide to be a good curation of C++: https://google.github.io/styleguide/cppguide.html
I generally stick to it in all projects I write. The only exception is that in a few cases I'll use features that are disallowed in Google C++ primarily for historical reasons, most notably exceptions: https://google.github.io/styleguide/cppguide.html#Exceptions
And if you're putting things that can throw exceptions in your ctors or dtors you're probably already writing code that is a timebomb
That to be said, even if you don't use exceptions in C++ - you must write exception safe code - e.g. as much as possible no manual "begin"/"end" but wrap things behind RAII, such that "end" is still called, in case of exception. Hence allocation through unique_ptr, shared_ptr, etc. is preferred over new/delete.
Because you never know the function you are calling whether deep down it won't throw an exception...
And some people choose to use more comprehensive documentation, like Google's C++ style guide, at the cost of imposing more restrictions that are largely in place for corporation-specific reasons that smaller personal codebases don't necessarily need to regard.
That's not a problem with javascript, it's a problem with the development culture, its schizoid relationship with the language and its obsessive need to be "cutting edge." You can, absolutely, just start writing a webapp because vanilla JS and even jQuery still work perfectly well. What you can't as easily do is find tutorials that don't assume Javascript has to be compiled from another language and pushed through a complex tech stack before you can even approach it.
Actually, this tutorial is not just about "building a game engine in C89", it's more about learning the concepts behind game engines, the language and the code is just illustrative examples.
I meant that i think is easier to understand C than other languages cause you see all the flow without weird library stuff, maybe i'm wrong, dunno :(
As i said previously, i'd never do a profesional game proyect (or even a hobby one to be sold) with C89, it takes lots of boilerplate and it feels like reinventing the wheel over and over. I'll have to code a hash map approach, a growing array (already did), a "garbage collector" in C89, things that you either don't need or have in the C++ Stdlib, so yep, you are absolutely right.
Also, i enjoy C :D
Maybe after this i could do the same with C++ macroprogramming
I have no opinion on use of C89 instead of C++, since that's just an implementation detail. Also, for your purposes, it works to your advantage, since C is basically the white-box of C++.
I say this partly in jest, but once I have wrapped all my primitives in structs C has a perfectly reasonable rule for implicit type conversions: "don't".
See https://dlthomas.github.io/using-c-types-talk/slides/slides.... (... which I should really turn into a blog post or something)
That said, can you name a few that work this way? If I've worked on such a platform, it's been a long while.
Part of the problem then is that working with any standard library functions is a huge pain. You have to manually "cast" back and forth all the time.
It's extra important, in writing this kind of C, to pick a granularity of types such that they help you make the distinctions you need without drowning you in casts. And I'm not at all sure such a granularity always exists. It worked out very well on the (greenfield) project I built this way, though.
Can you point to any resources I can use to learn?
Thanks for the suggestion! Seems like a good place to start.
Here are some reasons to choose C: - longevity. The language never changes. C++ does a good job, but not as good as C.
- portability. It runs everywhere.
- readability. Its a simpler language to read and debug.
- size. You can have the whole language memorized, and know how to write it very well.
- prefer C community over the C++.
- avoiding pandoras box. Once you allow one C++ feature, its easy to add another.
Have there been any efforts to write episode summaries/articles on the techniques he demonstrates?
Go to https://handmadehero.org/watch and scroll down the previous episodes section.
Also all the episodes aren't as long as they look, a large chunk of the time is on the QA at the end. That said it is a huge investment at this point, but definitely worth it. Some of his high-level ideas are great, and I've learned a lot.
That being said I don't know what it is but I prefer writing games in C. I experiment with higher level languages and use them for rapid prototyping. However, and maybe its simply nostalgia, I always come back to C.
And it's not even my favorite language by a long shot. For practically everything else, save for systems programming, I prefer languages like Common Lisp and (more recently) Haskell. Imperative programs are notoriously difficult to comprehend and maintain but there is something about game programming specifically that C leverages which makes it a good fit for game engine development; the mix of pointers and machine-sized types perhaps? I'm not sure. But it works really well.
My current conclusion is that we just enjoy problem solving within mental frameworks, and each language family gives unique mental frameworks that come with different challenges.
Now that I've slowed down life and am planning in years instead of weeks, I think I would absolutely enjoy (and I think my kids would too) making a new video game from scratch with SDL in C.
The only difficulty is, every time I try this, I keep running into weird issues that only I seem to be having and can't find a solution to online. For example, the last time I tried making a lemmings style game in C with SDL, I ran into weird framerate issues where for apparently no reason or pattern, it would go incredibly slow sometimes.
That network stack written in C gave us heartbleed for example. If C arrays were bounds checked by default (e.g. they carry around their size), this could've been easily avoided at no runtime cost.
How can you honestly complain about people wanting more safety in software? If it's speed ("high performance") then you're probably implementing yourself in C (e.g. Bounds checking) something that is built in either at runtime or even statically as can be done in some cases.
Modern successors to C, such as Zig, and modern successors to C++, such as Rust, are still too young to be used for serious re-implementations of the lower layers.
Rust is probably there, if under specified. D is definitely fine for writing operating systems (Question being whether to use druntime or not).
I know nothing about Zig, beyond having had a look and not being hugely impressed.
I found that when my project grew to a certain size, I started running into many cases where I needed many implementations of some base type, the kind of pattern that lends itself well to classes and simple inheritance. This pattern is totally 100% possible to roll yourself in C, but requires a bunch of boilerplate each time and I found that every time the implementation came out slightly differently, or that I had to go back and rewrite a bunch of code because I needed one more layer of flexibility than I anticipated.
I also really wish templates were in the language. Maybe not to the C++ level of flexibility, but at some point, you're going to need a dynamically allocating array or hashmap of a specific type, and your choices are either going to be *void or some macro thing that makes your eyes cross.
Also, build tools in 2019 are still kind of bad. I use CMake and try to do things "the right way", and I still run into trouble occasionally, mostly surrounding external dependencies and generation of files outside the scope of the C compiler. It's a shavable yak, but it's still a yak.
Still, the lack of mental overhead and the much faster compilation times compare favorably to C++. But if portability wasn't a goal, I'd seriously consider an alternative like D, Zig, or Nim next time.
Inheritance is an anti-pattern, you can accomplish the same with composition.
>you're going to need a dynamically allocating array or hashmap of a specific type, and your choices are either going to be void or some macro thing that makes your eyes cross.
I don't understand, why would you need a *void for a specific type? If you meant heterogeneous types, you can use a typedef union.
You can then use that simple mechanism to implement inheritance, component based design, message passing or whatever structure makes the most sense for your game design. I'm personally tend to lean towards component based for reusability and performance but that's by no means a hard-and-fast rule.
Which programs can most programmers read and understand?
mov ax, 13h
int 10h
I'm old.it really boggled the mind that it was possible to blt a sprite in rows rather than a pixel at a time, and under the hood, the computer was actually copying 4 bytes per instruction. Anyone else remember implementing the 320 row offset as the sum of two bit shifts? By the early 2000s, I seriously doubt that was any faster, but we all did it anyway for "performance".
And don't even get me started on palette hacks— redefining the 256 colors into bars of dark-to-light runs of a single colour, so that you could do smoke or halo effects by just shifting every affected pixel by one or two in either direction.
I don't know if he's around these days on HN, but a shout-out to Mark Sibly (Blitz) for bringing a lot of this stuff to the QBasicNews forum back in the day.
(I think the bit shifting and addition trick would still win over an IMUL.)
Edit: also, wasn't there a way of (ab-)using LEA to do some of this?
The site is really cool, thanks for pointing me to it!
For those missing the joke, that's the assembly language commands to switch to VGA mode from DOS. Set AX register to 13 hex and call interrupt 10. That gives us VGA graphics mode 320x200 pixels! Related: In the 1990s Michael Abrash via Dr. Dobbs Magazine, introduced the world to "Mode X" @ 320x240, which had the advantage of square pixels. That series of articles was responsible for my long-standing subscriptions to Dr Dobbs Journal.
http://www.pouet.net/prod.php?which=53816 "Puls" by Řrřola, 256b, MS-Dos
Along with this interesting explanation https://meatfighter.com/puls/ of what's happening with the "binary-search" raytracing, lattice-effect, and tricks to make it fit in 256b.
(And use VESA modes like 640x480x256 - thank Deity for DJGPP)
The goal is to have multiple implementations, in different languages and OSs. For example, right now I wrote (partial or full) support (in C) to:
PNG, windows GDI, linux X11, linux OpenGL, linux Framebuffer and Javascript Canvas
(I've just started! expect it to be incomplete, broken, faulty and buggy!)
All just to get to unchained mode (aka Mode-X) with hardware scrolling & split screen, 256 kB VRAM, VGA latch fills and VRAM to VRAM latch blitting. It was amazing to be able to set or copy four 256 color pixels by writing just a single byte.
Of course VGA latch blitting didn't make any sense on 486s, but on 8088 it could be almost 4x performance boost — 256 colors with similar CPU load as CGA's 4 colors! At least as long as you needed to blit something to an x-coordinate that's divisible by 4...
also old!
GLFW just goes from zero to OpenGL context ASAP, plus input events. With SDL I found that their drawing primitives are too limiting for my needs, and if you want to do any custom drawing at all, you need to abandon their drawing primitives entirely and just write GL codel. At that point, GLFW makes more sense.
SDL does have a few other nice things like audio support and text rendering, which you'd have to figure out separately with GLFW, but to each their own.
The project will not stick to SDL anyways, the idea is to use it as a high level API, not to build a game with SDL. I would like to use this engine for PSP also, and i'll have to take SDL out and put osLib in. problems for future me...
The alternative path for C++ is SFML, which has sensible abstractions for most of what you need.
glfw doesn't seem to have a function to draw teapots though, which should really rule it out as a serious contender.
Andre Lamoth's DOS game programming books are a good read as well, he used 16-bit Microsoft C. Sprinklings of assembler in both.
Why are we implementing a whole dynamically allocated stack for states before doing anything domain specific?
How many states could you possibly be expecting to have in a full game? 3? 12? 100? The examples of states were like the menu, action screen, and pause screen. So it sounds like very few. Drop the realloc'ing and free'ing and just statically allocate N states and be done with it. Save this complexity for something that really needs it.
Plus are you going to free the stack any time other than when you quit the app? I doubt it. The OS will free everything for you when you quit so there's no reason to waste time on that either.
The code so far looks like mostly a waste of time.
https://github.com/ensisoft/wdk/blob/master/sample/triangle....
C++ tho, but i could port it, i'll give it a glance
20 years ago, my first project was being part of 4 people team porting Metal Gear Solid from PS1 (PSX) to PC. This is when I saw how good written "C" code could be done. Even if it had only japanese comments in the code (understandable).
The pure and simple structure, where most of the functions had TCL-like interface:
int ACT_Work( int argc, sometype* argv ) { // bit of argc/argv parsing Do_Work() }
then above was exposed to a TCL-like scripting language, used for setting up level objects... We are talking about 500-600kb of memory used by the main executable + 300-400kb of 'overlay' memory where "shared, e.g. dll/so-like" code gets replaced depending on what objects needs to be in the game (Mentioned "overlay" as this was very well established practice back in the DOS days).
Things really were plain and simple. API-s too. It was so easy to grasp what's going on.
Then again, since this was running on what looks like really limited machine, it had to do only such and such. No fancy animation controllers, advanced physics, collision, etc. etc.
at some point, especially if it's a content creation tool (think Maya, or any Autodesk product, Houdini, etc.) then you need to be able to handle tons of "nodes", "entities", meta-data, etc. - but when it comes to a game, even nowadays - pre-cooking gets you there, and quick deserializing - either piecewise, or whole sections, and then pointer fixup.
Point is, is your game, and engine meant to be content creation tool too?
Once you get to the point of needing various tweaks, modifications, changes, more advanced UI, etc. - you may need better suited language, because malloc/free won't cut it for you.
Like just switch mode of a context and draw pixels as you want?
But you are right, it'll take a daaamn long time. I've the third and the forth parts almost ready, covering the engine architecture itself, the private scene scope and the image->texture->sprite stuff but i havn't started yet with ECS, fs, configurations, physics, networking... this field is really extense.
It's four and a half years into Handmade Hero at this point :-)
These tutorials exist and are really well thought out here: https://lazyfoo.net/tutorials/SDL/
Would you like to see a tutorial about how to do that in c89 compatible with windows, mac, Gnu/linux, consoles, etc? it'd take half million lines and 3 years.
There are better SDL tutorials from the 90's.
Would you mind reviewing https://news.ycombinator.com/newsguidelines.html and taking the spirit of this site more to heart? If you know more than others, the thing to do here is not to put others down, but to offer some of what you know, so we all can learn. Or you can ask about why they did X rather than Y. Perhaps they also know quite a bit, and chose X for an interesting reason.
Honestly, if you're going to be so critical you should suggest the tutorial should use Vulkan instead of OpenGL, but wait neither of those support all gaming platforms so you have to write a low level API wrapper for Metal and DirectX12 as well, then write a shader translator... Etc. The gap is large between a toy engine and a commercial engine, but there are a lot of fun games you can make with a toy.
i'll address that things when needed but the project has like 500 lines, don't think that momment is now, to be honest...
Anyways, thanks for your support, lovely fellow
One thing to remember is the act of coding is a bit different from architecting a program. If you are unsure of how it is going to work it is probably not going to work and as your program grows in size will become unmanageable as you continually have to rewrite it to take things into account that you didn't plan on. It helps to become familiar with design patterns and to pseudocode the overall structure. Expressing your desired architecture into code is a big enough challenge without trying to do it at the same time.