Apologize if this is too long of a post. I'm merely trying to get SDL to work in an object oriented fashion - there's no point in me moving on until I get past this point. Been spending a fair amount of time compiling this and getting errors. I'll post my header files and sources, along with my makefile and an output to see what's going on.
Here's render.h:
#ifndef RENDER_H
#define RENDER_H
#include <string>
#include <SDL/SDL.h>
using std::string;
class Render
{
public:
Render(string filename, int x, int y, SDL_Surface * destination);
~Render();
private:
SDL_Surface * m_optimizedImage;
void load_image(string filename);
void apply_surface(int x, int y, SDL_Surface * source, SDL_Surface * destination);
};
#endif
...and render.cpp:
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
#include "render.h"
using std::string;
Render::Render(string filename, int x, int y, SDL_Surface * destination)
{
this->m_optimizedImage = NULL;
load_image(filename);
apply_surface(x, y, m_optimizedImage, destination);
}
Render::~Render()
{
delete m_optimizedImage;
}
void Render::load_image(string filename)
{
SDL_Surface * loadedImage = IMG_Load(filename.c_str());
if (loadedImage != NULL)
{
m_optimizedImage = SDL_DisplayFormat(loadedImage);
SDL_FreeSurface(loadedImage);
}
}
void Render::apply_surface(int x, int y, SDL_Surface * source, SDL_Surface * destination)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, NULL, destination, &offset);
}
...and screenwriter.h:
#include <iostream>
#include <SDL/SDL.h>
#ifndef SCREENWRITER_H
#define SCREENWRITER_H
class ScreenWriter
{
public:
~ScreenWriter();
bool flip_screen();
void delay_screen(int milliseconds);
bool get_screen_state() const;
static ScreenWriter& get_instance();
SDL_Surface * get_screen() const;
private:
ScreenWriter();
void initialize();
bool m_screenFailure;
SDL_Surface * m_screen;
};
#endif
...and screenwriter.cpp:
#include <SDL/SDL.h>
#include "screenwriter.h"
#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480
#define SCREEN_BPP 32
ScreenWriter::ScreenWriter()
{
this->m_screenFailure = false;
initialize();
}
ScreenWriter::~ScreenWriter()
{
SDL_Quit();
}
ScreenWriter& ScreenWriter::get_instance()
{
static ScreenWriter instance;
return instance;
}
SDL_Surface * ScreenWriter::get_screen() const
{
return m_screen;
}
bool ScreenWriter::get_screen_state() const
{
return this->m_screenFailure;
}
void ScreenWriter::delay_screen(int milliseconds)
{
SDL_Delay(milliseconds);
}
int ScreenWriter::flip_screen()
{
if (SDL_Flip(m_screen) == -1)
{
return 1;
}
else
{
SDL_Flip(m_screen);
return 0;
}
}
void ScreenWriter::initialize()
{
if (SDL_Init(SDL_INIT_EVERYTHING == -1))
{
std::cout << "SDL_Init has failed";
}
else
{
SDL_Init(SDL_INIT_EVERYTHING);
//initialize screen
this->m_screen = SDL_SetVideoMode(SCREEN_WIDTH,
SCREEN_HEIGHT,
SCREEN_BPP,
SDL_SWSURFACE);
if (m_screen == NULL)
{
this->m_screenFailure = true;
}
else
{
this->m_screenFailure = false;
}
//set caption header
SDL_WM_SetCaption("Hello WOrld", NULL);
}
}
...and of course main.cpp:
#include <iostream>
#include <SDL/SDL.h>
#include "screenwriter.h"
#include "render.h"
int main(int argc, char * args[])
{
std::cout << "hello world!" << std::endl;
ScreenWriter * instance = ScreenWriter::get_instance();
instance->flip_screen();
Render render = new Render("look.png", 0, 0, instance->get_screen());
delete instance();
return 0;
}
...my output:
g++ -c main.cpp render.h screenwriter.h -lSDL -lSDL_image
main.cpp: In function ‘int main(int, char**)’:
main.cpp:10:55: error: cannot convert ‘ScreenWriter’ to ‘ScreenWriter*’ in initialization
main.cpp:12:69: error: conversion from ‘Render*’ to non-scalar type ‘Render’ requested
make: *** [main.o] Error 1
...my makefile
program : main.o render.o screenwriter.o
g++ -o program main.o render.o screenwriter.o -lSDL -lSDL_image
main.o : main.cpp render.h screenwriter.h
g++ -c main.cpp render.h screenwriter.h -lSDL -lSDL_image
render.o : render.h render.cpp
g++ -c render.h render.cpp -lSDL
screenwriter.o : screenwriter.h screenwriter.cpp
g++ -c screenwriter.h screenwriter.cpp -lSDL -lSDL_image
clean:
rm program main.o render.o screenwriter.o
the nitty gritty:
My goal with this is to have ScreenWriter implemented as a singleton to setup allegro and flag everything as needed through initialization. The second objective relies on render to just render by specifying x and y coordinates, along with a path to the file to render to load on the map. This is easy to do procedurally, but I'm ready to experiment with OO design on this.
So, any thoughts?
You have two syntax errors which are clear from your errors:
ScreenWriter * instance = ScreenWriter::get_instance();
should be
ScreenWriter & instance = ScreenWriter::get_instance();
because get_instance returns a reference, not a pointer, and
Render render = new Render("look.png", 0, 0, instance->get_screen());
should be
Render * render = new Render("look.png", 0, 0, instance->get_screen());
because new returns a pointer, not an object or reference.
Also,
delete instance();
should be
delete render;
because not only is the former completely wrong, but it's not being used on anything allocated by new. render is, however. So you have to delete it to avoid a memory leak.
I don't understand the "any thoughts" part of the question because it doesn't say about what. English isn't my first language though, so forgive me if I missed something.
Flip the screen after rendering the image. When you blit something, it's blitted on a buffer. SDL_Flip() just swaps those buffers so that your o/p can be seen. So you should be replacing those two lines, I guess.
Related
I have been trying to make a simple game in SDL. I Found that keeping every thing in on file was getting messy so i moved some functions into their own file and set up a global header file to store all the global things i would use. but from there it has been all errors!
Files:
-Main.cpp
-Makefile
-Assets/
-Player.bmp
-Build/
-build.deb
-Modules/
-Global.h
-HandleKeys.cpp
-HandleKeys.h
Main.cpp:
#include <stdio.h>
#include "Modules/Global.h"
#include "Modules/HandleKeys.h"
//Starts up SDL and creates window
bool init();
//Loads media
bool loadMedia();
extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;
//Frees media and shuts down SDL
void close();
//Loads individual image
SDL_Surface* loadSurface( std::string path );
//The window we'll be rendering to
SDL_Window* gWindow = NULL;
//The surface contained by the window
SDL_Surface* gScreenSurface = NULL;
//The images that correspond to a keypress
SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];
//Current displayed image
SDL_Surface* gCurrentSurface = NULL;
int PlayerX;
int PlayerY;
bool init()
{
//Initialization flag
bool success = true;
//Initialize SDL
if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
{
printf( "SDL could not initialize! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Create window
gWindow = SDL_CreateWindow( title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );
if( gWindow == NULL )
{
printf( "Window could not be created! SDL Error: %s\n", SDL_GetError() );
success = false;
}
else
{
//Get window surface
gScreenSurface = SDL_GetWindowSurface( gWindow );
}
}
return success;
}
string get(list<string> _list, int _i){
list<string>::iterator it = _list.begin();
for(int i=0; i<_i; i++){
++it;
}
return *it;
}
bool loadMedia()
{
//Loading success flag
bool success = true;
for( int i = 0; i <= KEY_PRESS_SURFACE_TOTAL; ++i )
{
if (i != KEY_PRESS_SURFACE_TOTAL){
Entity[ PLAYER_SURFACE ] = loadSurface( get(EntityPaths, i) );
}
}
return success;
}
void close()
{
//Deallocate surfaces
for( int i = 0; i < KEY_PRESS_SURFACE_TOTAL; ++i )
{
SDL_FreeSurface( Entity[ i ] );
Entity[ i ] = NULL;
}
//Destroy window
SDL_DestroyWindow( gWindow );
gWindow = NULL;
//Quit SDL subsystems
SDL_Quit();
}
SDL_Surface* loadSurface( std::string path )
{
//Load image at specified path
SDL_Surface* loadedSurface = SDL_LoadBMP( path.c_str() );
if( loadedSurface == NULL )
{
printf( "Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError() );
}
return loadedSurface;
}
int main( int argc, char* args[] )
{
//Start up SDL and create window
if( !init() )
{
printf( "Failed to initialize!\n" );
}
else
{
//Load media
if( !loadMedia() )
{
printf( "Failed to load media!\n" );
}
else
{
//Main loop flag
bool quit = false;
//Event handler
SDL_Event e;
//Set default current surface
gCurrentSurface = Entity[ PLAYER_SURFACE ];
//While application is running
while( !quit )
{
//Handle events on queue
while( SDL_PollEvent( &e ) != 0 )
{
//User requests quit
if( e.type == SDL_QUIT )
{
quit = true;
}
HandelKey(e);
}
SDL_Rect rect;
rect.x = PlayerX;
rect.y = PlayerY;
rect.w = 0;
rect.h = 0;
//Apply the current image
SDL_BlitSurface( gCurrentSurface, NULL, gScreenSurface, &rect );
//Update the surface
SDL_UpdateWindowSurface( gWindow );
}
}
}
//Free resources and close SDL
close();
return 0;
}
Makefile:
#OBJS specifies which files to compile as part of the project
OBJS = Main.cpp
#MODS specifies which Modules to Compile.
#NOTE: only add .cpp files, no .h files!
MODS = Modules/HandleKeys.cpp
#CC specifies which compiler we're using
CC = g++
#COMPILER_FLAGS specifies the additional compilation options we're using
# -w suppresses all warnings
COMPILER_FLAGS = -w
#LINKER_FLAGS specifies the libraries we're linking against
LINKER_FLAGS = -lSDL2
#OBJ_NAME specifies the name of our exectuable
OBJ_NAME = Build/build.deb
#This is the target that compiles our executable
all : $(OBJS)
$(CC) $(OBJS) $(MODS) $(COMPILER_FLAGS) $(LINKER_FLAGS) -o $(OBJ_NAME)
Global.h
#ifndef __GLOBAL_H__
#define __GLOBAL_H__
//Included by all files!
#include <SDL2/SDL.h>
#include <string>
#include <list>
using namespace std;
extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;
extern char* title = "Top Down";
enum Entity
{
PLAYER_SURFACE,
KEY_PRESS_SURFACE_TOTAL
};
extern list<string> EntityPaths
{
"Assets/Player.bmp",
};
//Starts up SDL and creates window
extern bool init();
//Loads media
extern bool loadMedia();
//Frees media and shuts down SDL
extern void close();
//Loads individual image
extern SDL_Surface* loadSurface( std::string path );
//The window we'll be rendering to
extern SDL_Window* gWindow;
//The surface contained by the window
extern SDL_Surface* gScreenSurface;
//The images that correspond to a keypress
extern SDL_Surface* Entity[ KEY_PRESS_SURFACE_TOTAL ];
//Current displayed image
extern SDL_Surface* gCurrentSurface;
extern int PlayerX;
extern int PlayerY;
extern const int PlayerSpeed;
#endif
HandleKeys.cpp:
#include "Global.h"
#include "HandleKeys.h"
void HandleKey(SDL_Event e){
if( e.type == SDL_KEYDOWN )
{
//Select surfaces based on key press
switch( e.key.keysym.sym )
{
case SDLK_UP:
PlayerY -= PlayerSpeed;
break;
case SDLK_DOWN:
PlayerY += PlayerSpeed;
break;
case SDLK_LEFT:
PlayerX -= PlayerSpeed;
break;
case SDLK_RIGHT:
PlayerX += PlayerSpeed;
break;
}
}
}
HandleKeys.h:
#ifndef __HANDLEKEYS_H__
#define __HANDLEKEYS_H__
extern void HandelKey(SDL_Event e);
#endif
Make file output:
g++ Main.cpp Modules/HandleKeys.cpp -w -lSDL2 -o Build/build.deb
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data+0x0): multiple definition of `SCREEN_WIDTH'; /tmp/ccdTvusg.o:(.data+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data+0x4): multiple definition of `SCREEN_HEIGHT'; /tmp/ccdTvusg.o:(.data+0x4): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.data.rel.local+0x0): multiple definition of `title'; /tmp/ccdTvusg.o:(.data.rel.local+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o:(.bss+0x0): multiple definition of `EntityPaths[abi:cxx11]'; /tmp/ccdTvusg.o:(.bss+0x0): first defined here
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: warning: relocation against `PlayerSpeed' in read-only section `.text'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccdTvusg.o: in function `main':
Main.cpp:(.text+0x3e0): undefined reference to `HandelKey(SDL_Event)'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccvPJ1dy.o: in function `HandleKey(SDL_Event)':
HandleKeys.cpp:(.text+0x49): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x5f): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x75): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: HandleKeys.cpp:(.text+0x8b): undefined reference to `PlayerSpeed'
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: warning: creating DT_TEXTREL in a PIE
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1
i have messed around with using the "extern" parameter but nothing relay works, i have also messed around with how i define things in both global.h and main.cpp.
I am on pop OS (a Ubuntu based Linux distro).
Edit: I added the wrong main.cpp (I Had another open in my vscode workspace!)
Edit2: I now have a new error:
/usr/lib/gcc/x86_64-unknown-linux-gnu/12.1.0/../../../../x86_64-unknown-linux-gnu/bin/ld: /tmp/ccwvE6bo.o: in function `main':
Main.cpp:(.text+0x3e0): undefined reference to `HandelKey(SDL_Event)'
collect2: error: ld returned 1 exit status
make: *** [Makefile:23: all] Error 1
The lines
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
in Main.cpp and the lines
extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;
in Global.h are definitions. A declaration with an initializer is always a definition.
By defining the same variable in more than one translation unit (i.e. .cpp file), you are violating the one-definition rule.
I suggest that you change
extern int SCREEN_WIDTH = 640;
extern int SCREEN_HEIGHT = 480;
in Global.h to the following:
extern const int SCREEN_WIDTH;
extern const int SCREEN_HEIGHT;
That way, it is only a declaration and no longer a definition.
You have a similar problem with title defined in Global.h:
extern char* title = "Top Down";
This line is a definition.
Header files should generally only contain declarations of variables, not definitions, because otherwise, if the header files is included by more than one translation unit, it will violate the one-definition rule.
Therefore, I suggest that you change this line to a declaration:
extern const char* title;
In one of the translation units, for example Main.cpp, you should then provide the definition:
const char* title = "Top Down";
The variable EntityPaths has a similar problem:
extern list<string> EntityPaths
{
"Assets/Player.bmp",
};
This is a definition. You should not define it in the header file. Instead, you should only provide a declaration:
extern list<string> EntityPaths;
You should define it in exactly one translation unit, for example in Main.cpp.
In Global.h, you have declared PlayerSpeed:
extern const int PlayerSpeed;
However, you did not define it anywhere. I suggest that you add
const int PlayerSpeed;
to Main.cpp.
The line
extern void HandelKey(SDL_Event e);
in HandleKeys.h has a typo. It should be HandleKey, not HandelKey.
I've looked at a lot of other similar questions but none of them have seemed to help.
I've used virtual functions before but this time they just don't seem to be working.
Header for parent class:
#ifndef OBJECT_OBJECT
#define OBJECT_OBJECT
#include <raylib.h>
#include <raymath.h>
class Object {
public:
Object() {}
Object(Rectangle rect, Color color);
Object(int x, int y, int width, int height, Color color);
virtual void Update(float deltaTime) {}
Rectangle rect;
Color color;
};
#endif /* OBJECT_OBJECT */
Source for parent class:
#include "Object.hpp"
Object::Object(Rectangle rect, Color color) {
this->rect = rect;
this->color = color;
}
Object::Object(int x, int y, int width, int height, Color color) {
this->rect = { (float)x, (float)y, (float)width, (float)height };
this->color = color;
}
Header for child:
#ifndef OBJECT_OBJECTS_PLAYER
#define OBJECT_OBJECTS_PLAYER
#include <stdio.h>
#include "../Object.hpp"
class Player : public Object {
public:
Player() {}
Player(Rectangle rect, Color color) : Object(rect, color) {}
Player(int x, int y, int width, int height, Color color) : Object(x, y, width, height, color) {}
void Update(float deltaTime) override;
int speed = 10;
};
#endif /* OBJECT_OBJECTS_PLAYER */
Source for child:
#include "Player.hpp"
void Player::Update(float deltaTime) {
if (IsKeyPressed(KEY_W | KEY_UP)) this->rect.y -= speed * deltaTime;
if (IsKeyPressed(KEY_S | KEY_DOWN)) this->rect.y += speed * deltaTime;
if (IsKeyPressed(KEY_A | KEY_LEFT)) this->rect.x -= speed * deltaTime;
if (IsKeyPressed(KEY_D | KEY_RIGHT)) this->rect.x += speed * deltaTime;
}
Full main.cpp file:
#include <raylib.h>
#include <raymath.h>
#include <vector>
#include <string>
#include "Object/Object.hpp"
#include "Object/Objects/Player.hpp"
void NewObject(Rectangle rect, Color color);
void NewObject(Object obj);
std::vector<Object> objects;
bool shouldClose;
int screenWidth;
int screenHeight;
int main(void) {
InitWindow(0, 0, "2D Platformer");
screenWidth = GetScreenWidth();
screenHeight = GetScreenHeight();
SetExitKey(-1);
ToggleFullscreen();
Player player({ 0, 0, 20, 20 }, (Color){0, 255, 0, 255});
NewObject(player);
Camera2D camera = { 0 };
camera.target = (Vector2){0, 0};
camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
camera.rotation = 0.0f;
camera.zoom = 1.0f;
while (!WindowShouldClose()) {
float deltaTime = GetFrameTime();
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode2D(camera);
for (int i = 0; i < objects.size(); i++) {
objects[i].Update(deltaTime);
DrawRectangleRec(objects[i].rect, objects[i].color);
}
EndMode2D();
EndDrawing();
}
CloseWindow();
return 0;
}
void NewObject(Rectangle rect, Color color) {
objects.push_back(*(new Object(rect, color)));
}
void NewObject(Object obj) {
objects.push_back(obj);
}
CMakeLists.txt files:
cmake_minimum_required(VERSION 3.11)
project(safcal)
cmake_policy(SET CMP0076 NEW)
find_package(raylib 3.0 QUIET)
if (NOT raylib_FOUND)
include(FetchContent)
FetchContent_Declare(
raylib
URL https://github.com/raysan5/raylib/archive/master.tar.gz
)
FetchContent_GetProperties(raylib)
if (NOT raylib_POPULATED)
set(FETCHCONTENT_QUIET NO)
FetchContent_Populate(raylib)
set(BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
add_subdirectory(${raylib_SOURCE_DIR} ${raylib_BINARY_DIR})
endif()
endif()
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE main.cpp)
add_subdirectory(Object)
target_link_libraries(${PROJECT_NAME} raylib)
target_sources(${PROJECT_NAME} PRIVATE
"Object.cpp"
)
target_sources(safcal PRIVATE
"Player.cpp"
)
The problem is with the Update function. Commenting anything to do with it being overridden stops the error.
EDIT:
The full error
[build] FAILED: safcal
[build] : && /bin/x86_64-linux-gnu-g++-10 -g CMakeFiles/safcal.dir/main.cpp.o CMakeFiles/safcal.dir/Object/Object.cpp.o -o safcal _deps/raylib-build/raylib/libraylib.a -lm -lpthread /usr/lib/x86_64-linux-gnu/libGL.so /usr/lib/x86_64-linux-gnu/libGLU.so _deps/raylib-build/raylib/external/glfw/src/libglfw3.a -lpthread /usr/lib/x86_64-linux-gnu/librt.so -lm -ldl && :
[build] /usr/bin/ld: CMakeFiles/safcal.dir/main.cpp.o: warning: relocation against `_ZTV6Player' in read-only section `.text._ZN6PlayerC2E9Rectangle5Color[_ZN6PlayerC5E9Rectangle5Color]'
[build] /usr/bin/ld: CMakeFiles/safcal.dir/main.cpp.o: in function `Player::Player(Rectangle, Color)':
[build] /home/callum/Documents/Programming/C++/SafCal/build/../Object/Objects/Player.hpp:9: undefined reference to `vtable for Player'
[build] /usr/bin/ld: warning: creating DT_TEXTREL in a PIE
I've used virtual functions before but this time they just don't seem to be working.
Object slicing occurred in your code, So you can't see the desired result。
Player player({ 0, 0, 20, 20 }, (Color){0, 255, 0, 255});
NewObject(player);
You can try to make the following changes:
std::vector<std::shared_ptr<Object>> objects;
...
void NewObject(std::shared_ptr<Object> obj) {
objects.push_back(obj);
}
...
NewObject(new Player());
In addition, the destructor of the base class needs to be declared as a virtual function。
It occurs every time i compile 2 cpp files in one of which i try to initialize a pointer of TMap or GameObject class. Im new to c++ and i am using SDL2.
I tried to make it static in Game class, this error also occured.
SDL_Renderer* Game::renderer = nullptr;
SDL_Window* Game::window = nullptr;
GameObject* Player = nullptr;///this doesnt create error
TMap* Map = nullptr;///this also doesnt create error
void Game::init(int w, int h, bool fullscreen)
{
Uint32 flag = 0;
if(fullscreen)
{
flag = SDL_WINDOW_FULLSCREEN;
}
if(SDL_Init(SDL_INIT_EVERYTHING)==0)
{
Game::window = SDL_CreateWindow("A game", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, w, h, flag);
Game::renderer = SDL_CreateRenderer(Game::window, -1, 0);
std::cout << "Game Initialised" << std::endl;
isrunning = true;
Player = new GameObject("assets/aliendisplay.png", 0 ,0);/////this creates error
Map = new Map("assets/lowtileset.png");///this also creates error
}
else
{
isrunning = false;
}
}
build : Main.cpp SecondCpp.cpp header.h
g++ Main.cpp SecondCpp.cpp -I. -IPATH\include\SDL2 -IPATH\SDL2_image\include\SDL2 -LPATH\SDL2\lib -LPATH\SDL2_image\lib -lmingw32 -lSDL2main -lSDL2 -lSDL2_image -o Exe
When running makefile, there are no errors.
I've been following LazyFoo's SDL tutorials (and also adding my own organization and coding style). When I got to his animation tutorial I decided to make a separate class to store the variables and methods related to the animation algorithm, rather than having global variables. He uses an array of SDL_Rects to define the boundaries of different sprites on a sprite sheet, so I used an SDL_Rect pointer to store the array in my custom class. When I compiled everything I didn't see an animation, when I compiled the original source code I did. When I started debugging things, I found that when I was rendering the sprites, the rects were actually full of garbage, even though when I initialize them the rects are just fine. I've tried to simplify the problem so many times, but every approach I take to recreate the bug in a simpler environment actually works as expected! So with that in mind I apologize for the large amount of code, because I can't seem to reduce the problem.
texture.h
#ifndef TEXTURE_H
#define TEXTURE_H
#include <SDL2/SDL.h>
#include <string>
class Animation {
public:
Animation(SDL_Renderer* renderer);
~Animation();
void load(std::string path, int frames, SDL_Rect* clips),
free(),
render(int x, int y),
next_frame();
private:
SDL_Renderer* _renderer=NULL;
SDL_Rect* _clips=NULL;
SDL_Texture* _texture=NULL;
int _frame=0, _frames=0, _width=0, _height=0;
};
#endif
texture.cpp
#include <stdio.h>
#include <SDL2/SDL_image.h>
#include "texture.h"
#include "error.h"
Animation::Animation(SDL_Renderer* renderer) {
_renderer = renderer;
}
Animation::~Animation() {
free();
_renderer = NULL;
}
void Animation::load(std::string path, int frames, SDL_Rect* clips) {
free();
SDL_Texture* texture = NULL;
SDL_Surface* surface = IMG_Load(path.c_str());
if (!surface)
throw ErrorIMG("Could not load image "+path);
SDL_SetColorKey(surface, SDL_TRUE,
SDL_MapRGB(surface->format, 0, 0xFF, 0xFF));
texture = SDL_CreateTextureFromSurface(_renderer, surface);
if (!texture)
throw ErrorSDL("Could not create texture from image "+path);
_width = surface->w;
_height = surface->h;
SDL_FreeSurface(surface);
_frames = frames;
_clips = clips;
printf("clips[%d]: w: %d h: %d\n", 0, _clips[0].w, _clips[0].h);
}
void Animation::free() {
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = NULL;
_clips = NULL;
_frames = 0;
_frame = 0;
_width = 0;
_height = 0;
}
}
void Animation::render(int x, int y) {
SDL_Rect crect = _clips[_frame/4];
printf("in render (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
SDL_Rect render_space = {x, y, crect.w, crect.h};
SDL_RenderCopy(_renderer, _texture, &_clips[_frame], &render_space);
}
void Animation::next_frame() {
SDL_Rect crect = _clips[_frame/4];
printf("in next frame (clips[%d]): w: %d, h: %d\n", _frame/4, crect.w, crect.h);
++_frame;
if (_frame/4 >= _frames)
_frame = 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include "texture.h"
class Game {
public:
Game();
~Game();
void main();
private:
void load_media();
SDL_Window* _window=NULL;
SDL_Renderer* _renderer=NULL;
Animation* _anim=NULL;
const int SCREEN_WIDTH=640, SCREEN_HEIGHT=480;
};
#endif
game.cpp
#include <SDL2/SDL_image.h>
#include "game.h"
#include "error.h"
void Game::main() {
load_media();
bool has_quit = false;
SDL_Event event;
while (!has_quit) {
while (SDL_PollEvent(&event))
if (event.type == SDL_QUIT)
has_quit = true;
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
SDL_RenderClear(_renderer);
_anim->render(100, 100);
_anim->next_frame();
SDL_RenderPresent(_renderer);
}
}
Game::Game() {
if (SDL_Init(SDL_INIT_VIDEO))
throw ErrorSDL("SDL could not initialize");
_window = SDL_CreateWindow("SDL Tutorial", SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH,
SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!_window)
throw ErrorSDL("Window could not be created");
Uint32 render_flags = SDL_RENDERER_ACCELERATED;
render_flags |= SDL_RENDERER_PRESENTVSYNC;
_renderer = SDL_CreateRenderer(_window, -1, render_flags);
if (!_renderer)
throw ErrorSDL("Renderer could not be created");
SDL_SetRenderDrawColor(_renderer, 0xff, 0xff, 0xff, 0xff);
if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG))
throw ErrorIMG("SDL_image could not initialize");
}
Game::~Game() {
delete _anim;
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_renderer = NULL;
_window = NULL;
IMG_Quit();
SDL_Quit();
}
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
for (int i=0; i < nclips; i++) {
clips[i].x = i*64;
clips[i].y = 0;
clips[i].w = 64;
clips[i].h = 164;
}
_anim = new Animation(_renderer);
_anim->load("sheet.png", nclips, &clips[0]);
}
You're storing a pointer to a temporary. The SDL_Rect* clips pointer that you pass to Animation::load is assigned to the member _clips used after the function returns. For this to work correctly, the data that is pointed to needs to live for as long as the Animation class is using it. The problem arises here:
void Game::load_media() {
const int nclips = 4;
SDL_Rect clips[nclips];
...
_anim->load("sheet.png", nclips, &clips[0]);
}
In this piece of code, clips is a local variable. That means it gets destroyed at the end of load_media(), and the memory contents at that location will become garbage.
There are a number of ways you could fix this. A simple one would be to use std::vector<SDL_Rect> instead of SDL_Rect*. std::vector can safely be copied and manages its internals for you. Your new code could look like:
class Animation {
...
std::vector<SDL_Rect> _clips;
...
}
void Animation::load(std::string path, int frames, std::vector<SDL_Rect> clips) {
...
_clips = clips;
...
}
void Game::load_media() {
const int nclips = 4;
std::vector<SDL_Rect> clips;
clips.resize(nclips);
...
_anim->load("sheet.png", nclips, clips);
}
And dont forget to #include <vector>. Documentation for std::vector is here. Note that std::vector has a size() method that can probably replace frames everywhere it appears.
The stack-allocated Game::load_media()::clips array disappears when it goes out of scope. Make a copy in Animation::load() instead of only storing a pointer.
I have started to make a game in C++ with SDL. My Code is:
main.cpp
#include "SDL/SDL.h"
#include "gameSystem.h"
int main(int argc, char *args[])
{
gameSystem systemHandler;
SDL_Surface *buffer;
SDL_Event event;
while(event.type != SDL_QUIT)
{
SDL_PollEvent(&event);
SLD_Flip(buffer);
}
}
gameSystem.cpp
#include "SDL/SDL.h"
#include "gameSystem.h"
gameSystem::gameSystem()
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_WM_SetCaption("GameName", NULL);
SDL_Surface *buffer;
bool fullscreen = false;
if (fullscreen == true)
{buffer = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE | SDL_FULLSCREEN);}
else
{buffer = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE);}
}
gameSystem::~gameSystem()
{
SDL_Quit();
}
gameSystem.h
class gameSystem
{
public:
gameSystem();
~gameSystem();
private:
SDL_Surface *buffer;
};
My linker options include: -lmingw32 -lSDLmain -lSDL -lwinmm -sdl_Mixer
I get the following error: "error: 'SLD_Flip' was not declared in this scope" on line 12 of main.cpp.
All the other SDL functions seem to be fine.
Does anyone know how to fix this problem?
You misspelled it. It should be SDL_Flip, not SLD_Flip.
Also, you haven't initialized your variable buffer in main. You are not setting its value inside of gameSystem::gameSystem(); the variable is out of scope.