56 comments

[ 2.6 ms ] story [ 88.4 ms ] thread
If you're into game programming, Casey Muratori's Handmade Hero [1] project is worth checking. From the website description, “Handmade Hero is an ongoing project to create a complete, professional-quality game accompanied by videos that explain every single line of its source code”.

[1] https://handmadehero.org/

Casey goes through the math, programming, and the logical processes of understanding how to build a an engine and a game with that engine. Its extremely deep, and extremely thorough, but it is by far the most extensive and explicit resource on the topic to date.
"Extremely deep" is an understatement. It's long, deep and plodding enough as to be completely inaccessible to any but the most dedicated, and you have to be willing to spend hundreds, if not thousands of hours by the time it's over, going through it.

I love and appreciate what he's trying to do, but it's overkill.

There's no shortcut to mastery. Following Casey's will get you half way there, much more so than the many quick "tutorials" you'll find elsewhere.
On the contrary, the market is flooded with quick, easy tutorials with the (sometimes implied) disclaimer that it's not production ready. To have just a single example of the opposite is wonderful.
(comment deleted)
It's definitely not overkill, it's probably one of the biggest free gifts to programmers I'm aware of. That said it would be a full time job for me for a year to get through it. I wish it had been available when I was a teenager and seemed to have infinite time on my hands.
Yes, you do have to be willing to spend thousands of hours to become that kind of game programmer.
Just going through videos 1 through 25 is pretty mind expanding, and can get experienced programmers started with a nice base for their own game, and only takes a few days.
I realize both other replies to this comment are just reiterating how amazing a resource handmadehero is, but I thought it still needed another voice.

It's really an amazing look at how the sausage is made through every single step. Even if you've done a significant amount of games work, I'd still recommend working your way through it.

My only caveat is there's so much useful stuff, it's hard to get through it all. It's all time well spent, though.

Allow me to recommend this book as well: 3D Math Primer for Graphics and Game Development https://www.amazon.com/Primer-Graphics-Development-Wordware-...

It helped me tremendously when I was into game development back in college. The math was always the biggest hangup for me. I'm in healthcare software now, so YMMV :)

This is nice! Too bad it's tied to one platform (Windows), instead of some more generic API (like SDL or opengl).
(comment deleted)
My thoughts exactly. I guess I’m so buried in the coding world that I actually have to do a double-take when reminded Windows is an option, or that people actually use it for development.
I do not recommend learning the Windows API. There are convenient crossplatform alternatives to it for this use-case.
(comment deleted)
It's software rendering, whether the implementation uses winapi or something else is irrelevant.
Using something else does potentially widen the audience. Being able to compile/run on a variety of platforms alone has value.
There's no harm in using/coding against the windows api, provided that you know what's a windows api quirk vs what's normal coding idiom. Granted, beginners may not be able to differentiate, but that's due to inexperience, which will be fixed in time. There's nothing wrong with learning a platform.
> There's no harm in using/coding against the windows api

It needlessly restricts your platform though. For example, I was very interested in the handmade hero tutorials linked elsewhere here, but was dismayed that the first few lessons are all windows api. I don't have a windows machine anymore, so I have no use for those lessons. I'm sure I can still learn from the later lessons, but I won't be able to simply follow along with his code. A significant number of people, especially people who would play Indie games, are now using macbooks nowadays. Maybe still not the windows gamers numbers, but enough that I wouldn't ignore it, and I've passed on quite a number of promising looking Indie games because they were windows only. Windows API is also notoriously clunky, at least, compared to the wrapper libraries.

Obviously if you only have a windows machine, by all means, focus on making it work for that, but why put roadblocks in your way for potential future ports when nowadays you really don't have to because great cross platform abstractions exist (SDL2, GLFW, SMFL, ...)

> There's nothing wrong with learning a platform.

Sure. If that's your goal. If its just a means to an end, why not use a library to do the hard work so you can focus on what you actually want to do (and not locking you out of other platforms later without a ton of work is an added bonus).

"It needlessly restricts your platform though"

Not necessarily. Casey discusses this issue well into his series, but it would be twenty or thirty hours in.

There are two general approaches to multiplatform.

Approach 1: the SDL way. Have an abstract system that represents a generic desktop environment (e.g. QT suite). Build your apps into that. The upside of this is that you can build once, run everywhere. Some downsides are that it will not necessarily feel like it fit into the ecosystem, and you need to learn an abstract platform. That platform may have license overhead. When they do version upgrades, you might not like what they do, but you are stuck with them.

Approach 2: native wrappers. Build your codebase in generic C/C++. Then write endpoints interface wrappers for each platform you deliver to. In this case, you could create a Windows codebase that #includes your game code, and calls into it from WinAPI. For linux, you can create a ncurses or GTK frontend, and then #include into the samre game codebase. Your product will feel like it was built for your target platform. Yes, you need to learn platform-specific ways of doing things. But once you have, your end-users get native feel. If you want to call a Windows voice API for the Windows version of your product, you can do it without depending on a vendor.

If you were building a forms application, Approach 2 would be painful. But if you are building a game, Approach 2 is viable. You probably need a single-window wrapper. It would need to have a menu system, to be able to display an image, and to be able to consume keystrokes and mouse events. I don't have much audio experience, but the little experience I have is that the moment you want to do something remotely sophisticated with audio (e.g. creating a music sequencer) you want to be working against native APIs. I would rather build something from first principles on a robust system API than debug bizarre threading behaviour in the SDL mixer library.

No, on Windows, SDL will interface directly with the Windows API, not GTK or Qt. And will give you a native window, the same as if you do it by hand.

Proof: http://hg.libsdl.org/SDL/file/35da714ed287/src/core/windows

If you don't like SDL that is your opinion or choice, but don't come here presenting false facts to influence other people into having your same preferences (e.g: over half of what you said was technically incorrect and not true). You should really do 1 minute of research before making ill-intentioned accusations like these.

Strictly speaking, SDL does all the Windows API boilerplate for you. And also does it on macOS, and on Linux with no substantial cost since SDL itself is very lightweight.

It also does more stuff for you, like loading fonts, images, sounds, etc... and again, in a crossplatform manner.

All of this was provided to the community as open source, for free. Sam Lantinga is a hero.

If you don't like a specific part of SDL or whatever project, find an alternative project, submit a bug report, submit a fix, or fork it. That benefits everyone. That's how open source works.

I introduced Qt because it is an example of a high-quality, full-system framework that you can chose to live in. I did not mean to imply that SDL used Qt, but I can see how it reads that way.

When I was following Handmade Hero, some of the fanbase were maintaining a linux version of the project in SDL, and moving them forward at the same pace as Casey's project.

   > It also does more stuff for you, like loading
   > fonts, images, sounds, etc... and again, in
   > a crossplatform manner.
I found it to be awkward for audio. When you pick a library, it can steer the direction of your software. You can end up having to do things the library-way instead of the you-way. As a general rule, the more the library does for you, the more likely this is to happen.

This is going back a bit, but I remember SDL media having background threads. If you let SDL draw you into a multithreaded approach, you would lose tuning options you might need later, because you would not be able to reason cache behaviour.

    > If you don't like a specific part of SDL or
    > whatever project, find an alternative project
Through your forceful words, here and elsewhere, I get the impression that you think that this is the "one true approach". But I think there is a respectable alternative: suck up the native-integration cost in order to forgo the dependency cost. Bedrock against the system where it is non-horrific to do so, in order to own your codebase. I don't think a hacker should have to apologise for choosing that approach.
I respect if you wish to develop at a lower level, or use target only a specific platform.

And I understand if you ran into issues with an open source library. I personally ran into multi-monitor issues with SFML in the past. Maybe it has been fixed now.

But a different thing is to ask others to not use a project. Think that SDL is non-profit, ran by volunteer work mostly.

In the past, cross-platform game development has been the target of many FUD campaigns so the tone is mostly out of frustration at this point.

Then one point I didn't mention was licensing. Some open source projects may have licenses that are incompatible with a proprietary license. I think this was the case of SDL 1.x but SDL 2.0 uses the zlib license which is pretty compatible.

I haven't used SDL for audio in many years, but most games nowadays use FMOD or Wwise and not platform native audio API's and they work just as well with SDL (or GLFW or whatever) as without. Both FMOD and Wwise offer a free version[1].

Note also, in case you're not already aware, that SDL 2 is quite a different beast than SDL 1.x and is much improved in many ways, although I can't speak for addon libraries (I don't know what SDL media is like, for example).

[1] The free version of Wwise has a limit of 500 sounds and your budget must be under $150K; while FMOD is free as long as your budget under $500k and you can only publish one game a year under this license -- either way, both are financially accessible to Indies and both are free during evaluation/development.

SDL does allow you to grab the platform specific window handle (HWND on windows), so you can still do platform specific logic while letting SDL do the boilerplate for you.

    SDL_SysWMinfo info;
    #ifdef _WIN32
    if(SDL_GetWindowWMInfo(window, &info)) {
        assert(info.subsystem == SDL_SYSWM_WINDOWS);
        HWND hwnd = win.window;
        HDC hdc = win.hdc;
        HINSTANCE hinstance = win.hinstance;
        ...
    }
    #endif
Honestly, though, it's not like the Windows API parts of those examples are clunky or hard to understand. It's extremely straightforward code (effectively probably just using parts of the Windows API that go back to 1985). The entry point simply creates the window and the WndProc callback handles a few window things (create, resize, close) and otherwise handles input and the game loop. From an educational standpoint I wouldn't see any harm in that code.
Well, you said it yourself. "WndProc". These abbreviations is something you will see frequently and you will have to spend time memorizing them.

Better identifiers are possible.

e.g: https://wiki.libsdl.org/SDL_CreateWindow . CreateWindow, not "WndProc" or some ambiguous abbreviation. If you want to turn your job into playing hangman all day and guess what the hell the actual abbreviated words are, use the Windows API.

If almost your entire userbase is running Windows you're gonna end up needing to deal with it anyways, one way or another. You can at least be glad to be dealing with Win32, which other than user32 (which is a bit crazy for compatibility reasons, though I'm not sure if X11 is much better) is actually far more reasonable than it's contemporaries.

Compare doing fast async file I/O on Win32 vs POSIX. On Win32 it just boils down to creating an IOCP pipe, creating some worker threads, setting up the file handles and using the right flags in read/write calls. It's a bit obtuse, but the code that does it ends up reasonable, and it's the same drill for sockets. Doing that on POSIX? You get blocking, uncancelable I/O calls and threads to work with, that's it. Good luck, though the documentation involved is far simpler (or just use something like libuv if you value your sanity).

Using a library here would be preferable, but I really don't see why using Win32 would be the problem and not the fact that it's non-portable.

Yes, you can use libuv. It's a crossplatform async I/O, with a sane API.

Then, guess what: Android, iOS, macOS, Linux, BSD... all of them are in some way or another, similar at the low level.

Windows API is unique, like TempleOS. But even the TempleOS API makes more sense, is more consistent and is better thought than the Windows API.

While the Windows API can be pretty clunky and has a ton of leftover cruft, and the UI-related APIs aren't too great thanks to being compatible with Windows 95 (use a toolkit if you want controls), it'd be really hard to argue that any of those (except maybe TempleOS; I haven't looked at it) are any more sane than Win32. You get pretty unified support for async notification through IOCP, which can be used for sockets, files and a few more things. On most of the systems you listed you'd get some flavor of kqueue at best, and probably only for sockets. On the NT side of things you have the handle system, which serve the same purpose as file handles without pretending that "read", "write", and "ioctl" are the only operation any of them will support (plus socket operations, if you want to include those). It also provides the ability to transfer them between processes (with access control!) and place them in a (semi-)global namespace without having to muck around with the actual filesystem.

Not to mention basic operations like reading information about a process doesn't involve having to parse text that can contain semi-arbitrary strings (/proc/<pid>/stat contains the filename near the start, have fun)

I can't say I like Windows as an OS, but API-wise? Not fantastic either, but compared to every other major desktop OS/kernel it's a saint, and provides more functionality. In the context of complex 3D games; if you need to do something like transfer 1GB of data from disk to GPU memory as fast as physically possible, you have to care about this.

OK, in fairness, some portions of the Windows API are well though out as you mention. Some people may also argue that other APIs like Direct3D have a more convenient design.

The UNIX philosophy ("Everything is a file") has pros and cons. It makes interoperability fairly trivial, and being a filesystem it has access control capabilities.

If you want to have something similar to the Windows' IO Completion Ports (IOCP) paradigm you can make use of https://www.kernel.org/doc/Documentation/scheduler/completio...

It really hard to compare them fairly since they're quite different in scope; X11 wouldn't exactly be considered a Linux API. Win32 is also split between several modules and the functions don't quite correspond to the syscalls; it's basically a bunch of wrappers around the actual NT API, win32k and some userspace servers. So it's really hard to draw a line somewhere if you wanted to compare them.
CreateWindow is CreateWindow in Win32 as well. How you name the function that handles messages is up to you. WndProc is merely a convention. It's not like a function pointer has any name, so for the API it's irrelevant.
Let's see, we'll use Steam stats for October 2017 as reference.

Windows: 98.04% OSX: 1.56% Linux: 0.32%

That's not a significant number in the least. Now the numbers may be skewed by developers vs players, mobile vs desktop etc, and yet even then it's still not worthwhile in general.

As to the 2nd part, I far more agree with that. There's no reason not to use SDL. But by that metric you should be using Unity/Unreal etc. anyway, unless if your focus is really on becoming an engine programmer.

And the reason for this is because back in the day, in 2003, Microsoft ran a Fear/Uncertainty/Doubt (FUD for short) campaign mostly based on false claims, such as that OpenGL would be put behind a compatibility layer with a significant performance cost. Simultaneously, Microsoft left the OpenGL Architecture Review Board giving a strong signal they were not supporting OpenGL anymore. Everyone freaked out and moved to Direct3D.

http://blog.wolfire.com/2010/01/Why-you-should-use-OpenGL-an...

http://chrishecker.com/images/3/33/Gdmogl.pdf

Despite the technical differences, if you think this is morable acceptable, by all means do you all your development on Windows. Allow someone else to build a monopoly with no real gain for you.

I've never bothered to seriously use DirectX, I've always used some flavour of OpenGL (quite successfully on Windows).

But speaking for other devs, a long time ago DirectX was indeed really crap. It took for example 16+ pages of code just to initialise. But in later iterations MS did a lot of work to clean it up and improve it and its performance, which is a large contributing factor as to why people started using it (along with of course having to deal with countless OpenGL extensions, which became a really tiring exercise).

Again I'm with you, I dislike the modern Microsoft practice of tying specific DX versions to specific OS versions, in trying to force people to upgrade.

Sadly the solution to the problem isn't either of OSX or Linux. Apple is too closed off and expensive in general, and Linux perhaps too abstract to be bothered with by the average gamer.

For the purposes of gamedev; Windows, consoles and possibly Mac are the only platforms are the only platforms that absolutely matter. If it runs on Linux, that's nice, but if it doesn't run on some common buggy setup you aren't going to see a response beyond "fix your machine". For Windows users you would see crazy workarounds (i.e API hooking) being broken out real fast. Nothing beyond a simple 2D indie game would consider Linux support more than a non-critical "bonus" feature.

You'll also find that people who work on high-performance games (i.e complex 3D) are pretty allergic to using existing libraries, and while it would make sense to use SDL in an example project like this, it's not unreasonable for someone to default to the "proper" way. Beyond things like the basic GL loader, linear algebra and font loading / text rendering libraries, anything that's available is almost guaranteed to be woefully insufficient, more effort than it's worth or loses you too much control. Not to mention you'll inevitably wind up with your own library that provides an equivalent of half the C++ std (even if most of it is implemented using the normal std), which often makes using anything that depends on the usual C++ std pretty painful.

For the above reasons, things like SDL or even libev/Boost.ASIO would only be considered suitable for toys. I can't say I'm too happy about having to re-implement things that have already been written a thousand times over already though.

(comment deleted)
Maybe in 2009 but now you've got also iOS and Android.

Android is technically Linux, with a lot of APIs built on top of it.

Windows on phones is no longer a thing and phone games are a significant market.

Plus, people use Android more than they use Windows.

There's harm. It's called "vendor lock-in".

https://en.wikipedia.org/wiki/Vendor_lock-in

Windows API is carefully designed to maximize costs of switching to another platform. Every single thing is different to every other OS.

I don't buy that argument. Each platform has similar but different functions for doing all common things, the graphics API's are converging on a similar design as well.

To make it clear, the main platforms for more AAA-like titles are Windows/PC, PS4, XboxOne, and now the Switch. The main platforms on mobile are iOS and Android. Each of these requires an adaptation layer to be written because they are all different to each other, some more than others though.

So you can "lock" yourself into one of these platforms, "lock" yourself into using an engine or existing library which covers some subset of those platforms, or write your own adaptation layer and engine.

For learning and experimenting it's quite easy and convenient to focus on a particular platform, Windows/PC in this case is a good simple default since it's available and doesn't require NDAs, licensing, etc.

I think the intention of this is to get closer to "on the metal" as a teaching tool of the math and low-level graphics rendering. Using SDL or others would abstract all that away.
Unfortunate this is downvoted so much.

Especially from the perspective of the beginner there is much to be wasted by learning proprietary API’s.

ALways nice to see simple examples, but just going through an example, I see this:

imgWidth = pow(2, log(sqrt(stat_p.st_size/3))/log(2));

And I have no clue why because if you take 128 as input value; it converts it either to ~126 if you don't trunc to int before doing imgWidth * imgWidth * 3, if you truncate to int, you get 108; for what reason?

From the comments that precede it (3DPlatformer/platformer.c line 89-91), I think the idea is that the input is the size (in bytes) of the bitmap W * H * 3, where W == H and W is a power of two. So the input for a square bitmap as the comments suggest would be 128 * 128 * 3, if we unravel from there, log(x) / log(2) extracts the most significant bit, pow(2, log(x) / log(2)) sets that bit.

pow(2, log(sqrt(128 * 128 * 3 / 3)) / log(2))

pow(2, log(128) / log(2))

pow(2, 7)

128

It's funny that on the one hand the code is beautifully terse enough that this ugly wart is easily discovered, yet on the other that the code would have such a wart to begin with.

If I run this in C#; it truncates to 6 and not 7... does C do an implicit round?

Broken down, you get this:

            int toTest = 128;
            var div = toTest / 3;
            var sqrt = Math.Sqrt(div);
            var logSqrt = Math.Log(sqrt);
            var log2 = Math.Log(2);
            var logDiv = logSqrt / log2;
            var pow = Math.Pow(2, logDiv);
            var result = pow * pow * 3;

            int result2 = (int)Math.Pow(2, Math.Log(Math.Sqrt(128 / 3)) / Math.Log(2));
            int total = result2 * result2 * 3;

		this	{Clusters.DAL.Tests.UtilitiesTests}	Clusters.DAL.Tests.UtilitiesTests
		toTest	128	int
		div	42	int
		sqrt	6.48074069840786	double
		logSqrt	1.8688348091416842	double
		log2	0.69314718055994529	double
		logDiv	2.6961587113893803	double
		pow	6.4807406984078613	double
		result	126.00000000000004	double
		result2	6	int
		total	108	int
Wouldn't this be easier to understand for newbies?

        [TestMethod]
        public void NextPowerOfTwo()
        {
            const int bitPowers = 32;
            const int StartValue = 2;
            const int PowerOf = 2;
            UInt32[] powers = new UInt32[bitPowers - 1];
            UInt32 value = StartValue;
            int i = 0;
            powers[i++] = value;
            for(; i < powers.Length; i++)
            {
                value *= PowerOf;
                powers[i] = value;
            }
            int[] valuesToTest = new int[] { 126, 128, 130 };
            int power;
            foreach(int valueToTest in valuesToTest)
            {
                power = 0;
                value = powers[power];
                while(valueToTest > value)
                {
                    power++;
                    value = powers[power];
                }
                System.Diagnostics.Debug.WriteLine($"{valueToTest} -> {value}");
            }
        }

        Test Name:	NextPowerOfTwo
        Test Outcome:	Passed
        Result StandardOutput:	
        Debug Trace:
        126 -> 128
        128 -> 128
        130 -> 256
toTest should be 128 * 128 * 3 to reflect 128 by 128 pixels of 3 bytes each, as would be the file size.

edit: just to add, I think "width = sqrt(filesize/3)" would be a better version for newbies. Even better would be to replace the code altogether.

I found what was causing the off-result for me; if you do: 128 / 3 (2 int's) in C# it will truncate to int; if you do 128 / 3.0 you'll get the expected output and everything starts to make sense :)
ok, two other things that may be throwing you off, the input is a filesize, not a width, the result of filesize / 3 is always an integer (even if float representation,) and the result of sqrt(filesize/3) is also always an integer (even if float representation,) as per the comments.

Also, taking the 2log and then 2power is a no-op because it doesn't truncate / ceiling in-between the two.

You kind of see where the code was going to, but it never quite made it there.

(comment deleted)
Wowwwwwww... This takes me back to the early 90s when I tried to write simple (2D) sprite games using Borland C++, Windows 3.x, and the BitBlt and DIB APIs.