我开始考虑如何构建我的菜单系统并提出一些问题,也许有些经验可以为我提供见解。
我有几个问题,我会尽可能地将它们与我当前的代码联系起来,并在必要时提供我的代码。
我有一个班级系统:
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
我正在后面移动它并且它不会渲染在顶部,因为在我的渲染循环中它是第二个。我不知道如何处理这个问题。任何建议,我将不胜感激!
答案 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();
它允许我移动两个菜单并在幕后完全调整渲染顺序到调用程序。