I have a problem creating this second class in my game, this is a class for player but when I call the texturemanager to draw the play, it's not working, but when i directly call the texturemanager to draw the player it was working. this is my class.
Game.h
#pragma once
#include <SDL/SDL.h>
#include "TextureManager.h"
#include "Player.h"
class Game
{
public:
Game(void);
~Game(void);
void startGame();
void init();
void gameLoop();
void eventHandler();
void render();
void exitGame();
private:
bool _isRunning;
SDL_Window* _window;
SDL_Renderer* _renderer;
SDL_Rect _spriteClips[2];
TextureManager _textureManager;
Player _player;
};
Game.cpp
#include "Game.h"
#include "Error.h"
Game::Game(void)
{
_window = nullptr;
_isRunning = true;
}
Game::~Game(void)
{
}
void Game::startGame()
{
init();
gameLoop();
}
void Game::init()
{
if (SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
_window = SDL_CreateWindow("Renderer", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
if (_window != nullptr)
{
_renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if(_renderer!= nullptr)
{
SDL_SetRenderDrawColor(_renderer, 255, 255, 255, 255);
}
else
fatalError("Failed to create renderer");
}
else
fatalError("Failed to create window!");
}
else
fatalError("Failed to initialize SDL!");
// temp image load
_textureManager.loadFromFile("assets/tiles.png", _renderer);
_spriteClips[0].x = 0;
_spriteClips[0].y = 160;
_spriteClips[0].w = 80;
_spriteClips[0].h = 80;
_spriteClips[1].x = 0;
_spriteClips[1].y = 80;
_spriteClips[1].w = 80;
_spriteClips[1].h = 80;
}
void Game::gameLoop()
{
while (_isRunning != false)
{
eventHandler();
render();
}
exitGame();
}
void Game::eventHandler()
{
SDL_Event evnt;
while (SDL_PollEvent(&evnt))
{
switch (evnt.type)
{
case SDL_QUIT:
_isRunning = false;
break;
}
}
}
void Game::render()
{
SDL_RenderClear(_renderer);
_textureManager.draw(0, 0, &_spriteClips[0], _renderer);
_player.draw(200, 200, &_spriteClips[1], _renderer);
// when i used this, it is working
//_textureManager.draw(200, 200, &_spriteClips[1], _renderer);
SDL_RenderPresent(_renderer);
}
void Game::exitGame()
{
SDL_DestroyRenderer(_renderer);
SDL_DestroyWindow(_window);
_window = nullptr;
_renderer = nullptr;
SDL_Quit();
}
TextureManager.h
#pragma once
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
class TextureManager
{
public:
TextureManager(void);
~TextureManager(void);
bool loadFromFile(std::string filePath, SDL_Renderer* renderer);
void free();
void draw(int x, int y, SDL_Rect* clip, SDL_Renderer* renderer);
int getWidth() { return _width; }
int getHeight() { return _height; }
private:
SDL_Texture* _texture;
int _width;
int _height;
};
TextureManager.cpp
#include "TextureManager.h"
#include "Error.h"
TextureManager::TextureManager(void)
{
_texture = nullptr;
_width = 0;
_height = 0;
}
TextureManager::~TextureManager(void)
{
free();
}
bool TextureManager::loadFromFile(std::string filePath , SDL_Renderer* renderer)
{
free();
SDL_Texture* newTexture = nullptr;
SDL_Surface* loadedSurface = IMG_Load(filePath.c_str());
if(loadedSurface != nullptr)
{
SDL_SetColorKey(loadedSurface, SDL_TRUE, SDL_MapRGB(loadedSurface->format, 0, 0xFF, 0xFF));
newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
if(newTexture != nullptr)
{
_width = loadedSurface->w;
_height = loadedSurface->h;
}
else
fatalError("unable to create texture from surface!");
SDL_FreeSurface(loadedSurface);
}
else
fatalError("unable to load image path " + filePath);
_texture = newTexture;
return _texture != nullptr;
}
void TextureManager::draw(int x, int y, SDL_Rect* clip, SDL_Renderer* renderer)
{
SDL_Rect renderQuad = { x, y, _width, _height };
if (clip != nullptr)
{
renderQuad.w = clip->w;
renderQuad.h = clip->h;
}
SDL_RenderCopy(renderer, _texture, clip, &renderQuad);
}
void TextureManager::free()
{
if (_texture != nullptr)
{
SDL_DestroyTexture(_texture);
_texture = nullptr;
_width = 0;
_height = 0;
}
}
Player.h
#pragma once
#include "TextureManager.h"
class Player
{
public:
Player(void);
~Player(void);
void draw(int x, int y, SDL_Rect* clip, SDL_Renderer* renderer);
private:
TextureManager _textureManager;
};
Player.cpp
#include "Player.h"
Player::Player(void)
{
}
Player::~Player(void)
{
}
void Player::draw(int x, int y, SDL_Rect* clip, SDL_Renderer* renderer)
{
_textureManager.draw(x, y, clip, renderer);
}
That's because Game and Player have two different texture managers. The game initializes its texture manager, that's why when you use it directly it works. The Player never initializes its manager, that's why nothing is drawn when you use it.
To fix it, you might want to only have one texture manager, the one that is in the Game class, and have Player just store a pointer or a reference to it. Here's one way to fix it:
Change the type of the _textureManager in the Player class (but not in the Game class) to be a reference:
TextureManager& _textureManager;
Make Player class accept a TextureManager reference in its constructor, and use it to initialize the private member:
Player::Player(TextureManager& textureManager)
: _textureManager(textureManager)
{
}
Make the Game object properly initialize the player in its constructor:
Game::Game(void)
: _player(_textureManager)
{
...
With these changes your code should now work.
Related
I have a Game class that through its constructor initializes the window and the SDL renderer. I understand from what I read so far (not much) that there should only be one renderer for each window.
Then I have a Player class where through the constructor I want to load a texture with an image, for which I need the renderer, therefore, I put the renderer as the constructor parameter and in this way I am forced to pass from the constructor from Game the renderer to the Player constructor (since it instantiated the Player class in The Game class).
The fact is that the renderer is passed before being created, and I don't know if there is another way to invoke the constructor of the Player from Game, since it forces me to put it that way. I leave the code for you to see:
Game class:
#pragma once
#include <iostream>
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include "player.hpp"
//#include "helpers.hpp"
using namespace std;
#define WINDOW_WIDHT 640
#define WINDOW_HEIGTH 480
class Game
{
public:
Game();
~Game();
void loop();
void update() {}
void input();
void render();
void draw() {}
private:
SDL_Window *window;
SDL_Renderer *renderer = nullptr;
SDL_Event event;
SDL_Texture *gTexture;
bool running;
Player player;
};
Game::Game() : player(renderer)
{
SDL_Init(0);
SDL_CreateWindowAndRenderer(WINDOW_WIDHT, WINDOW_HEIGTH, 0, &window, &renderer);
SDL_SetWindowTitle(window, "Intento...");
//inicializa la carga de pngs
int imgFlags = IMG_INIT_PNG;
if (!IMG_Init(imgFlags) & imgFlags)
{
cout << "No se puede inicializar SDL_Img" << endl;
}
running = true;
loop();
}
Game::~Game()
{
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
IMG_Quit();
SDL_Quit();
}
void Game::loop()
{
while (running)
{
input();
render();
update();
}
}
void Game::render()
{
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_Rect rect;
rect.x = rect.y = 0;
rect.w = WINDOW_WIDHT;
rect.h = WINDOW_HEIGTH;
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);
}
void Game::input()
{
while (SDL_PollEvent(&event) > 0)
{
switch (event.type)
{
case SDL_QUIT:
running = false;
break;
}
}
}
Clase Player:
#pragma once
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
//#include "helpers.hpp"
using namespace std;
class Player
{
public:
Player(SDL_Renderer *renderer);
~Player() = default;
const SDL_Rect getDest() { return dest; }
const SDL_Rect getSrc() { return src; }
void setDest(int x, int y, int w, int h);
void setSrc(int x, int y, int w, int h);
SDL_Texture *loadTexture(std::string path, SDL_Renderer *renderer);
private:
SDL_Rect dest;
SDL_Rect src;
};
Player::Player(SDL_Renderer *renderer){
loadTexture("mario.png", renderer);
/* setSrc(48, 48, 48, 48);
setDest(100, 100, 48, 48);
SDL_Rect playerRectSrc = getSrc();
SDL_Rect playerRectDest = getDest(); */
}
void Player::setDest(int x, int y, int w, int h){
dest.x = x;
dest.y = y;
dest.w = w;
dest.h = h;
}
void Player::setSrc(int x, int y, int w, int h){
src.x = x;
src.y = y;
src.w = w;
src.h = h;
}
SDL_Texture* Player::loadTexture(std::string path, SDL_Renderer *renderer)
{
SDL_Texture *newTexture = NULL;
SDL_Surface *loadedSurface = IMG_Load(path.c_str());
if (loadedSurface == NULL)
{
cout << "No se pudo cargar la imagen" << endl;
}
else
{
newTexture = SDL_CreateTextureFromSurface(renderer, loadedSurface);
if (newTexture == NULL)
{
cout << "No se pudo generar la textura para player" << endl;
}
SDL_FreeSurface(loadedSurface);
}
return newTexture;
}
The question is how to call the Player constructor after the renderer has been created? The only thing I can think of is not to create the texture through the constructor, but through a function, but it wouldn't be the right thing to do, right? that's what the constructor is for
The only thing I can think of is not to create the texture through the constructor, but through a function, but it wouldn't be the right thing to do, right? that's what the constructor is for
Right. However, SDL is a C library so it doesn't use C++ RAII, which is responsible for construction/destruction. First, we call constructors in the member initializer list (: foo{}, bar{foo}), which provides us with fully constructed member objects. Then, we do whatever we want in the constructor body ({ use(foo); }). However, in your case the constructor body ({ SDL_CreateWindowAndRenderer(...); } ) is needed before member initialization (: player{renderer}).
The question is how to call the Player constructor after the renderer has been created?
Construct all that SDL_ stuff before constructing the Player:
class RenderWindow {
SDL_Window* window;
SDL_Renderer* renderer;
public:
RenderWindow() {
SDL_Init(0);
SDL_CreateWindowAndRenderer(WINDOW_WIDHT, WINDOW_HEIGTH, 0, &window, &renderer);
SDL_SetWindowTitle(window, "Intento...");
}
// an easy double-free protection, you can use anything else
RenderWindow(RenderWindow&&) = delete;
~RenderWindow() {
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
}
// lvalue-only to forbid dangling like RenderWindow{}.raw_renderer()
auto raw_renderer() & { return renderer; }
};
Now, your Player and Game can be implemented in a natural RAII construct-then-use way:
class Player {
public:
Player(SDL_Renderer*); // TODO implement
};
class Game {
// note the declaration order: construction goes from top to bottom
RenderWindow render_window;
Player player;
public:
// SDL_* happens in render_window{},
// so player{render_window.raw_renderer()} gets an initialized SDL_Renderer
Game(): /*render_window{},*/ player{render_window.raw_renderer()} {}
};
I was making a little program to practice pointer usage and rendering.
I was trying to debug an Illegal memory access, and my program crashed.
The problem, is that it crashed and I had to force-quit the debugging session by pressing shift-f5
because my program was taking mouse focus, and I could not click anything.
Now my screen is constantly brighter than before, probably due to an draw operation that was supposed to draw a fullscreen white rectangle.
This will probably be fixed when I restart my PC.... but it is still a problem if this stacks, since I will have to restart every 3 debugging sessions to not burn my eyes.
Any advice on how to ENSURE my program releases resources?
Edit for reproducible example:
#include "SDL.h"
#include "SDL_render.h"
#include <stdio.h>
#include <iostream>
#include <list>
#include <string>
#include <ft2build.h>
#include <SDL_ttf.h>
#include <functional>
#include <memory>
#include FT_FREETYPE_H
struct element {
SDL_Rect* area;
SDL_Texture* texture;
SDL_Surface* surface;
std::string name = "";
std::function<void()> functionPointer;
element(SDL_Rect* rect, SDL_Texture* tex, SDL_Surface* surf, std::string elementName, std::function<void()> fp) {
area = rect;
texture = tex;
surface = surf;
name = elementName;
functionPointer = fp;
}
~element() {
SDL_FreeSurface(surface);
SDL_DestroyTexture(texture);
area = nullptr;
surface = nullptr;
texture = nullptr;
name = "";
functionPointer = nullptr;
}
};
struct screenVariables {
std::list<SDL_Texture*> textureList;
std::list<element*>& elementList;
SDL_Renderer* renderer;
std::list<SDL_Surface*> surfaceList;
screenVariables(std::list<SDL_Texture*> tex, std::list<element*> rec, SDL_Renderer* render,std::list<SDL_Surface*> surf) :
textureList(tex),elementList(rec), renderer(render),surfaceList(surf) {};
};
class gameHandler{
private:
SDL_Window* screen;
SDL_Renderer* renderer;
SDL_Texture* mainBlock;
bool saveAvailable = false;
bool quit = false;
std::list<SDL_Texture*> textureList;
std::list<element*> elementList;
std::list<SDL_Surface*> surfaceList;
screenVariables screenInfo{ textureList, elementList, renderer, surfaceList };
void nothing() {};
void continuePressed() {};
void newGame() {};
public:
gameHandler() {
SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS);
TTF_Init();
screen = SDL_CreateWindow("Game Window",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1920, 1080,
SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL);
renderer = SDL_CreateRenderer(screen, -1, 0);
mainBlock = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ABGR8888,
SDL_TEXTUREACCESS_TARGET, 1920, 1080);
}
//All methods that do operations on elements will operate on the LAST ADDED ELEMENT. Always create a new element and use push_back() to add it to the end.
//Everything commented out is not nessesary for reproduction, but
//will be left for reference.
/*bool showFirstElement(screenVariables& arg, const char* imgPath, SDL_Rect* area) {
SDL_Renderer* renderRef = arg.renderer;
//The following line means: end() returns an iterator, take the iterator and make it go back once, then deference it to get the value.
//This gets the value of the last added surface.
SDL_Surface* newSurface = *(--(arg.surfaceList.end()));
newSurface = SDL_LoadBMP(imgPath);
SDL_Texture* newTexture = *(--(arg.textureList.end()));
newTexture = SDL_CreateTextureFromSurface(renderRef, newSurface);
std::function<void()> nothingWrapper = [&]() { nothing(); };
element* toBeModified = *(--(arg.elementList.end()));
toBeModified->element::area = area;
toBeModified->texture = newTexture;
toBeModified->surface = newSurface;
toBeModified->name = imgPath;
toBeModified->functionPointer = nothingWrapper;
arg.elementList.push_back(toBeModified);
SDL_RenderCopy(renderRef, newTexture, NULL, area);
return true;
}
bool textOnElement(screenVariables& arg, const char* text,int points, element* el) {
SDL_Renderer* renderRef = arg.renderer;
TTF_Font* font = NULL;
SDL_Color textColor = { 0,0,0 };
font = TTF_OpenFont("TravelingTypewriter.ttf", points);
SDL_Surface* newSurface = *(--(arg.surfaceList.end()));
newSurface = TTF_RenderText_Solid(font,text,textColor);
SDL_Texture* newTexture = *(--(arg.textureList.end()));
newTexture = SDL_CreateTextureFromSurface(renderRef, newSurface);
el->surface = newSurface;
el->texture = newTexture;
SDL_RenderCopy(renderRef, newTexture, NULL, el->area);
return true;
}
element* checkClickedElement(screenVariables& arg, SDL_Point pos) {
for (element* i : arg.elementList) {
if (SDL_PointInRect(&pos, i->area)) {
return i;
}
return nullptr;
}
}
bool fillWithColor(screenVariables& arg, Uint32 red, Uint32 green, Uint32 blue, Uint32 alpha) {
SDL_Renderer* renderRef = arg.renderer;
SDL_Surface* surface = SDL_CreateRGBSurface(0, 1920, 1080, 8, red, green, blue, alpha);
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderRef, surface);
SDL_Rect fullScreen = { 1920,1080 };
std::string name = "R" + std::to_string(red) + "G" + std::to_string(green) + "B" + std::to_string(blue);
std::function<void()> nothingWrapper = [&]() { nothing(); };
element newElement = element(&fullScreen, texture, surface, name, nothingWrapper);
SDL_RenderCopy(renderRef, texture, NULL, NULL);
return true;
}
bool fillElementPointer(screenVariables& arg, SDL_Point leftTop, SDL_Point rightBottom, std::function<void()> fp,std::string name,element* newElement) {
SDL_Rect newRect;
newRect.x = (leftTop.x + rightBottom.x) / 2;
newRect.y = (leftTop.y + rightBottom.y) / 2;
newRect.w = (rightBottom.x - leftTop.x);
newRect.h = (leftTop.y - rightBottom.y);
newElement = &element(&newRect, nullptr, nullptr, name, fp);
arg.elementList.push_back(newElement);
return true;
}
//This allows me to create pointers that will not go out of scope when methods return. Always use this method if the element you will create needs to presist through functions.
element* createElementPointer(){
element* newPointer = nullptr;
return newPointer;
}
int showMenu(screenVariables& arg) {
fillWithColor(arg, 255, 255, 255, 255);
SDL_Point leftTop = { 200,700 };
SDL_Point rightBottom = { 600,200 };
std::function<void()> nothingWrapper = [&]() { nothing(); };
element* titleText = createElementPointer();
if (!fillElementPointer(screenInfo, leftTop, rightBottom, nothingWrapper, "titleText", titleText)) {
std::cout << "createElement failed in showMenu()";
}
leftTop.y = 100;
rightBottom.x = 0;
element* continueOrStart = createElementPointer();
if (saveAvailable) {
std::function<void()> continueWrapper = [&]() { continuePressed(); };
if (!fillElementPointer(screenInfo, leftTop, rightBottom, continueWrapper, "continueButton", continueOrStart)) {
std::cout << "createElement failed in showMenu() saveAvailable.";
}
textOnElement(arg, "Continue", 16, continueOrStart);
}
else {
std::function<void()> newGameWrapper = [&]() { newGame(); };
if (!fillElementPointer(screenInfo, leftTop, rightBottom, newGameWrapper, "newGameButton", continueOrStart)) {
std::cout << "createElement failed in showMenu() !saveAvailable.";
}
textOnElement(arg, "New Game", 16, continueOrStart);
}
return 0;
}
void mainLoop() {
SDL_Event event;
showMenu(screenInfo);
while (!quit) {
SDL_RenderClear(renderer);
while (SDL_PollEvent(&event) != 0) {
switch (event.type)
{
case SDL_MOUSEBUTTONDOWN:
if (event.button.button == SDL_BUTTON_LEFT) {
SDL_Point position = { event.button.x,event.button.y };
element* result = checkClickedElement(screenInfo, position);
if (result == nullptr) {
break;
}
else {
result->functionPointer();
}
}
break;
case SDL_QUIT:
quit = true;
break;
}
}
SDL_RenderPresent(renderer);
}
}
void clearResources() {
int index{ 0 };
for (element* i : screenInfo.elementList) {
delete i;
}
SDL_DestroyRenderer(renderer);
SDL_Quit();
} */
};
int main(int argc, char* argv[]) {
gameHandler handler; //This alone makes my screen brighter.
//Uncomment every function and let mainLoop() run, and it crashes.
//Although many attempts at making this exception safe, it did not work.
//handler.mainLoop();
return 0;
}
Im constantly getting a redefinition of default argument parameter 1 error and its preventing from building the project successfully to test code as I include it. Im relatively new to C++ and this form of building a game, having multiple header and cpp files referencing eachother.
Texture2D.cpp:
#include <iostream>
#include <SDL_image.h>
#include <string>
#include "Texture2D.h"
#include "Constants.h"
#include "Commons.h"
using namespace::std;
Texture2D::Texture2D(SDL_Renderer* renderer)
{
SDL_Renderer* mRenderer = NULL;
SDL_Texture* mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
Texture2D::~Texture2D()
{
Free();
mRenderer = NULL;
}
bool Texture2D::LoadFromFile(string path)
{
//remove the memory used for a previous texture
Free();
SDL_Texture* mTexture = NULL;
//load the image
SDL_Surface* pSurface = IMG_Load(path.c_str());
mWidth = pSurface->w;
mHeight = pSurface->h;
if (pSurface != NULL)
{
mTexture = SDL_CreateTextureFromSurface(mRenderer, pSurface);
if (mTexture == NULL)
{
cout << "Unable to create texture from surface. Error: " << SDL_GetError() << endl;
}
//Color key the image - The color to be transparent
SDL_SetColorKey(pSurface, SDL_TRUE, SDL_MapRGB(pSurface->format, 0, 0xFF, 0xFF));
SDL_FreeSurface(pSurface);
return mTexture;
}
else
{
cout << "Unable to create texture from surface. Error: " << IMG_GetError() << endl;
}
}
void Texture2D::Render(Vector2D newPosition, SDL_RendererFlip flip, double angle = 0.0f)
{
//clear the screen
SDL_SetRenderDrawColor(mRenderer, 0xFF, 0xFF, 0xFF, 0xFF);
SDL_RenderClear(mRenderer);
//set where to render the texture
SDL_Rect renderLocation = { 0, 0, mWidth, mHeight };
//render to screen
SDL_RenderCopyEx(mRenderer, mTexture, NULL, &renderLocation, 0, NULL, SDL_FLIP_NONE);
SDL_RenderPresent(mRenderer);
}
void Texture2D::Free()
{
if (mTexture != NULL)
{
SDL_DestroyTexture(mTexture);
mTexture = NULL;
mWidth = 0;
mHeight = 0;
}
}
Texture2D.h:
#pragma once
#ifndef _TEXTURE2D_H
#define _TEXTURE2D_H
#include <SDL.h>
#include <SDL_image.h>
#include <map>
#include <string>
#include "Commons.h"
class Texture2D
{
public:
Texture2D(SDL_Renderer* renderer);
~Texture2D();
bool LoadFromFile(std::string path);
void Free();
void Render(Vector2D newPosition, SDL_RendererFlip flip, double angle = 0.0f);
int GetWidth() { return mWidth; }
int GetHeight() { return mHeight; }
private:
SDL_Renderer* mRenderer;
SDL_Texture* mTexture;
int mWidth;
int mHeight;
};
#endif //_TEXTURE2D_H
Commons.h:
#pragma once
#ifndef _COMMONS_H
#define _COMMONS_H
struct Vector2D
{
Vector2D()
{
x = 0.0f;
y = 0.0f;
}
float x;
float y;
};
struct InitialVector2D
{
InitialVector2D()
{
initialx = 0.0f;
initialy = 0.0f;
}
float initialx;
float initialy;
};
enum SCREENS
{
SCREEN_INTRO = 0,
SCREEN_MENU,
SCREEN_LEVEL1,
SCREEN_LEVEL2,
SCREEN_GAMEOVER,
SCREEN_HIGHSCORES
};
#endif //_COMMONS_H
When giving a default parameter, you should only add the default in the header declaration. So, changing:
void Texture2D::Render(Vector2D newPosition, SDL_RendererFlip flip, double angle = 0.0f)
to:
void Texture2D::Render(Vector2D newPosition, SDL_RendererFlip flip, double angle)
should fix the error.
Simply remove the default specification here: void Texture2D::Render(Vector2D newPosition, SDL_RendererFlip flip, double angle = 0.0f) As the compiler tells you, that was already given in the function declaration. – πάντα ῥεῖ 47 mins ago
So I'm basically following a book called SDL Game Development by Shaun Mitchell.
I'm current on page 58, where it tells us how to implement polymorphism.
So far what I have down are:
- Create the main code where we do all our rendering updating etc. (Game.cpp and Game.h)
- Create a Texture manager as a singleton (TextureManager.h and TextureManager.cpp)
- Create a GameObject (GameObject.h and GameObject.cpp)
- Create a Player that inherits GameObject (Player.h and Player.cpp)
So my GameObject class basically has 3 methods: load, draw and update.
Load basically loads an image, and I use my TextureManager to do so.
Draw draws it and update just changes it positions and current frame.
I pass the name of the image which is basically the directory, the renderer and the textureID from the Game class where I call them.
Essentially the problem is that I pass in two different directories into a GameObject
and a Player, but it only loads up the last directory I pass in. As in both GameObject and Player becomes the same image.
So I think the problem lies in Game class, Player class or GameObject class.
I create the objects in Game class, and push them in vector at Game.cpp in init function
Where it says
m_go->load(100, 100, 96, 60, "assets/simba.bmp", "animate",m_pRenderer);
m_player->load(300, 300, 96, 60, "assets/download.bmp", "animate",m_pRenderer);
GameObject.h
#ifndef GAMEOBJECT_H
#define GAMEOBJECT_H
#include <string>
#include <iostream>
#include "SDL_image.h"
#include "TextureManager.h"
class GameObject
{
public:
virtual bool load(int x, int y, int width, int height, std::string name, std::string textureID, SDL_Renderer* pRenderer);
virtual void draw(SDL_Renderer* pRenderer);
virtual void update();
virtual void clean();
protected:
std::string m_textureID;
int m_currentFrame;
int m_currentRow;
int m_x;
int m_y;
int m_width;
int m_height;
};
#endif // GAMEOBJECT_H
GameObject.cpp
#include "GameObject.h"
bool GameObject::load(int x, int y, int width, int height, std::string name, std::string textureID, SDL_Renderer* pRenderer)
{
m_x = x;
m_y = y;
m_width = width;
m_height = height;
m_textureID = textureID;
m_currentRow = 1;
m_currentFrame = 1;
if(!TextureManager::Instance()->load(name, textureID, pRenderer))
{
return false;
}
return true;
}
void GameObject::draw(SDL_Renderer* pRenderer)
{
TextureManager::Instance()->drawFrame(m_textureID, m_x, m_y,m_width, m_height, m_currentRow, m_currentFrame, pRenderer);
}
void GameObject::update()
{
m_x += 1;
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
}
void GameObject::clean()
{
}
In my Player class, I do the same thing except I use the methods that are already made in GameObject
Player.h
#ifndef PLAYER_H
#define PLAYER_H
#include "GameObject.h"
class Player : public GameObject
{
public:
bool load(int x, int y, int width, int height, std::string name, std::string textureID, SDL_Renderer* pRenderer);
void draw(SDL_Renderer* pRenderer);
void update();
void clean();
};
#endif // PLAYER_H
Player.cpp
#include "Player.h"
bool Player::load(int x, int y, int width, int height, std::string name, std::string textureID, SDL_Renderer* pRenderer)
{
if(!GameObject::load(x, y, width, height, name, textureID, pRenderer))
{
return false;
}
return true;
}
void Player::draw(SDL_Renderer* pRenderer)
{
GameObject::draw(pRenderer);
}
void Player::update()
{
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
m_x -= 1;
}
void Player::clean()
{
}
This is the part where the images doesn't load the same thing happens in Game.cpp
So I basically created a vector that holds GameObjects, it told me to use it so that I could just for loop through it whenever I want to draw objects
Game.h
#ifndef GAME_H
#define GAME_H
#include <SDL.h>
#include <SDL_image.h>
#include <string.h>
#include <iostream>
#include <vector>
#include "TextureManager.h"
#include "Player.h"
class Game
{
public:
Game() {}
~Game() {}
bool init(const char* title, int xpos, int ypos, int width, int height, int flags);
void render();
void update();
void handleEvents();
void clean();
// a function to access the private running variable
bool running() { return m_bRunning; }
private:
SDL_Window* m_pWindow;
SDL_Renderer* m_pRenderer;
int m_currentFrame;
bool m_bRunning;
GameObject* m_go;
GameObject* m_player;
std::vector<GameObject*> m_gameObjects;
};
#endif
Game.cpp
#include "Game.h"
typedef TextureManager TheTextureManager; // Singleton TextureManager
Game* g_game = 0; // Game Object
bool Game::init(const char* title, int xpos, int ypos, int width,int height, int flags)
{
// attempt to initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING) == 0)
{
std::cout << "SDL init success\n";
// init the window
m_pWindow = SDL_CreateWindow(title, xpos, ypos,width, height, flags);
if(m_pWindow != 0) // window init success
{
std::cout << "window creation success\n";
m_pRenderer = SDL_CreateRenderer(m_pWindow, -1, 0);
if(m_pRenderer != 0) // renderer init success
{
std::cout << "renderer creation success\n";
SDL_SetRenderDrawColor(m_pRenderer,255,255,255,255);
}
else
{
std::cout << "renderer init fail\n";
return false; // renderer init fail
}
}
else
{
std::cout << "window init fail\n";
return false; // window init fail
}
}
else
{
std::cout << "SDL init fail\n";
return false; // SDL init fail
}
std::cout << "init success\n";
m_go = new GameObject();
m_player = new Player();
m_go->load(100, 100, 96, 60, "assets/simba.bmp", "animate",m_pRenderer);
m_player->load(300, 300, 96, 60, "assets/download.bmp", "animate",m_pRenderer);
m_gameObjects.push_back(m_go);
m_gameObjects.push_back(m_player);
m_bRunning = true; // everything inited successfully, start the main loop
return true;
}
void Game::render()
{
SDL_RenderClear(m_pRenderer); // clear the renderer to the draw color
//m_go.draw(m_pRenderer);
//m_player.draw(m_pRenderer);
for(std::vector<GameObject*>::size_type i = 0; i != m_gameObjects.size(); i++)
{
m_gameObjects[i]->draw(m_pRenderer);
}
SDL_RenderPresent(m_pRenderer); // draw to the screen
}
void Game::clean()
{
std::cout << "cleaning game\n";
SDL_DestroyWindow(m_pWindow);
SDL_DestroyRenderer(m_pRenderer);
SDL_Quit();
}
void Game::handleEvents()
{
SDL_Event event;
if(SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
m_bRunning = false;
break;
default:
break;
}
}
}
void Game::update()
{
//m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
//m_go.update();
//m_player.update();
for(std::vector<GameObject*>::size_type i = 0; i != m_gameObjects.size(); i++)
{
m_gameObjects[i]->update();
}
}
int main(int argc, char* argv[])
{
g_game = new Game();
g_game->init("Chapter 1", 100, 100, 640, 480, 0);
while(g_game->running())
{
g_game->render();
g_game->handleEvents();
g_game->update();
SDL_Delay(10); // add the delay
}
g_game->clean();
return 0;
}
As you can see at the end of the init function, I have m_go load up simba, and m_player load up download. Even though they move in different directions (GameObject moves to the right and Player moves to the left as you can see in the classes in update method).
My program only loads download for both.
I'm not sure if the TextureManager class is related but I'll post it anyway
TextureManager.h
#ifndef TEXTUREMANAGER_H
#define TEXTUREMANAGER_H
#include <SDL.h>
#include <SDL_image.h>
#include <string.h>
#include <iostream>
#include <map>
class TextureManager
{
public:
bool load(std::string fileName,std::string id,SDL_Renderer* pRenderer);
void draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
void drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer* pRenderer, SDL_RendererFlip flip = SDL_FLIP_NONE);
std::map <std::string, SDL_Texture*> m_textureMap;
static TextureManager* Instance()
{
if(s_pInstance == 0)
{
s_pInstance = new TextureManager();
return s_pInstance;
}
return s_pInstance;
}
//typedef TextureManager TheTextureManager;
protected:
private:
TextureManager(){}
static TextureManager* s_pInstance;
};
#endif // TEXTUREMANAGER_H
TextureManager.cpp
#include "TextureManager.h"
TextureManager* TextureManager::s_pInstance = 0;
bool TextureManager::load(std::string fileName, std::string id, SDL_Renderer* pRenderer)
{
SDL_Surface* pTempSurface = IMG_Load(fileName.c_str());
if(pTempSurface == 0)
{
return false;
}
SDL_Texture* pTexture =
SDL_CreateTextureFromSurface(pRenderer, pTempSurface);
SDL_FreeSurface(pTempSurface);
// everything went ok, add the texture to our list
if(pTexture != 0)
{
m_textureMap[id] = pTexture;
return true;
}
// reaching here means something went wrong
return false;
}
void TextureManager::draw(std::string id, int x, int y, int width, int height, SDL_Renderer* pRenderer, SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x = x;
destRect.y = y;
SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect,
&destRect, 0, 0, flip);
}
void TextureManager::drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer *pRenderer, SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;
srcRect.x = width * currentFrame;
srcRect.y = height * (currentRow - 1);
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x = x;
destRect.y = y;
SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect,
&destRect, 0, 0, flip);
}
You are using "animate" more than once for the textureID - this needs to be unique
Your Code:
m_go->load(100, 100, 96, 60, "assets/simba.bmp", "animate", m_pRenderer);
m_player->load(300, 300, 96, 60, "assets/download.bmp", "animate", m_pRenderer)
What happens is the texture is loaded but it overwrites the existing texture which has the "animate" key in the map. Make them unique and this should resolve your issue.
i am following SDL game development book by shaun mitcchel and was trying to compile the code and was getting error of constructor is private. i am using mingw for compiling.
main.cpp
#include "game.h"
game* g_game = 0;
int main(int argc, char* args[])
{
g_game = new game;
g_game->init("SDL",100,100,640,480,SDL_WINDOW_SHOWN);
while(g_game->running())
{
g_game->handleEvents();
g_game->update();
g_game->render();
}
g_game->clean();
return 0;
}
game.h
#ifndef GAME_H
#define GAME_H
#include <SDL.h>
#include <SDL_image.h>
#include <iostream>
#include "TextureManager.h"
class game
{
public:
game(){}
virtual ~game();
bool init(char* title, int xpos, int ypos, int width, int height, int flags);
void render();
void update();
void handleEvents();
void clean();
bool running()
{
return m_bRunning;
}
protected:
private:
SDL_Window* m_pWindow;
SDL_Renderer* m_pRenderer;
int m_currentFrame;
textureManager m_textureManager;
bool m_bRunning;
};
#endif // GAME_H
game.cpp
#include "game.h"
bool game::init(char* title,int xpos, int ypos, int width, int height,int flags)
{
//initialize SDL
if(SDL_Init(SDL_INIT_EVERYTHING)== 0)
{
std::cout << "SDL init success\n";
//init window
m_pWindow = SDL_CreateWindow(title,xpos,ypos,width,height,flags);
if(m_pWindow != 0)
{
std::cout << "window creation successful\n";
m_pRenderer = SDL_CreateRenderer(m_pWindow,-1,0);
if(m_pRenderer != 0) {
std::cout << "render init success\n";
SDL_SetRenderDrawColor(m_pRenderer,255,0,0,255);
}
else {
std::cout << "render init fail\n";
return false;
}
}
else {
std::cout << "Window init fail\n";
return false;
}
}
else {
std::cout << "Sdl init failed\n";
return false;
}
std::cout << "init success\n";
m_bRunning = true;
//to load
if(!ThetextureManager::Instance()->load("assets/animate- alpha.png","animate",m_pRenderer))
{
return false;
}
}
void game::render()
{
SDL_RenderClear(m_pRenderer);
ThetextureManager::Instance()->draw("animate", 0, 0,128,82,m_pRenderer);
ThetextureManager::Instance()->drawFrame("animate", 100,100,128,82,1,m_currentFrame,m_pRenderer);
SDL_RenderPresent(m_pRenderer);
}
void game::handleEvents()
{
SDL_Event event;
if(SDL_PollEvent(&event)) {
switch(event.type)
{
case SDL_QUIT:
m_bRunning = false;
break;
default:
break;
}
}
}
void game::clean()
{
std::cout << "cleaning game\n";
SDL_DestroyRenderer(m_pRenderer);
SDL_DestroyWindow(m_pWindow);
SDL_Quit();
}
void game::update()
{
m_currentFrame = int(((SDL_GetTicks() / 100) % 6));
}
TextureManager.h
#ifndef TEXTUREMANAGER_H_INCLUDED
#define TEXTUREMANAGER_H_INCLUDED
#include <iostream>
#include<map>
#include<SDL.h>
#include<SDL_image.h>
class textureManager {
public:
static textureManager* Instance()
{
if(s_pInstance == 0)
{
s_pInstance = new textureManager();
return s_pInstance;
}
return s_pInstance;
}
bool load(std::string fileName, std::string id,SDL_Renderer* pRenderer);
void draw(std::string id,int x,int y,int width,int height,SDL_Renderer* pRenderer,SDL_RendererFlip flip);
void drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer* pRenderer,SDL_RendererFlip flip);
std::map<std::string, SDL_Texture*> m_textureMap;
private:
textureManager() {}
textureManager(const textureManager&);
textureManager& operator=(const textureManager&);
static textureManager* s_pInstance;
};
typedef textureManager ThetextureManager;
#endif // TEXTUREMANAGER_H_INCLUDED
texturemanagement.cpp
#include "TextureManager.h"
textureManager* textureManager::s_pInstance = 0;
bool textureManager::load(std::string fileName, std::string id,SDL_Renderer* pRenderer)
{
SDL_Surface* pTempSurface = IMG_Load(fileName.c_str());
if(pTempSurface == 0)
{
return false;
}
SDL_Texture* pTexture = SDL_CreateTextureFromSurface(pRenderer, pTempSurface);
SDL_FreeSurface(pTempSurface);
if(pTexture != 0)
{
m_textureMap[id] = pTexture;
return true;
}
return false;
}
void textureManager::draw(std::string id,int x,int y,int width,int height,SDL_Renderer* pRenderer,SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x = x;
destRect.y = y;
SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, &destRect,0 , 0, flip);
}
void textureManager::drawFrame(std::string id, int x, int y, int width, int height, int currentRow, int currentFrame, SDL_Renderer* pRenderer,SDL_RendererFlip flip)
{
SDL_Rect srcRect;
SDL_Rect destRect;
srcRect.x = width * currentFrame;
srcRect.y = height * (currentRow - 1);
srcRect.w = destRect.w = width;
srcRect.h = destRect.h = height;
destRect.x = x;
destRect.y = y;
SDL_RenderCopyEx(pRenderer, m_textureMap[id], &srcRect, &destRect,0, 0, flip);
}
errors
C:\Users\lenovo\Desktop\node project\sdl\TextureManager.h||In constructor 'game::game()':|
C:\Users\lenovo\Desktop\node project\sdl\TextureManager.h|28|error: 'textureManager::textureManager()' is private|
C:\Users\lenovo\Desktop\node project\sdl\game.h|12|error: within this context|
C:\Users\lenovo\Desktop\node project\sdl\game.cpp||In member function 'void game::render()':|
C:\Users\lenovo\Desktop\node project\sdl\game.cpp|46|error: no matching function for call to 'textureManager::draw(const char [8], int, int, int, int, SDL_Renderer*&)'|
C:\Users\lenovo\Desktop\node project\sdl\game.cpp|47|error: no matching function for call to 'textureManager::drawFrame(const char [8], int, int, int, int, int, int&, SDL_Renderer*&)'|
You have textureManager m_textureManager member in class game (file game.h)
But in game.cpp you always use ThetextureManager::Instance()
You just need to remove m_textureManager member.