3 ms·
It's not really related, but that reminds me of how some games handled slow moving objects on the Mattel Intellivision console. Your code that runs every frame
by tzs 2y ago
It's not really related, but that reminds me of how some games handled slow moving objects on the Mattel Intellivision console.
Your code that runs every frame to update the graphics logically wants to do something like this:
X += Vx
Y += Vy
where (X, Y) is the location of the object at the start of the frame, and (Vx, Vy) is x and y velocities of the object in pixels/frame.
To allow for velocities that aren't an integer number of pixels per frame and that are slower than 1 pixel per frame you'd want to actually store X in a fixed point format, say (Xi, Xf) where Xi is the integer part of the X position, and Xf is the fractional part times 256, so X = Xi + Xf/256. Similarly for Y. The object only actually moves on the screen when Xi or Yi changes.
Similarly velocity would also be in that format: Vx = Vxi + Vxf/256, and similar for Vy.
With that, the position update in your loop would be something like this:
Xf += Vxf
if that wrapped
Xi += 1
Xi += Vxi
and similar for Y.
For each object you end up needing 8 bytes (1 byte for each of Xi, Xf, Yi, Yf, Vxi, Vxf, Vyi, Vyf). That doesn't sound like much but the Intellivision only had something like 240 bytes in the console available. (If you couldn't get your RAM requirements down to that it was possible to have extra RAM in the cartridge but that would raise the cost).
So someone figured out that you didn't actually need to store Xf and Yf. Just generate then at random as needed! The loop then becomes something like this:
rb = random_unsigned_byte()
if Vxf + rb wraps
Xi += 1
Xi += Vxi
and similar for Y.
That turns out to work quite reasonably. Essentially it is interpreting Vxf as meaning that the object has a Vxf/256 chance of crossing a pixel boundary on a given frame. Thinking of it that way then suggests getting rid of the addition of the random byte with a wrap check and just doing a compare instead:
if Vxf > random_unsigned_byte()
Xi += 1
Xi += Vxi
and similar for Y.
Net result: we've cut the RAM for storing position and velocity from 8 bytes per object to 6, at the cost of needing to generate 2 random bytes per object per frame.
- teo_zero 2y agoInteresting. I know this is a purely academic question as these constraints are something of the past, but was it really necessary to have 16 bits for the velocity? Was the ratio between the quickest and the slowest objects more than 256 times?
- kevin_thibedeau 2y agoOn 8-bit micros without a barrel shifter you'd naturally want to constrain number formats to byte boundaries. Much better to have excess fractional precision than deal with extra bit twiddling. Even with 16-bit Intellivision, the scarcity of RAM would drive the use of the smallest practical representation.
- tzs 2y agoAs kevin_thibedeau noted, the bit twiddling instructions available were limited. The Intellivision used a GI 1600. Here's a copy of the manual [1] and a Wikipedia article about it [2]. It can shift or rotate by 1 or 2. But you are right--a game might not need 16 bits for velocity. Suppose a game needed a maximum of just under 4 pixels/frame X velocity and needed a minimum of 1/64 pixel/frame. It could have stored velocity in one byte like this FFFFFFII where the Fs are the bits of the fractional part of the velocity in units of 1/64 pixels/frame and the Is are the integer part in pixels/frame. By putting the fractional part in the upper bits and the integer part in the lower bits no shifting will be required--just AND and OR. Then updating X would be something like this: X += Vx % 0x0003 if Vx > (random_unsigned_byte() | 0x0003) X += 1 You probably could even skip the OR. It makes no difference if the integer portion of velocity is 0. If the integer part is 1 skipping the OR in effect increases the velocity by 1/256 pixels/frame. Similarly if the integer velocity is 2 or 3 skipping the OR increase it by 2/256 or 3/256 pixels/frame, respectively. I don't think anyone would notice. For two objects in a race all the way across the screen with the same Vx, with one including the OR and one not, my back of the envelope calculation says that the speed boost would only get that one to the finish line less than half a millisecond earlier. [1] http://www.bitsavers.org/components/gi/CP1600/CP-1600_Microprocessor_Users_Manual_May75.pdf http://www.bitsavers.org/components/gi/CP1600/CP-1600_Microp... [2] https://en.wikipedia.org/wiki/General_Instrument_CP1600#Instruction_set_and_registers https://en.wikipedia.org/wiki/General_Instrument_CP1600#Inst...
- tzs 2y ago> X += Vx % 0x0003 Oops! That should be & not %.