So, again, making a pong clone, and run into a bump. I'm creating the game class gamestate for my state machine and in the definition, I instantiate two classes of the class I've created for the the different player paddles and I give them two different arguments that will decide if the class becomes the player one or player two object.
But it seems like the program won't allow me to pass an argument in the class definition. I've tried to put the class instantiation in the game class constructor but then the events/logic/render functions don't have access to the paddle classes. That leads me to think it has to be in the game class definition. Here's the code presenting the problem.
class Ball
{
public:
void events();
void logic();
void render();
Ball(int server);
~Ball();
};
class Paddle
{
private:
SDL_Rect Paddle_Bound;
int yVel;
SDL_Rect *clip;
int player_key;
public:
void events();
void logic();
void render();
Paddle(int player);
~Paddle();
};
class Game : public GameState
{
private:
int server;
TTF_Font *score = NULL;
Paddle PlayerOne(1);
Paddle Playertwo(2);
Ball Ball(2);
public:
void events();
void logic();
void render();
Game();
~Game();
};
The lines that say
Paddle PlayerOne(1);
Paddle Playertwo(2);
Ball Ball(2);
Are the ones raising errors that say "expected a type specifier". Now I know that means it wants something like int player but I need to pass a number so that the constructor can decide which player it'll be, which ultimately decides what keys move the class up and down the screen. So to be perfectly clear, I want to be able to pass an argument to a class instantiation so that the class constructor can turn the class into the left OR the right paddle. So how do I go about instantiating a class, inside another class, using arguments?
Thanks!
EDIT: For the constructor problem I have another example that is fully coded where I can't put the class initializer in the constructor. So basically in the title screen there is one button, and it has properties stored in a Button class that I create to manage buttons.
class GameState
{
public:
virtual void events() = 0;
virtual void logic() = 0;
virtual void render() = 0;
virtual ~GameState(){};
};
class Button
{
public:
SDL_Rect button_clip[2];
SDL_Rect button;
SDL_Surface *button_sprite = NULL;
Button();
};
class Title : public GameState
{
private:
SDL_Surface *Title_Message = NULL;
SDL_Rect *clip;
// THIS IS WHERE I HAVE TO PLACE THIS LINE
//Button Title_Button;
public:
void events();
void logic();
void render();
Title();
~Title();
};
Title::Title()
{
//THIS IS WHERE IT SHOULD BE ACCORDING TO WHAT YOU'RE TELLING ME
Button Title_Message;
//text/sprites
Title_Message = TTF_RenderText_Solid(font, "PONG", color);
Title_Button.button_sprite = load_image("Start.png");
//region for button
Title_Button.button.x = 200;
Title_Button.button.y = 350;
Title_Button.button.w = 100;
Title_Button.button.h = 50;
//clips not hover
Title_Button.button_clip[0].x = 0;
Title_Button.button_clip[0].y = 0;
Title_Button.button_clip[0].w = 100;
Title_Button.button_clip[0].h = 50;
//clips hover
Title_Button.button_clip[1].x = 0;
Title_Button.button_clip[1].y = 50;
Title_Button.button_clip[1].w = 100;
Title_Button.button_clip[1].h = 50;
}
Title::~Title()
{
SDL_FreeSurface(Title_Message);
SDL_FreeSurface(Title_Button.button_sprite);
}
void Title::events()
{
int x = 0;
int y = 0;
while (SDL_PollEvent(&event))
{
if (event.type == SDL_MOUSEMOTION)
{
x = event.motion.x;
y = event.motion.y;
if ((x > Title_Button.button.x) && (x < (Title_Button.button.x + Title_Button.button.w)) && (y > Title_Button.button.y) && (y < (Title_Button.button.y + Title_Button.button.h)))
{
clip = &Title_Button.button_clip[1];
}
else
{
clip = &Title_Button.button_clip[0];
}
}
if (event.type == SDL_QUIT)
{
quit = true;
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (event.button.button == SDL_BUTTON_LEFT)
{
if ((x > Title_Button.button.x) && (x < (Title_Button.button.x + Title_Button.button.w)) && (y > Title_Button.button.y) && (y < (Title_Button.button.y + Title_Button.button.h)))
{
set_next_state(GAME);
}
}
}
}
}
void Title::logic()
{
}
void Title::render()
{
apply_surface(Title_Button.button.x, Title_Button.button.y, Title_Button.button_sprite, screen, clip);
apply_surface((SCREEN_WIDTH - Title_Message->w) / 2, 100, Title_Message, screen);
}
The problem here is that the events/logic/render functions have no clue that Title_Message exists when I put it in the void Title::Title() constructor. But when I put it in the class definition, I can't pass an argument through it.
The values should be initialized in the constructor definiton: not in the class definition.
class Game : public GameState
{
private:
int server;
TTF_Font *score;
Paddle PlayerOne;
Paddle Playertwo;
Ball ball; // same class name and variable name won't work
public:
void events();
void logic();
void render();
Game();
~Game();
};
...
Game::Game()
: score(NULL)
, PlayerOne(1)
, PlayerTwo(2)
, ball(2)
{
...
}
EDIT: For the Title message stuff
class Title : public GameState
{
private:
SDL_Surface *Title_Message = NULL;
SDL_Rect *clip;
// THIS IS WHERE I HAVE TO PLACE THIS LINE
Button Title_Button;
Button Title_Message;
public:
void events();
void logic();
void render();
Title();
~Title();
};
Title::Title()
{
//text/sprites
Title_Message = TTF_RenderText_Solid(font, "PONG", color);
Title_Button.button_sprite = load_image("Start.png");
You can't do that outside of a method. Do that in the constructor
class Game : public GameState
{
private:
int server;
TTF_Font *score;
Paddle PlayerOne(1);
Paddle Playertwo(2);
Ball ballObject(2);
public:
void events();
void logic();
void render();
Game(): PlayerOne(1), Playertwo(2), ballObject(2){};
~Game();
};
Edit : An answer has already been posted, I apologize for posting the same answer. I am new at using the interface. So it took time to figure out the code block generation.
Edit 2: Repsonding to your edit.
You have to declare the object in the class itself. It's just you can't instantiate it in the declaration. You need to study about how c++ classes and classes in general work.
class class1
{
private:
class class2Obj(args);
public:
class1();
~class1();
}
The above example is wrong. To make this work you should do it like this
class class1
{
private:
class2 class2Obj;
public:
class1();
~class1();
}
class1::class1()
{
class2Obj(args);
}
Related
I took the design of a game from one person and now I have a problem.
Below there is an implementation of the class Base, in which all the initial parameters of the window are defined, and all its fields are brought into the global scope.
Base.h :
#pragma once
#include "Define.h"
#include "Audio.h"
#include "Font.h"
#include "Texture.h"
#include "GameObject.h"
class Base {
public:
static sf::RenderWindow wnd;
static Texture texture;
static Font font;
static Audio audio;
static sf::Event event;
static int scr_w;
static int scr_h;
static v2f cur_p;
static v2f cur_p_wnd;
static vector<unique_ptr<GameObject>> objects;
static float time;
static void SystemUpdate() {
time = float(clock.getElapsedTime().asMicroseconds()) / 1000.f, clock.restart();
cur_p = wnd.mapPixelToCoords(sf::Mouse::getPosition(wnd));
cur_p_wnd = v2f(sf::Mouse::getPosition(wnd));
}
static void CloseEvent() {
if (event.type == sf::Event::Closed || (event.type == sf::Event::KeyPressed && event.key.code == Keyboard::Escape)) wnd.close();
}
static bool IsKeyPressed(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyPressed)
if (event.key.code == code) return true;
return false;
}
static bool IsKeyReleased(const sf::Keyboard::Key& code) {
if (event.type == sf::Event::KeyReleased)
if (event.key.code == code) return true;
return false;
}
template <class T>
static void new_object(T* obj) {
B::objects.push_back(unique_ptr<GameObject>(obj));
}
template <class T>
static void delete_object(T* obj) {
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
if ((*itr)->ID == obj->ID) {
B::objects.erase(itr);
return;
}
}
}
static bool IsMouseInRect(Shape& s) {
return s.getGlobalBounds().contains(B::wnd.mapPixelToCoords(sf::Mouse::getPosition(B::wnd)));
}
Base(string init) {
if (init == "init") {
scr_w = sf::VideoMode::getDesktopMode().width;
scr_h = sf::VideoMode::getDesktopMode().height;
font = Font();
texture = Texture();
audio = Audio();
wnd.create(sf::VideoMode(scr_w, scr_h), "Sea Battle", sf::Style::Close, sf::ContextSettings(0, 0, 8));
cur_p = v2f(0, 0);
cur_p_wnd = v2f(0, 0);
wnd.setMouseCursorVisible(true);
wnd.setFramerateLimit(30);
srand(::time(0));
}
}
Base(void) {}
private:
static sf::Clock clock;
};
sf::RenderWindow B::wnd;
Texture B::texture;
Font B::font;
Audio B::audio;
sf::Event B::event;
int B::scr_w;
int B::scr_h;
v2f B::cur_p;
v2f B::cur_p_wnd;
float B::time;
sf::Clock B::clock;
vector<unique_ptr<GameObject>> B::objects;
It is included to the class Game, in which its constructor is called.
Game.cpp :
#include "Game.h"
Game::Game() {
B("init");
game_state = GameState::Planning;
GameObject* background = new GameObject(v2f(5000, 5000));
background->getRect().setFillColor(Color(255, 255, 255));
new_object(background);
new_object(new Field());
}
void Game::Update() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Update();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
UpdateUI();
}
void Game::Action() {
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Action();
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
ActionUI();
}
void Game::Draw() {
wnd.clear();
switch (game_state) {
case GameState::MainMenu:
break;
case GameState::Planning:
for (auto itr = B::objects.begin(); itr != B::objects.end(); ++itr) {
(*itr)->Draw(wnd);
}
break;
case GameState::Settings:
//
break;
case GameState::Game:
//
break;
default: break;
}
DrawUI();
wnd.display();
}
void Game::UpdateUI() {}
void Game::ActionUI() {}
void Game::DrawUI() {}
void Game::Play() {
while (wnd.isOpen()) {
SystemUpdate();
Update();
while (wnd.pollEvent(event)) {
CloseEvent();
Action();
}
Draw();
}
}
Game::~Game(){}
int main() {
Game game;
game.Play();
return 0;
}
The main game loop is in the class Game, and this class must call the Draw, Action, Update methods for each object contained in the object vector.(objects vector is in the Base.h)
But if I create any new object class and try to implement its Draw, Action, Update methods in it, then I will not be able to use in them all the variables in Base.h, brought to the global scope, and without them I cannot properly implement my objects, how can I change the architecture so that everything works?
///
For example, if I want the position of my object to change to the current position of the cursor, then I need a variable from Base.h, which I will refer to as B::cur_p, but the compiler simply gives an error: "unknown authenticator".
The answer to globals is NO globals (just like optimization, only use them if you know its worth the cost). It is bad for maintainability and bad for testing! (and in large projects bad for component dependency and bad for build times) If you want to learn about architecture, now is a good time te read up on dependency injection. e.g. https://en.wikipedia.org/wiki/Dependency_injection
Here is an example in code for your game :
#include <vector>
//-----------------------------------------------------------------------------
// just some structs to be able to use names from your Game to recognize.
struct RenderWindow {};
struct Texture {};
struct Font {};
struct Audio {};
struct GameObject
{
void update() {};
};
// etc
//-----------------------------------------------------------------------------
// global_data_itf.h
// model of a screen, those values belong together
struct screen_size_t
{
unsigned int width{ 3440 };
unsigned int height{ 1440 };
};
// Now define an interface for accessing your data.
// This also allows you
// to create mocks (e.g. with different screen sizes) for testing
// not something you can do with static or real global variables
//
// Note you should really make functions const and return const&
// if data is readonly!
//
class global_data_itf
{
public:
virtual RenderWindow& render_window() = 0;
virtual Texture& texture() = 0;
virtual Font& font() = 0;
virtual Audio& audio() = 0;
virtual const screen_size_t& screen_size() = 0;
// unique_ptr probably not needed. Give GameObject a move constructor
// and emplace gameobjects
virtual std::vector<GameObject>& objects() = 0;
virtual float& time() = 0;
virtual ~global_data_itf() = default;
protected:
global_data_itf() = default;
};
//-----------------------------------------------------------------------------
// global_data.h
// #include "global_data_itf.h"
// For the game you need a default implementation of the global_data_itf
// this is it.
class global_data final :
public global_data_itf
{
// todo constructor with proper initialization
virtual RenderWindow& render_window() override
{
return m_render_window;
}
virtual Texture& texture() override
{
return m_texture;
}
virtual Font& font() override
{
return m_font;
}
virtual Audio& audio() override
{
return m_audio;
}
virtual const screen_size_t& screen_size() override
{
return m_screen_size;
}
virtual std::vector<GameObject>& objects() override
{
return m_objects;
}
virtual float& time() override
{
return m_time;
}
private:
RenderWindow m_render_window;
Texture m_texture;
Font m_font;
Audio m_audio;
screen_size_t m_screen_size;
std::vector<GameObject> m_objects;
float m_time;
};
//-----------------------------------------------------------------------------
// Game.h
// #include "global_data_itf.h"
class Game
{
public:
explicit Game(global_data_itf& data);
void Update();
private:
global_data_itf& m_data;
};
//-----------------------------------------------------------------------------
// Game.cpp
// include "game.h"
Game::Game(global_data_itf& data) :
m_data{ data }
{
}
void Game::Update()
{
for (auto& object : m_data.objects())
{
object.update();
}
}
//-----------------------------------------------------------------------------
// main.cpp
// #include "global_data.h"
// #include "game.h"
int main()
{
global_data data;
Game game(data); // inject the dependency on data into game.
// if you have other classes needing access to global data
// you can inject global_data into those classes as well.
return 0;
}
Well at first, if you want to access your variables from wherever, you can make them real global variables, put them in a header, etc. If I got you right, this fixes the whole problem as variables and methods go separately.
In a pawn class that inherits from the cocos2d Sprite class, I used this->getBoundingBox() in it's update function. This caused an "Access violation at reading location" error. Then, I swapped "this" with "GAME::PLAYER", a variable in a namespace that references the player and it worked. Why does this->getBoundingBox() cause an error when GAME::PLAYER->getBoundingBox() works perfectly fine? Aren't they supposed to be the same thing? Just to note, "this->" works with any other function but getBoundingBox. Is it something I'm doing wrong? I'm not THAT good with C++
Here's pawn.h
#include <cocos2d.h>
#ifndef PLAYER_CONTROLLER
#define PLAYER_CONTROLLER GAME::PLAYER
class pawn : public cocos2d::Sprite {
public:
pawn();
~pawn();
static pawn* create();
static pawn* create(bool default_moving);
bool moving;
bool right;
int speed;
cocos2d::Rect getBounds();
void step();
void initOptions();
void update(float dt) override;
void move(cocos2d::Vec2 vec);
void moveX(int x);
void moveY(int y);
virtual bool touchBegan(cocos2d::Touch*, cocos2d::Event*);
virtual void touchEnded(cocos2d::Touch*, cocos2d::Event*);
};
namespace GAME {
static pawn* PLAYER;
};
#endif
Here's pawn.cpp
#include "player.h"
#include <cocos2d.h>
pawn::pawn() {
}
pawn::~pawn() {
}
bool pawn::touchBegan(cocos2d::Touch* touch, cocos2d::Event* event) {
this->move(cocos2d::Vec2(5, 0));
this->moving = false;
return true;
}
void pawn::touchEnded(cocos2d::Touch* touch, cocos2d::Event* event) {
this->moving = true;
}
void pawn::step() {
if (this->moving) {
if (this->right) {
this->move(cocos2d::Vec2(this->speed, 0));
}
else {
this->move(cocos2d::Vec2(-this->speed, 0));
}
if (this->getPositionX() < 0) {
this->right = true;
CCLOG("Going right V4");
}
else {
if (this->getPositionX() + this->getContentSize().width > cocos2d::Director::getInstance()->getWinSizeInPixels().width + cocos2d::Director::getInstance()->getVisibleOrigin().x){
this->right = false;
CCLOG("Going left V4");
}
}
}
}
void pawn::move(cocos2d::Vec2 vec) {
PLAYER_CONTROLLER->setPosition(cocos2d::Vec2(PLAYER_CONTROLLER->getPositionX() + vec.x, PLAYER_CONTROLLER->getPositionY() + vec.y));
}
void pawn::moveX(int x) {
}
void pawn::moveY(int y) {
}
void pawn::update(float dt) {
//cocos2d::Rect act = this->getBoundingBox();
this->getPosition();
this->step();
}
cocos2d::Rect pawn::getBounds() {
if (!PLAYER_CONTROLLER) {
CCLOG("Is this the problem?");
}
return PLAYER_CONTROLLER->getBoundingBox();
}
pawn* pawn::create() {
auto character = new pawn();
character->moving = true;
character->right = false;
character->speed = 5;
character->setPositionY(50);
if (PLAYER_CONTROLLER == NULL) {
CCLOG("There is no player, yet.");
CCLOG("Adding player");
PLAYER_CONTROLLER = character;
}
else {
CCLOG("There's already a player");
return NULL;
}
//character->setPositionX(40);
if (character->initWithFile("Base.jpg")){
return character;
}
CC_SAFE_DELETE(character);
return NULL;
}
pawn* pawn::create(bool default_moving) {
pawn* character = new pawn();
character->moving = default_moving;
character->setPositionX(40);
if (character->initWithFile("Base.jpg")){
return character;
}
CC_SAFE_DELETE(character);
return NULL;
}
Is it maybe because I call a pawn method from another class? I use a Collider class to call functions in pawn
Collider.cpp
#include "Collider.h"
#include "player.h"
Collider::Collider() : CollideMode(OVERLAP) {
}
Collider::~Collider() {
}
Collider* Collider::create() {
Collider* col = new Collider;
if (col->initWithFile("Base.jpg")){
col->setAnchorPoint(cocos2d::Vec2(0, 0));
col->setContentSize(cocos2d::Size(100, 100));
return col;
}
CC_SAFE_DELETE(col);
return NULL;
}
void Collider::collision(cocos2d::Vec2 intersect) {
CCLOG("IT IS COLLIDING");
if (intersect.x < intersect.y) {
PLAYER_CONTROLLER->move(cocos2d::Vec2(-intersect.x, 0));
CCLOG("X");
}
else if (intersect.x > intersect.y) {
PLAYER_CONTROLLER->move(cocos2d::Vec2(0, -intersect.y));
CCLOG("Y");
}
}
void Collider::update(float dt) {
//cocos2d::Rect col = this->getBoundingBox();
auto act = PLAYER_CONTROLLER->getBounds();
if (PLAYER_CONTROLLER) {
if (!PLAYER_CONTROLLER) {
CCLOG("There is no player?");
}
}
else {
CCLOG("Not colliding");
}
}
I don't seems any problem with this->getBoundingBox() inside update(float dt) function.
I've created small test :
declaration inside .h file
class MySprite: public Sprite {
public:
bool init() override;
void update(float) override;
CREATE_FUNC(MySprite);
};
Now method definition inside .cpp file
bool MySprite::init(){
if(!Sprite::init())
return false;
scheduleUpdate();
return true;
}
void MySprite::update(float dt){
auto rect=this->getBoundingBox();
CCLOG("Inside Update method of MySprite Bounding rect Width %f & Height %f",rect.size.width,rect.size.height);
}
Then I created an autoreleased object of MySprite and add to parent.
auto mysprite=MySprite::create();
mysprite->setContentSize(Size(10,10));
addChild(mysprite);
Run, Expected result on output console.
I see my mistake. It was the fact that I redefined the namespace "GAME" and it's variable, "GAME::PLAYER" every time I included pawn.h, the other source files that I called the pawn functions from didn't know what GAME::PLAYER or PLAYER_CONTROLLER ( just a macro for GAME::PLAYER ) was, as I had only defined PLAYER_CONTROLLER in pawn.cpp. That's why when you called PLAYER_CONTROLLER->method() in another file, it passed in NULL as "this", and also why PLAYER_CONTROLLER was referring to a different PLAYER_CONTROLLER than the one passed in as "this".
I solved it by using the extern keyword that makes the variables global between all files, which was my original intention.
pawn.h
#include <cocos2d.h>
#ifndef PLAYER_CONTROLLER
#define PLAYER_CONTROLLER GAME::PLAYER
#define INITIALIZE_PLAYER pawn* GAME::PLAYER = NULL
class pawn : public cocos2d::Sprite {
public:
pawn();
~pawn();
static pawn* create();
static pawn* getController();
static pawn* create(bool default_moving);
bool moving;
bool right;
int speed;
cocos2d::Rect getBounds();
void step();
void initOptions();
void update(float dt) override;
void move(cocos2d::Vec2 vec);
void moveX(int x);
void moveY(int y);
virtual bool touchBegan(cocos2d::Touch*, cocos2d::Event*);
virtual void touchEnded(cocos2d::Touch*, cocos2d::Event*);
};
namespace GAME {
extern pawn* PLAYER;
};
#endif
This is why I said I wasn't that good at C++.
I have a parent Menu class and children MainMenu, SettingsMenu, PauseMenu, etc.
I want to be able to hold them all in a vector...
I can do
std::vector<Menu*> myVector;
and then typecast each one when I push_back in the vector
pMainMenu = new MainMenu;
myVector.push_back((Menu*)pMainMenu);
and it compiles but something's not working right down the road...
It doesn't have to be a vector but I want to be able to iterate through them.
I'm always trying to implement the Observer pattern and I'm having difficulties with inheritance as well in that area!
For the Observer pattern I have an Observer class and Game inherits Observer. I have a Subject class inherited by InputComponent. Subject has a vector of Observer* called vObserver and a function called addObserver(Observer* observer) and adds the passed pointer in vObserver. I also have a function called Notify(event e), which iterates through vObserver and calls their onNotify functions.
So in Game, I have an InputComponent instance called inputComp. I do inputComp.addObserver(this) and inputComp.vObserver.size() is 1. Good. I have a call to Notify in InputComponent which does get triggered, but when it executes, the vObserver.size inside Subject is 0... idk what I'm doing wrong
EDIT:
class Menu
{
public:
virtual void draw() = 0;
virtual void onMouseMove(int x, int y) = 0;
virtual void onMouseButton(int button, bool is_down) = 0;
friend class InputComponent;
friend class MainMenuInputComponent;
protected:
SDL_Renderer* _renderer;
std::vector<Button> vButton;
std::vector<SDL_Texture *> vTexture;
};
class MainMenu : public Menu
{
public:
MainMenu(SDL_Renderer* renderer);
virtual void draw();
virtual void onMouseMove(int x, int y);
virtual void onMouseButton(int button, bool is_down);
friend class MainMenuInputComponent;
};
class InputComponent: public Subject
{
public:
virtual void processInput()
{}
static bool isMouseWithin(int mouseX, int mouseY, SDL_Rect rect)
{
if (mouseX >= rect.x && mouseX <= rect.x + rect.w && mouseY >= rect.y && mouseY <= rect.y + rect.h)
return true;
else
return false;
}
};
class MainMenuInputComponent : public InputComponent
{
public:
MainMenuInputComponent(MainMenu* owner)
:_owner(owner){}
virtual void processInput();
virtual void onBtnClick(std::string btnName);
MainMenu* _owner;
};
class Game : public Observer
{
public:
Game();
void initSDL();
void initGame();
void processGameInput();
void renderGame();
void update();
virtual void onNotify(Events e);
SDL_Window* myWindow;
SDL_Renderer* myRenderer;
std::vector<MainMenuInputComponent> vInputComponent;
std::stack<MainMenu*> menuStack;
};
Game::Game()
{
initSDL();
initGame();
}
void Game::initGame()
{
//Create the Main Menu
MainMenu* pMainMenu = new MainMenu(myRenderer);
//Add menu to the stack
menuStack.push((pMainMenu));
//Add it's components to respective arrays
MainMenuInputComponent inputComp = MainMenuInputComponent(pMainMenu);
vInputComponent.push_back(inputComp);
//Add Observer/Subject relationship.
inputComp.addObserver((Observer*)this);
int bob = (int)inputComp.vObserver.size(); //to see if size went up
}
void Game::processGameInput()
{
if (!menuStack.empty())
{
for (int i = 0; i < (int)vInputComponent.size(); i++)
{
//Menu* compOwner = (Menu*)(vInputComponent[i]._owner);
//std::unique_ptr<Menu, std::default_delete<Menu>> a = menuStack.top();
if ((vInputComponent[i]._owner) == menuStack.top())
{
vInputComponent[i].processInput();
}
//vInputComponent[i].processInput();
}
}
else
for (int i = 0; i < (int)vInputComponent.size(); i++)
{
vInputComponent[i].processInput();
}
}
void Game::renderGame()
{
SDL_RenderClear(myRenderer);
MainMenu* bob = menuStack.top();
if (!menuStack.empty())
(menuStack.top())->draw();
SDL_RenderPresent(myRenderer);
}
void Game::onNotify(Events event)
{
switch (event)
{
case POP_MENU:
menuStack.pop();
break;
case GOTO_SETTINGS:
//Menu* pSettingsMenu =(Menu*)(new SettingsMenu(myRenderer));
//menuStack.push(std::unique_ptr<Menu>(pSettingsMenu));
break;
// Handle other events, and update heroIsOnBridge_...
}
}
class Subject
{
public:
void addObserver(Observer* observer)
{
vObserver.push_back(observer);
}
void removeObserver(Observer* observer)
{
//vObserver.erase(std::find(vObserver.begin(), vObserver.end(), 8));
}
std::vector<Observer*> vObserver;
protected:
void notify(Events e)
{
for (int i = 0; i < (int)vObserver.size(); i++)
{
vObserver[i]->onNotify(e);
}
}
};
class Observer
{
public:
virtual ~Observer() {}
virtual void onNotify(Events e) = 0;
};
If MainMenu publically inherits from Menu, then you shouldn't even need to type cast the pointer to MainMenu to Menu at all. That is, this should work:
class Menu {};
class MainMenu : public Menu {};
std::vector<Menu*> myVector;
MainMenu* pMainMenu = // ...
myVector.push_back(pMainMenu);
However, what you really should be doing is using something like shared_ptr or unique_ptr. Here's a more complete example, using shared_ptr:
#include <vector>
#include <memory>
#include <iostream>
class Menu
{
public:
virtual void on_event() = 0;
// virtual destructor needed for polymorphic base classes
virtual ~Menu() {}
};
class MainMenu : public Menu
{
public:
virtual void on_event()
{
std::cout << "Hello world! from MainMenu" << std::endl;
}
};
class PauseMenu : public Menu
{
public:
virtual void on_event()
{
std::cout << "Hello world! from PauseMenu" << std::endl;
}
};
class SettingsMenu : public Menu
{
public:
virtual void on_event()
{
std::cout << "Hello world! from SettingsMenu" << std::endl;
}
};
int main()
{
std::vector<std::shared_ptr<Menu>> myVector;
myVector.push_back(std::make_shared<MainMenu>());
myVector.push_back(std::make_shared<PauseMenu>());
myVector.push_back(std::make_shared<SettingsMenu>());
for(auto& menu : myVector) {
menu->on_event();
}
return 0;
}
Expected output:
Hello world! from MainMenu
Hello world! from PauseMenu
Hello world! from SettingsMenu
This should also work and gives you the bonus feature of taking care of memory management for you.
C++ (Arduino wrapper) question: I'm writing a shoot em up game on an Arduino which has a LCD connected -
I have a base class (Sprite), and from this other classes are derived - Alien, Missile and Player. The constructor of the Alien class also has private member pMissile (a pointer to a Missile class) - 'an object within an object' would be a way to describe this I think.
[when an Alien fires a missile, it passes its own (x,y) coordinates to the missile, and the missile has its own method of moving starting from the Alien's coordinates]
My question is: How can I access the coordinates of the missile through the Alien object?
Streamlined code is below and I have also drawn a representation of the classes:
// Bass class - has a form/shape, x and y position
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 getX() const { return x; }
unsigned int getY() const { return y; }
protected:
unsigned char *spacePtr;
unsigned int x, y;
};
// Derived class "Missile", also a sprite and has a specific form/shape, and specific (x,y) derived from input sprite
class Missile : public Sprite
{
public:
Missile(): Sprite(&spaceMissile[0], 0, 0) {}
virtual void Move(); // its own method of moving
};
// Derived class "Alien" - has a specific form/shape, and specific (x,y) position
class Alien : public Sprite
{
public:
Alien();
virtual void Move(); // its own method of moving
private:
Missile *pMissile;
};
Alien::Alien(): Sprite(&spaceAlien[0], random(5, 75), random(4, 10))
{
Missile MissileArray[MaxAmmoSize];
pMissile = &MissileArray[0];
}
void Alien::Move()
{
if( random(10) % 2 == 0 )
x += 1;
if( random(10) % 3 == 0 )
y += 1;
if( (pMissile != NULL) && (random(10) == 1) )
{
pMissile->setCoord(x, y);
pMissile->Move(); // move the missile
pMissile++; // move onto the next missile in the array
}
Render();
}
/*****************************************************************************************/
Alien MONSTER;
Player HERO;
Alien *pMONSTER = &MONSTER;
void loop()
{
display.clearDisplay();
MONSTER.Move();
HERO.Move();
pMONSTER->getX(); // this is x location of MONSTER
**// how does pMONSTER access pMissile(x,y) within MONSTER.**
delay(100);
display.display();
}
Embedded C++ Class interaction
The common way is to add a getter function to Alien:
class Alien {
public:
Missile* getMissile() { return pMissile; }
}
To use it:
Alien* a = getAlienFromSomewhere();
auto pMissile = a.GetMissile();
if (pMissile != NULL) {
x = pMissile->getX();
y = pMissile->getY();
}
I imagine that you want to access your missile position through the alien to test the collision with your hero entity, but if you need to keep the track of your missiles you should not "walk" with your pointer to the next missile as shown in the Alien::Move(). Doing this you will lose the reference of the beginning of the array.
IMHO, I would do something like this in your alien class:
// Bass class - has a form/shape, x and y position
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& getX() const { return x; }
unsigned int& getY() const { return y; }
protected:
unsigned char *spacePtr;
unsigned int x, y;
};
// Derived class "Missile", also a sprite and has a specific form/shape, and specific (x,y) derived from input sprite
class Missile : public Sprite
{
public:
Missile(): Sprite(&spaceMissile[0], 0, 0) {}
virtual void Move(); // its own method of moving
};
// Derived class "Alien" - has a specific form/shape, and specific (x,y) position
class Alien : public Sprite
{
public:
Alien();
~Alien(); // a destructor to cleanup your missiles - arduino have almost no memory to handle leaks ;-)
virtual void Move(); // its own method of moving
inline Missile& getMissile(unsigned char n) { return pMissile[n]; }
inline Missile& operator[](unsigned char n) { return getMissile(n); }
inline unsigned int& getX(unsigned char n) { return getMissile(n).getX(); }
inline unsigned int& getY(unsigned char n) { return getMissile(n).getY(); }
private:
Missile *pMissile;
// adding the code to handle the count
unsigned char missileCount;
};
Alien::Alien():
Sprite(&spaceAlien[0], random(5, 75), random(4, 10)),
missileCount(0)
{
// this way of allocation creates a local object that is destroyed by the end of this scope
//Missile MissileArray[MaxAmmoSize];
//pMissile = &MissileArray[0];
// so you should do somethin like this
pMissile = new Missile[MaxAmmoSize];
}
Alien()::~Alien()
{
delete[] pMissile;
}
void Alien::Move()
{
if( random(10) % 2 == 0 )
x += 1;
if( random(10) % 3 == 0 )
y += 1;
if( (pMissile != NULL) && (random(10) == 1) )
{
// my proposal to fire it up
Missile& missile = pMissile[missileCount];
missile->setCoord(x, y);
missile->Move(); // move the missile
missileCount++; // move onto the next missile in the array
}
Render();
}
Using the code like this you could access the locations of your missiles by using:
MONSTER.getX(0) += 1;
MONSTER[0].getY() +=1;
MONSTER.getMissile(1).getX() = 10;
To have some clarity I recommend also the refactoring of the getX() and getY() methods to x() and y(), since they are returning references to the class contents (and doing this, you should also rename your x and y members to something else, or you can get crazy with name conflicts).
I have those inheritance classes :
Base Class: Entity
Derived from Entity Classes: Actor, Obj, Enemy
The Base class Entity contains an obj of a user-defined-type that i called "CollisionStuff".
When i run my program the destructor of CollisionStuff is called after every CollisionStuff constructor call and every time game-loop goes on.
so my call is: why is this happening?
As you can see below, i allocate dinamically some arrays in the setRectangle method, the programm calls the destructor, it deletes my data and when i try to use them... it calls "_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));".
Thank you in before
here my code: Entity.h
enum e_Type {tActor = 0, tObj, tEnemy, tBackg};
class Entity
{
public:
Entity(void);
~Entity(void);
float getH();
float getW();
void setWH(float W, float H);
bool CreateSprite(std::string path);
sf::Sprite& getSprite();
void setType(e_Type type);
e_Type getType();
CollisionStuff getColStuff();
static std::list<Entity*> List;
protected:
sf::Sprite m_sprite;
sf::Texture m_texture;
float m_h;
float m_w;
e_Type m_type;
CollisionStuff m_colStuff;
void addToList();
};
CollisionStuff.h
class CollisionStuff
{
public:
CollisionStuff();
~CollisionStuff(void);
void setRectangle(int W, int H);
void followTheSprite(Entity entity);
private:
sf::Vector2f* m_a;
sf::Vector2f* m_b;
sf::Vector2f* m_c;
sf::Vector2f* m_d;
/* this member data are sides of rectangle used
to manage collisions between object throughout the scenario
a
-------------
| |
c | | d
| |
-------------
b
*/
};
CollisionStuff.cpp
CollisionStuff::CollisionStuff()
{
//setRectangle(0, 0);
}
void CollisionStuff::setRectangle(int W, int H)
{
m_a = new sf::Vector2f[W];
m_b = new sf::Vector2f[W];
m_c = new sf::Vector2f[H];
m_d = new sf::Vector2f[H];
}
void CollisionStuff::followTheSprite(Entity entity)
{
entity.getSprite().setOrigin(0, 0);
sf::Vector2f UpLeftVertex = entity.getSprite().getPosition();
for(int i = 0; i < entity.getW(); i++)
{
m_a[i].x = UpLeftVertex.x + i;
m_a[i].y = UpLeftVertex.y;
m_b[i].x = UpLeftVertex.x + i;
m_b[i].y = UpLeftVertex.y + entity.getH();
}
for(int i = 0; i < entity.getH(); i++)
{
m_c[i].x = UpLeftVertex.x;
m_c[i].y = UpLeftVertex.y + i;
m_d[i].x = UpLeftVertex.x + entity.getW();
m_d[i].y = UpLeftVertex.y + i;
}
}
CollisionStuff::~CollisionStuff(void)
{
delete [] m_a;
delete [] m_b;
delete [] m_c;
delete [] m_d;
}
EDIT
Thank you for the answers.
Example of CollisionStuff use
Actor.cpp (it's a derived class of Entity)
Actor::Actor(void)
{
if(!CreateSprite("D://Sprites//MainChar.png"))
{
std::cout << "Impossibile creare sprite" << std::endl;
}
else
{
std::cout << "Creazione sprite riuscita" << std::endl;
m_sprite.setPosition(100.0f, 365.0f);
m_sprite.setOrigin(20, 35);
//m_sprite.setPosition(190.0f, 382.5f); // 200, 400
setWH(40, 70);
m_health = 100;
m_status = Good;
setType(tActor);
m_jCounter = -1;
m_action = Null;
setColStuff();
}
}
void Actor::setColStuff()
{
m_colStuff.setRectangle(m_w, m_h);
}
void Actor::physic()
{
//setColStuff();
m_colStuff.followTheSprite(*this);
}
main.cpp
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "Platform");
std::list<Entity*>::iterator i;
Background BG;
Level1 FirstLev;
Actor Doodle;
while(window.isOpen())
{
sf::Event event;
if(window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
Doodle.inputEvts();
}
Doodle.act(Doodle.getAction());
Doodle.physic();
window.clear();
window.draw(BG.getSprite());
window.draw(Doodle.getSprite());
FirstLev.drawLevel(window);
window.display();
}
return 0;
}
It's really hard to tell from the bit of code that you posted, but if I had to guess I'd say it's probably related to this:
CollisionStuff getColStuff();
you're returning CollisionStuff by value, which means a new copy will be created by whoever is calling this. It'll have the same pointers that the original CollisionStuff object allocated, and it'll delete them when it goes out of scope, leaving the original one with dangling pointers.
You can try returning by reference or by pointer, but either way you should write a copy constructor and override the assignment operator for CollisionStuff (Rule of Three).
Another idea would be to use std::vector<sf::Vector2f> instead of allocating the sf::Vector2f array yourself.