1

我正在尝试为游戏编写一个面向对象的菜单系统,松散地基于模型、视图、控制器的想法。到目前为止,在我的应用程序中,我将视图命名为“渲染器”,并且模型没有后缀。我创建了一个通用菜单类,它存储菜单的项目,它们是 menu_item 对象,还有一个菜单渲染器类,它为每个项目创建渲染器并渲染它们。问题是我不确定在哪里存储数据和逻辑与屏幕上每个项目的位置有关,以及如何检查它是否被悬停等等。我最初的想法是存储和设置一个每个菜单项上的 selected 属性,可以通过不同的视图以不同的方式呈现,但即便如此,我如何处理定位构成按钮的图形元素?

到目前为止的代码摘录如下:(更多代码在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);
}
4

1 回答 1

2

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

然后,您可以将小部件的更新委托给派生的 View(menu_renderer或 a cli_menu_renderer)。

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

你的控制器menu_controller有一个更新方法,在那里你也可以要求输入,比如if (menuview->toggle_select()) m_menu_model->toggle_selected();(所有菜单视图都必须实现)并调用设置器,但这是视图和控制器的不灵活耦合(你可以使用命令模式检查 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();
}
于 2012-08-21T18:22:08.130 回答