C ++菜单类系统结构:渲染顺序,变量类函数

时间:2013-07-04 01:29:41

标签: c++ class opengl inheritance rendering

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

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

我有一个班级系统:

KObect-> KControl->(K菜单,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。它似乎只限于允许一个函数结构(返回类型) / arguments)用于一般按钮。

我在网上找到的唯一另一件事并没有解释得多,但它谈到了'回叫功能'。我无法弄清楚如何声明这些,但我看到有人做了像

这样的事情
int FooFunc(int Foo);

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

//Magic they didn't explain

RegisterCallback( FooFunc ); 

我很困惑,因为他们不使用& sign,只是没有()的函数标识符。我也没有看到声明,所以我无法弄清楚他们是否宣称它是

void RegisterCallback(*); //善意可以*是什么?

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

如果你们想看到任何代码,请特别告诉我。我有这些课程(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个KButtons和2个KPanel。

class KPanel : public KControl 面板允许对物品进行分组,以便于隐藏,移动,arragnement,单选按钮等。

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

My Menu, I was moving the one in the back http://imageshack.us/a/img515/6958/a56.png

我正在后面移动它并且它不会渲染在顶部,因为在我的渲染循环中它是第二个。我不知道如何处理这个问题。任何建议,我将不胜感激!

2 个答案:

答案 0 :(得分: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.
});

渲染只是遍历组件向量,并调用它们的渲染方法。

这是我将如何处理这样的问题的大致概述,希望它有意义。

答案 1 :(得分:0)

评论和答案的变体已经解决了问题。我将选择上面的答案,以帮助我指导我的方向和lambda函数是我第二个问题的完美答案。

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

基类剥离了其他变量/ 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();

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

Menu1 http://imageshack.us/a/img854/6450/ce6.png

Menu2 http://imageshack.us/a/img198/4926/pzjb.png