Task 19: Motion Vectors, Angles & Rotation
It's time to start moving images around! In a few of the previous tasks you have typed in or loaded code that moves objects around on the screen. It's time to explore the different ways motion can be achieved using code.
All objects when placed on the screen have one thing in common, coordinates. There is typically at a minimum an X and Y coordinate associated with an object that correlates to a particular point on the screen. The CIRCLE statement requires a single set of coordinates while the LINE statement requires a pair of them. Another command we'll investigate later in this task is _MAPTRIANGLE which requires three coordinate pairs. By changing an object's coordinates you can effectively make an object move on the screen. The direction in which an object moves is known as its vector.
Let's start with a very simple bouncing ball demo. Load the code named BouncingBall.BAS located in your .\tutorial\task19\ directory.
Figure 1 - Simple Object Movement
To move an object around on the computer screen you need to know at the very minimum the following:
The horizontal and vertical vectors create a slope for the object to follow. The slope defines the direction and steepness of the object's path. For instance if BallXvector! and BallYvector! are both set to a value of 1 the ball would move to the right one pixel and down one pixel with each passing frame. The ball's slope would be 1 setting its travel direction to 135 degrees. By varying the value in the vector variables between -1 and 1 and then multiplying those vector variables by the balls speed the ball can be made to go in any direction at any speed.
Note: Remember that the computer screen's (0, 0) coordinate is located in the upper left corner. Slopes are effectively flipped vertically from what you learned in school where (0, 0) was located in the bottom left hand corner. With (0, 0) being in the lower left corner a slope of 1 would point in a 45 degree direction.
The next example allows the speed to be adjusted through the use of the right and left arrow keys. In lines 15 and 16 of the previous example the speed was incorporated into the vector values right away. In the following example the speed is not applied until later in the main loop. This example is included as BouncingBall2.BAS in your .\tutorial\task19\ directory.
Figure 2 - Object Movement With Speed Control
The previous example also converted all of the motion associated variables into a single TYPE definition. Vary rarely are you going to have a game with just one moving object. By utilizing a TYPE statement it's trivial to add more objects through the use of an array. More on that later.
In the next example the vertical and horizontal vectors can be manually changed to see the effect on the direction of the ball. The vectors are used to draw a slope line from the center of the object indicating speed and direction of object travel to help visualize how vectors work together to create 360 degree movement. Use of the right and left arrow keys allow the horizontal (X) vector to be varied between -1 and 1 and the up and down arrow keys allow the vertical (Y) vector to be varied between -1 and 1. This code is included as BouncingBall3.BAS in your .\tutorial\task19\ directory.
Figure 3 - Controlling Both Vector Values
X and Y vectors can be used to put an object into any 360 degree motion. The best way to do this is to convert an angle from 0 to 359 into the corresponding X and Y vector values for that heading. Another valuable tool to have at your disposal is the ability to calculate an angle between two objects. Once the angle from one object to another has been established that angle can be used to set the corresponding X and Y vector values. The following example program includes two functions that do this. The Angle2Vector() function returns the X and Y vector values for an angle passed to it. The P2PAngle() function returns the angle between two X,Y coordinate pairs. This angle can be fed into Angle2Vector() to effectively point an object in the direction of another. Use the mouse to move the red circle around on the screen. The green circle will always follow the red circle. This program is saved as BallChaser.BAS in your .\tutorial\task19\ directory.
Figure 4 - An Object Following Another
Both functions Angle2Vector() and P2PAngle() return values based on 0 degrees being North (up), 90 degrees being East (right), 180 degrees being South (down), and 270 degrees being West (left). Note that in the Angle2Vector() function the constant PIDIV180 has been used. That could just as easily have been the actual value of 0.0174532 there. Through the use of these two functions you can set any object, such as a sprite, in any direction on the screen or toward another object.
The P2PAngle() function can also be used to convert vector values to angles, the opposite of Angle2Vector(). Simply take the X,Y location of an object and the X,Y location values plus the vector values as the second point like so:
Angle! = P2PAngle(x!, y!, x! + xvector!, y! + yvector!)
Rotation of a sprite can be done with help from the _MAPTRIANGLE statement. The _MAPTRIANGLE statement can be used to convert a rectangle into two separate triangular areas. The four corners of the rectangle can then be rotated to any angle around a center point. The new corner coordinate locations are then used by _MAPTRIANGLE to map the triangular areas of the sprite to the new coordinates. The following is an example that uses _MAPTRIANGLE to rotate a series of sprites that create an animation that rotates in relation to mouse movement. Use the mouse to move the brains around on the screen and the zombie will chase it. This example has been saved as ZombieRotate.BAS in your .\tutorial\task19\ directory.
Figure 5 - They're Coming to Get You Barbara!
Figure 6 below is a graphical representation of how the sprite is rotated with the help of _MAPTRIANGLE.
Figure 6 - Rotating a Sprite
The RotateImage() subroutine is where all the rotation happens. RotateImage() requires three parameters:
RotateImage Angle!, InImage&, OutImage&
Angle! is the new direction of the sprite in degrees from 0 to 359.99. InImage& is the sprite image you wish to rotate and OutImage& will contain the resulting image from the rotation.
Line 131 through 138 identifies the coordinates of the four corners of the incoming sprite image. The coordinates are purposely shifted left and up half the width and height to create an image center coordinate of (0,0).
Lines 139 and 140 calculate the amount of rotation needed based on the Angle! passed in.
Lines 146 through 156 takes each corner coordinate pair one at a time and calculates new locations for them. The smallest and largest X and Y values are saved so the new width and height of the rotated image can be calculated in lines 157 and 158.
Lines 159 and 160 are used to move the rotated image right and down half the width and height to compensate for creating the (0,0) point in the center that was done in lines 131 through 138. Lines 161 through 168 then adds these offsets back in.
Line 169 creates a new image holder for the rotated image with the computed width and height from lines 157 and 158.
Finally lines 187 and 189 use _MAPTRIANGLE to grab the triangular areas from the original incoming sprite image and map them to the new rotated coordinates contained within the new rotated image.
The RotateImage() subroutine is my goto routine for rotating sprites of any size on the screen. Don't let the _MAPTRIANGLE statement intimidate you. Play around with it to get familiar with how it works. It's a very powerful graphics tool at your disposal. It can also be used to map images onto irregular polygon surfaces such as walls going into the distance.