I'm continuing with a game for an embedded microcontroller (Arduino), and I have a question on class interaction -- this question continues from my previous question here and I have based my code on the suggestion of sheddenizen (see response to the given link in 'here'):
I have three classes that inherit from a base class-
(i) class Sprite - (bass class) has a bitmap shape and (x,y) position on an LCD
(ii) class Missile : public Sprite - has a specific shape, (x,y) and also takes a obj
(iii) class Alien : public Sprite - has specific shape and (x,y)
(iv) class Player : public Sprite - ""
They all have different (virtual) method of moving and are shown on the LCD:
My streamlined code is below - specifically, I only want the missile to fire under certain conditions: when missile is created it takes an objects (x,y) value, how can I access a passed objects value within an inherited class?
// Bass class - has a form/shape, x and y position
// also has a method of moving, though its not defined what this is
class Sprite
{
public:
Sprite(unsigned char * const spacePtrIn, unsigned int xInit, unsigned int yInit);
virtual void Move() = 0;
void Render() { display.drawBitmap(x,y, spacePtr, 5, 6, BLACK); }
unsigned int X() const { return x; }
unsigned int Y() const { return y; }
protected:
unsigned char *spacePtr;
unsigned int x, y;
};
// Sprite constructor
Sprite::Sprite(unsigned char * const spacePtrIn, unsigned int xInit, unsigned int yInit)
{
x = xInit;
y = yInit;
spacePtr = spacePtrIn;
}
/*****************************************************************************************/
// Derived class "Missile", also a sprite and has a specific form/shape, and specific (x,y) derived from input sprite
// also has a simple way of moving
class Missile : public Sprite
{
public:
Missile(Sprite const &launchPoint): Sprite(&spaceMissile[0], launchPoint.X(), launchPoint.Y()) {}
virtual void Move();
};
void Missile::Move()
{
// Here - how to access launchPoint.X() and launchPoint.Y() to check for
// "fire conditions"
y++;
Render();
}
// create objects
Player HERO;
Alien MONSTER;
Missile FIRE(MONSTER);
// moving objects
HERO.Move();
MONSTER.Move();
FIRE.Move();
Since Missile is a subclass of Sprite you can access Sprite::x and Sprite::y as if they were members of Missile. That is by simply writing x (or this->x if you insist).
The launchpoint reference that you got in the constructor is gone by now, so your Missile::Move memfunction cannot access it any more.
If in the meantime the members x and y changed, but you want the original value, you can either save a reference to launchpoint (which might be dangerous, it is destroyed) or you have to keep a copy of the original coordinates.
Related
(sorry for my bad english)
I have a base class with vector of pointers on Drawable objects in it and method draw() that uses data from this vector.
class GameObject
{
protected:
std::vector<Drawable*> drawable;
...
void GameObject::draw() { for (const auto& object : drawable) window.draw(*object); }
In the derived classes I want to have an ability to add some Drawable objects
class Player : public GameObject
{
protected:
RectangleShape shape;
...
Player::Player(float x, float y, float z)
{
shape.setPosition [...]
drawable.push_back(&shape);
...
and draw them using method of base class pointer
std::vector<GameObject*> gameObjects;
...
for (auto& gameObject : gameObjects) gameObject->draw();
The program crashes (I think because the base class don't know anything about vector data in derived class).
I understand that I could make this method pure virtual and define it in the derived classes, but it's not that convenient. Maybe there is another way more similar to this?
upd:
Level::Level()
{
player = Player(500.f, 500.f); //Player player; in header file
gameObjects.push_back(&player);
}
void Level::display()
{
for (auto gameObject : gameObjects) gameObject->draw();
}
The problem is in the code added by your edit -- it looks like my crystal ball is working today.
You're creating a temporary Player and moving it into the player member variable. That ends up with a vector holding the address of the shape inside the temporary Player, which is immediately destroyed, leaving a dangling pointer.
Use a ctor-initializer-list to avoid the move:
Level::Level()
: player(500.f, 500.f /* where did Z go? */)
{
gameObjects.push_back(&player);
}
And disable the assignment operators to prevent doing this by accident in other places:
class Player
{
// ...
Player& operator=(const Player&) = delete;
Player& operator=(Player&&) = delete;
};
Should you use accessors on complex data types? Here is an example:
Say I've got a Player class and inside it theres a Sprite object. Let's say the Sprite object has a setPosition() accessor method. Now let's say I want to call that method through the player, so I create an accessor to the Sprite object in the Player.
class Player
{
public:
const Sprite& getSprite() const;
private:
Sprite sprite;
}
Now let's say I create a 'Player' object, how do I call setPosition on the sprite? I can't just say playerObject.getSprite().setPosition(); because the Sprite reference the getSprite() method returns is const.
Should I just make the sprite object public in this situation considering the sprite object has protection against all it's members? Or is that a bad practice and I do need to use accessors?
There is no good answer:
Having a 2D point:
class Point {
public:
// Skipping constructors here.
int x() const { return m_x; }
int x&() { return m_x; }
int y() const { return m_y; }
int y&() { return m_y; }
private:
int m_x;
int m_y;
};
looks like an overkill compared to
struct Point {
int x;
int y;
};
The first allows you to change the internal data (lets say) to a std::vector and the second data structure allows no changes. Even the first is cumbersome, I prefer that (but it is a matter of taste)
C++ (Arduino wrapper) question: This is related to this thread on double pointer member access regarding a shoot em up game.
I have a base class (Sprite), and from this other classes are derived - Alien and Player. For collision detection I want to pass pointers to Alien and Player, then access coordinates as in:
void Collision( Alien *pAlien, Player *pPlayer )
{
// obtain alien and player's (x,y) and do collision check here
pAlien->getX();
pPlayer->getX();
}
But after declaring the above function after the class I get this:
error: variable or field 'Collision' declared void
My question is: How can I resolve this? streamlined code and a screen shot are shown below.
/*****************************************************************************************/
// Bass class - has a form/shape, x and y position also has a method of moving
class Sprite
{
public:
Sprite(unsigned char * const spacePtrIn, unsigned int xInit, unsigned int yInit);
protected:
};
/*****************************************************************************************/
// Derived class "Alien" - has a specific form/shape, and specific (x,y) position
class Alien : public Sprite
{
public:
Alien();
virtual void Move();
};
/*****************************************************************************************/
// Derived class "Player" - has a specific form/shape, and specific (x,y) position
class Player : public Sprite
{
public:
Player(): Sprite(&spaceShip[0], xPlayer(), yPlayer()) {}
virtual void Move();
};
/*****************************************************************************************/
/*****************************************************************************************/
void Collision( Alien *pAlien, Player *pPlayer )
{
// obtain alien and player's (x,y) and do collision check here
pAlien->getX();
pPlayer->getX();
}
/*****************************************************************************************/
/*****************************************************************************************/
I have a problem getting the AI to work if I do it in a subclass.
Here is my main loop in which I access the player and enemy classes for their move, logic, and show functions.
//Logic
myPlayer.player_move();
myEnemy.enemy_logic();
//Rendering
myPlayer.player_show();
myEnemy.enemy_show();
So I access the Player class's function that moves the player then I access the logic(AI) function of the Enemy Class.
void Enemy::enemy_move(){
eX -= 2;
}
void Enemy::enemy_logic(){
Player myPlayer;
if(myPlayer.x > SCREEN_WIDTH / 2){
enemy_move();
}
}
So if the Players x value is greater than half the screen, I want the enemy to start moving left
I can compile the program but when I move the player past the halfway point of the screen, the enemy does nothing. If I were to put this if statement in the main.cpp then it works fine. Any advice?
It's because you're re-initializing your instance of the class Player. Instead, pass it as a parameter:
void Enemy::enemy_logic(Player myPlayer){
if(myPlayer.x > SCREEN_WIDTH / 2){
enemy_move();
}
}
That way the function uses your instance, and not a new instance in a different scope.
Because you are initializing an instance of Player as myPlayer that is not connected to anything.
You should define a private variable for myPlayer inside your Enemy Class or pass a pointer to the Player class instance as an argument, in this way you will be able to maintain the state.
If your enemies depend on a single player you can "bind" each enmy to a player like this:
class Player
{
Position m_x, m_y, m_z;
public:
Position const & x (void) { return m_x; }
};
class Enemy
{
public:
Enemy (Player &p) : m_player(p) { }
Player & m_player;
void logic (void)
{
if (m_player.x() > SCREEN_WIDTH/2)
{
move_x();
}
}
void move_x (void) { /*...*/ }
};
int main (void)
{
Player the_player;
Enemy the_enemy(the_player);
// move the player
the_enemy.logic(); // the_enemy knows the_player and acts accordingly
}
How can I refractor the following, to move my drawing functions from the h-file into a GraphicsManager class?
//drawingFunctions.h
void drawTexturedQuad( Texture texture, Vector2 pos, Vector2 dim) {
// bind texture...
glBegin(...); // draw
//...
glEnd(...);
}
//class file
#include "drawingFunctions.h"
class Player {
void drawPlayer(){ drawTexturedQuad( texture, pos, dim) }
};
class Enemy {
void drawEnemy(){ drawTexturedQuad( texture, pos, dim) }
};
class Item {
void drawItem(){ drawTexturedQuad( texture, pos, dim) }
};
// and so on for the other components
//gameloop file
// instantiate components objects
while (true) {
// input, logic
Player.drawPlayer();
Enemy.drawEnemy();
Item.drawItem();
// and so on
}
(The code is obviously simplified, I am just asking about the drawing here)
Should I...
pass a pointer to the GraphicsManager to every call of drawPlayer, drawEnemy etc from within the gameloop
have Player, Enemy etc have a pointer to GraphicsManger as a data member
have Player, Enemy etc extend a drawableGameComponent class that has a pointer to GraphicsManager as a data member
something else?
That sounds like a perfect use case for inheritance:
class Drawable
{
public:
void draw()
{
// gl stuff
}
protected:
Texture _texture;
Vector2 _pos;
Vector2 _dim;
};
class Player : Drawable
{
public:
// should modify _texture _pos and _dim somewhere.
};
// same thing for the other objects.
I would pass a renderer to the model, and ask it to draw itself.
class Player
{
public:
void draw(Renderer& renderer);
};
class Enemy
{
public:
void draw(Renderer& renderer);
};
Note you don't have to name the function drawPlayer or drawEnemy, because you already know that it's a Player or an Enemy by the class type. This uniform calling convention is perfect for extracting into a common interface:
class Model
{
public:
virtual void draw(Renderer& renderer) = 0;
virtual ~Model() {}
};
Then you can have each of your models inherit from Model, and each implement draw.
As I mentioned in a comment on #J.N.'s answer you can also have Renderer be an abstract class. For example I worked on a project which used OpenGL, GDI+, and also needed to create printouts of schematics.
class Renderer
{
public:
virtual render(const Triangle& triangle, const Texture& texture) = 0;
virtual ~Renderer() {}
};
I would go for the first possibility: passing the pointer to the GraphicsManager in the call. Eventhough it seems a bit overkill, the knowledge of which GraphicsManager is used is maintained on higher level and can be modified easier later on.
Having said this, I would still inherit from a Drawable interface and put the items that need to be drawn in a container, so you can just iterate through it to display the items via a virtual drawItem() function.
Like so (C++03, not tested):
std::vector<Drawable*> Items;
Items.push_back(&player);
Items.push_back(&enemy);
...
for (std::vector<Drawable*>::iterator it = Items.begin(); it != Items.end(): ++it)
{
(*it)->drawItem(&graphMgr);
}