Rustenstein 3D: Game programming like it's 1992
tech.nextroll.com
tech.nextroll.com
Some of the interesting bits of the engine are open source: https://github.com/gh123man/Portal-Raycaster
Sorry to be pedantic, but I think you mean the opposite? Big reward for modest effort?
Then the ratio is indeed high!
And don’t forget about all the other ways such corruption could happen, use after free etc.
On top of all that, in managed languages you generally have a stronger runtime type information on top, that doesn’t accept arbitrary memory address to implicitly be read as executable code. Even explicit static casts from Object to more defined type will fail if the object is not of expected type. Code must be defined as function objects in the language to begin with.
It’s a trade-off; performance in games is more important than correctness anyways so I tend to agree with you here.
People should learn more about what is in the toolbox.
Also, classes are merely one feature of C++. The language is a far cry from 40 years ago, when it was "C with classes".
To the point that we have the Orthodox C++ movement, and educating people to move beyond "C with classes" that keeps being taught around the world is a common discussion subject at C++ conferences, and ISO C++ papers.
1. Prevents memory corruption
2. Have way better compilation time than C++ and Rust, absolutely horrid compile time for both.
3. Have all sorts of developer-ergonomics features (like being able to write functions in arbitrary order (compared to C/C++))
4. Have built in reflection (none in C/C++)
5. IDEs/autocomplete/refactoring tools etc, just generally work better for C#/Java/etc.
Garbage collection is probably close to bottom on the list why I would use GCed language when making an indie or hobby scale game
I would never consider using Rust simply because it takes too long time to compile even for relatively small projects.
Especially not for hobby or indie scale game projects.
Now try that against D, any .NET language, Nim, Swift, Go, or any other GC language that supports value types and unsafe programming.
Chapter 5 on the "The Garbage Collection Handbook",
https://gchandbook.org/contents.html
Or if you prefer chapter 2 on "Uniprocessor Garbage Collection Techniques" https://www.cs.cmu.edu/~fp/courses/15411-f08/misc/wilson94-g...
Plenty of SIGPLAN papers about the subject.
Despite all marketing talk, the real reason why Swift went with ARC was easier interoperability with Objective-C, otherwise Apple would need to build something like .NET's RCW/CCW for COM interop on Windows, which follows the same concept as Cocoa's retain/release calls.
On that count alone I would never-ever consider it in hobby/games project of any sort.
It doesn't matter what features a programming language has, if it takes seconds if not minutes to compile even relatively small projects (less than 50-100k LoC).
Then if you are using modern C++ tooling like Visual Studio, you can make use of hot code reload.
Here are examples in Visual Studio, and Unreal.
And if you're not doing that, you're either wrong, or have a terrible project structure (comparable to including literally every header in every source file in C++)
Personally, I'm now doing hobby projects in Rust because it just feels right. But I've done entire systems in assembly, so I am a bit of a control freak. YMMV.
I play a lot of games, from PC to console to mobile to handheld to VR, I really don't get these kinds of takes. Until games from indie to AAA fix their existing loading and stuttering and microstuttering and various other issues, clutching pearls over GC non-determinism seems so silly. There's no GC to blame for why you can stare at a static scene with a can in a console AAA game under development for 6 years in a mature engine and see its shadow flicker on and off. I find it hard to believe we'd be in a much worse situation if GC languages were even more prevalent. Part of this is that there's already a lot of sources for non-determinism in a C++ engine that can cause issues including frame slowdowns. Of course with effort you can eliminate or bound a lot of it, though the same is true for GC'd languages. Real-time GCs have been a thing for decades but sure you probably aren't using one, nevertheless you can often tweak a non-real-time GC in useful ways if you need to -- or as is mentioned every time this topic comes up, do the same thing you'd do in C++ by allocating big pools ahead of time and reusing things (i.e. not making garbage). Memory management is not the hard part of making a game, though the more complex and demanding the game or constraining the hardware the more you'll need to consider it regardless of language, and change up what's idiomatic when programming in that language.
Not that it matters, because any positive t is going to mysteriously always be too much, but the 1ms number is really underselling ZGC. Maximum of 1ms pause times was the goal, in practice they saw a max of around 0.5ms, an average of 0.05ms (50us), and p99 in the above-linked article of 0.1ms (100us). A more interesting complaint would be the amount of memory overhead it needs. Tradeoffs are everywhere.
Reading such gameport took >1ms, and you wanted more than one reading per second :)
I suspect LÖVE / LÖVR would perform better than Java for games, but haven't tested or verified myself.
---
[0]: https://love2d.org
[1]: https://lovr.org
But historically games have been boundary pushers, and a GC's overhead isn't really desirable in that space.
https://unrealcommunity.wiki/memory-management-6rlf3v4i
https://docs.unrealengine.com/4.27/en-US/ProgrammingAndScrip...
I would add to the list of feature to tackle next:
- Convert from 320x200 aspect ratio to 320x240 aspect ratio. You can do that by converting from 320x200 to 1600x1200. This is easily done with x5/x6 which give you the same aspect ratio as 320x240 and you get no pixel selection artifacts.
You know, it would be interesting to follow the Carmack's route: start from Apple ][ programming for a couple of Ultima and Wizardry spinoffs, port them to PC. Then move to 80286 to make a scrolling engine for a double trilogy, and move up to 80386 to make a Wolfie clone, and continue from there. The point is to use real world machines or emulated env for development. One can probably learn a LOT programming by that way, although much if it is irrelevant in modern gamedev...
The more I think about it, the more I believe if someone can pull this off, although many skills learnt are useless in modern programming (like 6502 asm or 80286 asm or whatever tricks to get the games run smoothly on retro platforms), but the amount of effort definitely would pay off.
I don't think one needs to walk the full Carmack road. I think whoever goes from Shadowforge to Quake is impressive enough. Plus one does not need to implement all these games, many of them share similar engines.
(Bad formatting...)
June 22, 1996 Quake id Software GT Interactive Programming
May 31, 1996 Final Doom id Software GT Interactive Programming
October 30, 1995 Hexen: Beyond Heretic Raven Software id Software 3D engine
December 23, 1994 Heretic Raven Software id Software Engine programmer
September 30, 1994 Doom II: Hell on Earth id Software GT Interactive Programming
December 10, 1993 Doom id Software id Software Programming
1993 Shadowcaster Raven Software Origin Systems 3D engine
September 18, 1992 Spear of Destiny id Software FormGen Software engineer
May 5, 1992 Wolfenstein 3D id Software Apogee Software Programming
1991 Catacomb 3-D id Software Softdisk Programming
1991 Commander Keen in Aliens Ate My Babysitter! id Software FormGen Programming
December 15, 1991 Commander Keen in Goodbye, Galaxy! id Software Apogee Software Programming
1991 Commander Keen in Keen Dreams id Software Softdisk Programming
1991 Shadow Knights id Software Softdisk Design/programming
1991 Rescue Rover 2 id Software Softdisk Programmer
1991 Rescue Rover id Software Softdisk Programmer
1991 Hovertank 3D id Software Softdisk Programming
1991 Dangerous Dave in the Haunted Mansion id Software Softdisk Programming
1991 Dark Designs III: Retribution Softdisk Softdisk Programmer/designer
December 14, 1990 Commander Keen in Invasion of the Vorticons id Software Apogee Software Programming
1990 Slordax: The Unknown Enemy Softdisk Softdisk Programming
1990 Catacomb II Softdisk Softdisk Developer
1990 Catacomb Softdisk Softdisk Programmer
1990 Dark Designs II: Closing the Gate Softdisk Softdisk Programmer/designer
1990 Dark Designs: Grelminar's Staff John Carmack Softdisk Developer
1990 Tennis John Carmack Softdisk Developer
1990 Wraith: The Devil's Demise John Carmack Nite Owl Productions Developer
1989 Shadowforge John Carmack Nite Owl Productions Developer
But from what I know, once you grind out a good enough game engine, essentially you can reuse the same code base again and again. The same with tools. For example Romero's Tiled served all Softdisk games plus Wolfenstein. The only new code is some new functionalities and a lot of new game logic code. Nowadays, you need 6 months just for the concept...
I bet nowadays if a two-programmer team (one for engine and one for tool, both do some designs) with some C++ and game dev experience goes into jam mode, with proper asset support (say he/she downloaded/contracted the assets beforehand), he can pop out games similar to the Softdisk ones in even shorter timespan. After all nowadays computers are much faster and we don't need assembly any more.
The tricky part is to stay on jam mode, and I believe the Softdisk team did wonder back then. Everyone was highly motivated and worked probably 80+ hours per week. They believed they could change the world and they did.
This is The Way
kids: off my lawn
Patience... A couple of hours to render a simple scene at 800x600, and displayed using dithering due to a lack of colors in the palette. But it worked.
Did it also do the classical line based rendering?
The dithering code was also interesting, because my graphics card could not do true color I put 3x64 RGB shades in the palette and then cranked up the brightness for tetris like sets of pixels to simulate a true color display. Worked pretty well.
This was the DSP board:
https://www.signalogic.com/images/Ariel_DSP32-PC.jpg
I managed to gently overclock it to 24 MHz without problems, it did get a bit warmer but nothing too bad.
But then I thought about the kids in 80s, the Romeros and Carmacks, and a little before that the Garriots and Gates, those kids started with BASIC but immediately picked up assembly given the chance, because that was how high performance code could be written and they just learned it on the way. No Internet, very few books even for rich kids (usually a [Machine] programming guide is good enough). Now that I think about it, some of them were barely teenages.
I bet the real difference is 1) Much simpler machine arch and 2) They had to learn it for writing games and other high performance software.
That gradual ramp made it work.
I really don't get how people are scared of low level programming, computer languages don't bite, don't attack on dark street corners, don't whatever.
Yes bad programs might corrupt some data, just like removing the plug with computer on might do the same and no one thinks twice about it.
Back in the day we didn't had tooling like Compiler Explorer to understand how BASIC would map to Assembly, only trial and error, alongside pen and paper.
Anyone that wants to learn Assembly nowadays can jump there, click on the high level constructs and see how they map to Assembly instructions for their favourite CPU.
That's why I have always been thinking that people actually should still start from an Apple ][/C64/80286/NES/SNES/GB these days, or maybe just embedded system for cheaper introductory tuition/quicker employment. You ignore all bells and whistles of hardware accumulated through the last 30 or so years and start from bare metal, real mode and gradually crawl from there. But without ignoring the bells and whistles of software tools people built for these platforms. I bet nowadays we have better tools for developing on these platforms. As some other commenter said, we can spin up a VM or emulator and directly look at memory and registers.
But many more people look for the fun. The good bits without the pains of ancient tooling, lacking debuggers and graphics tools, etc.
I noticed it in the examples too, I’m speculating wildly that the ray cast is even angular steps rather than pinhole projection. Totally reasonable to not correct it, IMO, I’ve long thought we should have more non-linear cameras in games.
let rayAngle = player.angle - player.fieldOfView / 2;
for(let rayCount = 0; rayCount < screen.width; rayCount++) {
// ... SNIP ...
// the ray moves at constant increments
let rayCos = Math.cos(degreeToRadians(rayAngle)) / precision;
let raySin = Math.sin(degreeToRadians(rayAngle)) / precision;
// ... SNIP ...
// increment the angle for the next ray
rayAngle += incrementAngle;
}
It's also using Euclidean distance rather than planar distance for the apparent wall height calculation.Generate two points which represent the left and right edges of the screen - you'd put them in at say 45 degrees left and right of the forward vector of the player. Then to generate the direction vector for each column of the screen, just interpolate linearly between those two points, and find the vector from the player to that intermediate point.
The old-school original methods used pretty small cos/sin/atan lookup tables to do the ray and then the correction calc. Using the linear method you end up with a couple divisions per ray that aren't there in the lookup method. Divisions were (and are, depending on the platform) pretty slow. Linear method still works with lookup tables but they're relatively huge.
Also IIRC With the linear method door-indents need a workaround.
Was there a "peak" in simplicity when games were at their simplest for a reader from 2022? That is, they are modern enough to not be obscured by historical weirdness yet still simple enough to be approachable? Perhaps Doom is simpler to understand than Wolfenstein for this reason?
This is true even for modern games, because you might implement something differently for a mobile game than you might for a high end PC.
"dos-like is a programming library/framework, kind of like a tiny game engine, for writing games and programs with a similar feel to MS-DOS productions from the early 90s."
cough
Castle Wolfenstein, by Silas Warner for MUSE
kids: get off my lawn
This is where it all started:
kids: get off my lawn
the grenade explosion was the most satisfying ever. "Take that Nazi scum!"
the 1st Rogue-like I ever made was something I called WolfenHack. my personal cross between Castle Wolfenstein and NetHack. I later genericized my engine and built a zombie apocalypse on top of it.
Fast Inverse Square Root: https://news.ycombinator.com/item?id=24959157
Excellent article btw.
Anyway maybe when Cranelift gets more mature.
I still hope that alternative backends like Cranelift will fix the compilation speed.
No need for compiler vomit caused by a single error.
Readable implementation (in C) is this: https://github.com/fogleman/Craft