
Writing a game engine in pure C: The Graphic Initialization - buba
https://prdeving.wordpress.com/2019/06/05/how-to-write-a-game-engine-in-pure-c-part-2-the-graphic-initialization/
======
munificent
As a tutorial series, I think using plain C is a cool approach and will help
illuminate what's going on under the hood and what parts of the engine are
really essential.

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.

~~~
pavlov
_”...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.

~~~
munificent
This is such a great observation.

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.

[0]: [https://docs.racket-
lang.org/drracket/languages.html](https://docs.racket-
lang.org/drracket/languages.html)

~~~
haberman
> It makes me wonder if this can be solved technically by published curated
> language subsets.

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](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](https://google.github.io/styleguide/cppguide.html#Exceptions)

~~~
kevin_thibedeau
The problem with exception-less C++ is that new, delete, ctors, and dtors
become timebombs when they fail in a way that would have generated an
exception.

~~~
keldaris
I'm not sure why you were downvoted. I almost exclusively write exception-less
C++ (compiling everything with "-fno-exceptions"), but correctly handling
errors in constructors and destructors is absolutely an important concern.
It's not hard to do, obviously, but it does require forethought and heavily
encourages delegating complex logic to other parts of the code.

------
obituary_latte
Interested people may also be interested in Handmade Hero[0]. The game is
being developed — from scratch, engine and all — in real-time and streamed by
Casey Muratori. It’s cpp technically, but he uses very little and sticks to
mostly C if I remember correctly.

[0][https://handmadehero.org/](https://handmadehero.org/)

~~~
AceJohnny2
Handmade Hero is an impressive endeavor, but by now it's got over _500_ 1-2h
sessions. That's a lot for anyone to catch up on.

Have there been any efforts to write episode summaries/articles on the
techniques he demonstrates?

~~~
theossuary
Every video on his website has pretty great annotations placed on top of them
which describes every section of every video, and all the questions at the end
of session QA.

Go to [https://handmadehero.org/watch](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.

------
agentultra
Starting with state management in the first post is a different approach I
wouldn't normally expect.

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.

~~~
AlexMax
I have some recent experience with doing game programming in C and while I
think the experience was overall positive, I'm not sure that I would do it
again unless my aim was portability.

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.

~~~
codesushi42
_> I needed many implementations of some base type, the kind of pattern that
lends itself well to classes and simple inheritance._

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.

------
mhd
Reading the title my mind immediately went to

    
    
      mov ax, 13h
      int 10h
    

I'm old.

~~~
jackhack
Brought a smile to my face. I see this as a shibboleth for the oldschool
coders.

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.

~~~
mhd
320x240 was one of the benefits, but the more important part was that it
allowed you to use all the memory, double buffer, use split screens (for HUDs)
etc. Addressing became more complicated, though. (And IIRC, Doom ran in
320x200 Mode X, for some design/monitor reasons)

------
Sir_Cmpwn
Personally, I prefer GLFW over SDL:

[https://www.glfw.org/](https://www.glfw.org/)

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.

~~~
enriquto
are there any concrete, tangible advantages of glfw over freeglut? I see the
glut api as the paragon of usefulness and simple elegance, and I'm really
curious how glfw can be any better than that.

~~~
mrec
I haven't used glfw, but from the docs it leaves you in control of the main
loop, whereas glut takes that over and only offers you callbacks. When I used
glut (many many years ago) that was sometimes annoying.

glfw doesn't seem to have a function to draw teapots though, which should
really rule it out as a serious contender.

~~~
enriquto
legacy glut does not allow loop control, but freeglut does

------
LinuXY
For more inspiration I'd check out the Quake engine source. It's quite the
masterpiece. There's also been some modernization over the years by LordHavoc
of the DarkPlaces engine
[https://icculus.org/twilight/darkplaces/](https://icculus.org/twilight/darkplaces/)
\- I'm constantly impressed how well the HD remaster on DarkPlaces looks on
modern 4k hardware.

~~~
bluedino
The Wolf3D source is probably an even better start. Much simpler, the bad part
is that it's 16-bit Borland C.

Andre Lamoth's DOS game programming books are a good read as well, he used
16-bit Microsoft C. Sprinklings of assembler in both.

------
ursus_bonum
This looks like a lot of complexity up front with no foreseeable payoff.

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.

------
ensiferum
Theres also wdk which can give you open gl es/desktop context and and this is
separate from window creation so you can also create a context for headless
rendering. Supports window and linux.

[https://github.com/ensisoft/wdk/blob/master/sample/triangle....](https://github.com/ensisoft/wdk/blob/master/sample/triangle.cpp)

~~~
buba
uh, interesting....

C++ tho, but i could port it, i'll give it a glance

------
malkia
Right now I'm heavy into C++, especially C++17, but mostly due to the current
project I'm working on (tools engineer @ gamedev)

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?

~~~
malkia
e.g. "C" is still valid choice, if your data is well organized, known, and no
additional heavy modifications are need - e.g. you don't change much - mostly
load "pre-cooked " data, play it, keep few (okay dozen, maybe hundreth, even
thousand) variables around and that's it.

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.

------
davidscolgan
This is super cool, well done OP! It looks like you've got some pretty in
depth and interesting articles around the rest of your site too. Big props to
you and everyone else who puts themselves out there and works to create
something that helps others.

------
nategri
I finished my first game-ish C/SDL project a few months back, and found it to
be a very instructive experience. It compiles to WebAsm so you can fiddle with
it here:

[https://nematode.farm](https://nematode.farm)

------
z3phyr
Is there a way in modern operating systems to draw pixels directly in software
without using any external libraries (Ala SDL or Windows Graphics)?

Like just switch mode of a context and draw pixels as you want?

------
jandrese
Seems like it is going to take a long time to get to a fully functional game
if this is the second article and they're only just calling SDL_CreateWindow.

~~~
thrower123
One post a week is about as fast as most people can go if they are into nuts-
and-bolts technical topics and they are trying to juggle work and life and
other demands on their time. That's about the best I could ever manage when I
used to be blogging as I learned DirectX. And that was using C#, so you didn't
have to boil the ocean and implement all your own fundamental data structures
as you go (see the previous post in this series).

It's four and a half years into Handmade Hero at this point :-)

~~~
buba
Just want to explain how things works, i don't know if i'll ever finish this
proyect, but if i don't i want the things i did explain crystal clear, i want
every concept well presented so every single post in the serie can be a piece
by its own and you can learn something. I'm now trying to write two articles a
week but it's hard, coding C89 is not fast, there are a lot of cavities and
undefined behaivours xD

------
yellowarchangel
This is moreso "how to start using SDL2".

These tutorials exist and are really well thought out here:
[https://lazyfoo.net/tutorials/SDL/](https://lazyfoo.net/tutorials/SDL/)

~~~
buba
this is not about SDL, it's just an easy way to open a window, import assets,
handle events and reproduce sound.

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.

------
edoo
This is an awful SDL engine example. I've made rendering engines before. You
should for example have a second windowless openGL context with its own thread
so that you can be sending drawing commands at the same time you are updating
GPU ram buffers. Generally the main thread is required to be handling input
(due to cross platform requirements) and should only be doing that. Everything
else including file loading should be running in async threads. It would
require you to be a competent programmer though.

There are better SDL tutorials from the 90's.

~~~
dang
That crosses into personal attack, which we ban people for, and name-calling,
which the site guidelines ask you not to do.

Would you mind reviewing
[https://news.ycombinator.com/newsguidelines.html](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.

