Search code examples
c++cmakepathgame-enginesfml

I can't load my game resources into SFML with CMake


I am programming a little game in C ++ using Visual Studio Code with CMake as the Build System. So far there hadn't been any problem with accessing resources, but since I decided to tidy up my project by organizing it in directories, my GetTexture, GetSoundBuffer and GetFont functions are unable to load images from the Resources folder.

Obviously I know that when saving the files in different directories, I had to update the paths. So what at first was "Resources / image.png" became "../../../Resources/image.png", (which did work for all #include directives) but when I run the game I only see the black screen and console showing me messages like Failed to load image "../../../Resources/image.png". Reason: Unable to open file.

I've tried over and over again to rearrange the project but every time I compile this happens. I don't know much about CMake and I don't know if the problem is with my CMakeLists.txt file or with my previously mentioned functions, which I doubt as they worked perfectly before.

GetTexture:

sf::Texture& Game::GetTexture(std::string _fileName)
{
    auto iter = textures.find(_fileName);

    if (iter != textures.end())
    {
        return *iter->second;
    }

    TexturePtr texture = std::make_shared<sf::Texture>();

    texture->loadFromFile(_fileName);

    textures[_fileName] = texture;

    return *texture;
}

GetSoundBuffer:

sf::SoundBuffer& Game::GetSoundBuffer(std::string _fileName)
{
    auto iter = sounds.find(_fileName);

    if (iter != sounds.end())
    {
        return *iter->second;
    }

    SoundBufferPtr sound = std::make_shared<sf::SoundBuffer>();

    sound->loadFromFile(_fileName);

    sounds[_fileName] = sound;

    return *sound;
}

GetFont:

sf::Font& Game::GetFont(std::string _fileName)
{
    auto iter = fonts.find(_fileName);

    if (iter != fonts.end())
    {
        return *iter->second;
    }

    FontPtr font = std::make_shared<sf::Font>();

    font->loadFromFile(_fileName);

    fonts[_fileName] = font;

    return *font;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.18)
set(PROJECT_NAME "MyGame")
project(MyGame)

set(SFML_DIR "${CMAKE_CURRENT_LIST_DIR}/libs/SFML-2.5.1/lib/cmake/SFML")

file(GLOB ALL_REQUIRED_DLL "libs/required_dlls/*.dll")
file(COPY ${ALL_REQUIRED_DLL} DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

set(CMAKE_CXX_STANDARD 17)

set(SOURCE_FILES
    ...
  ${RES_FILES})
  

add_executable(${PROJECT_NAME} ${SOURCE_FILES})

find_package(SFML 2.5.1 COMPONENTS system window graphics network audio REQUIRED)
target_link_libraries(${PROJECT_NAME} sfml-audio sfml-graphics sfml-window sfml-system)

The organization of the project is as follows:

Build:
     (Cmake build stuff)

Engine:
     (All engine codes, no problem here)

Scenes:
     Scene1:
         include:
             (All .h files)
         src:
             (All .cpp files, here is where i call GetTexture, GetSoundBuffer
              and GetFont)

Resources:
     (All the images, sounds and fonts)

CMakeLists.txt
main.cpp 

To all this, it is also worth mentioning that I am using Linux.


Solution

  • When you run an executable in another location, the relative path will be different from what it was during compilation.

    One solution is to make everything relative to the executable's location as in:

    namespace fs = std::filesystem;
    int main( int argc, char* argv[] ) {
        fs::path path( fs::canonical( argv[0] ) );
        fs::path file = path.parent_path() / "sound.wav";
        std::cout << file << std::endl;
    }
    

    Result:

    Program stdout
    "/app/sound.wav"
    

    Code: https://godbolt.org/z/K7PPchdjT

    Another solution would be to incorporate the file within the actual executable. I have answered a similar question here:

    how to put files and exported functions in one dll?