[DirectX9] Managing multiple copy of the same sprite

Started by
7 comments, last by DarioMicera 12 years, 9 months ago
I'm a Computer Sience Student but i study directx by myself. I've got the basic of the directX functions (from making triangle to load a mesh) and now i wanted to start making little games for practicing. One i'm stuck on it's a space invader style shooter: my problem relies on the sprites, specifically the projectile sprite: i want to draw a sprite after space gets pressed and move this projectile up through the screen. The first problem i've encountered was that the sprite position would reset its position after pressing SPACE again. Searching on the web i've not found a clear answer.

Tried than to store the values of the sprite vector(called gun) in an array of vectors (called ammo), and i can actually have more bullets on the screen at same time,but it's still not a good way to manage the sprite, since the uses are limited and if i try to reutilize the sprite after the 30th shoot (i gave a fixed ammount of space to the array for testing purposes) every bullet on the screen disappears and reset the bullet position. Now to my code:


This is my message handler:

flag it's a boolean variable.
key it's a boolean array.

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_INPUT:
{
UINT buffersize;
GetRawInputData((HRAWINPUT)lParam,RID_INPUT,NULL,&buffersize,sizeof(RAWINPUTHEADER));
BYTE * buffer=new BYTE[buffersize];
GetRawInputData((HRAWINPUT)lParam,RID_INPUT,(LPVOID)buffer,&buffersize,sizeof(RAWINPUTHEADER));

RAWINPUT* raw=(RAWINPUT*)buffer;
if(raw->header.dwType==RIM_TYPEKEYBOARD)
{

USHORT keycode=raw->data.keyboard.VKey;
bool flag=raw->data.keyboard.Flags & RI_KEY_BREAK;
if(flag==0)
{

if(keycode==VK_RIGHT)
key[0]=TRUE;
if(keycode==VK_LEFT)
key[1]=TRUE;
if(keycode==VK_SPACE)
key[2]=FALSE;
}
if(flag==1)
{
if(keycode==VK_RIGHT)
key[0]=FALSE;
if(keycode==VK_LEFT)
key[1]=FALSE;
if(keycode==VK_SPACE)
key[2]=TRUE;
}

}
}
break;
default:
return DefWindowProc(hwnd,message,wParam,lParam);
}
return 0;
}



This function checks the values of the keys (pressed or released).
It also gives the bullet starting position.

void controls()
{
if(key[0]){
ship.pos.x+=7.0f;
}
if(key[1]){
ship.pos.x-=7.0f;
}
if(key[2]){
if(count>29)
{
count=0;
gun.x=ship.pos.x;
gun.y=ship.pos.y;
ammo[count]=gun;

}
else
{
gun.x=ship.pos.x;
gun.y=ship.pos.y;
ammo[count]=gun;
ship.fire=TRUE;
key[2]=FALSE;
}
count++;

}
}



and than my draw function.

void draw()
{
device->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
if(SUCCEEDED(device->BeginScene()))
{
spri->Begin(D3DXSPRITE_ALPHABLEND);
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
spri->SetTransform(&mat);
spri->Draw(texship,NULL,NULL,&ship.pos,0xFFFFFFFF);
if(ship.fire==TRUE)
{
for(int i=0;i<count;i++)
{
spri->Draw(texgun,NULL,NULL,&ammo,0xFFFFFFFF);
ammo.y-=5.0f;

}
}
spri->End();
device->EndScene();
device->Present(NULL,NULL,NULL,NULL);
}
}



What i need to know is:

what's the best way to manage a sprite in this case (multiple copy of the same sprite) ?
Does the sprite has to be regulated by a timer to accomplish my goal?


I saw some solution for XNA: they create new object each time the draw function is called and add them to a list, but that's still a weird way IMO since my code would actually have an infinite amount of bullets at player's disposal.

Thanks for the help in advance.
Advertisement
I'm not sure I understand your question, but I'll try to answer it.

It seems that you don't have a class for your projectile, so make one. It should contain the projectile's position, one static texture of the projectile, the draw function, and an update function. Then you can have a list where you store those projectiles, and when you're draw function is needed to be called, do a for loop:


//in your main Draw function

for(int i = 0; i < projectiles.Length; i++)
projectiles.Draw();

//the projectiles Draw function

void Draw()
{
sprite->Draw(projectileTexture,NULL,NULL, position,0xFFFFFFFF);
}



Also, I noticed that you were updating your projecitle's position after you draw it. It's a good practice to have that occur in an Update function like this:


void Update(float timeDelta)
{
position.Y -= (speed * timeDelta);
}


Where 'timeDelta' is the amount time past in miliseconds since the last update was called.

Hopefully I helped some.
Thanks for the reply, it's really appreciated.

I have some questions:

If i'm correct, i will create a projectile object (class projectile) each time the space key gets pressed, and store its info in an array of the same class, and get rid of the new object after i used it.

Now let's say i shoot an infinite amount of projectiles, won't that be bad in terms of memory usage? Won't this process be too "expensive" in the long run?

Also, just to clarify, i'm using simple 64x64 px sprites containing only 1 static frame.

EDIT: Adding some image to explain it better what is happening with my original code :

shoot1f.png


The ship starts to fire a bullet each time the space key is pressed. All the bullets travel from the position they were fired till through the top of the screen. I can have multiple bullets on the screen at same time as you can see.

shoot2.png


After i reach the 30th bullet (that's the size of the array) all the bullets on the screen,regardless of their position, disappear, and the ship starts to fire again. This happens each time i reach the 30th space key press. This problem relies, i think, in the fact that my array has a fixed size and i try to overwrite data in it to reuse the same array to store more bullets.

What i want it's having to utilize something that doesn't prevent me to limit the amount of bullets i can shoot. Even having an array of huge size, it's, like i said, really expensive process in the long run.

Hope i'm clearer now
I've managed to figure out the problem :D

The problem relied on the draw loop:

void draw()
{
device->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
if(SUCCEEDED(device->BeginScene()))
{
spri->Begin(D3DXSPRITE_ALPHABLEND);
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
spri->SetTransform(&mat);
spri->Draw(texship,NULL,NULL,&ship.pos,0xFFFFFFFF);
if(ship.fire==TRUE)
{
//before it was "for(int i=0;i<=count;i++)
for(int i=0;i<=[color="#ff0000"]19;i++)
{
//i created a class bullets to store the draw method and updating the sprite
ammo.Draw();
ammo.Update();
}
}
spri->End();
device->EndScene();
device->Present(NULL,NULL,NULL,NULL);
}
}



When i was using count as part of the loop i didn't think of the fact that when i reset the counter to zero (to reuse my array) it would reset the entire loop too, so that it would look like:

for(int i=0;i<=0;i++)
{
.......
}



So when this happened the loop doesn't take in consideration the bullets that were flying during the previous iteration (since the loop will end instantly).

also i figured out that the size of the array manage the max number of bullets that can be drawn into the screen at same time. In fact, testing the same program, with an array of size 2 and adjusting all the counters and loops accordingly, 2 bullets will fly but a 3rd key press will delete the first one and fire a new bullet.

Thanks for the help laugh.gif
Encountered another problem. When i fire the FIRST bullet, another bullet will be fired at coords 0,0 of the screen. I tried to avoid the first element of the array than but the result was that it won't shoot a bullet from my ship but the one at 0,0 will.

It's like the Draw call for the sprite gets called 2 times, but it's still weird since there is no double call and i can't figure out why this happens.

Any thoughts? :S

EDIT: Tried to initialize the ammo array to the ship position (in my main) and it still fires 2 bullet when space it's pressed for the first time, but at the initial ship position though :\
Have you tried to debug your game? Usually debugging can help a lot in cases like this. Other than that I can't really help, at least with the code you've posted.

Have you tried to debug your game? Usually debugging can help a lot in cases like this. Other than that I can't really help, at least with the code you've posted.


thanks for reply

I have my debug output level to max and Debug version is active if that's what you mean and there is no error or weird things of sort.

here is the full source:

#include <d3d9.h>
#include <d3dx9.h>
#include <Windows.h>

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam);
void initialize3D(HWND hwnd);
void clear();
void draw();
void CreateSprite();
void controls();
LPDIRECT3D9 object=NULL;
LPDIRECT3DDEVICE9 device=NULL;
LPDIRECT3DTEXTURE9 texship=NULL;
LPDIRECT3DTEXTURE9 texgun=NULL;
LPD3DXSPRITE spri=NULL;


RAWINPUTDEVICE rid[1];

//key state container

bool key[3];

//bullet container

D3DXVECTOR3 ammo[30];
int count=0;

//ship structure: position and fire trigger

struct Ship
{
bool fire;
D3DXVECTOR3 pos;
}ship;


int APIENTRY WinMain (HINSTANCE hinstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine, int CmdShow)
{
WNDCLASSEX win;
ZeroMemory(&win,sizeof(win));
win.cbSize=sizeof(WNDCLASSEX);
win.style=CS_HREDRAW | CS_VREDRAW;
win.lpfnWndProc=(WNDPROC)WndProc;
win.hInstance=hinstance;
win.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);
win.lpszClassName="Game";

RegisterClassEx(&win);

HWND hwnd=CreateWindow("Game","Space Invaders", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hinstance,NULL);

initialize3D(hwnd);
CreateSprite();

//Initializing ship's starting position and fire value

RECT rect;
GetClientRect(hwnd,&rect);
ship.fire=FALSE;
ship.pos.x=(rect.bottom-rect.top)/2.0f+64.0f;
ship.pos.y=(rect.bottom-rect.top)-74.0f;

//Initializing the RID

rid[0].usUsagePage=1;
rid[0].usUsage=6;
rid[0].dwFlags=0;
rid[0].hwndTarget=NULL;

RegisterRawInputDevices(rid,1,sizeof(RAWINPUTDEVICE));
ShowWindow(hwnd,CmdShow);
UpdateWindow(hwnd);

MSG msg;
ZeroMemory(&msg,sizeof(msg));
while (msg.message!=WM_QUIT)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
controls();
draw();
Sleep(0);
}
}

clear();
return (int)msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_INPUT:
{
UINT buffersize;
GetRawInputData((HRAWINPUT)lParam,RID_INPUT,NULL,&buffersize,sizeof(RAWINPUTHEADER));
BYTE * buffer=new BYTE[buffersize];
GetRawInputData((HRAWINPUT)lParam,RID_INPUT,(LPVOID)buffer,&buffersize,sizeof(RAWINPUTHEADER));

RAWINPUT* raw=(RAWINPUT*)buffer;
if(raw->header.dwType==RIM_TYPEKEYBOARD)
{

USHORT keycode=raw->data.keyboard.VKey;

//check key states

bool flag=raw->data.keyboard.Flags & RI_KEY_BREAK;
if(flag==0)
{
if(keycode==VK_RIGHT)
key[0]=TRUE;
if(keycode==VK_LEFT)
key[1]=TRUE;
if(keycode==VK_SPACE)
key[2]=FALSE;
}
if(flag==1)
{
if(keycode==VK_RIGHT)
key[0]=FALSE;
if(keycode==VK_LEFT)
key[1]=FALSE;
if(keycode==VK_SPACE)
key[2]=TRUE;
}

}
}
break;
default:
return DefWindowProc(hwnd,message,wParam,lParam);
}
return 0;
}

void initialize3D (HWND hwnd)
{
object=Direct3DCreate9(D3D_SDK_VERSION);
D3DPRESENT_PARAMETERS pres;
ZeroMemory(&pres,sizeof(pres));
pres.Windowed=TRUE;
pres.SwapEffect=D3DSWAPEFFECT_DISCARD;
pres.BackBufferFormat=D3DFMT_UNKNOWN;
pres.PresentationInterval=D3DPRESENT_INTERVAL_ONE;

HRESULT hr=object->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hwnd,D3DCREATE_HARDWARE_VERTEXPROCESSING,&pres,&device);

}

void clear()
{

if(spri)
{
spri->Release();
spri=NULL;
}
if(texgun)
{
texgun->Release();
texgun=NULL;
}
if(texship)
{
texship->Release();
texship=NULL;
}
if(device)
{
device->Release();
device=NULL;
}
if(object)
{
object->Release();
object=NULL;
}
}

void draw()
{
device->Clear(0,NULL,D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,0),1.0f,0);
if(SUCCEEDED(device->BeginScene()))
{
spri->Begin(D3DXSPRITE_ALPHABLEND);
D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
spri->SetTransform(&mat);
spri->Draw(texship,NULL,NULL,&ship.pos,0xFFFFFFFF);

// if Space key has been pressed....

if(ship.fire==TRUE)
{
// Draw the bullet starting from the ammo position

for(int i=0;i<30;i++)
{
spri->Draw(texgun,NULL,NULL,&ammo,0xFFFFFFFF);
ammo.y-=5.0f;
}

}
spri->End();
device->EndScene();
device->Present(NULL,NULL,NULL,NULL);
}
}

void CreateSprite()
{
D3DXCreateSprite(device,&spri);

D3DXCreateTextureFromFile(device,"ship.tga",&texship);
D3DXCreateTextureFromFile(device,"gun.tga",&texgun);
}

void controls()
{
// move right

if(key[0]){
ship.pos.x+=7.0f;
}

//move left

if(key[1]){
ship.pos.x-=7.0f;
}

//fire

if(key[2]){

//get ship position and store it in ammo

ammo[count].x=ship.pos.x;
ammo[count].y=ship.pos.y;
ship.fire=TRUE;
key[2]=FALSE;
if(count==29)
count=0;
else
count++;
}
}



Notice that i reverted the code to the original state for testing purposes.

Thanks again

I have my debug output level to max and Debug version is active if that's what you mean and there is no error or weird things of sort.


You should be able to insert breakpoints in your code, then when you run your program it pauses at the breakpoint and allows you to check variables and see what's going on.
Followed your suggestion beat and found that the problem was here:

[color=#000000] [color=#000088]if[color=#666600]([color=#000000]ship[color=#666600].[color=#000000]fire[color=#666600]==[color=#000000]TRUE[color=#666600])[color=#000000]
[color=#666600]{[color=#000000]
[color=#000088]for[color=#666600]([color=#000088]int[color=#000000] i[color=#666600]=[color=#006666]0[color=#666600];[color=#000000]i[color=#666600]<=[[color=#000000]b[color=#666600]][[color=#000000]color[color=#666600]=[color=#008800]"#ff0000"[color=#666600]][color=#006666]19[color=#666600][[color=#008800]/color]

;i++)
{
/[color=#666600]/[color=#000000]i created a [color=#000088]class[color=#000000] bullets to store the draw method [color=#000088]and[color=#000000] updating the sprite
ammo[color=#666600][[color=#000000]i[color=#666600]].[color=#660066]Draw[color=#666600]();[color=#000000]
ammo[color=#666600][[color=#000000]i[color=#666600]].[color=#660066]Update[color=#666600]();[color=#000000]
[color=#666600]}[color=#000000]
[color=#666600]}[color=#000000]


[color=#666600][font=CourierNew, monospace]

}[/font]




In this code the loop at first iteration checks ALL the elements of the ammo array and draw them all at once. I've fixed it by filtering the y coords below 0 (since i don't need them anyway) so it now looks like this:

if(ship.fire==TRUE)
{
ammo[count].projectile.x=ship.pos.x;
ammo[count].projectile.y=ship.pos.y;
if(count==29)
count=0;
else
count++;
ship.fire=FALSE;
}
for(int i=0;i<30;i++)
{
if(ammo.projectile.y<0)
{
}
else
{
ammo.Draw();
ammo.Update();
}
}



Tweaked the code a bit too as you can see, but now it works.

Thanks for the help, was really appreciated and i learned more things :P

This topic is closed to new replies.

Advertisement