分享
 
 
 

游戏UI设计(2.1)--窗口之父CXWnd的封装

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

窗口之父CXWnd的封装

[http://blog.csdn.net/mythma]

在一般的程序中的对话框、按钮、编辑框等都属于窗口。在MFC中,类CWnd作为窗口类的基类,抽象出了它们的共同特征,并由此派生出若干的子类。在普通的windows程序中,我们可以方便的使用MFC为我们封装好的这些类(CDialog, CButton ,CEdit等),而在游戏程序中,由于需要与dx/opengl结合以及效率等原因,因此不直接使用它们,这就需要我们自己来封装。如此之多的控件如何封装能?我们不妨也为所有的窗口、控件抽象一个基类,就叫她CXWnd。那就让我们从 CXWnd开始,一步一步来实现我们真正的UI吧。

我们先撇开DX具体绘制窗口的过程,看看窗口、控件有什么共同的特征,以及如何在CXWnd中来实现。

1、 窗口的大小与位置

窗口是一个矩形的区域,因此用两个变量就可以表示其大小(宽:m_Wdith, 高:m_Height)。

窗口的位置也可以用两个变量表示( X坐标: m_XPos , Y坐标:m_YPos)。但这两个变量表示窗口的绝对坐标(相对于屏幕左上角)还是相对坐标(相对于父窗口的坐上角)呢?

从用户的角度(即创建窗口)看,使用相对坐标方便一点;

从程序的角度(即绘制窗口)看,使用绝对坐标方便一点。

我们封装CXWnd的目的在于使用,因此要从用户的角度出发,即m_XPos和m_YPos表示的是窗口的相对坐标。当绘制窗口的时候,我们需要获得窗口的绝对坐标,因此需要提供一个成员函数来转化——GetAbsolutePos(float& x, float& y)。

(图就不用画了吧,:))

2、 窗口之间的联系

为了更具代表性,想一下(就不画了哦)一个带属性页的对话框的窗口层次,与它相关的窗口有一个父窗口(不包括祖父哦),若干的子窗口(各种控件),以及若干个同级窗口,因此就需要有一个指向其父窗口的指针(CXWnd* m_wndParent),一个指向其子窗口链表的指针(CXWnd* m_wndChild,m_wndChild指向窗口链表的第一个窗口,该窗口z序在子窗口中最小,既显示在最前面),再加上同级窗口间的指针(CXWnd* m_wndNextSibling指向下一个同级窗口;CXWnd* m_wndPreviousSibling指向前一个同级窗口)。这么多窗口之间用这四种指针联系起来,画出来就是一棵窗口关系树。为什么同级窗口需要两个指针呢?在消息循环中就可以找到答案了。

对这些指针,需要哪些操作呢?

除了基本的Get***Wnd() 和Set***Wnd()外,还需要:

添加一个子窗口AddChildWnd(wndChild : CXWnd*)

删除一个子窗口DeleteChildWnd(wndChild : CXWnd*)

删除所有的子窗口DeleteAllChildWnd(),用来释放内存。

获取子窗口个数 GetChildCount()

以上几个都是对单向链表的基本操作。

3、 消息循环

从经典的消息函数WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)那,得到消息后,主窗口需要传递下去,于是CXWnd有了一个相似的函数——PostMessage(UINT msg, WPARAM wParam, LPARAM lParam, void* Data);,其中对消息进行分类处理。

1) 鼠标消息

鼠标消息处理,当然是如下三个(不过在CXWnd中不需要实现它们,只是声明为纯虚函数,因为各个控件的鼠标处理函数都不尽相同)

virtual void OnMouseDown(int Button, int X, int Y) = NULL;

virtual void OnMouseMove(int X, int Y) = NULL;

virtual void OnMouseUp(int Button, int X, int Y) = NULL;

如何得知是哪个窗口得到了鼠标消息呢?那就需要挨个搜索各个窗口,看鼠标当前的坐标(注意是绝对坐标)在哪个窗口上。因此加入函数CursorIntersect(FLOAT X, FLOAT Y)来完成此功能。

鼠标消息是如何在窗口树中传递的呢?范围肯定是从大到小扫描,即先看是否在某窗口上,若在则看是否在该窗口的子窗口上……,终于找到最上的最底层的窗口,于是子窗口先处理鼠标消息,即最上的子窗口具有处理鼠标消息的优先权。因此消息的传递是从上而下,就称之为正序消息传递吧——PostMsgToChildren(……)负责把消息正序传给每一个子窗口。

2) 键盘消息

同鼠标消息一样,键盘消息的处理有如下两个纯虚成员函数:

virtual void OnKeyDown(WPARAM Key, LPARAM Extended) = NULL;

virtual void OnKeyUp(WPARAM Key, LPARAM Extended) = NULL;

键盘消息的处理的特殊之处在于处理它的窗口(控件)需要获得焦点(原因很简单),因此每个窗体需要一个存放其获取焦点的子窗体的指针成员变量(CXWnd* m_wndFocus)和一个判断其是否获得焦点的bool成员变量(bool m_bFocus),从而就有了SetFocusWnd(……)和GetFocus()成员函数。

键盘消息的传递与鼠标消息的传递一样。

3) 窗口绘制消息

窗口的绘制是与具体的窗口对象相关的,因此在CXWnd中不予实现,但窗口绘制的消息是要传递的,那该消息是如何传递的呢?

每个窗口都有自己的z序,最后面的要先绘制,最前面的要后绘制(要不就被当住了),对应与窗口树就是,父窗口要先绘制,然后最下子窗口后绘制,最后是最上的子窗口。因此最下的窗口具有处理窗口绘制消息的优先权。而最上的子窗口排在子窗口链表的最前面,因此该消息需要逆序从后往前传递。因此需要一个专门的传递该消息的成员函数——PostMsgToChildrenReverse(……)。

窗口的z序不是固定的,因此需要一个函数来改变它们的z序 ——MoveToFront(……)。

至此,CXWnd的主体功能已经完成了,看看它有那些成员吧:

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有