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 made a character class for the player, and I want to add a player member to the class then draw the player. I am using sfml and xcode. The error I am getting is: No matching member function for call to 'draw' on the: window.draw(player); line. It looks like I need to put a sprite object into .draw() so how do I make player sprite that is part of the class and draw it onto the window? I am new to c++ and sfml so any help is very much appreciated thanks.
#include <iostream>
#include <SFML/Graphics.hpp>
using namespace std;
class Character{
public:
string sprite;
int health;
int defense;
int speed;
int experience;
bool move;
int x_pos;
int y_pos;
sf::Texture texture;
//Constructor - Ran everytime a new instance of the class is created
Character(string image){
health = 100;
defense = 100;
speed = 100;
experience = 0;
x_pos = 0;
y_pos = 0;
texture.loadFromFile(image);
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setTextureRect(sf::IntRect(0, 0, 100, 100));
sprite.setPosition(x_pos, y_pos);
}
//Destructor - Ran when the object is destroyed
~Character(){
}
//Methods
void forward();
void backward();
void left();
void right();
void attack();
};
void Character::forward(){
cout << "Go Forward";
}
void Character::backward(){
cout << "Go Backward";
}
void Character::left(){
cout << "Go Left";
}
void Character::right(){
cout << "Go Right";
}
Character player("/Users/danielrailic/Desktop/Xcode /NewGame/ExternalLibs/Player.png");
int main() {
// insert code here...
int windowWidth = 1150;
int windowHeight = 750;
sf::RenderWindow window(sf::VideoMode(windowWidth, windowHeight ), "Awesome Game" );
while(window.isOpen()){
window.draw(player);
window.display();
window.setFramerateLimit(60);
}
}
You could simply add a member function that would return your player's sprite:
sf::Sprite Character::getSprite() {
sf::Sprite sprite;
sprite.setTexture(texture);
sprite.setTextureRect(sf::IntRect(0, 0, 100, 100));
sprite.setPosition(x_pos, y_pos);
return sprite;
}
Then pass the result to window.draw:
window.draw(player.getSprite());
Alternatively, you could invert the dependency and make the player responsible for all the drawing:
void Character::draw(sf::RenderWindow& w) {
w.draw(...);
}
There are other approaches, and the choice depends largely on a lot of external factors. This should get you started, though.
I'm just playing around with C++ SFML stuff and I kinda don't understand why my code isn't working. The thing I want to do is to draw like let's say 5, squares in Window randomly placed around the screen using vector, but I don't understand why it's not working. And it doesn't give any error as well, I can open game like normal, but it's just not rendering.
This is the main game class:
#include "main_game.h"
#include "main_menu.h"
void main_game::Initialize(sf::RenderWindow* window)
{
this->Player = new player();
this->Player->setOrigin(this->Player->getGlobalBounds().width / 2, this->Player->getGlobalBounds().height / 2);
this->TestObject = new testObject();
this->TestObject->Initialize();
this->TestObject->setOrigin(this->TestObject->getGlobalBounds().width / 2, this->TestObject->getGlobalBounds().height / 2);
}
void main_game::Update(sf::RenderWindow* window)
{
this->Player->setPosition(sf::Mouse::getPosition(*window).x, sf::Mouse::getPosition(*window).y);
this->Player->Update();
if (this->Player->CheckCollision(TestObject))
{
this->TestObject->setColor(sf::Color::Red);
}
else
{
this->TestObject->setColor(sf::Color::Cyan);
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::Key::Escape))
{
coreState.SetState(new main_menu());
}
}
void main_game::Render(sf::RenderWindow* window, std::vector<sf::Sprite> sprites)
{
this->TestObject->Render(*window, sprites);
window->draw(*this->Player);
}
void main_game::Destroy(sf::RenderWindow* window)
{
delete this->Player;
delete this->TestObject;
}
This is the testObject.h class
#pragma once
#include "entity.h"
class testObject : public Entity
{
public:
testObject();
void Initialize();
void Render(sf::RenderWindow &window, std::vector<sf::Sprite> sprites);
void Update();
private:
sf::RenderWindow window;
};
And this is testObject.cpp class
#include "testObject.h"
testObject::testObject()
{
this->Load("testObject.png");
}
void testObject::Initialize()
{
sf::Texture testObjectTexture;
sf::Sprite testObjectSprite;
testObjectTexture.loadFromFile("testObject.png");
testObjectSprite.setTexture(testObjectTexture);
std::vector<sf::Sprite> sprites(5, sf::Sprite(testObjectSprite));
srand(time(0));
for (unsigned int i = 0; i < sprites.size(); i++)
{
sprites[i].setPosition(1 + (rand() % 1024 - 32), rand() % 640 - 32);
}
}
void testObject::Render(sf::RenderWindow &window, std::vector<sf::Sprite> sprites)
{
for (unsigned int i = 0; i < sprites.size(); i++)
{
window.draw(sprites[i]);
}
}
void testObject::Update()
{
Entity::Update();
}
main_game.h:
#pragma once
#include "game_state.h"
#include "player.h"
#include "testObject.h"
class main_game : public tiny_state
{
public:
void Initialize(sf::RenderWindow* window);
void Update(sf::RenderWindow* window);
void Render(sf::RenderWindow* window, std::vector<sf::Sprite> sprites);
void Destroy(sf::RenderWindow* window);
private:
player* Player;
testObject* TestObject;
};
An easy fix to this is simply use an array, or any other data structure that can store by copy rather than store by reference instead of a vector, which stores by reference, to hold the sprites.
The problem is two fold, its not just a scope issue, if you moved the render function into the initialize scope, you will draw your sprite 5 times in the same place, because every element in your vector is pointing to testObjectSprite's memory location (the same place).
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.
If I declare a SDL_Surface pointer in a class, can i share it with another class to draw on it in somehow?
class foo{
private:
SDL_Surface* mainScreen;
public:
foo() {
mainScreen = SDL_SetVideoMode(400,300,32, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_OPENGL);
}
~foo() {
SDL_FreeSurface(mainScreen);
}
SDL_Surface* getSurf() {
return mainScreen;
}
};
class fee{
private:
SDL_Surface* screen_passed;
public:
void draw(SDL_Surface* screen) {
screen_passed = screen;
SDL_Surface* img;
SDL_Surface* app;
app = IMG_Load("image.png");
img = SDL_DisplayFormatAlpha(app);
SDL_FreeSurface(app);
SDL_Rect destR;
destR.x=0;
destR.y=0;
SDL_BlitSurface(img, NULL, screen, &destR);
}
};
int main(int argc, char* argv[]) {
SDL_Init(SDL_INIT_VIDEO);
foo a;
fee b;
b.draw(a.getSurf());
SDL_Flip(a.getSurf());
sleep(5);
return 0;
}
compiles and run, but the screen is black, can anyone help?
Screen is black probably because you're using double buffering and never flip the buffer (call SDL_Flip(a.getSurf()) after b.draw).