mkdewidar
mkdewidar

Reputation: 51

LNK 2001 unresolved with C++ SFML

I'm trying to make a character which moves around the screen in C++ using the SFML library. Every time a make a project i end up with the same LNK 2001 error.

Please explain to me what I've done wrong and how do i fix it. I'm still a beginner so if you have any recommendations about fixing errors or writing code I'd love it.

Main.cpp:

#include "Game.cpp"
#include "Game.h"

int main ()
{
    Game Game;

    Game.GameRun();
}

Game.h:

#pragma once

#include <SFML\Window.hpp>
#include "Character.h"

class Game
{
public:
    void GameRun ();
    void UpdateGame(Character player);
    void Controls(int sourceX, int sourceY, Character player);
};

Character.h:

#pragma once

#include <SFML\Graphics.hpp>
#include <SFML\System.hpp>
#include <SFML\Window.hpp>
#include "Game.h"

class Character
{
public:
    enum Direction {Down, Left, Right, Up};

    int characterX;
    int characterY;

    sf::Texture txt_character;
    sf::Sprite spr_character;
};

Game.cpp:

#include "Game.h"
#include "Character.h"

#include <iostream>

inline void Game::GameRun()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML test");

    Character player;

    player.characterX = 1;
    player.characterY = Character::Down;

    player.txt_character.loadFromFile("character sprite sheet.png");
    player.spr_character.setTexture(player.txt_character);

    while(window.isOpen())
    {
        Game::UpdateGame(player);
    }
}

inline void Game::UpdateGame(Character player)
{
    sf::Event event;
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML test");

    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
        window.close();
    }
    Game::Controls(player.characterX, player.characterY, player);

    player.spr_character.setTextureRect(sf::IntRect(player.characterX * 32, player.characterY * 32, 32, 32));

    window.draw(player.spr_character);
    window.display();
    window.clear();
}

inline void Game::Controls(int sourceX, int sourceY, Character player)
{
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
    {
        sourceY = Character::Up;
        sourceX++;
        if (sourceX * 32 >= player.txt_character.getSize().x)
            sourceX = 0;
        player.spr_character.move(0, -2);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
    {
        sourceY = Character::Down;
        sourceX++;
        if (sourceX * 32 >= player.txt_character.getSize().x)
            sourceX = 0;
        player.spr_character.move(0, 2);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
    {
        sourceY = Character::Left;
        sourceX++;
        if (sourceX * 32 >= player.txt_character.getSize().x)
            sourceX = 0;
        player.spr_character.move(-2, 0);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
    {
        sourceY = Character::Right;
        sourceX++;
        if (sourceX * 32 >= player.txt_character.getSize().x)
            sourceX = 0;
        player.spr_character.move(2, 0);
    }
}

Errors:

1>main.obj : error LNK2001: unresolved external symbol "public: static int Character::characterY" (?characterY@Character@@2HA)

1>main.obj : error LNK2001: unresolved external symbol "public: static int Character::characterX" (?characterX@Character@@2HA)

1>C:\Users\SONY\Documents\Visual Studio 2010\Projects\Top Down Game\Debug\Game.exe : fatal error LNK1120: 2 unresolved externals

Thanks :D

EDIT: FYI i had the code without classes and it was working perfectly, it was just a big block of code in the main.cpp

EDIT: link to folder for program: https://www.dropbox.com/s/3yyjti8zwu019s7/Top%20Down%20Game%20With%20Classes.rar

Upvotes: 4

Views: 294

Answers (1)

Emile Bergeron
Emile Bergeron

Reputation: 17430

EDIT 2014/03/08: I tried your sources. Remove the #include "Game.cpp" inside the Main.cpp file and the inline keyword inside the Game.cpp file.

The logic behind wasn't working so I fixed it a minimum so it will work as soon as you have the compiling (linking) error fixed. You should try my code to see if it helps out of the box.

A little explanation first

Passing the player object by value (as when you have void someMethod(Character player)) sends a copy of the object to the method. So when you make changes to the player object, those changes are only made to the copy and not the actual player. I changed your code so it sends a pointer instead (void someMethod(Character * player). The method receive a pointer to the actual player object and changes made to that object reflects on the actual player object.

There are a couple solution other than using a pointer (which some people might say is overkill or too dangerous).

It could be instead :

  • A reference void someMethod(Character& player)
  • or a return value Character someMethod(Character player)
  • or a mix of the two Character someMethod(const Character& player)
  • or a member variable (defined in the header file)

Another thing is that you keep a sf::Texture by value inside your Character class. sf::Texture are really "heavy" objects, so they should not be passed by value and should be kept as pointer. The best thing you could do (and that I've not done in your code below) is to completely remove the sf::Texture from the Character class and only keep the sf::Sprite which already holds the sf::Texture (in a optimised way, so you don't bother with it).

Here's the code

Main.cpp

#include "Game.h"

int main ()
{
    Game Game;

    Game.GameRun();
}

Game.h

#ifndef GAME_H_
#define GAME_H_

//#include <SFML/Window.hpp> /* you don't need this */
#include <SFML/Graphics/RenderWindow.hpp>


// forward declaration, this prevents recursive/circular dependencies.
// only works with pointer or referenced type.
class Character;

class Game
{
public:
    void GameRun ();

    // I change the param to pointer to Character for logic reasons.
    // You were doing changes on a Character COPY of the player,
    // which was deleted when leaving the scope of the functions.
    void UpdateGame(Character * player);
    void Controls(Character * player);
private:
    // I added this here, so you can create the window in the 
    // GameRun and UpdateGame methods where needed.
    sf::RenderWindow window;
};

#endif

Game.cpp

#include "Game.h"
#include "Character.h"

#include <SFML/Window/Event.hpp> /* you only need this */

#include <iostream>

void Game::GameRun()
{
    // I moved the window creation here because you were 
    // creating a new window each update which made the windows
    // flickers each frame, making it impossible to close properly.
    window.create(sf::VideoMode(800, 600), "SFML test");

    Character * player = new Character();

    player->characterX = 1;
    player->characterY = Character::Down;

    // if the file isn't available, this will prevent further problems.
    if (player->txt_character.loadFromFile("character.png"))
    {
        player->spr_character.setTexture(player->txt_character);
    }

    while (window.isOpen())
    {
        Game::UpdateGame(player);
    }
}

void Game::UpdateGame(Character * player)
{
    sf::Event event;


    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed){

            window.close();

        }
    }
    Controls(player);

//  player.spr_character.setTextureRect(
//          sf::IntRect(player.characterX * 32, player.characterY * 32, 32,
//                  32));

    window.clear();
    window.draw(player->spr_character);
    window.display();

}

// You were already sending the player so I removed the other
// params and used the player instead.
void Game::Controls(Character * player)
{
    int sourceX = player->characterX;

    if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
    {
        player->characterY = Character::Up;
        sourceX++;
        if (sourceX * 32 >= player->txt_character.getSize().x)
            sourceX = 0;
        player->spr_character.move(0, -2);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
    {
        player->characterY = Character::Down;
        sourceX++;
        if (sourceX * 32 >= player->txt_character.getSize().x)
            sourceX = 0;
        player->spr_character.move(0, 2);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
    {
        player->characterY = Character::Left;
        sourceX++;
        if (sourceX * 32 >= player->txt_character.getSize().x)
            sourceX = 0;
        player->spr_character.move(-2, 0);
    }
    if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
    {
        player->characterY = Character::Right;
        sourceX++;
        if (sourceX * 32 >= player->txt_character.getSize().x)
            sourceX = 0;
        player->spr_character.move(2, 0);
    }
    player->characterX = sourceX;
}

Character.h

#ifndef CHARACTER_H_
#define CHARACTER_H_

//#include <SFML\Graphics.hpp>
//#include <SFML\System.hpp>
//#include <SFML\Window.hpp>
//#include "Game.h"
#include <SFML/Graphics/Sprite.hpp> /* Don't include more than you need */
#include <SFML/Graphics/Texture.hpp>

class Character
{
public:
    enum Direction {Down, Left, Right, Up};

    int characterX;
    int characterY;

    sf::Texture txt_character; // Don't keep the texture value, it's really heavy.
    sf::Sprite spr_character;
};

#endif

General C++ information

Upvotes: 3

Related Questions