PS2 Linux Programming
Sprite Movement with Angles
This tutorial will show how to draw and manipulate 2D sprites using the triangle strip primitive. Full freedom of movement of the sprite in 2 dimensions will be introduced.
The GS supports a Triangle Strip primitive, which is a series of continuous triangles sharing sides. The first triangle is drawn with 3 pieces of vertex information (1, 2, and 3 in the diagram below), and the succeeding ones are drawn whenever one further piece of vertex information is added (vertex 4 draws the second triangle in the diagram below). Full information on the format of a GS triangle strip is described on page 36 of the GS Users Manual.
Four vertices are required in order to define the quad using a triangle strip primitive. Each vertex is independently defined and has colour, position, and texture coordinates. Suitable transformations can be used to translate, scale, and rotate the sprite.
The structure used to hold the sprite information is shown below and is similar to the one used in the previous tutorial. The main additions are that the position on screen is now held as floats rather than ints, there are variables to store half the width and half the height of the sprite as floats, and there is a float to hold the angle of rotation of the sprite in radians.
float x, y; // position on screen (center)
int z; // z depth (big = near)
float fHalfW, fHalfH; // width/2 and height/2
int w, h; // width and height
int u, v; // offset from texture top left
unsigned char r, g, b, a; // colour and alpha
int FCountMax; // Counter max value
int FCount; // Frame Counter
int NFrames; // number of animation frames
float Rot; // Rotation angle in radians
Sprite coordinates are best defined in “local space” with the origin being defined as the point at which the sprite is to be rotated about. This makes rotating, mirroring etc. much easier. As the name suggests, local space coordinates define the positions of the vertices of the quad relative to an origin which is within the same coordinate system as the quad itself. In this example, the local origin will be defined at the centre of the quad, i.e. the point where the two diagonals of the quad meet.
The simplest 2D transform is translation. This is achieved by adding a translation amount for each axis on to the coordinates of all of the vertices of the sprite. For example, to translate a sprite 4 units right and 10 units down, 4 is added to the x component and 10 to the y component of all 4 vertices that make up the sprite.
Rotation is a little harder. When rotation is applied, the vertices are rotated about the origin. The method to be used will depend on how the vertex information is stored for the sprite. If the absolute (or world) position of the vertices are stored, a translation can be used to position the point of rotation of the sprite at the origin, perform rotation, then translate the sprite back to where it was originally positioned. If there is access to the untransformed sprite coordinated, as in the example being created here, then rotation can simply be applied to these local coordinates. The equation for rotating a point about the origin is as follows (note that this is rotation about the Z axis in 3D space):
X’ = X * cos(A) – Y * sin(A)
Y’ = X * sin(A) + Y * cos(A)
A is the angle of rotation, X’ and Y’ are the rotated coordinates and X, Y are the original coordinates. Thus, in order to rotate the quad, the above equation is applied to all four coordinated to obtain the new, or rotated coordinates.
Moving the sprite in the direction it is pointing is now relatively straightforward. The angle of rotation, A, is known so to move the sprite, the following amounts are added to the positions of all the vertices of the quad:
dx = D * sin(A)
dy = D * cos(A)
The application of the above movement equation can be seen in operation in the MoveSprite() function within main.cpp.
Investigating the source code it can be seen that the standard maths functions of sinf and cosf which are defined in <math.h> are used. These functions on the PS2 are __SLOW__, even although they are using floating point as opposed to doubles. They are not just a little slow, they are slow enough to be useless when creating real games. For the purposes of this tutorial these standard maths functions are acceptable since they are not being used very often, but in future tutorials hand crafted assembly language routines will be used which are considerably faster.
Much of the initialisation that is performed in main.cpp has been described in previous tutorials and will not be repeated here. Before the render loop is entered, the sprite structures are initialised with appropriate information. Within the render loop the sprite animation sequence can be changed with the triangle and square buttons, the sprite can be rotated with the left and right buttons and the circle and cross buttons are used to move the sprite backwards and forwards. Notice that the movement and rotation buttons are all pressure sensitive, so the harder the buttons are pressed the greater the associated action.
The GS packet is constructed using the BuildSprite2D() function. From the position of the centre of the sprite the coordinates of the untransformed vertices are first determined. These coordinates are then rotated by the angle that is contained within the sprite structure to determine the rotated coordinates of the vertices.
The prim field of the GIFtag is set up to draw a triangle strip primitive and texture mapping is enabled.
Notice that the XYZ data for the registers are constructed from the rotated coordinates of the quad. Also, in order to preserve accuracy, the floating point coordinate values are multiplied by 16.0f (also in floating point) before the vertex information is packed into the register data.
On running the program, two sprites are drawn on screen, one is static, the other is an animated character which walks around the screen under control of the user. Full information on controlling the character is contained within the accompanying readme file.
This tutorial has introduced the concepts associated with 2d movement and rotation under control of user input. In the next tutorial the techniques will be extended to investigate the use of matrices to transform the vertices.
Dr Henry S Fortuna
University of Abertay Dundee