Creating a small 2D game, using Direct3D 9 and C++. I have come across a problem I have not seen before. The game is a shop based one so I have a customer class. I have created the object in the game and it renders and behaves how I want for the minute. I decided to put the object in to a Vector instead because I will, at some point, need a collection of them at any one time. My problem is, however, when I put the object in to a Vector, it no longer is visible on the screen!
I get no errors. I have outputted all the return codes from the DirectX functions in to a test file and nothing failed. It ran fine, it just isn't visible on the screen! Nothing has changed other than putting it in to a Vector.
I changed the Vector to a Vector of pointers because I was told that the Vector was simply creating the object, copying it and deleting the original. However, it would now seem that my Direct3D pointers become invalid using this method, pretty quickly. I think I know what is going wrong but not sure the best course of action.
Here is the customer class:
#include "Headers.h"
#include "Playercard.h"
class Customer{
private:
LPDIRECT3DTEXTURE9 texture, playerInfoCard;
D3DXVECTOR3 position, direction, playerCardPosition;
D3DXVECTOR3 center;
LPD3DXFONT font;
POINT curPos;
HRESULT rc;
//Playercard playerCard;
int depthBuffer;
int walkingAlternator;
// Some information about the customer
char name[10];
int age;
double cash;
int itemWanted;
char productWanted[20];
bool happy;
bool male;
bool female;
bool displayPlayerCard;
RECT textBox;
Playercard playercard;
void assignNameAndGender()
{
srand((unsigned int)time(0));
int name_decider = rand() % 16 + 1;
switch(name_decider)
{
case 1:
strcpy_s(name, "John");
male = true; female = false;
break;
case 2:
strcpy_s(name, "Chris");
male = true; female = false;
break;
case 3:
strcpy_s(name, "Ben");
male = true; female = false;
break;
case 4:
strcpy_s(name, "Jack");
male = true; female = false;
break;
case 5:
strcpy_s(name, "Jamie");
male = true; female = false;
break;
case 6:
strcpy_s(name, "Bill");
male = true; female = false;
break;
case 7:
strcpy_s(name, "Liam");
male = true; female = false;
break;
case 8:
strcpy_s(name, "Alex");
male = true; female = false;
break;
case 9:
strcpy_s(name, "Nikki");
male = false; female = true;
break;
case 10:
strcpy_s(name, "Alice");
male = false; female = true;
break;
case 11:
strcpy_s(name, "Lucy");
male = false; female = true;
break;
case 12:
strcpy_s(name, "Emily");
male = false; female = true;
break;
case 13:
strcpy_s(name, "Laura");
male = false; female = true;
break;
case 14:
strcpy_s(name, "Mary");
male = false; female = true;
break;
case 15:
strcpy_s(name, "Katie");
male = false; female = true;
break;
case 16:
strcpy_s(name, "Emma");
male = false; female = true;
break;
}
}
void assignAge()
{
srand((unsigned int)time(0));
age = rand() % 53 + 12; // An age between 12 and 65
}
void assignCash()
{
if(age < 16)
cash = 5.00;
if(age >= 16 && age <= 18)
cash = 15.00;
if(age >= 19 && age <= 21)
cash = 20.00;
if(age >= 22 && age <= 25)
cash = 25.00;
if(age >= 26 && age <= 30)
cash = 30.00;
if(age > 31)
{
int randNum = rand() % 9 + 1;
switch(randNum)
{
case 1:
cash = 20.00;
break;
case 2:
cash = 25.00;
break;
case 3:
cash = 30.00;
break;
case 4:
cash = 35.00;
break;
case 5:
cash = 40.00;
break;
case 6:
cash = 45.00;
break;
case 7:
cash = 50.00;
break;
case 8:
cash = 55.00;
break;
case 9:
cash = 60.00;
break;
}
}
}
public:
Customer()
{
direction.x = (float)cos(0.558)*4; // 30 degree movement
direction.y = (float)sin(0.558)*4; // 30 degree movement
position.x = 500;
position.y = 500;
displayPlayerCard = false; // Only true when mouse clicked
happy = true; // A new customer is always happy at first!
walkingAlternator = 1; // Help control sprite movement
strcpy_s(productWanted, "Xbox");
assignNameAndGender(); // Assign customer a name
assignAge(); // Assign customer an age
assignCash(); // Assign customer an amount of cash
playercard.load(name, male, age, cash, productWanted);
}
~Customer()
{
}
void move()
{
if(KEY_DOWN(0x57) || KEY_DOWN(VK_UP))
{
position.y -= direction.y;
position.x += direction.x;
walkingUp();
}
else if(KEY_DOWN(0x41) || KEY_DOWN(VK_LEFT))
{
position.y -= direction.y;
position.x -= direction.x;
walkingLeft();
}
else if(KEY_DOWN(0x53) || KEY_DOWN(VK_DOWN))
{
position.y += direction.y;
position.x -= direction.x;
walkingDown();
}
else if(KEY_DOWN(0x44) || KEY_DOWN(VK_RIGHT))
{
position.y += direction.y;
position.x += direction.x;
walkingRight();
}
else
{
// Reset the sprite here so that the feet are together in resting position AND so that sprite is facing the way it was going when it stopped
}
}
void loadGraphics()
{
D3DXCreateTextureFromFileEx(d3dDevice, "player_information_card.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0,
D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &playerInfoCard);
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper1.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0,
D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
D3DXVECTOR3 position(100.0f, 100.0f, 0.0f);
D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);
D3DXCreateFont(d3dDevice, // the D3D Device
20, // font height of 30
0, // default font width
FW_NORMAL, // font weight
1, // not using MipLevels
false, // italic font
DEFAULT_CHARSET, // default character set
OUT_DEFAULT_PRECIS, // default OutputPrecision,
DEFAULT_QUALITY, // default Quality
DEFAULT_PITCH | FF_DONTCARE, // default pitch and family
"Georgia", // use Facename Arial
&font); // the font object
}
void draw()
{
d3dSprite->Draw(texture, NULL, ¢er, &position, D3DCOLOR_ARGB(255, 255, 255, 255));
if(displayPlayerCard)
{
playerCardPosition = position;
playerCardPosition.y -= 128;
playerCardPosition.x += 32;
SetRect(&textBox, (int)playerCardPosition.x+5, (int)playerCardPosition.y+5, (int)playerCardPosition.x+256-5, (int)playerCardPosition.y+128-5);
d3dSprite->Draw(playerInfoCard, NULL, ¢er, &playerCardPosition, D3DCOLOR_ARGB(255, 255, 255, 255));
font->DrawTextA(d3dSprite, playercard.plCard().c_str(), -1, &textBox, DT_LEFT, D3DCOLOR_ARGB(255, 255, 255, 255));
}
}
void walkingDown()
{
if(walkingAlternator == 1 || walkingAlternator == 2 || walkingAlternator == 3 || walkingAlternator == 4 || walkingAlternator == 5
|| walkingAlternator == 6 || walkingAlternator == 7 || walkingAlternator == 8 || walkingAlternator == 9 || walkingAlternator == 10)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper2.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
else if(walkingAlternator == 11 || walkingAlternator == 12 || walkingAlternator == 13 || walkingAlternator == 14 || walkingAlternator == 15
|| walkingAlternator == 16 || walkingAlternator == 17 || walkingAlternator == 18 || walkingAlternator == 19 || walkingAlternator == 20)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper3.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
if(walkingAlternator >= 20)
{
walkingAlternator = 1;
}
}
void walkingUp()
{
if(walkingAlternator == 1 || walkingAlternator == 2 || walkingAlternator == 3 || walkingAlternator == 4 || walkingAlternator == 5
|| walkingAlternator == 6 || walkingAlternator == 7 || walkingAlternator == 8 || walkingAlternator == 9 || walkingAlternator == 10)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper1_back2.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
else if(walkingAlternator == 11 || walkingAlternator == 12 || walkingAlternator == 13 || walkingAlternator == 14 || walkingAlternator == 15
|| walkingAlternator == 16 || walkingAlternator == 17 || walkingAlternator == 18 || walkingAlternator == 19 || walkingAlternator == 20)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper1_back1.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
if(walkingAlternator >= 20)
{
walkingAlternator = 1;
}
}
void walkingLeft()
{
if(walkingAlternator == 1 || walkingAlternator == 2 || walkingAlternator == 3 || walkingAlternator == 4 || walkingAlternator == 5
|| walkingAlternator == 6 || walkingAlternator == 7 || walkingAlternator == 8 || walkingAlternator == 9 || walkingAlternator == 10)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper1_back3.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
else if(walkingAlternator == 11 || walkingAlternator == 12 || walkingAlternator == 13 || walkingAlternator == 14 || walkingAlternator == 15
|| walkingAlternator == 16 || walkingAlternator == 17 || walkingAlternator == 18 || walkingAlternator == 19 || walkingAlternator == 20)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper1_back4.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
if(walkingAlternator >= 20)
{
walkingAlternator = 1;
}
}
void walkingRight()
{
if(walkingAlternator == 1 || walkingAlternator == 2 || walkingAlternator == 3 || walkingAlternator == 4 || walkingAlternator == 5
|| walkingAlternator == 6 || walkingAlternator == 7 || walkingAlternator == 8 || walkingAlternator == 9 || walkingAlternator == 10)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper4.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
else if(walkingAlternator == 11 || walkingAlternator == 12 || walkingAlternator == 13 || walkingAlternator == 14 || walkingAlternator == 15
|| walkingAlternator == 16 || walkingAlternator == 17 || walkingAlternator == 18 || walkingAlternator == 19 || walkingAlternator == 20)
{
D3DXCreateTextureFromFileEx(d3dDevice, "Characters/female_shopper5.png", D3DX_DEFAULT_NONPOW2, D3DX_DEFAULT_NONPOW2, 0, 0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT,
D3DX_DEFAULT, D3DX_DEFAULT, D3DCOLOR_XRGB(255, 255, 255), NULL, NULL, &texture);
walkingAlternator++;
}
if(walkingAlternator >= 20)
{
walkingAlternator = 1;
}
}
void checkInteractivity()
{
GetCursorPos(&curPos);
if (GetKeyState(VK_LBUTTON) & 0x80 &&
curPos.x >= position.x && curPos.x <= position.x+64 &&
curPos.y >= position.y && curPos.y <= position.y+128)
{
displayPlayerCard = true;
}
else
{
displayPlayerCard = false;
}
}
D3DXVECTOR3 returnPosition()
{
return position;
}
D3DXVECTOR3 returnPosition(bool returnCentreOfSprite)
{
return position;
}
};
Here is the code for the main
#include "Headers.h."
#include "Customer.h"
#include "Background.h"
#include "Counter.h"
#include "GameStations.h"
#include "ConsoleCab.h"
#include "ClothesStand.h"
#include "GamePanel.h"
std::vector<Customer*> customers;
Background background;
Counter counter;
GameStations gameStations;
ConsoleCab consoleCab;
ClothesStand clothesStand;
GamePanel gamePanel;
void initDirectX(HWND hWnd); // Initializes Direct3D Graphics
void render(); // Render graphics
void cleanUp(); // Cleans everything up and releases memory
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hWnd; // The handle to the window function
WNDCLASSEX window; // Pointer to window struct
ZeroMemory(&window, sizeof(WNDCLASSEX)); // Clears window class so we can use it
window.cbSize = sizeof(WNDCLASSEX); // Size of window
window.style = CS_HREDRAW | CS_VREDRAW; // Redraws the entire window if a movement or size adjustment changes the height of the client area.
window.lpfnWndProc = WindowProc; // Pointer to the window procedure
window.hInstance = hInstance; // Handle to current instance
window.hCursor = LoadCursor(NULL, IDC_ARROW); // We'll stick with the normal cursor here
window.lpszClassName = "Window"; // Gives the class a name
RegisterClassEx(&window); // Registers the window class
hWnd = CreateWindowEx(NULL,
"Window", // Name of the class
"Space Game", // Title of the window
WS_EX_TOPMOST | WS_POPUP, // Fullscreen
0, 0, // Position of window (0,0 for fullscreen)
SCREEN_WIDTH, SCREEN_HEIGHT, // Screen resolution (Uses global declaration)
NULL, // Parent window (None)
NULL, // Menus (None)
hInstance, // Application handle
NULL); // Set to NULL as we aren't using more than 1 window
ShowWindow(hWnd, nCmdShow); // Display window
initDirectX(hWnd); // Initialises the directX graphics
MSG msg = {0}; // msg holds the Windows events message queue
customers.push_back(new Customer());
for (std::vector<Customer*>::iterator customerIT = customers.begin(); customerIT != customers.end(); customerIT++)
{
(*customerIT)->loadGraphics();
} // Object properties become invalid here.
background.loadGraphics();
counter.loadGraphics();
gameStations.loadGraphics();
consoleCab.loadGraphics();
clothesStand.loadGraphics();
gamePanel.loadGraphics();
/************************************** Main game loop *************************************************/
while(TRUE) // Main game loop
{
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) // If messages are waiting
{
TranslateMessage(&msg); // Translates the keystroke messages into the correct format
DispatchMessage(&msg); // Sends message to the windowsProc function
if(msg.message == WM_QUIT) // If the message was a quit message
break; // Breaks out of main game loop
}
else
{
for (std::vector<Customer*>::iterator customerIT = customers.begin(); customerIT != customers.end(); customerIT++)
{
(*customerIT)->checkInteractivity();
(*customerIT)->move();
}
gamePanel.hoverDetection();
render();
}
}
/*******************************************************************************************************/
// We are out of the main game loop (Due to a WM_QUIT signal)
cleanUp(); // Do some housekeeping before quitting
return msg.wParam; // Return the WM_QUIT message to Windows. End of program
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message) // Sorts through messages
{
case WM_DESTROY: // When window has been closed
{
PostQuitMessage(0); // Closes the application
return 0;
}
break;
}
return DefWindowProc (hWnd, // Returns any messages the switch statement didn't pick up
message,
wParam,
lParam);
}
void initDirectX(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION); // Create DirectX9 interface
D3DPRESENT_PARAMETERS d3dParameters; // Pointer to DirectX9 parameters
ZeroMemory(&d3dParameters, sizeof(d3dParameters)); // Clears the structure so we can use it
d3dParameters.Windowed = FALSE; // Fullscreen
d3dParameters.SwapEffect = D3DSWAPEFFECT_DISCARD; // Get rid of old framess
d3dParameters.hDeviceWindow = hWnd; // Sets the window to be used by Direct3D
d3dParameters.BackBufferFormat = D3DFMT_X8R8G8B8; // Sets the back buffer format to 32-bit
d3dParameters.BackBufferWidth = SCREEN_WIDTH; // Sets the width of the buffer
d3dParameters.BackBufferHeight = SCREEN_HEIGHT; // Sets the height of the buffer
d3d->CreateDevice(D3DADAPTER_DEFAULT, // Creates a device class
D3DDEVTYPE_HAL,
hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dParameters,
&d3dDevice);
D3DXCreateSprite(d3dDevice, &d3dSprite);
}
void render()
{
d3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(10, 0, 32), 1.0f, 0); // Clears the screen to a dark blue
d3dDevice->BeginScene(); // Begins Direct3D scene
d3dSprite->Begin(D3DXSPRITE_ALPHABLEND); // Begin sprite drawing
background.draw();
counter.draw();
gameStations.draw();
consoleCab.draw();
clothesStand.draw();
gamePanel.updateInformation(564.55, 800.56, 1034.54, "July");
for (std::vector<Customer*>::iterator customerIT = customers.begin(); customerIT != customers.end(); customerIT++)
{
(*customerIT)->draw();
}
gamePanel.draw();
d3dSprite->End(); // End drawing
d3dDevice->EndScene(); // Ends the Direct3D scene
d3dDevice->Present(NULL, NULL, NULL, NULL); // Presents the Direct3D scene (Displays to screen)
}
void cleanUp()
{
d3dDevice->Release(); // Closes the Direct3D device and releases memory
d3d->Release(); // Closes Direct3D and releases memory
}
I apologise in advance if I have not explained something properly or missed something out. I am notoriously bad for trying to explain things! :/ Can anyone help me out? Beginning to think I should have stuck with XNA for this project and learnt about pointers and vectors a little more in depth later on... too late now though :(
Thanks
Vector stores elements by copying them. So, when you do vector.push_back(Customer())
you actually do the following:
Step #3 is the problem because of now vector has a copy of an invalid object: all its resources were released. I'd bet this is the problem but your code doesn't define destructor of Customer (or you didn't paste it) so, I'm not that sure. But anyway, the design is very far from perfect.
What I may suggest is storing pointers to Customer. If my theory is correct this will help to run this particular code sample.
std::vector<Customer*> customers;
customers.push_back(new Customer());
Of course, you'll have to deal with releasing of the allocatied memory.
And by the way, start checking D3D calls result. Without this any debugging will be a nightmare (and it is already). My guess is that d3dSprite->Draw
fails because of missed texture, you may start checking from there.
Another problem is in loadGraphics
method. This code:
D3DXVECTOR3 position(100.0f, 100.0f, 0.0f);
D3DXVECTOR3 center(0.0f, 0.0f, 0.0f);
Is probably supposed to set initial values to the object fields but in fact here you construct new local objects that get destroyed on exit. Fields Customer::position
/Customer::center
in fact left uninitialized. Then, in Draw
method you use these uninitialized fields. Try fixing this and hope this will help.