牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

使用C++和DirectX开发游戏GUI(五)

// represents a column in our listbox
class gui_listbox_column
{
public:
gui_listbox_column() { }
virtual ~gui_listbox_column() { }

virtual void draw(uti_rectangle &where);

void setname(const char *name) { m_name = name; }
uti_string getname(void) { return(m_name); }

int getwidth(void) { return(m_width); }
void setwidth(int w) { m_width = w; }

private:
uti_string m_name;
int m_width;
};

// an item in our listbox
class gui_listbox_item
{
public:
gui_listbox_item() { m_isselected = 0; m_indent = 0; }
virtual ~gui_listbox_item() { }

virtual draw(int colnum, uti_rectangle &where);

void clearallcolumns(void); // boring
void setindent(int i) { m_indent = i; }
int getindent(void) { return(m_indent); }

void settext(int colnum, const char *text); // boring
uti_string gettext(int colnum = 0); // boring

void setitemdata(unsigned long itemdata) { m_itemdata = itemdata; }
unsigned long getitemdata(void) { return(m_itemdata); }

void setselected(int s = 1) { m_isselected = s; }
int getselected(void) { return(m_isselected); }

private:
int m_isselected;
int m_indent; // # of pixels to indent this item
unsigned long m_itemdata;
uti_pointerarray m_coltext;
};

// the listbox itself
class gui_fancylistbox : public gui_window
{
public:
gui_fancylistbox() { m_multiselect = 0; }
virtual ~gui_fancylistbox() { clear(); }

int getselected(int iter = 0);

virtual int wm_command(gui_window *win, int cmd, int param);
virtual int wm_paint(coord x, coord y);
virtual int wm_lbuttondown(coord x, coord y);

gui_scrollbar_horz &gethscroll(void) { return(m_hscroll); }
gui_scrollbar_vert &getvscroll(void) { return(m_vscroll); }

virtual int wm_sizechanged(void); // the window's size has changed somehow

gui_listbox_item *getitemat(int index); // boring
gui_listbox_item *additem(const char *text); // boring
int delitem(int index); // boring
int delallitems(void); // boring
gui_listbox_column *getcolumn(int index); // boring
int addcolumn(const char *name, int width); // boring
gui_listbox_column *getcolumnat(int index); // boring
int delcolumn(int index); // boring
int delallcolumns(void); // boring

int clear(void); // delete columns & items

int getnumitems(void);
int getnumcols(void);

void deselectall(void);
void selectitem(int item);
void selecttoggleitem(int item);

void deselitem(int item);

private:
int m_numdispobjsy;
int m_vertgutterwidth; // # of pixels between items vertically

gui_scrollbar_horz m_hscroll;
gui_scrollbar_vert m_vscroll;

bool m_multiselect; // is this multi-selectable?
uti_pointerarray m_items; // array of gui_listbox_items
uti_pointerarray m_columns; // array of gui_listbox_columns
};

  列表框是到现在为止你做的最难的控件吧?但这仅仅是因为它是最通用的.一个能够处理多列、缩进、多重选择列表框控件将在实践中证明他对你的游戏是不可或缺的.停下来并想想在大多数游戏里用到列表框的地方,你就会很快发现这一点.

  我把我的列表框控件分成两部分:一个多列的"报表风格"的列表控件和一个图标列表控件,它创建一个类似于当你在windows"我的电脑"里选择大图标察看方式的显示.

  图表列表控件比较容易建立.它使用了一列静态图标(在一次代码重用),所有的具有相同的大小.我使用图标的宽度除列表框的宽,这让我知道有几列可用.(如果证明我的列表框比大图表小,我假设我只有一列,并让绘制系统剪裁图标以使他们不会超出我的绘制区域).一旦我有了列数,我通过图标的总数除以它计算出我所需要的行数.这样我就知道我该怎样设定要包括的滚动条.

  注意当控件改变大小时必须重新计算这些值.为此我设定了一个wm_sizechanged()消息,calcall()将会在窗口绘制区域被改变的时候调用它.

  报表风格列表控件要复杂一些.我先写了两个辅助类,gui_listbox_column和gui_listbox_item,它们包含了所有的关于列表中给定物件和列的信息.

  gui_listbox_column是两者中较简单的.主要的列表框类有一个成员变量身份的gui_listbox_column的动态数组,这代表了目前列表框中的列.gui_listbox_column包含了在列表框中所需要的列的所有信息,包括列的名字,列的对齐,显示或隐藏,大小等等.

  主要的列表框类也有一个gui_listbox_item的动态数.gui_listbox_item类包含了与我们的报表风格列表框中特定行(或物件)相关的所有信息.目前这个类最重要的数据成员是代表每列数据的字串数组.我也让每个物件通过m_itemdata成员存储一个附加的32位数据.这个技术类似于windows允许你通过位你的列表物件调用SetItemData()和GetItemData()来存储32位数据.这个细节很重要,因为它允许列表框的用户为每个物件存储一个指针-通常一个与该物件有关的特定类,以使它以后可用.

  怎么绘制列和物件呢?我倾向于在要绘制的列表框中在每个单独的物件/列上有个绝对的控件.到最后,我决定让列表控件通过不断调用两个虚函数,gui_listbox_item::draw()和gui_listbox_column::draw()来绘制他的物件和列.每个函数使用一个代表列或者物件在屏幕上位置的矩形.默认的对这些draw()函数的开发仅仅分划出与矩形中特定列和子物件相关的文本;然而,我先在可以简单的为需要独特外观的物件或列派生和重载draw().这种技术目前工作的很好,但是我还不足以宣称这是最好的方法.

  然而,绘制物件比行需要更多的工作.物件需要用高光绘制,这决定于他们是否被选择.这并不很难,但一定不能忘记.

  然后就是滚动条的问题了.我的列表框包含两个成员,m_horzscrollbar和m_vertscrollbar,他们都是GUI滚动条.当列表框的大小被改变时(wm_sizechanged()),他会看看数据的宽度和高度并决定是否显示滚动条.

总结

  真是绕了一大圈子,但是幸运的是你对为GUI创建控件有了个大致的想法.这里我想强调的是"风格就是乐趣".在做你的GUI时不要害怕创新-做做你曾经梦想过的东西,和使你的游戏最酷的东西.如果你的游戏很依赖你的GUI的效能这一点尤其重要,比如你在作即时战略游戏.

  还要记住当创建控件的时候,你需要为你的游戏的其他部分考虑平衡问题-细节表现力和开发时间是成正比的.尽量给你的玩家最易于上手的GUI,但同时不要花时间做50种不同的控件.你要在功能、好事、复杂性、坏事中做出平衡.

  控件就到这吧.下一章也是最后一章中我们会看看资源编辑器,序列化窗口和创建对话框.祝愉快!

posted on 2006-07-07 12:01 杨粼波 阅读(1244) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理