0

我开始考虑如何构建我的菜单系统,并且有一些问题,也许有经验的人可以为我提供见解。

我有几个问题,我会尽我所能将它们与我当前的代码联系起来,并在必要时提供我的代码。

我有一个班级系统:

KObect->KControl->(KMenu,KPanel,KButton) KControl 也继承自 KEvent

我创建了一个菜单,我可以通过单击并拖动菜单项来移动它们。当我制作了两个菜单时,我意识到有一些我没有想到的东西。

我希望这个菜单系统成为一个可以在其他代码中重用的库。如果我可以创建菜单对象并调用一个函数,例如KObject::Render()并让它处理对象的所有内容,例如事件、循环更新和渲染顺序,那就太好了。目前,我设置它的方式是应用程序是这样的:

KMenu Menu1;
KMenu Menu2;

//Some code like size/hight/color set up if default is unwanted

while(Running) {
    //SDL Poll events code
    Menu1.Render();
    Menu2.Render();

    Menu1.Loop();
    Menu2.Loop();
}

问题是如果我这样设置,Main1 也会在 Menu2 之前呈现。对此的解决方案可能会使如何根据实际可见的对象来处理事件变得更加明显(如果我的两个菜单重叠,当我单击顶部菜单时它们都会移动)。

下一个问题是关于函数行为。现在我正在做很多openGL原语来构建菜单。假设我有一个按钮,但我不确定库的用户是否想要放置纹理、字符串(我将在内部使用 SDL 处理字体)或 openGL 原语,我必须从根本上更改了按钮的渲染功能。我不确定处理这种事情的最佳方法。

最后,当我处理诸如点击按钮之类的事件时,我需要一种让用户定义函数行为的方法。一种考虑是在接受函数指针的按钮类中声明一个函数。然后,当单击按钮时,它只是调用该函数。当我研究这个时,我发现我无法弄清楚如何创建它以便用户可以为函数指定参数和值。(即,我必须声明一个函数指针void (*foo)(int)或类似的东西来声明一个指向需要一个 int 输入并返回 void 的函数的指针。似乎只允许一个函数结构(返回类型/参数)用于通用按钮。

我在网上找到的唯一另一件事并没有解释太多,但它谈到了“回调函数”。我不知道如何声明这些,但我看到有人做了类似的事情

int FooFunc(int Foo);

int FooFunc(int Foo) {
    Foo += Foo
    return Foo;
}

//Magic they didn't explain

RegisterCallback( FooFunc ); 

我很困惑,因为他们不使用 & 符号,只是没有 () 的函数标识符。我也没有看到声明,所以我不知道他们是否将其声明为

无效注册回调(*);// 好名声 * 可以是什么?

我现在提出这两个问题是因为如果我能弄清楚这一点,那么我可能会解决另一个关于多个渲染函数定义几个然后有一个指针的问题,我只是根据用户的初始化来回切换。

如果你们想看任何代码,请告诉我。我有这些类(KApp、KButton、KControl、KEvent、KMenu、KMenuSystem、KObject、KPanel)

class KEvent 完全虚拟,捕获事件并将它们转发给继承类

class KApp : public KEvent 这将是使用我的菜单的用户定义程序

class KMenuSystem 一个包含文件,仅包含所有派生的菜单项。

class KObject 基类。极少

class KControl : public KObject 此类用于处理用户可能看到或与之交互的任何视觉内容。

class KMenu : public KControl 这是一个菜单项。这需要在其他任何事情之前声明(尽管我认为我还没有这样设置)我将在下面发布菜单项的图片。KMenu 现在包含 3 个 KButton 和 2 个 KPanel。

class KPanel : public KControl 面板将允许对项目进行分组,以便于隐藏、移动、排列、单选按钮等。

class KButton : public KControl 我认为这很明显

我的菜单,我正在移动后面的菜单 http://imageshack.us/a/img515/6958/a56.png

我在后面移动那个,它没有在顶部渲染,因为在我的渲染循环中它是第二个。我不确定如何处理。任何建议,我将不胜感激!

4

2 回答 2

1

不久前我做过类似的事情。

我会有一个事件列表及其相应的回调函数。

enum Event {
   MOUSE_CLICK = 0,
   NUM_EVENTS
};

std::array< std::list<std::function<void()>, NUM_EVENTS > events_callbacks;

创建一个EventListener

class EventListener {
public:
   virtual ~EventListener() { 
      // stop listening all events
   }

   void listen_event( Event e, const std::function<void()> & callback ) {
       // insert callback into corresponding event slot in event_callbacks
   }

   void stop_listen_event( Event e ) {
       // remove callback from corresponding event slot in event_callbacks

private:
   // this keep track of which event is being listen and allow removal through the iterator
   std::list< std::pair<Event, std::list<std::function<void()>::iterator> > events;
};

现在您可以拥有一个UIComponent所有 UI 对象都派生自的类。

class UIComponent : public Eventlistener {
public:
   virtual void render() const = 0;
};

现在您的所有 UI 对象都可以使用该listen_event方法监听事件。

你所有的菜单、按钮和边框都可以放在一个UIDecorator类中,也就是decorator模式。

class UIDecorator : public UIComponent {
public:
   UIDecorator( UIComponent * component ) {
      component_ = component;
   }
private:
   UIComponent * component_;
}

创建一个带有边框、菜单、按钮的窗口变成这样

UIWindow window;
UIBorder b( &window );
UIMenu m( &b );
UIButton bu( &m );

然后将其插入屏幕上的 UIComponents 数组中。

std::vector<UIComponent *> components;
components.push_back( &bu );

要解决渲染顺序问题,可以在事件回调函数中处理。

bu.listen_event( MOUSE_CLICK, []{
    // do nothing if mouse click position is outside the region
    // test if there is any component in front of this component
    // decide to bring this component to the front or not.
});

渲染简单地遍历组件向量,并调用它们的渲染方法。

这是解决此类问题的粗略概述,希望它有意义。

于 2013-07-04T02:32:35.673 回答
0

评论和答案的变化已经解决了这些问题。我将选择上面的答案,因为他的帮助引导我朝着这个方向前进,而 lambda 函数是我第二个问题的完美答案。

而不是制作一个向量和一个新类。我刚刚在我的基类中创建了一些指针和函数。用户无需为他们创建的每个项目声明和调用单独的渲染函数,而是将它们添加到基础对象并调用对象渲染函数。事件将操纵基类指针系统来改变渲染顺序。这是我的做法。

剥离其他 vars/fnc 的基类

class KObject {
    protected:
        static KObject* _Top;
        static KObject* _Bottom;

        KObject* _Down;
        KObject* _Up;

    public:

        virtual void Up(KObject* up);
        virtual void Down(KObject* down);
        virtual void Top(KObject* top);
        virtual void Bottom(KObject* bottom);

        virtual void BringToFront(KObject* obj);
        virtual void SendToBack(KObject* obj);
        virtual void Promote(KObject* obj);
        virtual void Demote(KObject* obj);

        virtual void Add(KObject* obj);
        virtual void Del(KObject* obj);
        virtual void Render();
        virtual void Render(int x, int y)
};

指针系统的实现在这里。我还没有测试所有的功能,但把它放在前面并添加作品:) 我第一次遇到指针冲突,所以其他的可能有点错误。我不确定我是否真的需要其他人。

#include "KObject.h"
KObject* KObject::_Top = NULL;
KObject* KObject::_Bottom = NULL;

void KObject::Render() {
    Render(0,0);
}

void KObject::Render(int x, int y) {
    if(_Bottom != NULL) {
        KObject* Current = _Bottom;
        while( Current != NULL ) {
            Current->Render(x,y);
            Current = Current->Up();
        }
    }
}

KObject* KObject::Up() {
    return _Up;
}


KObject* KObject::Down() {
    return _Down;
}


KObject* KObject::Top() {
    return _Top;
}


KObject* KObject::Bottom() {
    return _Bottom;
}



void KObject::Up(KObject* up) {
    _Up = up;
}


void KObject::Down(KObject* down) {
    _Down = down;
}


void KObject::Top(KObject* top) {
    _Top = top;
}


void KObject::Bottom(KObject* bottom) {
    _Bottom = bottom;
}

void KObject::BringToFront(KObject* obj) {
    if( obj != NULL && _Top != obj ) {

        if( _Bottom == obj ) _Bottom = obj->_Up;
        else obj->_Down->_Up = obj->_Up;

        obj->_Up->_Down = obj->_Down;

        _Top->_Up = obj;
        obj->_Down = _Top;
        obj->_Up = NULL;
        _Top = obj;
    }
}

void KObject::SendToBack(KObject* obj) {
    if( obj->_Down != NULL ) {
        obj->_Down->Up(obj->_Up);
        obj->_Up->Down(obj->_Down);

        obj->_Up = _Bottom;
        obj->_Down = NULL;
        _Bottom = obj;
    }
}

void KObject::Promote(KObject* obj) {

    if( obj->_Up != NULL ) {
        KObject* Temp;
        Temp = obj->_Up;

        obj->_Up = Temp->Up();
        Temp->Up(obj);

        Temp->Down( obj->_Down);
        obj->_Down = Temp;
    }

}

void KObject::Demote(KObject* obj) {

    if( obj->_Down != NULL ) {
        KObject* Temp;
        Temp = obj->_Down;

        obj->_Down = Temp->Down();
        Temp->Up(obj->_Up);

        Temp->Down(obj);
        obj->_Up = Temp;
    }

}

void KObject::Add(KObject* obj) {
    if( obj != NULL ) {
        if( _Top != NULL ) {
            obj->Down(_Top);
            _Top->Up(obj);
            _Top = obj;
        }else{
            _Top = obj;
            _Bottom = obj;
        }
    }
}

void KObject::Del(KObject* obj) {
    if( obj != NULL ) {
        if( _Top == obj ) {
            _Top = obj->Down();
            _Top->Up(NULL);
        }else if ( _Bottom == obj ) {
            _Bottom = obj->Up();
            _Bottom->Down(NULL);
        }else {
            obj->Up()->Down(obj->Down());
            obj->Down()->Up(obj->Up());
        }       
    }
}

在我的 menu.cpp 文件中,当我处理单击和拖动事件时:

void KMenu::OnLButtonDown(int mx, int my) {
    if( my < _Y+_MenuBarHeight ) {
        tX = mx;
        tY = my;
        Floating = true;
        if( _Top != this ) {
            BringToFront(this);
        }
    }
    focus = this;
}

void KMenu::OnLoop() {
    if(Floating) {
        int x;
        int y;
        if(SDL_GetMouseState(&x,&y)&SDL_BUTTON(1)) {
            _X = _X - ( tX - x );
            _Y = _Y - ( tY - y );
            tX = x;
            tY = y;
        }else{
            Floating = false;
        }

    }
    if(focus != NULL) {
        focus->OnFocus();
    }

}

在我的应用程序中

#include "KMenuSystem.h"
//Inititilzations
KMenu Menu1;
KMenu Menu2;
KObject MenuGroup;

MenuGroup.Add(&Menu1);
MenuGroup.Add(&Menu2);

//Program Render Loop
MenuGroup.Render();

它允许我在调用程序的幕后完全移动两个菜单并调整渲染顺序。

菜单1 http://imageshack.us/a/img854/6450/ce6.png

菜单2 http://imageshack.us/a/img198/4926/pzjb.png

于 2013-07-05T15:03:21.573 回答