error LNK2001: unresolved external symbol

Started by
34 comments, last by falcon93 12 years, 9 months ago

It appears you are writing a namespace, not a class.


Why I thought static methods would be good is becouse I can reach them even from the player-class and enemy-class (see image in my first post). How would you have done if you wanted to access the Draw method from classes that is "below" the GraphicsEnigne?

For complex games, objects do not render themselves. But that aside, a simple way is the following:

void Player::Draw(GraphicsEngine &graphics) const
{
// Using "this" pointer for clarity
graphics.drawSprite(this->position, this->texture, /* ... */);
}

Whatever you end up doing, you should almost certainly not be exposing all those variables as public.

Be aware that using preprocessor constants inside a class doesn't have any significance, as the preprocessor does not understand or respect C++ scoping rules. Try making it a const <type> (DWORD?) instead.
[/quote]



Namespace? Whats the difference between a namespace and a class?

My plan was that all game objects that should be drawn should call on the GraphicEngines Draw method. However, if the Draw method wouldn't be static, I would have been forced to send references to it deep into sub classes. Therefore I thought that if the Draw method was static, I could reach it everywere in my game. And all variables that is used in a static method must also be static, right?

Then 3 small questions showed up:

1. Don't I need to send a whole vertex array as a parameter into my method?
2. Why is it a '&' infront of the "graphics" object? I guess that it's some kind of pointer, but how did you know that you should use it?
3. Could you please explain that "const" statement? I've actually never used it :/

Thanks very much for helping :)
Advertisement

Namespace? Whats the difference between a namespace and a class?
[/quote]
A namespace is a space for names =]. That is, the identifier "string" in namespace std is different from the namespace "falcon". In this way you can write any class names you want, provided you keep your namespaces separate from any libraries you are using.

Large programs might use namespaces to delimit module boundaries, or simply to separate all "our" code from "outside" code (in libraries). Small programs are often written in the default, global namespace.

A class is a blueprint for creating objects. If you are never going to create objects, then it doesn't make sense to pretend you are writing a class.

My plan was that all game objects that should be drawn should call on the GraphicEngines Draw method.
[/quote]
Fair enough.

However, if the Draw method wouldn't be static, I would have been forced to send references to it deep into sub classes.
[/quote]
Yes. I think you'll probably find it isn't all that deep, though!

Therefore I thought that if the Draw method was static, I could reach it everywere in my game.
[/quote]
While true, it doesn't make it a good idea. When I was starting out, I used to write code like this. Eventually my program was this big mass of spaghetti where code from anywhere could make calls into completely unrelated subsystems. I later realised that this "design" is almost indistinguishable from the absence of design.

If you want to write code that is easy to read and maintain, you absolutely do not want to be able to reach across into unrelated modules like that. It takes some discipline, but you should be able to minimise the number of areas of the code that actually need to touch the GraphicsEngine. You'll probably find it isn't all that many places, once it has been factored a little better.

This often involves inverting control somewhere. So (for example), instead of your player object drawing the HUD (which is usually simple because it has access to its own health, ammo, etc), you might have a HUD object which handles this.

This is maybe a poor example, because the player in this case is drawing themselves, but it is an example of how one might approach this.

And all variables that is used in a static method must also be static, right?
[/quote]
You cannot access instance member variables in a static method, unless you happen to have an instance object.


1. Don't I need to send a whole vertex array as a parameter into my method?
[/quote]
Perhaps. Depends on the nature of the game. It would be nice if the client (e.g. the Player class) doesn't need to talk about graphics in such low level detail. It should be able to say "I want this drawn" and let the GraphicsEngine figure out how to get that on the screen. Hence my "drawSprite" member function.


2. Why is it a '&' infront of the "graphics" object? I guess that it's some kind of pointer, but how did you know that you should use it?
[/quote]
That is C++ syntax for a "reference". They are a (better) alternative to pointers for most common uses. They cannot be null and cannot be reseated (something akin to pointing to a new object). They express the idea "here is an object that already exists". A pointer could be NULL, could be pointing to one object, could point to an array. You cannot tell


3. Could you please explain that "const" statement? I've actually never used it :/
[/quote]
It marks the member function as "const". That is, the Player object is considered "const" for the duration of this method. This means that you cannot change the values of member variables.

This is an aspect of "const correctness". I think I've put enough on your plate for the moment, you might want to research that later!
It looks like I have very, very much more to learn :)

Well, I've read your reply and decided to do it in the more "correct" way, which means accessing the Draw method from references sent to all classes that will need to call for the Draw method.

However, now I have a new problem. I've done a few changes to my GraphicsEngine as I want it to support drawing many textures at the same time. Now I call the Draw methods twise with different data, but the textures is flickering, like they are drawn once per two loops. I've pasted my code from the GraphicsEngine below:


GraphicsEngine.h
[source lang="cpp"]
#pragma once


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


#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")


class GraphicsEngine
{

public:
GraphicsEngine(HWND hWnd);
~GraphicsEngine(void);
void Update(void);
void Draw(LPCWSTR texture_path, float x, float y);

private:
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
LPDIRECT3DVERTEXBUFFER9 v_buffer;
D3DPRESENT_PARAMETERS d3dpp;
VOID* pVoid;
IDirect3DTexture9* texture;
D3DSURFACE_DESC surface;

private:
struct CUSTOMVERTEX { float X, Y, Z, RHW; D3DCOLOR COLOR; float U, V; };
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
CUSTOMVERTEX vertices[4];

};
[/source]



GraphicsEngine.cpp
[source lang="cpp"]
#include <d3d9.h>
#include <d3dx9tex.h>
#include "GraphicsEngine.h"
#include "Main.h"


GraphicsEngine::GraphicsEngine(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);

ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));

d3dpp.Windowed = false;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;

d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);

d3ddev->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX), 0, CUSTOMFVF, D3DPOOL_MANAGED, &v_buffer, NULL);
}


GraphicsEngine::~GraphicsEngine(void)
{
}


void GraphicsEngine::Update(void)
{
v_buffer->Lock(0, 0, (void**)&pVoid, 0);
memcpy(pVoid, vertices, sizeof(vertices));
v_buffer->Unlock();
}


void GraphicsEngine::Draw(LPCWSTR texture_path, float x, float y)
{
D3DXCreateTextureFromFileEx(d3ddev, texture_path, 0, 0, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, NULL, NULL, &texture);
texture->GetLevelDesc(0, &surface);

vertices[0].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[0].X = x;
vertices[0].Y = y;
vertices[0].Z = 0;
vertices[0].RHW = 1;
vertices[0].U = 0.0f;
vertices[0].V = 0.0f;

vertices[1].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[1].X = x + surface.Width;
vertices[1].Y = y;
vertices[1].Z = 0;
vertices[1].RHW = 1;
vertices[1].U = 1.0f;
vertices[1].V = 0.0f;

vertices[2].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[2].X = x + surface.Width;
vertices[2].Y = y + surface.Height;
vertices[2].Z = 0;
vertices[2].RHW = 1;
vertices[2].U = 1.0f;
vertices[2].V = 1.0f;

vertices[3].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[3].X = x;
vertices[3].Y = y + surface.Height;
vertices[3].Z = 0;
vertices[3].RHW = 1;
vertices[3].U = 0.0f;
vertices[3].V = 1.0f;

d3ddev->Clear(9, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->BeginScene();
d3ddev->SetFVF(CUSTOMFVF);
d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));
d3ddev->SetTexture(0, texture);
d3ddev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
[/source]


And here's main.cpp
[source lang="cpp"]
#include <Windows.h>
#include "Main.h"
#include "GraphicsEngine.h"


LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY: PostQuitMessage(0); return 0;
}

return DefWindowProc(hWnd, message, wParam, lParam);
}


int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
ZeroMemory(&wc, sizeof(WNDCLASSEX));

wc.cbSize = sizeof(WNDCLASSEX);
wc.hInstance = hInstance;
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = L"AvoidBallClass";
wc.style = CS_VREDRAW | CS_HREDRAW;

RegisterClassEx(&wc);

HWND hWnd;
hWnd = CreateWindowEx(NULL, L"AvoidBallClass", L"AvoidBall", WS_OVERLAPPED, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);

MSG msg;

GraphicsEngine* graphics = new GraphicsEngine(hWnd);

while (true)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

graphics->Update();
graphics->Draw(L"texture.jpg", 100, 100);
graphics->Update();
graphics->Draw(L"texture.jpg", 400, 400);
}

// TODO: Clean up resource.

return msg.wParam;
}
[/source]



What am I doing wrong? :(

Every call to Draw() clears and updates the screen, besides doing a fantastic level of work including loading the texture from a file! The update makes no sense, as it copies the data to the vertex buffer *after* you've asked Direct3D to render stuff - it is never in sync with the values passed in to Draw().

The draw method should not load files, nor should it clear the screen or present it.Perhaps add a "begin" and "end" method to the graphics engine, which WinMain() would call. To avoid loading files at runtime, add a "texture" class that handles a texture in memory. A player object might have a texture which it can pass to the renderer.

A simple implementation of the draw function would set the vertices, copy them to the vertex buffer and submit them to Direct3D.

In the end, your loop should look something like this:

// Don't dynamically allocate unless necessary!
GraphicsEngine graphics(hWnd);

Texture texture(L"texture.jpg");

while (true)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

graphics.Begin();
graphics.Draw(texture, 100, 100);
graphics.Draw(texture, 400, 400);
graphics.End();
}
I've done some cleanup in my GraphicsEngine.cpp, and added Begin() and End() methods. I've also created a Sprite class that I want to send into the GraphicsEngine and get all info from. I don't know where the "vertex buffer copy code" should be though. Below is my current code:

GraphicsEngine.h
[source lang="cpp]
#pragma once


#include <d3d9.h>
#include <d3dx9.h>
#include "Sprite.h"


#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")


class GraphicsEngine
{

public:
GraphicsEngine(HWND hWnd);
~GraphicsEngine(void);
void Begin(void);
void Draw(Sprite sprite);
void End(void);

public:
LPDIRECT3DDEVICE9 d3ddev;

private:
LPDIRECT3D9 d3d;
LPDIRECT3DVERTEXBUFFER9 v_buffer;
D3DPRESENT_PARAMETERS d3dpp;
IDirect3DTexture9* texture;
D3DSURFACE_DESC surface;
VOID* pVoid;

private:
struct CUSTOMVERTEX { float X, Y, Z, RHW; D3DCOLOR COLOR; float U, V; };
#define CUSTOMFVF (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1)
CUSTOMVERTEX vertices[4];

};
[/source]


GraphicsEngine.cpp
[source lang="cpp]
#include <d3d9.h>
#include <d3dx9tex.h>
#include "GraphicsEngine.h"
#include "Main.h"
#include "Sprite.h"


GraphicsEngine::GraphicsEngine(HWND hWnd)
{
d3d = Direct3DCreate9(D3D_SDK_VERSION);

ZeroMemory(&d3dpp, sizeof(D3DPRESENT_PARAMETERS));

d3dpp.Windowed = false;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = hWnd;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
d3dpp.BackBufferWidth = SCREEN_WIDTH;
d3dpp.BackBufferHeight = SCREEN_HEIGHT;

d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dpp, &d3ddev);

d3ddev->CreateVertexBuffer(4*sizeof(CUSTOMVERTEX), 0, CUSTOMFVF, D3DPOOL_MANAGED, &v_buffer, NULL);
}


GraphicsEngine::~GraphicsEngine(void)
{
}


void GraphicsEngine::Begin(void)
{
d3ddev->Clear(9, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
d3ddev->BeginScene();
d3ddev->SetFVF(CUSTOMFVF);
d3ddev->SetStreamSource(0, v_buffer, 0, sizeof(CUSTOMVERTEX));
}


void GraphicsEngine::Draw(Sprite sprite)
{
vertices[0].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[0].X = sprite.x;
vertices[0].Y = sprite.y;
vertices[0].Z = 0;
vertices[0].RHW = 1;
vertices[0].U = 0.0f;
vertices[0].V = 0.0f;

vertices[1].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[1].X = sprite.x + sprite.width;
vertices[1].Y = sprite.y;
vertices[1].Z = 0;
vertices[1].RHW = 1;
vertices[1].U = 1.0f;
vertices[1].V = 0.0f;

vertices[2].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[2].X = sprite.x + sprite.width;
vertices[2].Y = sprite.y + sprite.height;
vertices[2].Z = 0;
vertices[2].RHW = 1;
vertices[2].U = 1.0f;
vertices[2].V = 1.0f;

vertices[3].COLOR = D3DCOLOR_ARGB(255, 255, 255, 255);
vertices[3].X = sprite.x;
vertices[3].Y = sprite.y + sprite.height;
vertices[3].Z = 0;
vertices[3].RHW = 1;
vertices[3].U = 0.0f;
vertices[3].V = 1.0f;

v_buffer->Lock(0, 0, (void**)&pVoid, 0);
memcpy(pVoid, vertices, sizeof(vertices));
v_buffer->Unlock();

d3ddev->SetTexture(0, texture);
d3ddev->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2);
}


void GraphicsEngine::End()
{
d3ddev->EndScene();
d3ddev->Present(NULL, NULL, NULL, NULL);
}
[/source]


Sprite.h
[source lang="cpp]
#pragma once


#include <d3dx9tex.h>
#include "GraphicsEngine.h"


class Sprite
{

public:
Sprite(GraphicsEngine graphics, LPCWSTR texture_path, float x, float y, float width, float height);
~Sprite(void);

private:
IDirect3DTexture9* texture;
D3DSURFACE_DESC surface;

public:
float x;
float y;
float width;
float height;

};
[/source]


Sprite.cpp
[source lang="cpp]
#include "Sprite.h"
#include "GraphicsEngine.h"


Sprite::Sprite(GraphicsEngine graphics, LPCWSTR texture_path, float x, float y, float width, float height)
{
D3DXCreateTextureFromFileEx(graphics.d3ddev, texture_path, 0, 0, 0, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_FILTER_NONE, D3DX_FILTER_NONE, 0, NULL, NULL, &texture);

this->x = x;
this->y = y;
this->width = width;
this->height = height;
}


Sprite::~Sprite(void)
{
}
[/source]



From this I get the 4 following errors:


error C2061: syntax error : identifier 'Sprite' c:\users\simon blom\desktop\avoidball\avoidball\graphicsengine.h



error C2061: syntax error : identifier 'GraphicsEngine' c:\users\simon blom\desktop\avoidball\avoidball\sprite.h



error C2061: syntax error : identifier 'GraphicsEngine' c:\users\simon blom\desktop\avoidball\avoidball\sprite.h



IntelliSense: identifier "Sprite" is undefined c:\users\simon blom\desktop\avoidball\avoidball\graphicsengine.h



What am I doing wrong? :(
You have a circular dependency. Sprite.h includes GraphicsEngine.h, and GraphicsEngine.h includes Sprite.h. That will not work. You can solve this by using forward declaration. For that to work you can't pass GraphicsEngine and Sprite by value as you do now.
I'll explain the last post a bit:

The compiler translates each cpp file, and when looking at Sprite.cpp, it couldn't find Sprite because it first included Sprite.h, then GraphicsEngine.h, then Sprite.h again, HOWEVER there's a pragma once, thus it skips the second inclusion and tries to make sense of GraphicsEngine.h. The problem is that the Sprite class is NOT known at that point (remember that you included GraphicsEngine.h BEFORE the compiler actually knew the Sprite class).




You can solve this by forward declaring either one or (sometimes better) both types. Simply put "class GraphicsEngine" right before class Sprite { .... } and/or the other way around. As long as you don't actually USE the type (ie. access methods, pass it by value) from within the header, you are fine.

*edit*

Actually, the preprocessor pastes everything into one big "file" and then the compiler kicks in, however the order in which the compiler finds those types in the big file is the same as described above.

I'm not sure I understood the "forward" tecnique... Could you please post a small example? unsure.gif

I'm not sure I understood the "forward" tecnique... Could you please post a small example? unsure.gif


Sure thing:

Sprite.h:
[source]
#pragma once

// includes

class GraphicsEngine;
class Sprite
{
....public:

Sprite(GraphicsEngine& engine);

private:

GraphicsEngine& m_engine;
};
[/source]

Sprite.cpp
[source]
#include "Sprite.h"
#include "GraphicsEngine.h"
[/source]

The important point is that you cannot use/pass a forward declared type by value, ie "GraphicsEngine engine", but have to use/pass it by reference and/or pointer (GraphicsEngine& engine OR GraphicsEngine* engine): The reason behind this is that the compiler is required to know the size of the object to pass, in order to pass it by value (or use it as a member in a class), which it cannot on forward delcared types. Using references/pointers requires NO knowledge of the underlying type. But note that calling methods on that type or accessing members requires knowledge of the type, hence you are forced to place all code that accesses GraphicsEngine inside Sprite.cpp (and vice verca for GraphicsEngine.cpp if you forward declare Sprite as well).

*edit*

Please note that it also makes sense to pass the engine by reference: You don't want to copy it, but pass a reference TO the SAME object to all sprites, so those sprites can access the shared state of that single engine. Creating a copy of an engine would be a hard thing to implement, since you have to decide whether both instances access & render to the same window, etc... It is easier to forbid copying an engine alltogether by either inheriting from boost::noncopyable or by explicitly declaring the copy constructor private:

[source]

class CannotBeCopied

{

public:

...

private:

CannotBeCopied(const CannotBeCopied&);

};

[/source]



public:
Sprite(GraphicsEngine graphics, LPCWSTR texture_path, float x, float y, float width, float height);
~Sprite(void);



I'm sending a reference from the GraphicsEngine into the Sprite here, right? But then I have to include the GraphicsEngine header file? Or?

This topic is closed to new replies.

Advertisement