How should I pass an object to a class to check for collisions? - c++

I'm making a Breakout game in C++ using the SFML library, I have a "paddle" class as well as a "ball" class, both being updated and rendered in a "game" class, which uses a render window from a "window" class.
My issue is that in order to determine whether the Ball object has collided with the Paddle object, I obviously need access to the Paddle object's x/y position data members. I'm using an update (or tick) function for each object, I'd rather not just pass a reference to the Paddle object into the Ball's update function.
What is the general accepted way to do achieve my desired functionality?
Here is my Ball header:
class Ball
{
public:
Ball(float x, float y, int size, sf::Vector2f moveSpeed);
~Ball();
void Tick();
void Move();
void Render(sf::RenderWindow& window);
void Reset();
sf::Vector2f GetMoveSpeed() { return m_moveSpeed; }
private:
void CheckCollision();
int m_size;
sf::Vector2f m_moveSpeed;
sf::Vector2f m_position;
sf::CircleShape m_ballCircle;
};
Here is my Game update function:
void Game::Update()
{
m_window.Update();
m_ball.Tick();
m_paddle.Tick();
}

Use separate object which implements algorithm for updating ball. This object knows about all your data: ball and paddle, and calculates new position.
You can inject algorithm into your game class, which would hold pointer to interface. This allows you to change algorithm without changing Game, Ball or Paddle classes.

Let your Game check collision and handle them appropriately:
void Game::Update()
{
m_window.Update();
m_ball.Tick();
m_paddle.Tick();
if(areColliding(m_ball, m_paddle))
{
resolveCollision(m_ball, m_paddle);
}
}
In this case, areColliding and resolveCollision could be private member functions of Game.

Related

Drawing from a vector of sprites SFML

Every object in my game world has a vector of sprites that visually represent that object. My issue is that i cant seem to draw them properly on the screen:
This is the drawable object that every drawable inherits from:
class Drawable {
private:
static vector<Drawable*> sprites;
protected:
vector<sf::Texture> myTextures;
vector<sf::Sprite> mySprites;
public:
Drawable();
static vector<Drawable*> getSprites();
void draw(sf::RenderWindow&) const;
};
And its .cpp:
vector<Drawable*> Drawable::drawables;
Drawable::Drawable() {
drawables.push_back(this);
}
vector<Drawable*> Drawable::getDrawables() {
return drawables;
}
void Drawable::draw(sf::RenderWindow& window) const {
for (auto sprite : mySprites) {
window.draw(sprite);
}
}
Example of a object that inherits from drawable:
class Terrain : public Drawable {
private:
void loadSprite(string);
public:
Terrain(string);
};
and its .cpp:
Terrain::Terrain(string fileName) {
loadSprite(fileName);
}
void Terrain::loadSprite(string fileName) {
sf::Texture texture;
texture.loadFromFile(fileName);
myTextures.push_back(texture);
sf::Sprite sprite;
sprite.setTexture(texture);
mySprites.push_back(sprite);
}
In this case the terrain sprite is only a white-box during run-time. I think this is because the "texture" and "sprite" var in loadSprite gets destroyed after the method goes out of scope.
I know i could probably solve the issue by saving "texture" and "sprite" in the terrain-class (not creating them locally in a method like now). But this seems unnecessary to me, cant i store them in the vectors mySprites and myTextures instead?
I think this is because the "texture" and "sprite" var in loadSprite gets destroyed after the method goes out of scope.
You're right. sf::Sprite stores a reference to sf::Texture. loadSprite would work one-time only if you'd do sprite.setTexture(myTextures.back());. But std::vector's elements will be reallocated as you push_back. I recommend std::vector<std::shared_ptr<sf::Texture>> for simplicity.
Better yet, load all the textures at once, so that you don't have duplicate ones and use IDs to refer to them.

Access to a method of Object C in an Object B both created by Object A

I think the problem is quite basic and there was the same Question for sure somewhere here but i wasn't able to find.
So here is the Problem. Top has access to MyCircle and calls the SetSize function. But MyCircle has no access to MyRect.
I am able to access MyRect if i am providing a pointer to MyRect within the Constructor of MyCircle, but i think there should be another way. Maybe i just got it wrong at all :( Thanks for your help.
in the
class Top{
public:
Rect myRect;
Circle myCircle;
void Run();
};
class Rect{
public:
int size;
};
class Circle{
public:
int size;
void SetSize(int buffer);
};
void Circle::SetSize(int buffer){
myRect.Size = buffer;
}
void Top:Run(){
myCircle.SetSize(10);
}
Don't pass a rectangle to a circle class, the circle should have no knowledge about the rectangle (or vice versa).
Instead make a function in Top using both the circle and rect and act on that.
I don't know exactly what you want to do, but suppose you want to set the sizes equally. Than make e.g. a SetSize method in Top setting the sizes for both the circle and rectangle:
e.g.
class Top{
public:
Rect myRect;
Circle myCircle;
void Run();
void SetSize(int size)
}
where SetSize is implemented as:
myRect.SetSize(size);
myCircle.size = size;
It's always better to put code handling multiple derived objects in the base class instead of in one of the children. If there will be too much (or unrelated) functionality in the base class, create a different class (like SizeHandler).
btw, it's better not to use public properties but always get/set methods.

Making custom types drawable with SFML

I picked up using SFML recently, and I decided as a learning experience I would make a pong clone using it. I've defined a class called Ball which draws uses SFML to draw a RectangleShape. When I try to draw this custom type to the screen with the window.draw() function however, I get errors because Ball isn't an sf::Drawable. I would appreciate help with this, being new to SFML.
To use window.draw(object) object's class must inherit the drawable interface and implement the abstract sf::Drawable::draw function.
It sounds like the sf::RectangleShape is a member of Ball. SFML knows how to render the shape, but not Ball itself. Ball's class declaration should look like this:
class Ball : public sf::Drawable //,...
{
//...
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const;
//...
};
And draw should be implemented like this:
void Ball::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
//assuming m_shape is the sf::RectangleShape
target.draw(m_shape, states);
}

SFML texture displaying as a white box

I have a texture and sprite in a base class that is being extended by another class, however when drawn, the sprite displays as a white box. I know this is something to do with the sprite losing it's link to the texture object, but I'm kind of new to C++, so I'm not really sure how it happened.
Here is the code (I've removed some of the irrelevant parts to cut down the size):
Pickup.h:
#ifndef PICKUPS_PICKUP_H
#define PICKUPS_PICKUP_H
#include <SFML\Graphics.hpp>
#include "..\Player.h"
namespace Pickups
{
class Pickup
{
private:
sf::Vector2f position;
sf::Texture texture;
sf::Sprite sprite;
public:
Pickup();
bool drop(float dt);
void draw(sf::RenderWindow* window);
void setTexture(sf::String name);
void setPos(sf::Vector2f position);
sf::Vector2f getPos();
void isColliding(Player* player);
virtual void onCollect(Player* player) = 0;
};
}
#endif
pickup.cpp:
#include "Pickup.h"
namespace Pickups
{
Pickup::Pickup()
{
}
void Pickup::draw(sf::RenderWindow* window)
{
window->draw(sprite);
}
void Pickup::setTexture(sf::String name)
{
if (!texture.loadFromFile("images/pickups/" + name + ".png"))
std::cout << "Error loading image: images/pickups/" + name.toAnsiString() + ".png" << std::endl;
else
sprite.setTexture(texture);
}
}
Health.h:
#ifndef PICKUPS_HEALTH_H
#define PICKUPS_HEALTH_H
#include "Pickup.h"
namespace Pickups
{
class Health : public Pickup
{
private:
int worth;
public:
Health(sf::Vector2f position, int worth);
void onCollect(Player* player);
};
}
#endif
health.cpp:
#include "Health.h"
namespace Pickups
{
Health::Health(sf::Vector2f position, int worth)
{
setTexture("health");
setPos(position);
this->worth = worth;
}
void Health::onCollect(Player* player)
{
player->addLives(worth);
}
}
(I don't know if this is part of the problem, but I might as well post it too)
I store the pickups in a vector like so:
std::vector<Pickups::Health> pickups;
A std::vector copies or moves the inserted elements, so as long as you have the default copy constructor or as long as you do not change this dirty a texture per element-style, (the elements just need to have one common texture object to actually point to, so you waste much much memory) the pointer that the sf::Sprite object holds to the texture gets invalid. To see why we need to think whats happens on insertion:
You setup a nice Pickupish object and add it to the vector which calls the copy-constructor. Lets say your nice object that you wanted to add is object A and the now added/copied object is B. Both have a sprite S and a texture T. Both textures are valid, but the problem is this: A's S points to A's T, so after copying it to B B's S points also to A's T! As I assume A is just temporary so it gets destructed, together with its texture, and there you have it, a nice white box.
You can solve this in some other dirty ways like making your own copy-constructor in Pickup like this:
Pickup::Pickup(const Pickup& other)
: position(other.position), texture(other.texture), sprite(other.sprite)
{ sprite.setTexture(texture); }
or by storing std::unique_ptr<Pickups::Health>'s and not just Pickups::Health's.
However a much better way you should use is some kind of Resourcemanager, which just stores all relevant textures, ideally one, a big tileset, because loading once but big is faster than loading multiple but small textures. You can write your own very simple manager or use some other e.g. the one from the great Thor library. To set a specific tile as texture for a Sprite just call sf::Sprite::setTextureRect.
I want to mention some additional improvements to your design. Let Pickup derive from sf::Drawable and define its pure virtual draw function, which you can make private. Thus your from Pickup deriving object doesn't need to know from any sf::RenderTarget and you can just do target.draw(myPickupObject).
There is no need to store the position, just let Pickup derive from sf::Transformable, too. You don't have to implement any functions, the only thing you need to do is applying the matrix to the sf::RenderStates object thats passed to draw.
Overall your draw function might look like this:
void Pickup::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
//'applying' transformation-matrix
states.transform *= getTransform();
target.draw(sprite, states);
}
So your Pickup has now only sf::Sprite as member and overall your header just needs to include SFML/Graphics/Sprite.hpp.
For avoid this type of problem I always declare my Texture as a pointer and deleting it in the destructor of the class. Like this your Texture will always exist whenever your object is not destroyed.
And it's always good to verify the loading of the image :
if (!texture.loadFromFile("images/pickups/health.png")){
//some error code
}
But it's not the problem here.

Informing GUI objects about screen size - Designing

I have a problem with designing classes for my game which I create.
In my app, there is:
class CGame which contains all the information about game itself,
e.g. screen width, screen height, etc. In the main() function I
create a pointer to CGame instance.
class CGUIObject which includes fields specifying it's position and
draw() method, which should know how to draw an object according to
screen size.
class CGUIManager which is a singleton and it includes a list of
CGUIObject's. For each object in a list it just calls draw()
method.
For clarity's sake, I'll put some simple code:
class CGame
{
int screenWidth;
int screenHeight;
};
class CGUIObject
{
CPoint position;
void draw(); // this one needs to know what is a screen's width and height
};
class CGUIManager // it's a singleton
{
vector<CGUIObject*> guiObjects;
void drawObjects();
};
And the main.cpp:
CGame* g;
int main()
{
g = new CGame();
while(1)
{
CGUIManager::Instance().drawObjects();
}
return 0;
}
Now the problem is, that each CGUIObject needs to know the screen size which is held by CGame, but I find it very dumb to include pointer to CGame instance in every object.
Could anyone, please, tell me what would be the best approach to achieve this?
Is there a reason that you are needing your screen resolution in your CGUIObject's?
They have a position already, so if you have them draw themselves in local space, you can apply a transform to them in your CGUIManager in order to lay them out. You abstract your layout from the GUI objects themselves this way, and the objects don't need to know which container they are living in (the screen, a window, a tab etc).