8-bit Projectile Logic: How do you do it?

PowerfulBadBoy

New Member
Jul 17, 2019
24
11
3
Please note that when I refer to projectiles I refer to them in the gaming sense not the physics sense IE: "Bullets" that are unaffected by gravity

parodiusgameplay.png

If you've ever played Gradius (or even better... Parodius!) for the MSX or other 8 bit consoles, you'll notice the massive amounts of homing projectiles.
Whether it's wall turrets spewing bullets in your general direction or floating baddies moving directly to your current position, it's a great mechanic!

In today's world of vector maths and floating point values it's relatively simple to do this sort of thing but my one question is;
How would you do this sort of thing on such limited hardware without resorting to C's built in floats? (As that would most likely be inefficient)

How would I calculate the "angle" that the bullet would take?
How would I deal with moving it in values of less than 1 pixel? (Say for instance a bullet needed to move 1 vertical pixel for every 2 horizontal pixels)
Would it be most efficient to use a bunch of pre-calculated tables for this sort operation?
 

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
Ask a series of questions:
is it up, down or on a row with you
is it right, left or on a column
Now you already have chosen between 8 vectors just by answering those questions.
Want better, start comparing how up to how left, multiplying and dividing may be slow but multiplying and dividing by 2 is fast.
Is it more up than left?
Is it more than twice as much up as left?
etc.
 
  • Like
Reactions: PowerfulBadBoy

JoshuaScholar

New Member
Nov 6, 2019
24
10
3
Third option, take the vector toward the object, and divide it down to something approaching a pixel size but fixed point. Keep the position of the projectile in fixed point... Once again, divide is slow but divide by 2 isn't and you can do that repeatedly and you could also do other tricks.

I like math so I always keep the impression that even though the 6502 is slow as molasses there's always a fast way to approximate any math you need.
 
  • Like
Reactions: PowerfulBadBoy

PowerfulBadBoy

New Member
Jul 17, 2019
24
11
3
Another cool trick I've thought of to get more angles and speeds is to use a SIGNED integer for projectile speeds (x and y) with negative values meaning "move 1 pixel every ABS(value) frames" seeing as a projectile is unlikely to move at a speed of over 64 pixels per frame.
 
May 22, 2019
504
266
63
Yes, fixed point integer math is how I'd do it. When I worked in banking, we used to actually store bank account information (like balances) as a 32 bit integer. You could do the same thing for sub-pixel positioning for objects in a game.

For example, if your position can be expressed in one byte, use two bytes. You use the high byte when displaying the object, but use both bytes when doing the math to move the object.

Let's look at a simple case. This is the Y component of a vector:

Position: $20 A3, Velocity: $00 62

In the first frame, the object displays on row $20.
$2003 + $0062 = $2105
So in the second frame, you display the object on row $21
$2105 + $0062 = $2167
Now the object is on row 21.

So that's your sub-pixel positioning taken care of.

The other issue is how to handle computing angles. Let's assume that a turret or ship can be positioned in 256 positions. This lets you store 64 SIN and 64 COS values in your lookup tables. Since those values with necessarily be two bytes, you need 256 bytes to store the table.

You can pre-compute the sins with a loop like this:

Code:
0 PRINT "{clear}"
10 DIM SH(64):DIM SL(64)
20 DIM CH(64):DIM CL(64)
30 S= /128
40 FOR A=0 TO 64
50 SH(A)=INT(SIN(A*S))
60 SL(A)=INT((SIN(A*S)-SH(A))*256)
70 CH(A)=INT(COS(A*S))
80 CL(A)=INT((COS(A*S)-CH(A))*256)
90 NEXT
REM VERIFICATION LOOP
REM THIS PRINTS OUT THE ANGLES SO YOU CAN CHECK THEM BY HAND
REM THE ANGLE AT 32 SHOULD BE 0 181 (0.707 * 256)
100 PRINT:PRINT "ANGLE","SINH","SINL","COSH","COSL"
110 FOR A=0 TO 64 STEP 4
120 PRINT A, SH(A), SL(A), CH(A), CL(A)
130 NEXT

Remember that SIN and COS use radians in BASIC, and a full rotation in Radians is 2π. So to get 256 even angles, we need to divide 2π by 256. However, we only need to store 64 values, since you can get the other half of the curve by negating the number. ie: SIN(RIGHT) is 90 and SIN(LEFT) is -90.

Of course, if you have the room to spare, you can store all 128 values, and this will speed things up slightly.
 

BruceMcF

Active Member
May 19, 2019
150
49
28
One of the old tricks was to only allow things to fire at the 8 cardinal and sub-cardinal directions (N NE E SE S SW W NW). Then the pixel direction is always either horizontal, vertical, or at a 45 degree angle. So if you have subpixel virtual positioning, you only need to know how many sub-pixels it moves per frame horizontally / vertically and how many sub-pixels it moves per frame when moving at a 45 degree angle.
 
  • Like
Reactions: PowerfulBadBoy

MJoergen

New Member
Nov 13, 2019
1
1
3
Denmark
github.com
The way I would do it - as others have hinted at - is to use fixed point arithmetic, more precisely have 8 fractional bits. So a pixel x-coordinate will use one extra byte, where the lowest order byte is the fractional part. That way, you don't have to do any shifting. So an increment of half a pixel corresponds to adding the value $000080.
 
  • Like
Reactions: PowerfulBadBoy

GregZone

Member
Sep 29, 2019
34
24
8
As others have noted, the key is that in the 8 bit world you are dealing with low pixel resolution (relatively speaking), often with only 8 bit math required, which means many complex math functions can actually be resolved into quick look-up tables (where the required set of possible input values is actually quite small).
This also ties in with the limited CPU performance available, meaning you often need to find a faster solution than implementing the actual real-time math. So a look-up table with the pre-determined results for your typically small set of (actually required) input values, can often be the best solution.