OOP游戏菜单系统开发概念

OOP Game Menu System Development Conceptualising

本文关键字:开发 系统 游戏 菜单 OOP      更新时间:2023-10-16

我正试图为游戏编写一个OO菜单系统,大致基于模型、视图和控制器的思想。到目前为止,在我的应用程序中,我已经将视图命名为"渲染器",模型没有后缀。我创建了一个通用菜单类,它存储菜单的项目,即menu_item对象,还有一个菜单渲染器类,它为每个项目创建渲染器并渲染它们。问题是,我不确定将数据存储在哪里,也不确定每个项目应该在屏幕上的位置的逻辑,以及如何检查它是否被悬停在上面等等。我最初的想法是在每个菜单项上存储和设置一个选定的属性,不同的视图可以对其进行不同的渲染,但即便如此,我该如何定位组成按钮的图形元素呢?

到目前为止,代码摘录如下:(更多代码请访问https://gist.github.com/3422226)

/**
 * Abstract menu model
 *
 * Menus have many items and have properties such as a title
 */
class menu {
protected:
    std::string _title;
    std::vector<menu_item*> _items;
public:
    std::string get_title();
    void set_title(std::string);
    std::vector<menu_item*> get_items();
};
class menu_controller: public controller {
private:
    menu* _menu;
public:
    menu_controller(menu*);
    virtual void update();
};
class menu_item {
protected:
    std::string _title;
public:
    menu_item(std::string title);
    virtual ~menu_item();
    std::string get_title();
};
class menu_renderer: public renderer {
private:
    menu*   _menu;
    bitmap  _background_bitmap;
    static font _title_font;
    std::map<menu_item*, menu_item_renderer*> _item_renderers;
public:
    menu_renderer(menu*);
    virtual void render();
};
font menu_renderer::_title_font = NULL;
menu_renderer::menu_renderer(menu* menu) {
    _menu = menu;
    _background_bitmap = ::load_bitmap("blackjack_menu_bg.jpg");
    if (!_title_font)
        _title_font = ::load_font("maven_pro_regular.ttf",48);
}
void menu_renderer::render() {
    ::draw_bitmap(_background_bitmap, 0, 0);
    /* Draw the menu title */
    const char* title = _menu->get_title().c_str();
    int title_width = ::text_width(_title_font, title);
    ::draw_text(title, color_white, _title_font, screen_width() - title_width - 20, 20);
    /* Render each menu item */
    std::vector<menu_item*> items = _menu->get_items();
    for (std::vector<menu_item*>::iterator it = items.begin(); it != items.end(); ++it) {
        menu_item* item = *it;
        if (!_item_renderers.count(item))
            _item_renderers[item] = new menu_item_renderer(item, it - items.begin());
        _item_renderers[item]->render();
    }
}
class menu_item_renderer: public renderer {
private:
    unsigned _order;
    menu_item* _item;
    static font _title_font;
public:
    menu_item_renderer(menu_item*, unsigned);
    virtual ~menu_item_renderer();
    virtual void render();
};
font menu_item_renderer::_title_font = NULL;
menu_item_renderer::menu_item_renderer(menu_item* item, unsigned order) {
    _item = item;
    _order = order;
    if (!_title_font)
        _title_font = ::load_font("maven_pro_regular.ttf",24);
}
menu_item_renderer::~menu_item_renderer() {
    // TODO Auto-generated destructor stub
}
void menu_item_renderer::render() {
    const char* title = _item->get_title().c_str();
    int title_width = ::text_width(_title_font, title);
    unsigned y = 44 * _order + 20;
    ::fill_rectangle(color_red, 20, y, title_width + 40, 34);
    ::draw_text(title, color_white, _title_font, 30, y + 5);
}

您的模型class menu需要add_view(menuview *v)update()方法。

然后,您可以将小部件的更新委托给派生视图(menu_renderercli_menu_renderer)。

当控制器运行(或执行命令)并且必须使用Setter(如m_menu_model->set_selected(item, state))更新模型并且模型在Setter上调用update()时,控制器需要知道模型(作为成员)。

你的控制器menu_controller有一个更新方法,你也可以要求输入,比如if (menuview->toggle_select()) m_menu_model->toggle_selected();(所有菜单视图都必须实现)并调用setter,但这是视图和控制器的不灵活耦合(你可以用命令模式检查MVC以获得更高级的组合)。

对于"位置",可以设置成员变量,如int m_x、m_y、m_w、m_h。但是这些成员是特定于具有GUI的视图的,因此只有派生的视图需要它们。

然后,您可以使用这些值与鼠标位置进行比较,并使用鼠标悬停检测方法,如下所示:

// View menu_item 
bool menu_item::over()
{
    if (::mouse_x > m_x
            && ::mouse_x < m_x + m_w
            && ::mouse_y > m_y
            && ::mouse_y < m_y + m_h) {
        return true;
    }
    return false;
}

// update on gui menu item
bool menu_item::update()
{
    if (over()) {
        m_over = true;
    }
    else {
        m_over = false;
    }
    // onclick for the idea 
    if ((::mouse_b & 1) && m_over) {
        // here you could invoke a callback or fire event
        m_selected = 1;
    } else {
        m_selected = 0;
    }
    return m_selected;
}
// update the command line interface menu item
bool cli_menu_item::update()
{
    if ((::enterKeyPressed & 1) && m_selected) {
        // here you could invoke a callback or fire event
        m_selected = 1;
    } else {
        m_selected = 0;
    }
    return m_selected;
}

void menu_item_renderer::render() {
    // update widgets
    _item->update();
    // ...
}
// Model
void menu::add_view(menuview *v) {
  m_view=v;
}
void menu::update() {
  if (m_view) m_view->update();
}
bool menu::set_selected(int item, int state) {
  m_item[index]=state;
  update();
}