Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: My platforming game written in C 89 (zamfofex.neocities.org)
127 points by zamfofex on July 7, 2023 | hide | past | favorite | 43 comments
I have been working on this game somewhat sparsely for the past three years or so. However, it is still in a very early stage, and there is a lot of work to do.

One of my friends encouraged me to post about it here, since he felt people might find interesting how I don’t use any libraries for it.

The game can be played on a browser by virtue of WebAssembly. There are native ports using either MiniFB or SDL2, but you have to build those yourself.

I decided to write the game in C because I feel like it is a simple language that a lot of people can understand well enough, and I didn’t think I needed anything more involved or complicated.

Over time, the game’s code became complicated, and I decided to use advanced features such as coroutines and higher order procedures, besides general overly engineered abstractions.

But then, over time I grew to feel like a lot of those abstractions didn’t really add anything to it, and just made it more complicated (and oftentimes more buggy) than it really needed to be, so a few weeks ago, I made a significant refactoring to it to remove all of those abstractions without removing almost any features from the game.

Milestones are released effectively whenever I get the game’s code to a decently presentable state. Sometimes, this means there are a lot of changes and big refactorings, but sometimes it means there are only a few small changes.

The game uses a simple 2D model system to generate images for the character’s animations dynamically during initialisation. (The source code can be browsed using a web browser on its page.)

The character can be controlled using the arrow keys or “WASD” (“A” and “D”) (double-tap a direction to jump).




Brilliant work so far for something made entirely from scratch, I'm not sure how many people will be aware how difficult that makes things (even getting a window to appear at all is a lot of work). I can see from the comments your going for something like jump king, just a few pieces of advice:

* If you look at jump king, getting over it, games like that that pull it off really well, something you'll notice is that the aesthetic is very different than other games in their respective genre. The player sprite in jump king for example is a lot more squished than you would find in a usual platformer. This is actually very important, the graphics do a lot to inform the player on what they should expect; if they see a generic looking platformer, they will expect generic platformer controls, even if told otherwise.

* Similarly, animation is a good way to give feedback to the player. Currently the only way I know I can jump is to try to jump. Again, looking at jump king, the player knows they can jump when the sprite is crouched down.

* I'd either add a slight delay after the first keytap before moving, or try to get the controls to be more "sticky". Currently a lot of the time when you try to jump you just end up running off the platforms. A lot of platformers actually have a small amount of extra collision off of the platform, so players can run slightly off it and still be on it, that would probably go a long way to helping. You obviously don't want it to be easy, but currently it feels less frustrating (which I imagine is what your going for, like those other games) and more confusing.

Other than that, solid work!


You got this far into writing a game...why would you ever make double tapping A/D be jump? It's like some kind of weird Andy Kaufman prank on your desired audience.


You have to be some kind of madman to make a game in straight C. The controls being what they are is kind of fitting. I mean, it might be a prank, but like QWOP and Getting Over It, I think it's just the product of a certain kind of mind. I'm not going to be able to finish this, but I admire the craft of it.


> You have to be some kind of madman to make a game in straight C.

....why? A significant chunk of commercial games released were done in C (maybe not 'straight", as they might plug into Lua or Python); and probably the majority of open source games were.

C is fairly straightforward to develop games in, and is one of the realms it excels in; so I have no idea why you'd feel this way.


Roller Coaster Tycoon was written in assembly despite it being the 90s. That’s a lot crazier than writing a platformer in C.

Many, if not most, are C++ with scripting. If you’re working on something that doesn’t need scripting, pure C is totally fine.


I don't get your latter point either. Both Lua and Python's (the two most common scripting interfaces) native interface is in C. There's no reason they would plug into C++ any better.


While the scrolling and animation is smooth, why not have a dedicated jump button? With the current controls, the character would jump when I was just trying to walk around normally. It's also really difficult to gauge how much of a runway is needed to make the jumps with double tapping


I find this incredibly challenging to play with the current control scheme. Either up or space would be nice to jump with. Heck even if a single mouse click was jump it would be easier.

Having to time holding down left and then double taping it is an exercise in frustration.

Other than that it looks interesting, I enjoyed the moving platforms.


Yeah, I kind of wondered whether it was an experiment with a control system that could work on mobile


Skill issue


Please change your jump controls. Having the same keys for walking also jump when double-pressed makes twitchy platforming really difficult (especially given how loose the friction seems to be) and not in a good way.

Typically I've seen the spacebar, up button or W used for this. In terms of mechanics It's just adding y (or -y, whichever is up) to the player's velocity vector after a ground check.

Otherwise, good job.


This is going to be a very personal opinion, so please don't hear too much into it, and keep doing what you think is best, what you're doing is awesome :)

That said, I absolutely love the pixel art, characters and animation. I think you should leave aside platforming for interactive pixel art rich environments. Kind of like an adventure game but extremely interactive? Say maybe you could pick up flasks and mix potions to do things, and the environment could be reactive as well. I think that would be much more interesting and unique (I'd love to play it anyways :) ).


ANSI C aka C89 is a lovely language. It's sad that the standards committee and implementors abandoned the guiding philosophy for the language in favor of the principle of most surprise. Go is probably the most old school C like language philosophically speaking, but on account of its primary purpose being pushing ads data to data centers around the world instead of writing Unix it leaves a bit to be desired for those of us who like low level programming.


Once I started using designated initializers and compound literals it was C99 bare minimum for me.

Not sure what dead weight comes with as I'm not a C standards pedant, but there's clearly some welcome niceties that don't seem to ruin the language in my experience.


I think ANSI C struct initialization is considerably more readable really. If there are enough members that it gets confusing then tab formatting the values along with a field name comment at the top works great. As for arrays, I don't see how designated initializers really offer anything interesting. Sparse array literals aren't really a pragmatically useful thing in my experience.

I'll allow of course that they don't "ruin the language" they just increase the cognitive burden of reading code.


It's a substantial quality of life improvement to be able to express things like:

  vv = 3f_add(&v, &(3f_t){.x = .1, .y = .2, .z = .3 });
vs. having to declare and name the literal 3f_t beforehand, wasting my time and cluttering up the code with noise.


I actually was also thinking that way.

But then, after looking back at my old code, I struggled and crafted my 1SLOC guiding principle. I never looked back.

Wasting time when writing code is actually an investment into future read speed.

I even wrote some time ago about my rationale for 1SLOC https://blog.pwkf.org/2022/09/18/always-optimize-for-dummies...


I agree with the principle, but I don't agree that this is where it applies. Initializing things separately just makes it easier to make mistakes that are difficult to spot and harder for compiler to come up with meaningful warnings. It also doesn't make it immediately obvious to the reader that it's just a value to be passed as argument rather than further operated on.


Yea I definitely need variable length arrays.


First of all - I'm about 50% sure whether this is sarcastic or not. If it's not, then this comment is addressed to you. If it's sarcasm, then consider it as an explanation for everyone else.

Variable modified types are cool - you can use them to cast a raw pointer to a N times N matrix and the math works out - you can just do m[i][j] instead of m[i*N+j].

But the evil part is when you declare variable length arrays on the stack. There are exactly two possible scenarios - either you have an upper bound on the allocated size or you don't.

If you have an upper bound and it is reasonably small, use a fixed length array on the stack - there is no point in saving a few bytes on the stack.

If you don't have an upper bound, VLAs will blow your stack, given the right inputs.

If you need a way to allocate unbounded arrays in a LIFO pattern, which 99% of the time are tiny, use a dedicated index-incrementing allocator, falling back on malloc when necessary. You can even cast the resulting pointer to a variably modified type, so that sizeof math works out


It is unfortunate that VLA type was made optional in C11. It's like throwing the baby out with the bathwater. Luckily, they decided to make it mandatory again in C23 again, with only the automatic allocation optional.


Hope you don't need single line comments or even declaring variables in the middle of your code.


It's cool, but... the controls are needlessly wonky. Nintendo decided on a separate jump button, even eschewing joypad 'up' for jump, for a reason; it's much more precise to manipulate a character's lateral motion with one hand and control their jump with the other hand.

I'm working on a platform game myself, in Java for Android -- no libraries except the Android runtime. I always think it's cool when people write games this way. Things like Unity are great for cobbling together something quickly to iterate on an idea, but the last time I tried a Unity mobile game it made my phone hot.


Can we have a few screenshots?


You just click on "Play", then you take a screenshot as the game opens in your browser. The screenshot is yours to do with as you please.


Not so easy on mobile - I'm not sure I can get past the level select screen to see what it looks like. It's not unreasonable to ask for a screenshot of a game on either its itch.io page OR its homepage.


It is not unreasonalble, I agree! I just haven’t gotten around it. You can get past it by chosing either chapter (by clicking on the left and right sides of the screen to move in the UI), then choosing it (by pressing on both sides of the screen at the same time).


There is something very wrong with the controls for jumping, as others have pointed out.


Are you supposed to use the a second character to weight the platform down or is just unfinished? Also, the jump to the highest "staircase" will be cheesed by most players after they fail the running jump. I think cheese is fine, but not this early in the game.

Getting pushed by the blocks, notably off the ⅃ in the test world (reminds me of swimming in Dire, Dire Docks where you clip the floor while swimming and get pushed forward) looks like you can gain half a block with a perfect hit

Related in the test world you can jump the whole way on the floor without having to run under the ⅃, the easiest way to see this is line the ⅃ up so it's touching the right end of your monitor (maybe a little off the monitor if you can't get it) then jump twice towards it and you will clip through.


On the second screen: The jump to the blue platform is hard but possible and eliminates the need to run on the red platform as you can jump straight away to another platform. I'd like to find a setup that makes this at least semi-consistent.

https://pasteboard.co/bfJT7sJIuTSt.png The red platform is the lowest of the first group of three floating platforms on the second screen, blue is the middle platform.


I'm not super familiar with meson so I was having trouble building.. I'm guessing that you need to set the option somehow for what engine you want to build, but I wasn't able to figure out how.

I managed to build the sdl2 engine by hacking the meson.build file, but could you tell me how you're supposed to do this?

Looks really cool! I did a similar from scratch side-scroller game for a project in college that was a lot of fun. Thanks for sharing!


Thank you for trying out my game! The intended way to build a single engine/port (e.g. SDL2 in your case) is to run:

    meson setup -Dengines=sdl2 build
    meson compile -C build


Thanks so much!!


I wasn't sure I would be able to play on mobile but I was pleasantly surprised by the simple controls and managed to reach the third scene of chapter 1 before getting stuck, so, good job.

I don't think anything needs to change with the controls, the people saying that aren't the audience and don't have the genre literacy to know what this type of control setup is doing(for those watching: Jump King, Getting Over It both have the same kind of ruthless difficulty).

Regarding the technology, it's healthy to simplify the architecture - I've played a lot with the same kinds of thought processes of adding a lot of abstraction and gradually moved towards simpler answers. You already have a lot of answers that work, but my own take is that games of this type and scale need, roughly, these pieces:

* A "god object" entity model(every entity is one struct containing all necessary state for all possible behaviors). Going beyond that adds memory savings that you don't need for modern machines, or modularity that will introduce more to debug.

* State machine formalisms to describe what screen to draw, how to transition between them, what animations to play, and other behaviors. All control flow logic encompasses the behavior of a state machine, so you have to decide which formalism is best to use for the context and will aid debugging. That's why people sometimes gravitate towards coroutines and other high-level abstractions. Simple "scripting languages" that just describe a sequence of opcodes are something that frequently appear in old games.

* A simple constraint solver for the platforming physics. The problem that all physics systems have to solve is "there are multiple possible solutions that will resolve this motion: which one is the best?" This tends to be obfuscated in platforming physics by a convention of throwing away information about potential solutions early(e.g. moving on the x axis and resolving collision there, then doing the same on the y axis). Instead I recommend breaking down the motion into multiple permutations of "x then y" vs "y then x", then sorting and ranking the outcomes relative to a heuristic of unimpeded free movement. It's a more expensive check that also gives a better-feeling result.

* A pipeline to load the graphics, collision and scene assets. Although you can hard-code a lot of these things in a simple game, it can be convenient to export them from a spreadsheet and make it data-driven. Things like describing what "chapter 1" is are basically asset problems. I noticed that you have dialogue boxes described with a tileset and code to draw the tiles. This is how I would have done things once, since it is "how they used to do it" - today I would be likely to turn everything at runtime into an image, and to build the text itself, compile pre-baked images of text in the build process using tools like imagemagick. That makes the runtime simpler and reduces the kinds of assets it tracks, while allowing total freedom with the content of dialogues.

Sometimes with these projects, the engine development is being used to avoid content work. I suggest a physical graph paper notebook to create a location for that content to be designed, and to engage in some blind contour sketching to warm up to doing anything visually-oriented. When you have a whole storyboard for the game it becomes easier to conceptualize what code is necessary to get the effect, and do exactly that instead of generalizing it.

Two commercial "back in the day" games I would also look at for more inspiration: Xargon, Little Big Adventure 1. Both written in C, original source code is available. Studying those will give you a sense of where you are relative to finished work.


> Jump King, Getting Over It both have the same kind of ruthless difficulty

Yes, that’s exactly the kind of thing I was going for! Though I will say: I feel like my controls, although difficult at first, are easier to get used to than either of those two, or at least they were to me. After a while playing with the game, you “get the hang of it” so to say, and it starts feeling more natural.

Also, thank you for your advice! I have some thoughts/questions about some of it:

> Things like describing what "chapter 1" is are basically asset problems.

Well, my current solution is to encode the different “assets” (chapters, characters, images) as source code, and link them with the rest of the game at compile time. The biggest benefit is that I end up with a single executable that is easy to run, and don’t need to bother loading assets at runtime. To me, it doesn’t seem to matter ultimately whether I store information about how a chapter should play out as plain data or as code, and making it code seems simpler insofar.

Though a lot of the assets the game uses (e.g. the dialogue boxes you mentioned) are generated during intialisation, and then just displayed as a set of images during gameplay. Effectively, it shifts some “during build” work to “when the game starts”. The benefits include a simpler build system and fewer required build dependencies, besides being more straight‐forward to work with.

> A simple constraint solver for the platforming physics.

I don’t think I fully understand what you mean by this. Are you saying you feel like it would be better to avoid performing collision checks in order of axis? I.e. that it would make more sense to perform the checks in conjunction somehow?


Nice job! How does the drawing work?

I enjoyed trying to reach the very top of the test level. Spotted your greeting :)


The character sometimes passes (horizontally and vertically) through walls/ceilings, and I can't seem to control when this behavior occurs.

Is there a special game mechanic in this platformer?


If a platform is one tile tall, it will act as a semi‐solid that you can jump up through. If that’s not what you mean, then any kind of evidence (like a video or a screenshot) or some description about what you were doing that caused it would be very helpful!


> Over time, the game’s code became complicated, and I decided to use advanced features such as coroutines and higher order procedures, besides general overly engineered abstractions.

> But then, over time I grew to feel like a lot of those abstractions didn’t really add anything to it, and just made it more complicated (and oftentimes more buggy) than it really needed to be, so a few weeks ago, I made a significant refactoring to it to remove all of those abstractions without removing almost any features from the game.

Abstractions, while incredibly useful, can be overrated.

When I started writing my first C game about a decade ago, I used C89 too. However, after some time I realized that C89's limitations don't make my code cleaner at all and just make it more annoying to write. Since most of those games are game jam projects written under extreme time pressure, it made absolutely no sense for the language to pester me with pointless things like variable declarations outside of for loops or lack of designated initializers, so my toolkit uses C99 now. Initially I also made some use of nested functions, but I disable GNU extensions these days too.

Now I have about 40 games written on top of that codebase, a few of them being bigger projects. I've ended up with an "engine" of sorts that I have never really written - I've been always writing games, not an engine, and only then abstracted things away that actually begged to be abstracted. It did grew some fancy features and abstractions over years of using it, but not every game benefits from using them, so they're completely optional. In its essence it's mostly just a toolbox with handy things I find useful when writing the sort of games I usually write. Writing a game is essentially just filling up functions that update its state based on input and passing time, and that render an image based on that state. The engine can then maintain a state machine between various "stages" implemented that way. For many simple games, that's exactly the abstraction level they need.

I've often seen people making big eyes after being told that I use C to write games in under 48h at game jams - and yet I never really felt disadvantaged when compared to other teams that used big and fancy ready-made engines. Sure, there are kinds of games where I'd use a big engine like Godot too, but... most of those games aren't that kind :) Keeping things simple actually helps, especially where what you need is being able to jump straight into implementation instead of having to think about how to fit your ideas into whatever abstractions you ended up with due to your framework of choice.

(although one thing where using a ready-made engine helps a lot is collaborations with random programmers on your team - however, I usually either work alone or with a single artist, so I can enjoy programming on my own terms :))


> I've often seen people making big eyes after being told that I use C to write games in under 48h at game jams - and yet I never really felt disadvantaged

Another comment posted that you would have to be "a madman" to code a game in C, and I was genuinely baffled. Gamedev is one of the realms C truly excels in and is actually relatively easy. Once you're used to it, and know the boilerplate that needs to be hoisted, it's not particularly difficult or time consuming.

It's weird to me that (I assume, at least) professional developers still have this weird mysticism over C as if it's just arcane incantations compared to what they do.


That sounds awesome! This is the most advanced game I have ever made, so I don’t feel like I have as much experience as you. I’m glad you seem to have liked what I came up with in some way or another!

I feel like designated initialisers can be circumvented by simply assigning the fields individually. It’s a bit more verbose, but I feel like it works almost as well, the biggest downside being that the compiler won’t warn you if you miss some.

Regarding loops and variables, I opted for just declaring all variables at the top of each function definiton (only using an initialiser for ‘static’ variables), which I find almost reminds me of how Python handles variables, except with a chunk of declarations at the start of each function.

Of course, this is all subjective, but I find this style looks neat, and I don’t think it gets in my way at all, personally.


But what is the actual point of clinging to C89 for a game like this? Do you expect to port it to obscure DSP platforms?

Note: I freaking love C and often defend/advocate it here and elsewhere. I just don't understand why anyone would choose a more limited version. Artistic expression? Something something limitations something creativity?


MiniFB and SDL2 are both libraries, just FYI.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: