[HN Gopher] Space-shooter.c: cross-platform, top-down 2D space s...
       ___________________________________________________________________
        
       Space-shooter.c: cross-platform, top-down 2D space shooter written
       in C
        
       Author : ingve
       Score  : 147 points
       Date   : 2021-12-11 11:47 UTC (11 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | cozzyd wrote:
       | $ time make       rm -rf build       mkdir build       cp -r
       | assets build/assets       gcc -std=c11 -Wall -Wno-unused-result
       | -fno-common -DSOGL_MAJOR_VERSION=3 -DSOGL_MINOR_VERSION=3
       | -D_POSIX_C_SOURCE=199309L -o build/space-shooter -g
       | -DSPACE_SHOOTER_DEBUG src/shared/*.c src/game/*.c
       | src/platform/linux/*.c -lX11 -ldl -lGL -lm -lpthread -lasound
       | real 0m0.437s       user 0m0.288s       sys 0m0.079s
       | 
       | that's what I like to see...
       | 
       | Conversely, I recently wrote a simple grafana plugin (my first
       | time using typescript and "modern" web tooling) and I don't know
       | what the hell yarn does but I can compile and play this game
       | several times before it finishes.
        
       | markus_zhang wrote:
       | Kudos for not using something like SDL2. I'd be very reluctant to
       | do so because C is so barebone, but I admire anyone who can pull
       | this off.
        
         | jstimpfle wrote:
         | I totally recommend to go for it. It's not that scary, just a
         | few concepts to learn. For example on Windows, you roughly need
         | to know about HANDLE, HWND, WNDCLASSEX, RegisterWindowClass(),
         | CreateWindow(), how to write a window proc to handle events
         | coming from the OS, how to write a simple message loop to pump
         | those events. To put something on the screen, look up the
         | drawing context HDC, the RECT structure, and use FillRect() to
         | draw colored axis-aligned quads.
         | 
         | Optionally, later you move to a custom memory-backed backbuffer
         | allocated using CreateDIBSection(), so you can just set each
         | pixel using the CPU as a uint32_t RGBA value. That allows you
         | to go wild, you can proceed to write your own 3D game engine
         | with nothing to distract you - it's you, the CPU, and the
         | backbuffer memory. (It will be running at software rasterizer
         | speeds, of course - but it should be easy to get very good
         | performance at say 640x480).
         | 
         | It shouldn't take you more than a few hours to maybe 2 days to
         | get the ball rolling, depending on your prerequisites. I
         | initially found the Win32 API to be a bit arcane with its
         | overboarding use of preprocessor and of typedef'ed types (even
         | "pointer-to" typedefs like LPCSTR instead of simply "const char
         | *"). But beyond these superficialities, I find that large parts
         | of it are fairly well designed, and anyway the code to
         | interface with the OS can all be kept in a central place.
         | 
         | Once you're a bit accustomed to these things, maybe afterwards
         | you'll look back and wonder how you could put up with the piles
         | of abstractions all these fluff libraries put on top. And
         | personally, while this approach is not suited to quickly hack
         | up a GUI in a day, I find it's a great feeling to be in control
         | of everything, and this will show in the quality of the product
         | as well.
        
       | harel wrote:
       | I love this. My ambition is to become less busy (i.e., retire) to
       | do stuff like that; fun code, with no obligations.
        
         | tsherif wrote:
         | Thanks! My first child was born last year, and I started a new
         | job 3 months earlier, so time was definitely not an abundant
         | resource! But between the stress of all that and the pandemic,
         | I found squeezing in an hour or two for this project here and
         | there was one of the few things keeping me sane, and that's
         | what got me through it.
        
       | krapp wrote:
       | I know it goes against the HH ethos but... you should consider
       | using SDL2 in the future for projects like this. You'll get
       | greater (and better tested) cross-platform compatibility, support
       | for more peripherals, better image support, etc, and not have to
       | write most of this on your own.
        
         | desine wrote:
         | I think you're missing the point.
        
           | krapp wrote:
           | I'm not missing the point, I'm just saying that in general,
           | outside of specifically having the goal of not using any
           | third party libraries, SDL is a good idea for a project like
           | this.
        
             | flohofwoe wrote:
             | IMHO SDL mostly makes sense on Linux because it hides a lot
             | of really ugly window system and GLX setup code.
             | 
             | On Windows (with Win32+DXGI+D3D11) and macOS (with
             | Cocoa+Metal+MetalKit), things like setting up a window, 3D
             | device and swap chain is just a few lines of relatively
             | straightforward code, so SDL is by far not as useful there
             | as on Linux.
        
             | jstimpfle wrote:
             | Libraries like that might make it easier to get started,
             | but they tend to limit what you can do. For example, I
             | recently created a Desktop GUI app that could take inputs
             | from a networking socket. How do you do that cleanly? There
             | is often a way around limitations, for example by creating
             | a separate thread, or by polling every so many milliseconds
             | (which is ugly and a waste of resources). In my case,
             | interfacing directly with Win32 without a 3rd party layer
             | in between, it was easy to create an Event Object for the
             | socket and am calling MsgWaitForMultipleObjects() in my
             | message loop. Not sure what's a good solution to do this
             | with SDL, but why should I even bother...
        
         | tsherif wrote:
         | In a sense, I did use SDL. The source code of SDL, along with
         | GLFW and Sokol, were my primary references when writing the
         | platform layers.
        
         | mkotowski wrote:
         | From the description of the project, I would dare a guess that
         | the intent of this project was to go as self-contained as
         | reasonably possible:
         | 
         | > [...] written in standard C11 using only system libraries
         | (with system libraries defined as anything included in the C
         | standard library or supported operating systems).
        
       | mahesh_rm wrote:
       | Can't build on mac, anybody figured that out?
        
         | tsherif wrote:
         | For now, it's Windows/Linux only. I hope to write a Mac
         | platform layer eventually. I just don't currently have a Mac
         | machine to work on...
        
           | ryandrake wrote:
           | Unfortunately, the Mac port would likely involve calling into
           | (or out from) a little Objective-C, unless you used the
           | ancient Carbon APIs for graphics and window management.
        
             | OliverM wrote:
             | So it's not possible to interface with Metal or the
             | Accelerate framework on macOS using pure C? I've found some
             | outdated C wrappers for metal but nothing up to date, and
             | wondered if something in the later versions assumed Swift
             | or Objective C library consumption...
        
             | tsherif wrote:
             | Ack! Right I hadn't thought of that. I'll have to decide if
             | I'm ok with breaking the "written in standard C" constraint
             | on this project...
        
         | sjmulder wrote:
         | There's no platform code for macOS yet. (It's a goal of the
         | project to not use cross-platform abstractions)
        
           | aninteger wrote:
           | Does Mac not ship with X11 anymore?
        
       | sjmulder wrote:
       | Cool. Also nice to see a well written ARCHITECTURE.md. I like
       | this bit about dealing with memory:
       | 
       | > Almost all memory allocations in space-shooter.c are static,
       | with dynamic allocations only used to load image and sound assets
       | when the game initializes. This leads to a nice "programmer peace
       | of mind" benefit that once the game initializes, I no longer have
       | to worry about errors related to allocating or freeing memory.
       | 
       | It's something I also do myself in C projects - in fact in small
       | programs I hardly ever find myself doing dynamic allocations at
       | all. Functions that generate data don't allocate the memory
       | themselves but receive a reference to an output location. That
       | principle extends to larger programs. (It's not novel either,
       | most system level libraries do this)
        
         | b20000 wrote:
         | very cool project. i wonder if the libraries used in this
         | project also offer the guarantee of static allocations only /
         | dyn allocations only at startup. i've been using the JUCE
         | toolkit for a project the past 10 years and it has allocations
         | all over the place. need 16 bytes? malloc. need to concat that
         | string? a few mallocs for handfuls of bytes. i am stuck with
         | it, so i have had to override malloc and friends and build my
         | own fixed block size allocator. very annoying to find out you
         | are only doing static allocations but then all the libs you use
         | together amount to 3000 malloc calls per second. i even found
         | out that a call like glBufferSubData will call malloc or
         | related functions. so doing that on each frame seems to be a
         | bad idea as well.
        
           | tsherif wrote:
           | > i even found out that a call like glBufferSubData will call
           | malloc or related functions. so doing that on each frame
           | seems to be a bad idea as well.
           | 
           | That's interesting and not what I'd expect at all! Is there
           | documentation you could point me to or did you find that out
           | with some tooling? And is there a better way to update
           | attribute buffers for instanced draw calls?
        
             | b20000 wrote:
             | I did not look up any documentation to verify this but
             | since I did override all allocation functions I could keep
             | statistics of how many calls I was getting for each
             | function, and when I commented out the glBufferSubData
             | calls I could see my stats drop. The contribution was
             | somewhere around 300 calls/s I think... this is with the
             | panfrost driver with gallium/mesa on an ARM platform. My
             | plan is to use persistent buffers which are memory mapped
             | once. Hopefully that will get rid of these allocation
             | calls.
        
         | tsherif wrote:
         | Thanks! This was a revelation for me on a few levels:
         | 
         | 1. That memory management doesn't have to be scary with a
         | little forethought, at least for programs where you can set a
         | reasonable upper bound on the resource requirements.
         | 
         | 2. That it's possible to structure at least a subset of
         | programs in such a way that error states can only be entered
         | during initialization, and that makes the rest of the program
         | much easier to reason about.
        
         | lfowles wrote:
         | I practice something similar, I've statically allocated the
         | physical RAM modules when I built my computer. O:)
        
       | agys wrote:
       | The scroll-speed and the graphics remind me of the magnificent
       | Xenon II Megablast.
       | 
       | https://www.youtube.com/watch?v=v9nD9DQwd80
        
         | malkia wrote:
         | Bitmap Brothers had such unique art style! G.O.D.S.!
        
           | kingcharles wrote:
           | And the amazing music by Bomb the Bass:
           | 
           | https://www.youtube.com/watch?v=bWbJeUEKzc8
        
         | tsherif wrote:
         | Hadn't seen this before, but that's a flattering comparison.
         | Thanks!
        
       | techjuice wrote:
       | Nice work you have here, I will go ahead and ask for the curious
       | ones what is your background and what would your top recommended
       | books, videos, and additional educational resources that you used
       | to learn C and game development in C?
       | 
       | Also, have you thought about making your own indie game studio to
       | do this full time or on the side if you are not already doing so?
        
         | tsherif wrote:
         | Thanks!
         | 
         | If you go through the architecture doc (which I'm almost done
         | writing), you'll find links to most of the references I used:
         | https://github.com/tsherif/space-shooter.c/blob/master/ARCHI...
         | 
         | Top one, though, would be Handmade Hero:
         | https://handmadehero.org/
        
         | swalls wrote:
         | Not OP but I'm currently writing a game in C: just throw
         | yourself into it. The language itself is minimal enough that
         | you probably won't need much guidance other than looking up
         | library functions and with modern tools like the various
         | sanitizers in clang and valgrind it's hard to go _too_ wrong.
        
       | larodi wrote:
       | Loving the fact that OOP here ends with the structures and no
       | heavy java-style class bloat. The code is very well ordered, and
       | to my surprise - not that long! Which speaks of proper
       | architecture. Good work, good example!
        
         | tsherif wrote:
         | Thanks! I definitely tried to focus on only using as much
         | abstraction as was actually helpful in getting it to work the
         | way I wanted it to.
        
       | pengaru wrote:
       | Always a pleasure to look at someone's self-contained small game
       | written in C, especially when the source is relatively well
       | organized.
       | 
       | During the pandemic lock-downs a musician buddy pulled me into a
       | 24-hour IRC-hosted "wild" compo, which usually sees mostly
       | ANSI/music submissions. I took some boilerplate code from another
       | C game I had shipped and hacked something together in an all-
       | nighter we called SARS:
       | 
       | https://git.pengaru.com/cgit/sars/.git/tree/
       | 
       | https://www.pouet.net/prod.php?which=85496
       | 
       | It uses GLAD, SDL2+OpenGL, and autotools in terms of third-party
       | dependencies. There are some git submodules but they're in-house
       | developed vendored stuff I try to reuse in the interests of
       | saving time and not repeating bugs/fixes across projects.
       | 
       | Being a 24-hour hack it's super rushed and messy in places. But I
       | think it may be interesting to skim relative to space-shooter.c
       | just to see how differently one can structure these things, since
       | it too is quite small and pure C with a smattering of GLSL.
        
         | tsherif wrote:
         | Sweet, thanks for sharing! I'll definitely take a look.
        
       | desine wrote:
       | Oh wow, it's all done using no 3rd party libraries too. Gonna
       | bookmark this just to browse through later
        
       | rwmj wrote:
       | The title led me to think it might be a single file, but still
       | this is good stuff.
       | 
       | You could probably make it a single file if you used the
       | wonderfully obscure XPM image format for your sprites and assets.
       | It is both a C source fragment and an image format in one!
       | https://en.wikipedia.org/wiki/X_PixMap
        
         | tsherif wrote:
         | That's an interesting idea. Probably won't change the format
         | for this project, but something I might consider in future
         | ones. Thanks for the tip!
        
         | MLafayette wrote:
         | > The title led me to think it might be a single file
         | 
         | You think Node.js is a single file?
        
         | mkotowski wrote:
         | What are upsides of using this format instead of a more generic
         | array of pixels (something like output of imagemagick to .h
         | files or PPM files)?
         | 
         | BTW I know one can use resource files (.rc) on Windows to embed
         | various resources, but is there an analogous mechanism for
         | linux binaries?
        
           | yissp wrote:
           | The examples in the wiki article show the difference pretty
           | well. XPM lets you sort-of see what the image is just by
           | looking at the source file.
           | 
           | E.G. compare this                 static char blarg_bits[] =
           | {       0x13, 0x00, 0x15, 0x00, 0x93, 0xcd, 0x55, 0xa5, 0x93,
           | 0xc5, 0x00, 0x80,       0x00, 0x60 };
           | 
           | with this                 static char * blarg_xpm[] = {
           | "16 7 2 1",       "* c #000000",       ". c #ffffff",
           | "**..*...........",       "*.*.*...........",
           | "**..*..**.**..**",       "*.*.*.*.*.*..*.*",
           | "**..*..**.*...**",       "...............*",
           | ".............**."       };
        
       ___________________________________________________________________
       (page generated 2021-12-11 23:00 UTC)