Loved it, and would concur that having a better sense of what the hole looks like from above before the shot (or being able to click to see the hole from above like a map) would be awesome. But, I loved playing it. And, I sucked.
Any recommendations to learn to build cross-platform games like this?
I work on frontend projects day-to-day. I'd love to build a simple cross-platform game in C/Zig/C++ for some variety and to learn lower-level programming.
I've looked at Handmade Hero in the past but its specific to Windows at the moment.
This might be obvious, but one of the key activities is to minimize platform-dependent code so that most of the game logic is portable and shared across all platforms. So no Win32-isms or Cocoa-isms or <unistd.h> littered all over your game.
One way (out of many) to do this is have a cross-platform interface for each non-portable thing, with separate platform-specific implementation files that you swap in depending on which platform build you're doing. For example, if your game has sound, you'll probably have a platform-independent sound.h the rest of the game calls into, and then sound_windows.c, sound_linux.c, sound_mac.c, etc. files that contain the platform-specific implementations of those functions. Repeat for graphics, input, and other things that cannot be done (or you don't want to do) in a portable way.
Another thing is the game loop. Back in the DOS days, when you were the only thing running on the system, you implement main() did something like:
while (game_running) { do_everything(); }
Modern platforms expect to be in the driver seat, and will instead call into your code, so this turns into something like:
function windows_call_me_please() {
do_everything();
}
Obviously this is a simplification, and there is a lot more to it, but hopefully it answers some basic questions.
Also choosing a base platform agnostic framework like SDL which has a lot of the OS interactions (keyboard & mouse) already abstracted away for you gets you pretty far on the cross-platform road.
Handmade Hero gives good advice how to modularize to make a game portable. For many things you want to achieve, you can have the platform call the game instead of the other way around. Essentially it's (asynchronous) event driven architecture which enforces modularization.
Think of a system as shipping data packets around, then it should be much easier to see how to swap out the platform parts.
Libraries can be helpful. But at the baseline, what you have to solve is the problem of adjusting your dependency load such that adding new target platforms is a matter of rewriting small details, which means taking a language-designer's view of your system and saying "OK, if I need to automate the rewrite, then I need a compilation mechanism". Sometimes you have to change asset formats or runtime I/O mechanisms, but you can select what layer of the design you are aiming to standardize and go from there. e.g. records of chess games have a few defined formats. They don't need to track the assets used to draw the board, or the account information of the players.
If what you want is all of it: every pixel and frame of output the same, all input devices treated identically across all environments, then you'll gravitate towards a VM type of design, like Another World, Z-Machine or SCUMM, to name three well-known examples of such a design.
It’s 99% due to nostalgia but look at chocolate doom. It’s a tightly coded 2.5D game. There’s TONS of documentation on how it works. Recreating the engine in a familiar language and playing the WADs of you childhood is fun. But if you want 3d with OpenGL maybe look at quake. That’s how I learned GL.
Level 20, but wish there were more. The fun ones were trying to skip most of the course from the tee. Throw a leaderboard on there and no one's going to be working.
Great work, awesome orchestration of cross platform libs.
Impressive to see C being able to create x-plat 3D games that run in Browser + popular Desktop + Mobile OS's.
Typically there's big companies with large dev teams working on accomplishing such a feat & this project does it with with a 50 year old language and a handful of libs
Emscripten. If you use something like SDL porting is trivial.
Working on a project to port a 20 plus year old c/cpp code base to the web. The tooling was like 98% there but I've had to upstream a few fixes or find a workaround every now and then.
Good on you man. If I got an email that said “opportunity: port 20+ cpp app to web” I would chuckle and close that sucker real quick.
Modern cpp I maybe can deal with. That 90s stuff is real bad.
It is remarkable to me that, once I’m in full screen mode and into a level, I can’t tell that this isn’t the desktop version. All the same crispness and responsiveness is there. I probably knew in my mind that this is possible these days, but every bit of muscle memory I apply here is telling me that Wasm, Emscripten, etc have become quite good, in a way that an unfamiliar game couldn’t tell me.
Thanks for giving me an easier way to share this good old game with friends!
Are you the same Penwielder who made Penwielders Scrapbook (a Neverball level)? I seem to have an uncanny memory of usernames from the old Neverball forum. Anyway, your name inspired me to find that level and add it to the in-game downloads.
That depends on the code. Neverball has acquired a high level of portability thanks in large part to SDL2 and OpenGL (ES). So compiling to Emscripten was mostly about rearranging the pieces that were already there. My original patch was surprisingly small. (https://github.com/Neverball/neverball/commit/517c93b7bcf63f...) I would say I spent more time on integration with browser APIs, so it would behave and feel more like a web app.
With the sokol libs as platform abstraction it works quite well to develop and debug a native build in a regular C/C++ IDE and then only cross-compile to WASM. Most bugs are platform agnostic and would also happen in the native build. For the rare case that problems only happen in the WASM build, regular printf-debugging usually works well enough, or in case of WebGL specific problems there will be validation errors on the JS console from the browser's WebGL implementation).
Seems really great, I played all the way through. Here's a couple low-hanging QOL suggestions:
* Use right-click for the camera control, rather than left-click away from the ball. It's unnerving to have the camera change while you're trying to line up a shot.
* Show a shot counter, and assign each hole a par.
* Slightly tune down the friction. I had the ball stop dead on slopes a couple of times, and it stopped way earlier than I expected on slower shots.
After spending my career working in higher level languages, I'm always impressed by what people can do with languages like C. It's not just for drivers anymore.
It used to be the norm to write games in C. In fact it used to be the norm to write them in assembly, C was seen as a higher level abstraction that sped up games development once upon a time.
Yeah, I'm pretty sure most game devs of that era were doing something similar. It seems that once games went 3D, it was too impractical to do assembly anymore. So basically a lot of early PS1/N64/Saturn games were likely some pretty rough C code if I had to guess.
It's less that but that the C tooling matured to the point where it was actually good enough to use. Particularly in terms of good enough output. Tight loops and often called functions were and sometimes still are (in SIMD terms) written in assembly so they could be hand optimized.
More exotic hardware also relied on the vendors providing a C compiler and the quality there could vary wildly.
It was never just for drivers. Most of what you use in high-level languages is just syntactical sugar for things that might take three lines in C instead of one line in your usual language.
Three uncorrelated lines, two of which take a buffer, length pair which is also
uncorrelated, calling something with undefined behavior if the buffer and length aren't the correct pairing.
This is excellent. I had a similar (but not as well executed) game on an old Sony-Ericsson phone back in the day, and I spent way to much time playing it. Took me back. Thanks for sharing this!
They seem to have a build script for "OSX" but maybe it's just incomplete / work-in-progress? It uses glfw, which I know for sure still runs on Mac because I use it in my own cross-platform graphics projects.
TBF, the only reason why macOS isn't currently supported seems to be some minor build system details (e.g. the toplevel CMakeLists.txt file has explicit support for Windows, Linux, Android, iOS and Emscripten, but not macOS).
I'd love to hear more about the lightmapping interpolation for moving objects. I wonder what kinds of efficiencies the author had to find to make it performant.
looks like it doesn't (yet?) have networking support. my fiancée plays GFW almost daily after it was on sale recently, it's a fantastic casual to play both with friends or with strangers online, pretty chill community
I was doing it daily for a while. For some reason the acquisition news made me stop, or maybe it was just the timing. Maybe it's time to get back into the zone.
-- Wanted to share a memory. Growing up there was this put-put course that was called Magic Mountain that my family and I used to go to. It was sort of a big hill that they made into a mountain so that when you were walking on it you felt like you were a giant. Anyway they had this small stream that came down the side of the mountain and wound in-between all of the courses. There was this one hole where the stream intersected at the top of this really obnoxious little course with lots of switchbacks and weird angles. You were "supposed to" (quote on quote) jump the stream and continue on with the hole, but if you managed to knock your ball into the stream there was a ball catch at the bottom that would push the ball back onto the course near the hole so it didn't get washed out to the bottom of the entire mountain. It was possible to make a hole in one if you knocked the ball into the stream - the little river would carry the ball down to the bottom and then gently push it into the hole. Your game reminded me of that memory. Thanks.