包含 SDL2/SDL_Image 时 SDL 包含错误

SDL include error when including SDL2/SDL_Image

这是一个简单的 SDL2 程序,它使用 SDL_Image 并且运行良好:

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>

// Manage error messages
void check_error_sdl(bool check, const char* message);
void check_error_sdl_img(bool check, const char* message);

// Load an image from "fname" and return an SDL_Texture with the content of the image
SDL_Texture* load_texture(const char* fname, SDL_Renderer *renderer);


int main(int argc, char** argv) {
     // Initialize SDL
     check_error_sdl(SDL_Init(SDL_INIT_VIDEO) != 0, "Unable to initialize SDL");
 
     // Create and initialize a 800x600 window
     SDL_Window* window = SDL_CreateWindow("Test SDL 2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                           800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
     check_error_sdl(window == nullptr, "Unable to create window");
 
     // Create and initialize a hardware accelerated renderer that will be refreshed in sync with your monitor (at approx. 60 Hz)
     SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
     check_error_sdl(renderer == nullptr, "Unable to create a renderer");
 
     // Set the default renderer color to corn blue
     SDL_SetRenderDrawColor(renderer, 100, 149, 237, 255);
 
     // Initialize SDL_img
     int flags=IMG_INIT_JPG | IMG_INIT_PNG;
     int initted = IMG_Init(flags);
     check_error_sdl_img((initted & flags) != flags, "Unable to initialize SDL_image");
 
     // Load the image in a texture
     SDL_Texture *texture = load_texture("img_test.png", renderer);
 
     // We need to create a destination rectangle for the image (where we want this to be show) on the renderer area
     SDL_Rect dest_rect;
     dest_rect.x = 50; dest_rect.y = 50;
     dest_rect.w = 337; dest_rect.h = 210;
 
     // Clear the window content (using the default renderer color)
     SDL_RenderClear(renderer);
 
     // Copy the texture on the renderer
     SDL_RenderCopy(renderer, texture, NULL, &dest_rect);
 
     // Update the window surface (show the renderer)
     SDL_RenderPresent(renderer);
 
     // Wait for 10 seconds
     SDL_Delay(5000);
 
     // Clear the allocated resources
     SDL_DestroyTexture(texture);
     IMG_Quit();
     SDL_DestroyRenderer(renderer);
     SDL_DestroyWindow(window);
     SDL_Quit();
 
     return 0;
}

// In case of error, print the error code and close the application
void check_error_sdl(bool check, const char* message) {
     if (check) {
         std::cout << message << " " << SDL_GetError() << std::endl;
         SDL_Quit();
         std::exit(-1);
     }
}

// In case of error, print the error code and close the application
void check_error_sdl_img(bool check, const char* message) {
     if (check) {
         std::cout << message << " " << IMG_GetError() << std::endl;
         IMG_Quit();
         SDL_Quit();
         std::exit(-1);
     }
}

// Load an image from "fname" and return an SDL_Texture with the content of the image
SDL_Texture* load_texture(const char* fname, SDL_Renderer *renderer) {
     SDL_Surface *image = IMG_Load(fname);
     check_error_sdl_img(image == nullptr, "Unable to load image");
 
     SDL_Texture *img_texture = SDL_CreateTextureFromSurface(renderer, image);
     check_error_sdl_img(img_texture == nullptr, "Unable to create a texture from the image");
     SDL_FreeSurface(image);
     return img_texture;
}

我编译它:

g++ -std=c++0x -Wall -pedantic sdl2_test.cpp -o sdl2_test `sdl2-config --cflags --libs` -lSDL2_image

它正确执行并显示图像。

但是在同一系统上我的游戏在编译时出现了这个错误:

g++ -c frogmain.cc
In file included from level.hh:2,
                 from game.hh:1,
                 from frogmain.cc:2:
/usr/local/include/SDL2/SDL_image.h:27:10: fatal error: SDL.h: No such file or directory
 #include "SDL.h"
          ^~~~~~~
compilation terminated.
make: *** [makefile:5: frogmain.o] Error 1

这是我的 makefile:

FrogGame: frogmain.o game.o level.o sprite.o actor.o vector.o
    g++ -std=c++0x -Wall -pedantic -o FrogGame frogmain.o game.o level.o sprite.o actor.o vector.o `sdl2-config --cflags --libs` -lSDL2_image
    #g++ -o FrogGame frogmain.o game.o level.o sprite.o actor.o vector.o
    
frogmain.o: frogmain.cc game.hh level.hh sprite.hh vector.hh
    g++ -c frogmain.cc
    
game.o: game.cc level.hh sprite.hh actor.hh vector.hh
    g++ -c game.cc
    
level.o: level.cc sprite.hh actor.hh vector.hh
    g++ -c level.cc

actor.o: actor.cc vector.hh
    g++ -c actor.cc

sprite.o: sprite.cc vector.hh
    g++ -c sprite.cc

vector.o: vector.cc
    g++ -c vector.cc

唯一需要SDL的class是关卡class,这里是.cc文件:

#include <iostream>
#include <sys/time.h>
#include <chrono>
#include "level.hh"
#include "sprite.hh"
#include "actor.hh"
#include "vector.hh"
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>

using namespace std;

level::level()
{
    this->levelPlaying = false;
    this->levelPaused = false;
    this->levelWon = false;
    this->curTime = 0;
    this->timeLimit = 0;
    this->screen = 0;
    this->renderer = 0;
    this->imageArray = 0;
    this->imLength = 0;
    
    this->createScreen();
}

void level::setLevelPlaying(bool p)
{
    this->levelPlaying = p;
}

bool level::getLevelPlaying()
{
    return this->levelPlaying;
}

void level::setLevelPaused(bool p)
{
    this->levelPlaying = p;
}

bool level::getLevelPaused()
{
    return this->levelPlaying;
}

void level::setLevelWon(bool p)
{
    this->levelWon = p;
}

bool level::getLevelWon()
{
    return this->levelWon;
}

void level::setCurTime(int t)
{
    this->curTime = t;
}

int level::getCurTime()
{
    return this->curTime;
}

void level::setTimeLimit(int t)
{
    this->timeLimit = t;
}

int level::getTimeLimit()
{
    return this->timeLimit;
}

bool level::createScreen()
{
    bool success = 0;
    //get size of screen
    SDL_DisplayMode dm;
    SDL_GetDesktopDisplayMode(0, &dm);
    if (dm.w < 1920 && dm.h < 1080)
    {
        screenW = dm.w;
        screenH = dm.h;
    }
    else
    {
        screenW = 1920;
        screenH = 1080;
    }

    success = this->checkSDLError(SDL_Init(SDL_INIT_EVERYTHING) == 0, "SDL Initialization failed");
    
    this->screen = SDL_CreateWindow("Test SDL 2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
                                           screenW, screenH, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
    
    success = this->checkSDLError(this->screen, "Window Initialization failed");
    
    this->renderer = SDL_CreateRenderer(this->screen, -1, 0);
    success = this->checkSDLError(this->renderer, "Renderer Initialization failed");
    
    return success;
}

bool level::loadImageArrayToMemory(const char** imageNames, int length)
{
    IMG_Init(IMG_INIT_PNG);
    //allocate space for all textures
    this->imageArray = new SDL_Texture*[length];
    this->imLength = length;
    //run through entire list
    for (int i = 0; i < length; i++)
    {
        //load the image to a temp surface
        SDL_Surface* tmp = IMG_Load(imageNames[i]);
        this->checkSDLError(tmp, "Unable to load image");
        //create a texture from the surface, for hardware rendering
        this->imageArray[i] = SDL_CreateTextureFromSurface(this->renderer, tmp);
        this->checkSDLError(imageArray[i], "Unable to create texture");
        //destroy the surface
        SDL_FreeSurface(tmp);
    }
    return 1;
}

bool level::destroyImageArrayFromMemory()
{
    //loop through image array
    for (int i = 0; i < this->imLength; i++)
    {
        //destroy the texture in each index of the array
        SDL_DestroyTexture(imageArray[i]);
    }
    //delete the array
    delete[] imageArray;
    
    return 1;
}

bool level::loadSoundArrayToMemory()
{
    //TODO
    return 0;
}

bool level::destroySoundArrayFromMemory()
{
    //TODO
    return 0;
}

bool level::levelLoop()
{
    const int TPF = 33;
    auto curT = chrono::high_resolution_clock::now();
    auto prevT = curT;
    auto diff = 0;
    
    vector p(4,5);
    vector d(78,90);
    
    actor a(p, d, 1, 100);
    
    while (this->getLevelPlaying())
    {
        //create a destination rectangle for the images
        SDL_Rect tmp;
        tmp.x= 0;
        tmp.y = 0;
        tmp.w = 1920;
        tmp.h = 1080; 
            
        //get current time
        curT = chrono::high_resolution_clock::now();
        //get the difference of the current time from the last time the screen was updated
        diff = chrono::duration_cast<chrono::milliseconds>(curT - prevT).count();
        if (diff > TPF)
        {
            //check for keyboard input
            this->checkForInput();
            
            //clear the screen
            SDL_RenderClear(this->renderer);
            
            //for every texture in the imageArray
            for (int i = 0; i < imLength; i++)
            {
                tmp.x = tmp.x + 20;
                tmp.y = tmp.y + 20;
                // Copy the texture on the renderer
                SDL_RenderCopy(this->renderer, this->imageArray[i], NULL, &tmp);
                // Update the window surface (show the renderer)
                SDL_RenderPresent(this->renderer);
            }
            
            prevT = curT;
            
            //add to the current level time
            this->curTime+=diff;
        }
        if (this->curTime > 4000)
        {
            this->setLevelPlaying(false);
        }
    }
    //destroy images in memory to free up space and avoid leaks
    this->destroyImageArrayFromMemory();
    
    return this->getLevelWon();
}

void level::checkForInput()
{
    cout << "inside checkforinput\n";
    //create an empty event
    //SDL_Event event;
    //loop through all events in the queue
    while (SDL_PollEvent(&event))
    {
        cout << "inside while loop\n";
        switch(event.type)
        {
            //cout << "inside switch\n";
            //a keyboard button is pressed down
            case SDL_KEYDOWN:
                cout << "inside case\n";
                //if it is escape key
                if (event.key.keysym.sym == SDLK_ESCAPE)
                {
                    cout << "Escape key pressed" << endl;
                    this->setLevelPlaying(false);
                }
                else if (event.key.keysym.sym == SDLK_q)
                {
                    cout << "Q key pressed" << endl;
                }
                continue;
            default:
                cout << "unhandled key pressed\n";
        }
    }
}

bool level::checkSDLError(bool c, const char* s)
{
    if (c == 0)
    {
         std::cout << s << " " << SDL_GetError() << endl;
         SDL_Quit();
         return 0;
     }
     return 1;
}

我同时安装了 SDL2 和 SDL2Image。我的 makefile 是否有问题,这就是为什么我可以在这个简单的示例中使用 SDLImage 而不能在我的更复杂的程序中使用?我有另一个 raspberry pi,我的游戏的示例和 makefile 都可以正常工作。

当您 编译 .cc [到 .o].

您只传递用于 link .o 文件的 c++ 行上的标志。

其中一个 .o 目标需要 sdl2-config 以及它的 c++ 命令。


这是你的直接问题。然而...

当您执行以下操作时:sdl2-config --cflags,它[部分] 生成(例如)-I/usr/local/include/SDL2,因此如果源 .cc 文件执行了:#include <SDL.h>,它就可以工作。

但是,您的 .cc 文件确实:#include <SDL2/SDL.h>.

这可能行不通,因为它位于不同的目录级别 [除非您添加 -I/usr/local/include,这是次优的,因为您硬连接了路径。

如果 可以正常工作,那是因为编译器假设 /usr/local/includedefault 路径的一部分 .h 文件


更新:

You'll have to excuse me I'm not very good at compiling yet. I tried adding -I/usr/local/include/SDL2/SDL.h in some different spots and it didn't change anything. Could you spell it out a bit more for me?

想要:-I/usr/local/include/SDL2/SDL.h那指向一个文件

你想要:-I/usr/local/include/SDL2指向目录

但是,您应该做 两件事:(1) 修复您的 makefile 和 (2) 更改 level.cc 中的 #include 指令以使其兼容

这是我对您的 makefile 所做的调整:

OBJS += frogmain.o
OBJS += game.o
OBJS += level.o
OBJS += sprite.o
OBJS += actor.o
OBJS += vector.o

SDLCFG := sdl2-config

CPLSDL = $(shell $(SDLCFG) --cflags)

LIBSDL = $(shell $(SDLCFG) --libs)
LIBSDL += -lSDL2_image

COPTS += -std=c++0x
COPTS += -Wall
COPTS += -Werror
COPTS += -pedantic

FrogGame: $(OBJS)
    g++ -o FrogGame $(OBJS) $(LIBSDL)

frogmain.o: frogmain.cc game.hh level.hh sprite.hh vector.hh
    g++ -c $(COPTS) frogmain.cc

game.o: game.cc level.hh sprite.hh actor.hh vector.hh
    g++ -c $(COPTS) game.cc

level.o: level.cc sprite.hh actor.hh vector.hh
    g++ -c $(COPTS) $(CPLSDL) level.cc

actor.o: actor.cc vector.hh
    g++ -c $(COPTS) actor.cc

sprite.o: sprite.cc vector.hh
    g++ -c $(COPTS) sprite.cc

vector.o: vector.cc
    g++ -c $(COPTS) vector.cc

clean:
    rm -f $(OBJS) FrogGame

如果你从那里复制粘贴,TAB 应该是四个空格 [因为这里的代码块是如何格式化的]。因此,您需要手动编辑以将其改回。 (即 make 对前导 TAB 很挑剔)。

level.cc 中,您有以下两个 #include 指令:

#include <SDL2/SDL.h>
#include <SDL2/SDL_Image.h>

你想要:

#include <SDL.h>
#include <SDL_Image.h>