Problem with SDL

Started by
4 comments, last by Servant of the Lord 9 years, 3 months ago

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);
}

Advertisement

First minor issue: You shouldn't start your variable names with an underscore. Variable names starting with underscores are reserved for the compiler.

Your real issue: You have two separate TextureManagers. smile.png

If I create:


int MyIntA;
int MyIntB;

...then those are two separate integers. Even if I give them the same name, that doesn't make them the same instance.

The TextureManager called Game::_textureManager is not the same as the TextureManager called Player::_textureManager. Even if you name them the same, that doesn't make them the same.

What you need to do is make the Player's TextureManager be a reference to the real texturemanager. Either you can pass Player a reference to the real texturemanager using the Player class's constructor, or you can pass in the reference to the texturemanager into the draw() function, like you do with the SDL_Renderer.

First minor issue: You shouldn't start your variable names with an underscore. Variable names starting with underscores are reserved for the compiler.

Your real issue: You have two separate TextureManagers. smile.png

If I create:


int MyIntA;
int MyIntB;

...then those are two separate integers. Even if I give them the same name, that doesn't make them the same instance.

The TextureManager called Game::_textureManager is not the same as the TextureManager called Player::_textureManager. Even if you name them the same, that doesn't make them the same.

What you need to do is make the Player's TextureManager be a reference to the real texturemanager. Either you can pass Player a reference to the real texturemanager using the Player class's constructor, or you can pass in the reference to the texturemanager into the draw() function, like you do with the SDL_Renderer.

ohh. about the _ in variable, i was watching this tutorial in youtube and he is using _ in private variable. and im not quite familiar with passing reference, can you show me the code on on both your suggestion? thanks


First minor issue: You shouldn't start your variable names with an underscore. Variable names starting with underscores are reserved for the compiler.

This is the first time I've heard this after writing C++ code for 12 years. I think it's a pretty common practice to prefix private variable names with an underscore. We do it in my project (> 150k lines of code) and cross-compile it on multiple different systems and haven't run into any name conflicts yet...

Hero of Allacrost - A free, open-source 2D RPG in development.
Latest release June, 2015 - GameDev annoucement

First minor issue: You shouldn't start your variable names with an underscore. Variable names starting with underscores are reserved for the compiler.


That is incorrect like that. According to this post which reinforces my previous suspicion, his use of underscores is without issues. He is using a single underscore followed by a lower case letter which the standard allows him to.
I'm not a fan of that notation but those are stylistic complains, not technical. I am however rather annoyed whenever I see (void) as a function parameter. The code is obviously C++, could never be C, so there is absolutely no point in putting that there.

Thanks guys, I meant to link to the post here where I explain the underscores, so I wouldn't have to re-type it out. Apparently I failed to add it my post. ph34r.png

The version of the standard I have on my computer says:

"Certain sets of names and function signatures are always reserved to the implementation:
Each name that contains a double underscore__ or begins with an underscore followed by an uppercase letter is reserved to the implementation for any use.
Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace." - (17.6.4.3.2 in draft version N3376)

So:

  • Don't start any name with double underscores.
  • If it's in the global namespace, never start names (variable / class / function / macro names) with an underscore.
  • If it's in a class or function or elsewhere, you can start with a single underscore as long as you don't follow it with an uppercase letter.

These three rules can be simplified and generalized as, "don't start variable names (or class names) with underscores".
The OP is technically safe, but unless he knows why he is safe, he's basically walking on a rickety bridge without knowing it.
I had intended to link to the fuller explanation - but I must've accidentally deleted that when I deleted my griping about explicit void parameters. laugh.png

Even if you violate the actual rules, 99.99% of the time you won't encounter any problems. I used underscore prefixes with uppercase letters for a long time before I found out it wasn't allowed.
It's just that compilers can potentially generate extra hidden variables and macroes in your classes and functions to help implement or optimize the generated code. If the compiler does this, and if it happens that you accidentally used a variable name that they are trying to use, you can run into problems. Most likely, you'll just get funky compiler-time errors about conflicting variable names (while only being able to see one variable in your code), but even so it's important to be aware of the possibility, as unlikely as it is.

im not quite familiar with passing reference, can you show me the code on on both your suggestion? thanks


Are you familiar with pointers? Pointers and references are almost identical in concept.
If I do:


int myVariableA;
int myVariableB;

Both these variables are different variables, holding different data. They are independent of each other. Even if I name them the same, they aren't actually the same.

But you can create a "reference" variable. A reference variable is creating a dummy variable that actually modifies the variable it is refering to. If I do this:


int &myReference = myVariableA; //The '&' sign means it's a reference variable.

This makes 'myReference' point at and represent 'myVariableA'. It is a second way to access myVariableA's data.
Any time you modify 'myReference', you're actually modifying the variable that it is refering to (in this case, 'myVariableA').

When you read myReference's value, it actually reads from 'myVariableA'. References are not copies of the variable, they are another way to access the same variable. References don't hold their own data, they read and write the data of the variable they are referring to.

If it is a const reference, then it can only read, but not modify, the variable they refer to. You may want to learn more about const variables and const member-functions - but I don't want to pile on too much information at once, so below I'll use regular (non-const) references.

To solve your current issue, you don't want two different texture managers holding different textures and not sharing information. Instead, you want one texture manager, and you want to give other classes access to that one texture manager. In this case, we can give other classes references to the real texture manager, and they can access the one texture manager through their references to it.

So Game can have the real texture manager as a member. Player (and other classes) can then take a reference to it in their constructors, like this:


class Player
{
    Player(TextureManager &_textureManager); //We'll pass the variable into the constructor by reference also.
    
    //...etc....

    private:
    TextureManager &_textureManager; //The '&' sign means it's a reference variable, so we'll own a reference instead of a copy.
}

And the constructor's definition can look like this:


Player::Player(TextureManager &textureManager) 
	: _textureManager(textureManager) //Assign 'textureManager' parameter to the '_textureManager' member-variable.
{
    
}

I know that looks kinda funny, because we have to introduce another feature here: member-initializer lists.
It's a special way of writing code that only applies to class constructors, allowing you to initialize the class's member variables before the constructor runs. I only bring it up because references (unlike pointers) must always be initialized when they are created, and can't be initialized after creation.

Game's constructor can now pass in a reference to Game's TextureManager to Player, when Game constructs 'Player'.


Game::Game()
    : _player(_textureManager) //Pass _player our _textureManager, so _player can store a reference to it.
{
   //...etc...
}

Now anytime _player uses its TextureManager reference, it'll really be using the TextureManager that it is referring to (in this case, Game's TextureManager). Player isn't creating an independent TextureManager, it isn't creating a copy either. It is creating a second representation of the same variable data.

Even though you use pointers, if you don't yet understand them, pointers and references are basically different ways of doing the same thing. Pointers can be changed to point to different variables later, but once a reference is assigned you never can to reassign them. Pointers can also be null (pointing to nothing) but references must always point to something.

There's one other issue I need to mention... (dry.png) Because Player is storing a reference to Game's TextureManager, you have to be absolutely positive that Game's TextureManager is constructed before Player's reference to it (otherwise you'll be referencing something that doesn't yet exist!), and you have to be absolutely positive that Game's TextureManager isn't destroyed before Player's reference is destroyed (otherwise you'll be referencing something that has ceased to exist!).

In this case, since Game owns TextureManager and Player, it can control what order they get constructed in.

In C++, classes construct their member variables from top to bottom. So here:


class Game
{
public:
	//...etc...

private:
        //...etc...

	TextureManager _textureManager; //_textureManager gets constructed first.
	Player _player; //And then _player gets constructed.

        //...etc...
};

That's something you need to remember if you reorder your member variables. Because '_player' depends on '_textureManager', '_textureManager' must be higher up the class declaration than '_player' is.

That's alot of information to absorb at one time. tongue.png

I glossed over and simplified some rules (just like I simplify the underscore rule), but note that everywhere I say "must", "never", "always", then those are code-words that mean that there are times where it is valid to break the "rules" that I mentioned, as you learn more about how C++ actually works, and that the "rules" are merely safe guidelines for the 99% of normal usage.

In C++, "never" and "always" are mere suggestions. wink.png

There are always cornercases where you can step outside the rules, for good or bad.

This topic is closed to new replies.

Advertisement