Создание экземпляра производного класса из базового класса (C++ с использованием FLTK для построения графического интерфейса)

Я кодирую аркадную игру pong для введения в проект С++ в университете. В игре есть режим тренировки для одного игрока, в котором мяч просто отскакивает от противоположной стороны экрана, и режим для двух игроков, в котором используются клавиши w и s для движения ракетки игрока 1 и клавиши вверх и вниз для движения игрока 2. Первоначально я написал игру для одного класса, но после консультации с моим лектором он предложил построить игру, используя три класса для дополнительных преимуществ.

Fl_Window
 ^
 |
Game Interface
^            ^
|            |
One Player   Two Player

Диаграмму, которую мне прислал мой лектор, можно увидеть выше.

У меня есть класс игрового интерфейса, который содержит методы, общие для обоих режимов игры (например, рисование мяча, столкновения с верхней и нижней частью экрана и перемещение ракетки игрока 1), и два других класса для режимов одного игрока и двух игроков, которые содержат методы зависит от режима (например, перемещение ракетки игрока 2, система подсчета очков и рисование ракетки игрока 2).

Сначала я создаю экземпляр класса игрового интерфейса и запускаю функцию меню инициализации игры, которая создает меню с выбором режима игры. Когда выбран любой режим, функция в классе игрового интерфейса стирает виджеты меню, рисует ракетку игрока 1, рисует мяч и отправляет мяч в произвольном направлении с постоянной скоростью.

Затем я хочу, чтобы был создан экземпляр правильного класса игрового режима и запускалась другая конкретная функция инициализации (либо для одного игрока, либо для двух игроков) из этого класса.

Я также хочу иметь возможность вернуться из любого игрового режима в главное меню и иметь возможность выбрать другой режим.

Это мой первый крупномасштабный проект на С++, так что простите меня за плохое общее концептуальное понимание!

Как и где я могу создать эти экземпляры определенных классов игрового режима? В базовом классе интерфейса игры или в основном?

Я инициализирую игровое меню следующим образом:

//Main Function
int main()
{
GameInterface MyInterface(GameInterface::WindowSizeX,
GameInterface::WindowSizeY, "PONG");
MyInterface . InitialiseMenu();                                                             

return Fl::run();                                                                           
} 

Который запускает функцию:

void GameInterface :: InitialiseMenu()
{
begin();

MenuTitle = new Fl_Box (400, 50, 100, 50, "Welcome to PONG");
MenuTitle -> labelcolor (FL_WHITE);
MenuTitle -> labelsize (MenuTitleTextSize);
MenuTitle -> box (FL_NO_BOX);
MenuTitle -> show();

TwoPlayerMode = new Fl_Button (400, 300, 150, 50, "Two Player Mode");
TwoPlayerMode -> callback(TwoPlayerMode_cb, this);
TwoPlayerMode -> show();

show();
}

(Примечание: в настоящее время я просто пытаюсь заставить класс для 2 игроков работать с игровым интерфейсом, прежде чем я попытаюсь заставить работать все три класса)

Используемая функция обратного вызова:

//Define two player mode button call back function
void GameInterface :: TwoPlayerMode_cb(Fl_Widget* w,void* data )
{
((GameInterface*) data) -> TwoPlayerMode_cb_i(w);  
} 

void GameInterface :: TwoPlayerMode_cb_i(Fl_Widget* w)
{
TwoPlayerMode -> hide();
MenuTitle -> hide();
InitialiseGameObjects();

}

Функция инициализации игрового объекта:

//Define initialise game objects
void GameInterface :: InitialiseGameObjects()
{
Fl::add_timeout(0.01, GameInterfaceUpdate_cb, this);

begin();

Ball =  new Fl_Box (400, 300, 10, 10);
Ball -> box(FL_FLAT_BOX);
Ball -> color (FL_WHITE);
Ball -> show();    

Player1Paddle = new Fl_Box(0, 300, PaddleSizeX, PaddleSizeY);
Player1Paddle -> box(FL_FLAT_BOX);
Player1Paddle -> color(FL_WHITE);
Player1Paddle -> show();

InitialBallDirectionGenerator();

BallVelocityX = InitialBallDirectionX * InitialBallSpeed;


BallVelocityY = InitialBallDirectionY * InitialBallSpeed;

//TwoPlayerModeInitialise_i();

end(); 

}

Там, где написано //TwoPlayerModeInitialise_i(); Я хочу, чтобы метод, содержащийся в классе для двух игроков, запускался и инициализировал другие объекты, функции и т. д., необходимые для режима игры для двух игроков. Я предполагаю, что мне нужно создать экземпляр этого класса в этот момент? Затем я хочу, чтобы методы обоих классов управляли игрой (например, движение ракетки игрока 1 исходит из класса игрового интерфейса, а движение ракетки игрока 2 исходит из класса режима двух игроков).

Мои занятия сделаны так:

//Game Interface Class
class GameInterface : public Fl_Window
{
//Defining Public Members
public:                                                 
GameInterface(int width, int height, const char* title=0) : Fl_Window(width, height, title)     
{
color(FL_BLACK);                                                                        
show();                                                                                 
}
...
};

&

class TwoPlayerMode : public GameInterface 
{
...
};

Любая помощь будет принята с благодарностью, так как я действительно застрял в том, куда идти дальше! Спасибо.


person Sam Clatworthy    schedule 01.01.2015    source источник


Ответы (2)


Вы должны спроектировать игровой режим так, чтобы он инициализировал игроков в массиве. В однопользовательском режиме массив содержит только один объект Player. В режиме для двух игроков их 2. Таким образом, многие функции могут быть одинаковыми для обоих классов.

Любые функции, которые вы хотите переопределить в производных классах, должны быть объявлены виртуальными в базовом классе, чтобы позволить ему быть полиморфным. Не забудьте также объявить деструктор виртуальным в базовом классе!

Вы должны использовать POINTER для объекта GameInterface, потому что указатели полиморфны и могут быть в любом игровом режиме...

// create a pointer to a GameInterface object
GameInterface *game;

// start one player mode
game = new OnePlayer();
game->run();

// before you change the game mode delete the current game
delete game;

// start two player mode
game = new TwoPlayer();
game->run();

Вы можете создать класс обертки для меню, если не хотите иметь какой-либо код в основном. Затем просто создайте объект меню в главном и запустите его:

int main()
{
// assuming default constructor exists, create the menu
GameSelectMenu menu;

// run the menu
menu.run();

return 0;
}

Сам класс меню будет содержать указатель GameInterface и будет отвечать за управление памятью при переключении режимов игры. Вы можете создавать функции для запуска различных режимов, которые будут назначать GameInterface* как объект OnePlayer или TwoPlayer. Это также будет место, где вы создаете пользовательский интерфейс для фактического меню, чтобы пользователь мог фактически изменить режим игры...

//.. Within run() function of menu class

std::string input;
std::cout << "choose a game (enter 'a' or 'b'): "
std::cin>> input;

// check input for errors
...

// run a mode depending on input
if (input == "a") startOnePlayer();
else if (input == "b") startTwoPlayer();

 // remember to delete the GameInterface object when you have finished with it if it has been initialised
if (game != NULL) delete game;

...
person user2796283    schedule 02.01.2015

Я бы рекомендовал добавить публичную функцию GameInterface getBaseClass() { return *this; } в GameInterface, а затем, когда она вызывается для экземпляра подкласса, она должна возвращать основной базовый класс.

Затем вы можете создать конструктор в одном или двух игроках, который берет GameInterface и отправляет его вверх по строке:

OnePlayer(GameInterface base) : GameInterface(base) {
    //here you could call any additional init functions
}

РЕДАКТИРОВАТЬ: абстрактный пример, который работает, когда я его тестирую

class Base {
protected:
    int x;

public:
    Base(int x) : x(x) {}
    Base getBaseClass() { return *this; }
    virtual int getX() {
        return x + 1;
    }
};

class Derived : public Base {
public:
    Derived(Base b) : Base(b) {}

    Base getBaseClass() {

    }

    int getX() {
        return x;
    }
};

int main() {
    Base t(6);
    Derived d(t);
    std::cout << d.getX() << '\n'; //6

    Base y = d.getBaseClass();
    std::cout << y.getX() << '\n'; //7
}
person Eadword    schedule 01.01.2015
comment
Спасибо за помощь, но я до сих пор не понимаю следующее: 1) Должен ли я создавать экземпляр класса, чтобы использовать его методы? 2) Если да, то как и где мне создать этот экземпляр для подкласса? - person Sam Clatworthy; 02.01.2015
comment
1. Если они статические, их можно запустить без экземпляра, а это нет. Обратите внимание, что конструктор является исключением, поскольку он возвращает новый объект. 2. Подкласс будет сделан, когда вы захотите конвертировать, потому что они сделали выбор. Например, для первого игрока это будут игровые данные OnePlayer (gameinterface); - person Eadword; 02.01.2015
comment
Я попытался создать экземпляр подкласса так, как вы предложили, но я вообще не могу вызывать из него методы, почему так сложно создать экземпляр производного класса из базового класса, производного от Fl_Window? - person Sam Clatworthy; 06.01.2015
comment
Я добавляю пример, который я сделал... Когда вы пытаетесь его построить, вы используете указатель? Если это так, используйте звездочку *nameofref, чтобы получить объект, который вам нужно построить. - person Eadword; 07.01.2015