【问题标题】:Creating an instance of a derived class from a base class (C++ using FLTK to build a GUI)从基类创建派生类的实例(C++ 使用 FLTK 构建 GUI)
【发布时间】:2026-01-04 21:20:06
【问题描述】:

我正在为街机游戏 pong 编写代码,以便在大学介绍 c++ 项目。游戏有一个单人练习模式,球只是从屏幕的另一侧反弹,还有一个两人模式,使用 w 和 s 键进行球员 1 的球拍运动,使用上下键进行球员 2 的运动。我最初是在一节课上编写游戏的,但在与我的讲师协商后,他建议使用三个课来构建游戏以获得额外成绩。

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

上面可以看到我的讲师发给我的图表。

我有一个游戏接口类,其中包含两种游戏模式共有的方法(例如,绘制球、屏幕顶部和底部的碰撞以及移动玩家 1 的球拍)和其他两个用于单人和双机模式的类包含特定于模式的方法(例如移动玩家 2 的球拍、计分系统和绘制玩家 2 的球拍)。

最初,我创建了一个游戏界面类的实例并运行了一个初始化游戏菜单函数,该函数创建了一个可选择游戏模式的菜单。 When either mode is selected a function within the game interface class wipes the menu widgets, draws player 1's paddle, draws the ball and sends the ball off in a random direction with constant speed.

然后我希望创建一个正确的游戏模式类的实例,并从该类中运行另一个特定的初始化函数(一个玩家或两个玩家)。

我还希望能够从任何游戏模式返回到主菜单并能够选择其他模式。

这是我的第一个 C++ 大型项目,所以请原谅我对整体概念理解不佳!

我将如何以及在哪里创建特定游戏模式类的这些实例?在基础游戏界面类中还是在 main 中?

我这样初始化游戏菜单:

//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 
{
...
};

任何帮助将不胜感激,因为我真的不知道下一步该去哪里! 谢谢。

【问题讨论】:

    标签: c++ class derived-class base-class fltk


    【解决方案1】:

    您应该设计游戏模式,以便在数组中初始化玩家。在单玩家模式下,数组中只有一个 Player 对象。在双人模式下,它有 2 个。这样,两个类中的许多功能都可以相同。

    任何你想在派生类中重写的函数都应该在基类中声明为 virtual 以允许它是多态的。记得在基类中也将析构函数声明为虚拟!

    您应该使用指向 GameInterface 对象的 POINTER,因为指针是多态的,然后可以是任一游戏模式...

    // 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();
    

    如果您不想在主目录中包含任何代码,您可以为菜单创建一个环绕类。然后只需在 main 中创建一个菜单对象并运行它:

    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;
    
    ...
    

    【讨论】:

      【解决方案2】:

      我建议将公共函数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
      }
      

      【讨论】:

      • 感谢您的帮助,但我仍然不明白以下内容:1)我是否必须创建一个类的实例才能使用它的方法? 2)如果是这样,我将如何以及在哪里为子类创建这个实例?
      • 1.如果它们是静态的,它们可以在没有实例的情况下运行,但它们不是。请注意,构造函数是一个例外,因为它返回新对象。 2.当你想转换时,子类将被创建,因为他们做出了选择。以玩家一为例,它将是 OnePlayer gamedata(gameinterface);
      • 我尝试按照您建议的方式创建子类的实例,但我似乎根本无法从中调用方法,为什么创建派生类的实例如此困难来自从 Fl_Window 派生的基类?
      • 我正在添加一个我制作的示例...当您尝试构建它时,您使用的是指针吗?如果是这样,请使用星号 *nameofref 来获取对象,这是您需要构建的对象。