Shooting fireballs
In this version of our application the flying box is going to shoot fireballs
when a [SPACE] key is pressed on a keyboard.
The source code that comes with this tutorial (see Downloads section above) extends the previous solution.
To see all the tutorials that belong to this project click on the project name above.
Each time the [SPACE] key is pressed we need to create a sphere object with a fireball mesh and modify it's position in the world relative to the flying box's position. Besides that we also have to take care of some mechanism that will allow us to move each fireball in the right direction till a certain limit in space and to release the fireball's mesh from our memory once it does reach this limit. Sounds complicated. To reduce the complexity we have decided to create a new Fireball class which will hold the fireball's mesh and position in space.
Fireball.cpp
#include "Fireball.h";
Fireball::Fireball(D3DXVECTOR3& position, LPD3DXMESH mesh){
this->position = position;
this->mesh = mesh;
}
Notice that we include the Fireball.h header file which holds function signatures and variables definitions.
Fireball.h
#ifndef __Fireball_h__
#define __Fireball_h__
#include <d3d9.h>
#include <d3dx9.h>
class Fireball
{
public:
D3DXVECTOR3 position;
LPD3DXMESH mesh;
Fireball(D3DXVECTOR3& position, LPD3DXMESH mesh);
};
#endif
The Fireball's class constructor accepts position and mesh pointer of virtually any object, making it possible to use this class for many purposes.
First of all we have to include the fireball.h header in the class where WinMain is defined so we can take advantage of this class in our application.
#include "Fireball.h"
Now we have to decide what object we are going to use to store the collection of fireballs.
In this project we are going to work with vector. Although it provides a sequentional access to its elements which is quiet slow, we have chosen to use it because most of the developers are usually familiar with the vector object. So if for instance you prefer to use a list to store your fireballs feel free to do so. In order to work with vector we have to include it in our application:
#include <vector>
We then define the fireballs vector where we can store multiple objects of the type Fireball we have created earlier.
vector<Fireball> fireballs;
The drawBoxFireMove() method is responsible for drawing each move of the fireball in our world. It takes the position and the mesh as parameters.
void drawBoxFireMove(D3DXVECTOR3 & position, LPD3DXMESH mesh){
if(position.z >= MAX_FIREBALL_Z) return;
D3DXCreateSphere(
directXDevice,
0.2f, // radius
8, // slices
2, // stacks
&mesh,
0);
D3DXVECTOR3 direction = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vectorA, vectorB;
D3DXMATRIX matrix;
vectorA = D3DXVECTOR3(position.x, position.y, position.z);
position.z -= timeDelta;
vectorB = D3DXVECTOR3(position.x, position.y, position.z);
direction = vectorA - vectorB;
D3DXVec3Normalize(&direction, &direction);
position = vectorA + (direction * timeDelta);
D3DXMatrixTranslation(&matrix, position.x, position.y, position.z);
directXDevice->SetTransform(D3DTS_WORLD, &matrix);
mesh->DrawSubset(0);
}
Since any given fireball can be rendered by using this method we have to take care that only the visible fireballs (these who did not yet reach the maximum z position value defined by MAX_FIREBALL_Z constant) are rendered and the rest is ignored. We then create a sphere object which holds the mesh of a fireball. In order to move an object from a position A to a position B we perform calculations with vectors that should not be difficult to understand if you have some fundamental knowledge of Linear Algebra.
In order to perform these calculations we first define the direction vector, the position vectors A and B and the matrix that holds the transformation. We then initialize our vectorA with X, Y, Z values of position passed as parameter to the drawBoxFireMove() method. We subtract the timeDelta (amount of movement) from the Z position value to move the fireball each time a bit further from its previous position and store new position X, Y, Z values in vectorB. We determine the direction vector by substracting vectorB from vectorA. Then we normalize the direction vector, we calculate the new position of the fireball in space and we translate the matrix to that new position.
After the matrix was transformed we inform the directXDevice that we have performed the transformation by calling the SetTransform() method and passing the D3DTS_WORLD constant and transformed matrix as parameters. Finally we draw the object with DrawSubset(0). It is 0 because sphere only has 1 subset to draw.
You will come across this matrix transformation code multiple times. In fact when you have to move an object you have to perform matrix transformation. Notice that in order to move a fireball in the opposite direction in place of subtracting the timeDelta you will add its value to the position.z.
Inside the drawScene() method we have to call the drawBoxFireMove() method for each fireball stored in fireballs vector like that:
// draw fireballs
for(vector<Fireball>::reverse_iterator iterFireballs = fireballs.rbegin();
iterFireballs != fireballs.rend(); ++iterFireballs){
drawBoxFireMove(iterFireballs->position, iterFireballs->mesh);
}
Inside the DlgProcControlPanel() method we define a static variable to hold the number of fireballs fired by the flying box so far (note that this is not the number of elements in the fireballs vector):
static int numberOfFireballsFired = 0;
and we add the processing of the following case:
case ID_FIREBALLS:
numberOfFireballsFired += 1;
SetDlgItemText(hwnd, ID_FIREBALLS,
(LPCSTR)CStr(numberOfFireballsFired).c_str());
break;
Then inside the WndProc() method we add the processing of [SPACE] keydown event:
case VK_SPACE: // box fireball
{
D3DXVECTOR3 posFireball = posBox;
LPD3DXMESH meshFireball = 0;
Fireball fireball(posFireball, meshFireball);
fireballs.push_back(fireball);
SendMessage(hwndControlPanel, WM_COMMAND, ID_FIREBALLS, 0);
}
break;
If [SPACE] key is pressed we create new Fireball and add it to the fireballs vector by calling push_back() vector's method. Then we send a message to the hwndControlPanel window for further processing.
Notice that all the fireballs are drawn in drawScene() method and the rest of the program only adds fireballs to the fireballs vector.
In the
next tutorial we show how to create asteroids and how
to perform run-time cleanup of unnecessary objects.