dmbaggett
So I made space invaders out of box drawing characters.
BASIC was slow so I tried using C. (Yes, there was a minimal C compiler for the H89!) But then C was too fast and “for (i=0; i<10000; i++);” didn’t seem to slow things down like it did in BASIC so then I was stumped. “C is too fast for games!” — me
The H89 had a built-in monitor and a 5 1/4” floppy drive. Its precursor, the H8, was much like this emulated S100/Altair, with LEDs and switches as your only I/O.
The packer was the final step after a level was pre-sorted and otherwise processed. It was quite fast, so it added only a little bit of extra time to the primary work of pre-rendering every frame of the level to recover the sort order (which typically took around an hour).
I did experiment with solver algorithms but they were so obviously going to be too slow that I abandoned the idea.
Another challenge was dealing with foreground objects: you have to somehow sort these into the pre-sorted background polygons. This mostly worked with bucket sorting but we had to use the gross hack of allowing each foreground object to have tunable “push-back” to fix sorting glitches. This required manual work for each level.
Finally, while precomputing the per-frame sort order for a game like Crash would be trivial now, in 1995 we had to build our own Beowulf cluster thingy and farm the level precompute out in pieces to the artists’ SGI workstations, and levels typically took an hour to process. The artists LOVED that. :)
And this thread comes full circle: Mark Cerny actually significantly improved the performance of my original version of the Crash collision detection R3000 code. His work on this code finally made it fast enough, so it’s a really good thing he was around to help out. Getting the collision detection code correct and fast enough took over 9 months —- it was very difficult on the PS1 hardware, and ended up requiring use of the weird 2K static RAM scratchpad Sony including in place of the (removed) floating point unit.
GOOL was mainly used for creature control logic and other stuff that didn’t have to be optimized so much to be feasible. Being able to use a lisp dialect for a bunch of the code in the game saved us a ton of time. The modern analogue would be writing most of the code in Python but incorporating C extensions when necessary for performance.
Andy made GOAL (the successor lisp to GOOL) much more low-level, and it indeed allowed coding essentially at the assembly level (albeit with lispy syntax). But GOOL wasn’t like this.
https://ifdb.org/viewgame?id=c896g2rtsope497w
Graham Nelson ported my port to his Inform language, and Inform is probably your best choice if what you actually want to do is write a (plain text) adventure game.
If you want to learn C programming, writing a text adventure in C would be a fun learning project! But aside from pedagogy there’s no real reason to write a text adventure in anything other than Inform, TADS, etc. Not only is it much easier to use one of these purpose-built languages, with Inform you get multi-platform compatibility going back to the 8-bit era for free!
Personally if I had any free time, I’d be more interested in looking at how to use a frontier LLM like llama as an integral part of a text adventure. There was something like this using GPT-2 circulating on here a while back, but it was pretty rough.
However, it’s clear that if you figured out how to precisely control the LLM so it didn’t produce crazy stuff, you could realize the dream of truly realistic NPCs in these games. Text adventures would seem to be a perfect laboratory for experimenting with this.
https://en.wikipedia.org/wiki/ATPCO
However, interpreting this data properly is decidedly nontrivial (>1M LoC).
Pricing does not imply booking is OK, though. And even circa 1999, Southwest hassled us (ITA Software) about even showing their fares, without us offering any way to book any flights on any carrier.
Sony explicitly forbade this, presumably because they envisioned the API established by the C SDK as a way to ensure future backward-compatibility. We just ignored the rules and hoped the superior performance we could achieve would overcome any bureaucratic obstacles.
We also had to reverse engineer some of the PS1’s really nice capabilities. I only learned the hardware supported vector pipelining for strip rendering by noticing the coordinate values move from one register set to the next in the debugger.
Seeing that was a revelation: when rendering a polygonal mesh, you naively have to project three coordinates for each triangle. But if you convert your mesh into sequences of polygonal strips where each triangle shares an edge with the next triangle in the mesh, you only need to project a single additional vertex for each additional polygon in the strip. Observing the behavior in the debugger, it was obvious to me that the Sony hardware had in fact been optimized for this approach. But not only were we not given any documentation on this, we were instead told to use the C SDK, which didn’t expose this capability at all.
The PS1 also had 2KB of “scratchpad” that was slower than registers but much faster than RAM. Hearsay was this was a way for the CPU designers to make use of the physical space on the die meant for floating point instructions which the MIPS CPU in the PS1 didn’t have.
I used the scratchpad to store projected 2D “stamps” of the 3D world (stored in an octree) and Crash: a kind of low-res Crash Flatland. I could then check for X/Y collisions between Crash and the world very rapidly by inspecting the flatland bitmap stored in the 2K scratchpad. This was Mark Cerny’s idea; he was our producer on the Crash games and has also been responsible for PS2 through PS5 hardware.
Another Python versioning gotcha I've never see discussed is use of instructions like AVX2 in C extensions. There seems to be no way in Python versioning to say "this version is for CPUs with AVX2 instructions" so I guess public C extensions have to be compiled for the least common denominator instruction set. Does anyone know otherwise? How do other packaging systems like cargo deal with this?
On the plus side, my crappy pacman clone was good enough to convince Andy Gavin to (years later) bring me on as the first developer hire at Naughty Dog. The system works! (I guess?)
What I think you’re seeing in Warped is what we did with Crash 1 and 2 as well: I had to sort the foreground object polygons into the pre-sorted background polygons. That was modified bucket sorting, but there were manually tuned adjustments to make it work and stable between frames.
None of these techniques is relevant anymore given that all the hardware has Z buffers, obviating the need to explicitly order the polygons during the rendering process. But at the time (mid 90s) it was arguably the key problem 3D game developers needed to solve.
A key insight is that sorting polygons correctly is inherently O(N^2), not O(N lg N) as most would initially suspect. This is because polygon overlap is not a transitive property (A in front of B and B in front of C does NOT imply A in front of C, due to cyclic overlap.) This means you can't use O(N lg N) sorting, which in turn means sorting 1000 polygons requires a million comparisons -- infeasible for hardware at the time.
This is why many games from that era (3DO, PS1, etc) suffer from polygons that flicker back and forth, in front of and behind each other: most games used bucket sorting, which is O(N) but only approximate (and not stable frame to frame).
The handful of games that did something more clever to enable correct polygon sorting (Doom, Crash and I'm sure a few others) looked much better as a result.
Finally, just to screw with other developers, I generated a giant file of random data to fill up the Crash 1 CD and labeled it "bsptree.dat". I feel a bit guilty about that given that everyone has to download it when installing the game from the internet. :)