my problem is that when I want to divide the code into smaller functions it throws me a runtime error: access violation.
Since everything is passed by value I have no idea why such an error would occur. There is nothing to do about memory.
General code:
// main.cpp
#include "stdafx.h"
#include "SFML/Graphics.hpp"
#include "GameEngine.h"
int main()
{
sf::RenderWindow* window = new sf::RenderWindow(sf::VideoMode(200, 200), "SFML works!");
GameEngine game(window);
game.run();
delete window;
return 0;
}
// DrawableVertices.h
#pragma once
#include "SFML\Graphics.hpp"
using namespace sf;
class DrawableVertices : public Drawable, public Transformable
{
VertexArray vertices;
Texture* pTexture;
public:
virtual void draw(RenderTarget& target, RenderStates states) const;
DrawableVertices(VertexArray vertices, Texture* pTexture = nullptr);
DrawableVertices();
Texture* getTexture();
VertexArray* getVertices();
};
// DrawableVertices.cpp
#include "stdafx.h"
#include "DrawableVertices.h"
DrawableVertices::DrawableVertices(sf::VertexArray _vertices, sf::Texture* _pTexture)
{
vertices = _vertices;
pTexture = pTexture;
}
DrawableVertices::DrawableVertices()
{
vertices = sf::VertexArray(sf::Points, 1);
pTexture = nullptr;
}
void DrawableVertices::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform *= getTransform();
states.texture = pTexture;
target.draw(vertices, states);
}
sf::Texture* DrawableVertices::getTexture() { return pTexture; }
sf::VertexArray* DrawableVertices::getVertices() { return &vertices; }
Working code (all in one function):
//GameEngine.h
#pragma once
#include "DrawableVertices.h"
class GameEngine
{
RenderWindow* pWindow;
public:
GameEngine(RenderWindow* window);
void run();
};
//GameEngine.cpp
#include "stdafx.h"
#include "GameEngine.h"
GameEngine::GameEngine(RenderWindow* window)
{
pWindow = window;
}
void GameEngine::run()
{
const int X_DIFF = 8;
const int PLAYER_Y = 24;
const int PLAYER_X = 48;
VertexArray playerShape(TrianglesStrip, 4);
playerShape[0].position = Vector2f(X_DIFF, 0);
playerShape[0].color = Color(0, 127, 255);
playerShape[1].position = Vector2f(PLAYER_X - X_DIFF, 0);
playerShape[1].color = Color(0, 127, 255);
playerShape[2].position = Vector2f(0, PLAYER_Y);
playerShape[2].color = Color(0, 0, 255);
playerShape[3].position = Vector2f(PLAYER_X, PLAYER_Y);
playerShape[3].color = Color(0, 0, 255);
DrawableVertices player(playerShape);
player.setOrigin(PLAYER_X / 2, PLAYER_Y / 2);
player.setPosition(64, 64);
while (pWindow->isOpen())
{
Event event;
while (pWindow->pollEvent(event))
{
if (event.type == Event::Closed)
pWindow->close();
}
pWindow->clear();
pWindow->draw(player);
pWindow->display();
}
}
Not working code, throwing access violation while drawing player
[ pWindow->draw(player) line ]
//GameEngine.h
#pragma once
#include "DrawableVertices.h"
class GameEngine
{
RenderWindow* pWindow;
DrawableVertices player;
void createPlayer();
public:
GameEngine(RenderWindow* window);
void run();
};
// GameEngine.cpp
#include "stdafx.h"
#include "GameEngine.h"
GameEngine::GameEngine(RenderWindow* window)
{
pWindow = window;
createPlayer();
}
void GameEngine::createPlayer()
{
const int X_DIFF = 8;
const int PLAYER_Y = 24;
const int PLAYER_X = 48;
VertexArray playerShape(TrianglesStrip, 4);
playerShape[0].position = Vector2f(X_DIFF, 0);
playerShape[0].color = Color(0, 127, 255);
playerShape[1].position = Vector2f(PLAYER_X - X_DIFF, 0);
playerShape[1].color = Color(0, 127, 255);
playerShape[2].position = Vector2f(0, PLAYER_Y);
playerShape[2].color = Color(0, 0, 255);
playerShape[3].position = Vector2f(PLAYER_X, PLAYER_Y);
playerShape[3].color = Color(0, 0, 255);
player = DrawableVertices(playerShape);
player.setOrigin(PLAYER_X / 2, PLAYER_Y / 2);
player.setPosition(64, 64);
}
void GameEngine::run()
{
while (pWindow->isOpen())
{
Event event;
while (pWindow->pollEvent(event))
{
if (event.type == Event::Closed)
pWindow->close();
}
pWindow->clear();
pWindow->draw(player);
pWindow->display();
}
}
What's worse when I display all player's information like position etc. inside run() method it shows the proper result. It means that player is initialised properly and it exists like it is supposed to.
Sorry for a lot of code, but wanted to show everything that could be useful. Obviously my whole code is bigger, I just put the part making problems into a separate project.
// Edit
Yeah, debugger says that pTexture member of player has a bad pointer.
image from debugging working and not working code
I suppose that overloading = operator might fix the problem. However, I want to understand how it works that this code works without any problems:
void GameEngine::run()
{
DrawableVertices player = DrawableVertices(playerShape);
// ...
pWindow->draw(player);
}
And packing it into a fuction doesn't:
void GameEngine::createPlayer()
{
// ...
DrawableVertices player = DrawableVertices(playerShape);
}
void GameEngine::run()
{
createPlayer();
// ...
pWindow->draw(player);
}
The error (if I'm not wrong) is very simple
In this constructor
DrawableVertices::DrawableVertices(sf::VertexArray _vertices, sf::Texture* _pTexture)
{
vertices = _vertices;
pTexture = pTexture;
}
you copy pTexture on itself; so pTexture start undefined and remain undefined.
I suppose that your intention was
DrawableVertices::DrawableVertices(sf::VertexArray _vertices, sf::Texture* _pTexture)
{
vertices = _vertices;
pTexture = _pTexture; // _pTexture !
}
p.s.: sorry for my bad English.
Related
Im following this (https://www.udemy.com/course/free-learn-c-tutorial-beginners/learn/lecture/1838486#announcements ) tutorial and im stuck.
It seem that windows throws that exception and it cant be handled.
I think I try write something to some wrong place but cant see it.
Heres the line that causes the problem.
m_buffer[(y * SCREEN_WIDTH) + x] = color;
And here is everything else
//====================================main.cpp=======================================
#include<iostream>
#include "Screen.h"
#include<SDL.h>
using namespace std;
int main(int argc, char* argv[]) {
Screen screen;
if (!screen.init()) {
cout << "Error initialising SDL." << endl;
}
while (true) {
// update particles
// draw particles
for (int y = 0; y < Screen::SCREEN_HEIGHT; y++) {
for (int x = 0; x < Screen::SCREEN_WIDTH; x++ ){
screen.setPixel(x, y, 128, 0, 255);
}
}
//Draw the screen
screen.update();
// see for messages/events
if (!screen.processEvents()) {
break;
}
}
screen.close();
return 0;
}
//==========================================================================================
screen.h
#ifndef SCREEN_H_
#define SCREEN_H_
#include<SDL.h>
#include<iostream>
class Screen
{
public:
const static int SCREEN_WIDTH = 800;
const static int SCREEN_HEIGHT = 600;
private:
SDL_Window* m_window;
SDL_Renderer* m_renderer;
SDL_Texture* m_texture;
Uint32* m_buffer;
public:
Screen();
bool init();
void update();
void setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue);
bool processEvents();
void close();
};
#endif // !SCREEN_H_
//=======================screen.cpp======================================================
#include "Screen.h"
Screen::Screen() : m_window(NULL), m_renderer(NULL), m_texture(NULL), m_buffer(NULL)
{
}
bool Screen::init() {
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
return false;
}
m_window = SDL_CreateWindow("Particle Fire Explosion",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); //-----------------SDL_WINDOW_HIDDEN SDL_WINDOW_SHOWN
if (m_window == 0) {
SDL_Quit();
return false;
}
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_PRESENTVSYNC);
m_texture = SDL_CreateTexture(m_renderer,
SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STATIC, SCREEN_WIDTH, SCREEN_HEIGHT);
if (m_renderer == NULL) {
SDL_DestroyWindow(m_window);
SDL_Quit();
return false;
}
if (m_texture == NULL) {
SDL_DestroyWindow(m_window);
SDL_DestroyRenderer(m_renderer);
SDL_Quit();
return false;
}
Uint32* buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
memset(buffer, 0xFF, SCREEN_WIDTH * SCREEN_HEIGHT * sizeof(Uint32));
return true;
}
bool Screen::processEvents() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT)
{
return false;
}
}
return true;
}
void Screen::setPixel(int x, int y, Uint8 red, Uint8 green, Uint8 blue) {
Uint32 color = 0;
color += red;
color <<= 8;
color += green;
color <<= 8;
color += blue;
color <<= 8;
color += 0xFF;
m_buffer[(y * SCREEN_WIDTH) + x] = color;
}
void Screen::update() {
SDL_UpdateTexture(m_texture, NULL, m_buffer, SCREEN_WIDTH * sizeof(Uint32));
SDL_RenderClear(m_renderer);
SDL_RenderCopy(m_renderer, m_texture, NULL, NULL);
SDL_RenderPresent(m_renderer);
}
void Screen::close() {
delete[] m_buffer;
SDL_DestroyRenderer(m_renderer);
SDL_DestroyTexture(m_texture);
SDL_DestroyWindow(m_window);
SDL_Quit();
}
//==========================================================================================
Maybe someone can see what is the broblem?
Your Screen::init() method never sets m_buffer, so when setPixel() tries to dereference it, it is dereferencing a NULL pointer, which invokes undefined behavior.
this line is wrong
Uint32* buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
you mean
m_buffer = new Uint32[SCREEN_WIDTH * SCREEN_HEIGHT];
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;
}
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.
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.
so i have just started playing with sdl and i have it working fine in a single class but for some reason when i separate things into seperate classes the display opens a insta closes. an ideas ?
Main Class Header
#pragma once
#include <SDL.h>
#include <glew.h>
#include <iostream>
#include "Input.h"
#include "Display.h"
#include "RenderingEngine.h"
#include "PhysicsEngine.h"
class Main
{
public:
Main();
~Main();
/* Engine settings & Engine Controlls*/
void start();
void stop();
void pause(bool value);
void run();
private:
/* Loop Controllers */
bool running;
bool paused;
/* Engine Initialisation */
void initSDL();
RenderingEngine render_core = RenderingEngine(4, 2);
PhysicsEngine physics_core = PhysicsEngine();
Display display = Display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
Input input;
};
Main Class
#include "Main.h"
Main::Main()
{
initSDL();
start();
}
Main::~Main()
{
}
void Main::initSDL()
{
SDL_Init(SDL_INIT_EVERYTHING);
}
void Main::start()
{
if (running) return;
running = true;
run();
}
void Main::stop()
{
if (!running) return;
running = false;
exit(0);
}
void Main::pause(bool value)
{
paused = value;
}
void Main::run()
{
while (running)
{
if (!paused)
{
}
render_core.render();
display.swapBackBuffer();
input.update();
}
}
int main(int argc, char *argv[])
{
Main engine;
return 0;
}
Display Header
#pragma once
#include <iostream>
#include <SDL.h>
class Display
{
public:
Display(const char* name, int x, int y, int w, int h, Uint32 flags);
~Display();
void swapBackBuffer();
private:
int x;
int y;
int w;
int h;
const char* name;
Uint32 flags;
SDL_Window *window;
SDL_GLContext opengl;
};
Display Class
#include "Display.h"
Display::Display(const char* n, int x, int y, int w, int h, Uint32 f)
{
this->x = x;
this->y = y;
this->w = w;
this->h = h;
this->name = name;
this->flags = flags;
this->window = SDL_CreateWindow(n, x, y, w, h, f);
this->opengl = SDL_GL_CreateContext(window);
SDL_GL_MakeCurrent(window, opengl);
printf("Display: initialised\n\n");
}
Display::~Display()
{
SDL_GL_DeleteContext(opengl);
SDL_DestroyWindow(window);
printf("Display: destroyed\n\n");
}
void Display::swapBackBuffer()
{
SDL_GL_SwapWindow(window);
}
Render Engine class.... there isn't anything important in the header
#include "RenderingEngine.h"
RenderingEngine::RenderingEngine(int major_version, int minor_version)
{
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, major_version);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minor_version);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
setClearColour(0, 0, 0, 1);
printf("Rendering Engine:: initialised\n\n");
}
RenderingEngine::~RenderingEngine()
{
printf("Rendering Engine:: destroyed\n");
}
void RenderingEngine::setClearColour(float r, float g, float b, float a)
{
glClearColor(r, g, b, a);
}
void RenderingEngine::clearScreen()
{
glClear(GL_COLOR_BUFFER_BIT || GL_DEPTH_BUFFER_BIT);
}
void RenderingEngine::render()
{
clearScreen();
}
input class
#include "Input.h"
Input::Input()
{
printf("Input:: initialised\n");
}
Input::~Input()
{
printf("Input:: destroyed\n");
}
void Input::setMouseVisabilityTo(bool value)
{
if (value) SDL_ShowCursor(1);
else SDL_ShowCursor(0);
}
int Input::getMouseX()
{
return mouseX;
}
int Input::getMouseY()
{
return mouseY;
}
void Input::update()
{
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
break;
case SDL_KEYDOWN:
keyboard[event.key.keysym.sym] = true;
break;
case SDL_KEYUP:
keyboard[event.key.keysym.sym] = false;
break;
case SDL_MOUSEBUTTONDOWN:
mouse[event.button.button] = true;
break;
case SDL_MOUSEBUTTONUP:
mouse[event.button.button] = false;
break;
case SDL_MOUSEWHEEL:
break;
case SDL_MOUSEMOTION:
mouseX = event.button.x;
mouseY = event.button.y;
break;
}
}
}
i know there are a lot a files so the help will be greatly appreciated, this has been bugging me for a while now
my edited main.h file
#include "Display.h"
#include "RenderingEngine.h"
#include "PhysicsEngine.h"
class Main
{
public:
Main() :
render_core(4, 2),
display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL),
physics_core(),
input()
{
running = false;
paused = false;
initSDL();
start();
}
~Main();
/* Engine settings & Engine Controlls*/
void start();
void stop();
void pause(bool value);
void run();
private:
/* Loop Controllers */
bool running;
bool paused;
/* Engine Initialisation */
void initSDL();
RenderingEngine render_core;
PhysicsEngine physics_core;
Display display;
Input input;
Is this some weird version of C++ (e.g. C++11) where you can declare and initialize a non-static member variable all in the same statement?
You should not be doing that kind of thing, the order in which your render context is constructed and initialized in relation to the rest of your software is extremely important. This is what constructors are for.
Assuming you actually are intentionally (ab)using C++11 here, your window closes immediately because this statement:
Display display = Display("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
Constructs a Display object, and then makes a copy of that object and assigns it to display. After this statement "returns", the original object is destroyed.
And look at what the destructor for Display does:
Display::~Display()
{
SDL_GL_DeleteContext(opengl);
SDL_DestroyWindow(window);
printf("Display: destroyed\n\n");
}
Long story short, do not initialize your members this way. Constructors were perfectly fine before C++11 came along and made life more difficult.
Consider something like the following constructor instead:
Main () : render_core (4, 2),
display ("game engine", 900, 900, 900, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL),
physics_core ()
{
initSDL ();
start ();
}
...
private:
RenderingEngine render_core;
PhysicsEngine physics_core;
Display display;
This solution initializes all of the members of Main when it is constructed, without any copy-assignment and is compatible with a much wider range of C++ compilers. No temporary Display object in this code means that your window is not going to be created and then immediately destroyed.