Asteroids and the ship in my MMBasic game are polygons that get rotated by a small angle every frame. My first attempt was the obvious one: compute COS and SIN once per object, then loop over the vertices and rotate each by hand. It works, but it eats frame time as soon as the screen fills up. The MATH family in MMBasic does the same job in fewer lines, and faster.
The obvious version
Here’s how I had it first, a FOR loop per asteroid with its own cos_rot and sin_rot:
SUB DrawAsteroid_Old(idx)
LOCAL INTEGER i
LOCAL FLOAT cos_rot, sin_rot
LOCAL INTEGER temp_x(15), temp_y(15)
cos_rot = COS(RAD(arot(idx)))
sin_rot = SIN(RAD(arot(idx)))
FOR i = 0 TO apoints(idx) - 1
temp_x(i) = ax(idx) + (avertices(idx, i, 0) * cos_rot - avertices(idx, i, 1) * sin_rot)
temp_y(i) = ay(idx) + (avertices(idx, i, 0) * sin_rot + avertices(idx, i, 1) * cos_rot)
NEXT i
END SUB
Correct, readable, and every iteration costs MMBasic four multiplications plus two additions per vertex. With 40 asteroids of 8 to 12 corners that adds up.
Using MATH V_ROTATE and MATH SLICE
There’s a lot more in MMBasic’s MATH commands than most tutorials show. I stopped storing the vertices in a 3D array and switched to two 2D arrays, one for x and one for y. Then I pull a row out with MATH SLICE, rotate it with MATH V_ROTATE, and add the position with MATH ADD.
DIM FLOAT vert_x(MAX_AST-1, 15), vert_y(MAX_AST-1, 15)
DIM FLOAT src_x(15), src_y(15), tmp_x(15), tmp_y(15)
SUB DrawAsteroid_New(idx)
MATH SLICE vert_x(), idx, , src_x()
MATH SLICE vert_y(), idx, , src_y()
MATH V_ROTATE 0, 0, RAD(arot(idx)), src_x(), src_y(), tmp_x(), tmp_y()
MATH ADD tmp_x(), ax(idx), tmp_x()
MATH ADD tmp_y(), ay(idx), tmp_y()
POLYGON apoints(idx), tmp_x(), tmp_y(), RGB(WHITE)
END SUB
The data flow stays the same. What changes is that the rotation isn’t a vertex-by-vertex BASIC loop anymore, it’s a single MATH V_ROTATE call running internally. MATH ADD with a scalar shifts the whole array in one go. A few lines per asteroid, and the loop disappears.
I ran both versions head-to-head with 40 asteroids of 10 vertices over 1000 iterations. The MATH version was clearly ahead, the exact ratio depends on the device and the vertex count. The prerequisite was a refactor from a single 3D vertex array (idx, vertex, xy) into two 2D arrays for x and y, otherwise MATH SLICE can’t grab a row. Since then, before every geometry-heavy inner loop I check first whether MATH V_ROTATE, MATH ADD, MATH SCALE, MATH SLICE, or MATH SET already does the job, and most of the time one of them does.