万星星@豌豆荚 欢迎加入我们
一个吃软饭的男人!!!!!我只想写程序####
微博:http://weibo.com/wanlianwen
posts - 172,  comments - 1253,  trackbacks - 0

背景:项目需求要用到CEGUI,于是匆忙上手,后来发现很多东西不理解导致开发不顺畅,尤其是CEGUI中的各种文件格式的理解。找到官方WIKI的教程,仔细看下来,虽不是那么震撼,对整个系统的理解还是不无裨益。有一类开发者为快枪手,擅长快速上手,然不求甚解,面对棘手问题比较抓狂。我自己虽不缺少快枪手的技术,但每每遇到商业开发,都会谨慎的仔细学习用到的库,以求全盘掌控,往往比较累。在阅读官方教程同时随手做了翻译,一则体验了evernote笔记软件;二则很久没写字,锻炼一下文字组织能力;三则希望对那些不想看英文的朋友有帮助(我自己找的时候发现这方面资料不多)。
注:evernote的笔记导出来显然很丑,或许我不会用,或许我歪用了它,希望得到高人指点!


CEGUI 实践1:入门

创建于:2012-5-7 18:13
更新时间:2012-5-8 17:26
来源:http://www.cegui.org.uk/wiki/index.php/CEGUI_In_Practice_-_Introduction

CEGUI 实践1

欢迎来到如何使用CEGUI 系列教程的第一篇。教程主要是通过代码进行讲解,我也会尝试使用少量的.layout 布局。你一旦清楚了如何在代码中使用各种部件(Widget),通过脚本来控制它们也就变得非常容易。

【请注意,我是一名Ogre3d 使用者,所以初始化设置是从如何引导并启动Ogre3d 开始。】

介绍CEGUI

首先请注意,CEGUI 使用了许多单件类。单件类,如果你没有使用过,可以理解为在代码中允许全局访问,且保证只创建一个类实例。下面是本例中将会用到的一些单件:

CEGUI::System - 大魔法师(译注:教父、大师,指统治级别)。有很多设置和获取缺省值的函数。
CEGUI::WindowManager - 管理CEGUI 所有窗口,用于创建或删除。

CEGUI::SchemeManager - 管理所有配色方案(Scheme)。

CEGUI::FontManager - 管理应用程序中用到的不同字体。

开始

我们从一些设置工作开始,第一件要做的事情就是建立系统并使其跑起来。因为我是一名Ogre 使用者,所以用Ogre 来构建可运行的系统。还有多种以其它渲染系统来启动的方法,参见这里:The Beginner Guide to Getting CEGUI Rendering

Ogre3d 方式演示如下:

包含必要的头文件
include "CEGUI.h"#include "RendererModules/Ogre/CEGUIOgreRenderer.h"


启动CEGUI
CEGUI::OgreRenderer* renderer = &CEGUI::OgreRenderer::bootstrapSystem();


【注意:bootstrapSystem 是一个比较新的方法,在CEGUI 0.7.1 中才引入。Wiki 上的一些例子还在使用旧版本的CEGUI ,你需要确认自己正在使用的版本。】

上面的代码创建一个Ogre3d 渲染实例用于Ogre3d和CEGUI,如果运行没问题的话,后面就不用再怎么管它了。它建立起用Ogre 渲染CEGUI 的关联。请注意:如果Ogre 是自动创建渲染窗口(大多如此)的话,你需要这么调用一下。假如你想手动创建一个Ogre3d 窗口,可以调用CEGUI::OgreRenderer::bootstrapSystem(Ogre::RenderWindow *) 重载版本。

还有一点值得一提的是bootstrapSystem() 会创建一个CEGUI::System 实例。这一点很重要,因为如果你已经创建过CEGUI::System ,这里就会抛出一个异常。

呃,来点脚本文件

上面我们就只调用了那一个函数,在进行更多CEGUI 处理之前,我们需要了解一些基础知识。

CEGUI 是一个高度脚本化的库,大量的内容素材定义在各种类型的.xml 文件中。首先提到的是配色方案(*.scheme),GUI 中用到的每个部件都定义在.scheme 文件中。它还可以包含后面提到的子脚本文件,后面的教程会对这些文件进行详细讲解。下面是一个你可能会碰到的示例:

<?xml version="1.0" ?><GUIScheme Name="TaharezLook">
        <Imageset Filename="TaharezLook.imageset" />
        <Font Filename="DejaVuSans-10.font" />
        <LookNFeel Filename="TaharezLook.looknfeel" />
        <WindowRendererSet Filename="CEGUIFalagardWRBase" />
        <FalagardMapping WindowType="TaharezLook/Button"      TargetType="CEGUI/PushButton"  Renderer="Falagard/Button"       LookNFeel="TaharezLook/Button" />
        <FalagardMapping WindowType="TaharezLook/Checkbox"    TargetType="CEGUI/Checkbox"    Renderer="Falagard/ToggleButton" LookNFeel="TaharezLook/Checkbox" /></GUIScheme>

接着提到的脚本是布局文件(*.layout)。它也是xml 格式的文件,用于定义显示在屏幕上的窗口的布局。比如想创建一个聊天窗口,我们可能需要一个ChatWindow.layout 文件存放在某个地方。它应该描述窗口外观(大小,屏幕位置等),输入框和发送消息按钮的摆放位置。下面是一个演示.layout 文件的小例子:

<?xml version="1.0" encoding="UTF-8"?><GUILayout >
    <Window Type="TaharezLook/FrameWindow" Name="ConsoleRoot" >
        <Property Name="Text" Value="Chat Window" />
        <Property Name="TitlebarFont" Value="DejaVuSans-10" />
        <Property Name="TitlebarEnabled" Value="True" />
        <Property Name="UnifiedAreaRect" Value="{{0.114991,0},{0.358182,0},{0.519469,0},{0.775455,0}}" />
        <Window Type="TaharezLook/Editbox" Name="ConsoleRoot/EditBox" >
            <Property Name="MaxTextLength" Value="1073741823" />
            <Property Name="UnifiedAreaRect" Value="{{0.0201637,0},{0.787097,0},{0.694549,0},{0.957693,0}}" />
            <Property Name="TextParsingEnabled" Value="False" />
        </Window></GUILayout>

字体(*.font)脚本也非常有用,用于描述CEGUI 中用到的字体,下面是一个例子:

<?xml version="1.0" ?><Font Name="DejaVuSans-10" Filename="DejaVuSans.ttf" Type="FreeType" Size="10" NativeHorzRes="800" NativeVertRes="600" AutoScaled="true"/>

另外一个重要的脚本是图像集(*.imageset),定义每种部件的视觉效果。CEGUI 中用户看到的部件视觉部分对应于一张大纹理文件中的坐标。比如,按钮在.imageset 中定义为一张纹理图像(*.png,*.bmp,*.jpg 等)中像素点100×320开始宽高为50×50的图形。这些是需要在图像集中定义的。下面是一个例子:

<?xml version="1.0" ?><Imageset Name="TaharezLook" Imagefile="TaharezLook.tga" NativeHorzRes="800" NativeVertRes="600" AutoScaled="true">
        <Image Name="MouseArrow" XPos="138" YPos="127" Width="31" Height="25" XOffset="0" YOffset="0" /></Imageset>

最后是感观风格(*.looknfeel)脚本。这个文件看起来相当邪恶(译注:庞杂),却能将所有人从噩梦中拯救,这里为了节省空间不再提交例子。这个文件用于确定所有部件(CEGUI 中window/object/item 表示的)的感观和反馈效果。比如,按钮在鼠标悬停时的效果,如何构建窗口的边框和背景。每种配色方案一般都有自己的感观风格,使得CEGUI 部件的基本构造更加可定制化。

接着开始部分

现在我们对CEGUI 中用到的脚本文件有了一些基本认识(别担心,随着学习的深入,你会发现它们更容易理解,并不再那么吓人,开始阶段用到最多的是.layout 文件。),接下来让我们开始做一些有用的事情!

截止到上次的代码,仅仅是启动了CEGUI 。但就其本身而言,它并不知道你想干嘛。首当其冲的是告诉它我们想使用的配色方案。如上所述,.Scheme 文件包含一个部件列表和其它一些脚本文件,可以引入图像集、感观风格和字体各一个。

// Load the scheme
CEGUI::SchemeManager::getSingleton().create( "TaharezLook.scheme" );


如果你想使用.Scheme 文件中未指定的图像集或者字体,实现起来很简单,用相关的管理对象加载它们即可。由于本篇是入门教程,我将会在后面章节中解释这些管理对象。

下一步,定义一些缺省值:

// Set the defaults
CEGUI::System::getSingleton().setDefaultFont( "DejaVuSans-10" );
CEGUI::System::getSingleton().setDefaultMouseCursor( "TaharezLook", "MouseArrow" );


使用全局CEGUI::System 对象的函数来设置缺省字体和鼠标光标。参考TaharezLook.scheme (在cegui/datafiles/schemes文件夹中),你会发现它通过标签加载了DejaVuSans-10.font 文件中定义的字体。标记"MouseArrow" 可以在图像集"TharezLook" 中找到。我想这些都是自解释的,无需多言。

嗯,现在CEGUI 清楚了我们想使用的一些缺省设置。我们创建一个根窗口,作为其它一切窗口的载体。

CEGUI 采用父/子关系来组织窗口,所以第一要务是创建所有其它窗口的父窗口:

CEGUI::Window* myRoot = CEGUI::WindowManager::getSingleton().createWindow( "DefaultWindow", "_MasterRoot" );


上面WindowManager 单件的函数调用创建一个"DefaultWindow" 类型名为"_MasterRoot" 的窗口。这个缺省窗口就是根窗口。缺省窗口是空的(或者说透明的)。根窗口的名字是随意指定的,然而我个人喜欢用_MasterRoot ,因为我的其它窗口一般不会用这个名字。

窗口创建后,需要设置它为根窗口:

CEGUI::System::getSingleton().setGUISheet( myRoot );


系统对象的函数调用,把
myRoot 作为缺省窗口。记住上面myRoot 创建时起的名字"_MasterRoot" 。

总结

虽然本篇教程不是特别精彩,但CEGUI 到这里已经设置完毕,我们接下来不断添加窗口,做一些GUI 的小实验,比如创建窗口,按钮,进行点击等等有趣的事情。后面的教程会演示如何使CEGUI 识别点击,窗口拖拽,输入等等!谢谢阅读!


CEGUI 实践2:创建部件(Widgets)

创建于:2012-5-8 16:38
更新时间:2012-5-8 18:30
来源:http://www.cegui.org.uk/wiki/index.php/CEGUI_In_Practice_-_Creating_widgets

CEGUI 实践2

欢迎回来,这是CEGUI 实践系列教程第二篇。本篇在前一篇教程CEGUI 实践 - 入门的基础上构建,演示如何创建部件并管理它们。

部件

上个例子中我们演示了如何用Ogre 启动系统,并简单的介绍了CEGUI 的脚本文件。我们接下来要理解什么是部件以及如何使用它们。先创建一个窗口用于在屏幕中间显示一张图片:

CEGUI::Window *myImageWindow = CEGUI::WindowManager::getSingleton().createWindow("TaharezLook/StaticImage","PrettyWindow" );


CEGUI的代码实现了很多派生类用于窗口创建,基类CEGUI::Window 是一般的窗口。CEGUI::WindowManager 调用工厂类来创建窗口并返回窗口指针。第一个参数,"TaharezLook/StaticImage" 告诉类厂创建什么样的窗口,这些类型在.scheme 文件中列出,更加详细的定义在其它的.xml 文件中。第二个参数是窗口名。

【注意:需要提醒的是,窗口名不是强制必须的,但需要避免名字重复。一些用户倾向于采用类似ParentName/ChildName 的命名习惯,例如"ConsoleWindow/SendButton" 或"_MasterRoot/HealthBar" 。】

窗口一旦创建,myImageWindow 即指向一个派生于CEGUI::Window 的CEGUI::DefaultWindow 窗口。通过查看.scheme文件你会发现部件的派生关系是通过TargetType=tag 指出的。

接下来我们需要为窗口设置一些属性。可以通过属性集来做到,或者通过函数调用。后一种方式多少有点不爽,因为你可能需要强制转换到正确的窗口类型以访问其成员函数,但可能更加直观一些。

统一度量系统

我们创建好部件之后,现在需要把它摆放到某个位置并指定大小。通过代码方式实现,OK 不?

不着急下手,先来理解CEGUI 中怎么处理定位。CEGUI 使用了一种统一度量系统。

CEGUI::UDim(scale,offset);


第一个数字是屏幕上的一个相对点,取值范围是[0, 1] ,所以沿着X 轴(屏幕上是从左向右)看去,UDim(0.5,0) 在屏幕的中间位置。Udim(0.0,50) 在距离屏幕左边的+50 像素位置,Udim(0.75,10) 在水平方向上位于屏幕的3/4 位置处加额外10像素。

屏幕上的点(UVector2)由两个UDim 组成:

CEGUI::UVector2(UDim x,UDim y);


同理,CEGUI::Rect由四个UDim 组成:

CEGUI::URect(CEGUI::Udim left,CEGUI::Udim top,CEGUI::Udim right,CEGUI::Udim bottom);


既然我们清楚了如何定位,先把我们创建的窗口移到屏幕中央:

部件演练

myImageWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.5,0),CEGUI::UDim(0.5,0)));


看上去可能有点丑,分开来看吧。setPosition接收一个UVector2 参数 ,每个UVector2 由X 和 Y Udim 组成,每个Udim 又是由比率和绝对浮点数构成。

这里我们已经移动窗口到屏幕中央,但是CEGUI 怎么知道它有多大?这里设置大小为150×100 像素【注意:为了可读性我没加命名空间】。


myImageWindow->setSize(UVector2(UDim(0,150),UDim(0,100)));


我们指定比率值为0 ,实际的像素大小就等于那个绝对参数值。绝对参数值是比率值的增量,在这里不希望大小受比率参数的影响。如果要创建一个占满整个窗口的闪屏,一般大小设置为UVector2(UDim(1,0),UDim(1,0)) ,原点设置为UVector2(Udim(0,0), UDim(0,0)) 。


窗口大小现在也确定了,需要告诉它显示什么图像!可以通过PropertySet 做到,实现如下:

myImageWindow->setProperty("Image","set:TaharezLook image:full_image");


第一个参数是想设置哪个属性,第二个参数是给属性设置什么值。示例中的第二个参数是一个字符串,由两个部分组成。'set:' 部分指定了引用的图像集,'image:' 部分指定了图像,这里显示的是full_image 。

TaharezLook 的属性列表可以访问这里获取[1]

窗口创建完成之后,需要添加到当前的根窗口中:

CEGUI::System::getSingleton().getGUISheet()->addChildWindow(myImageWindow);


这样窗口就会显示出来.

总结

通过这篇短小的教程,我们简单介绍了PropertySet 的用法(很重要!),讲解了一些CEGUI 中统一度量系统是如何工作的知识。虽然还不十分有用,但是我们已经开始理解如何使用CEGUI 的一些功能了。下一讲,我们将学习如何利用交互。


CEGUI 实践3:管理输入

创建于:2012-5-16 14:21
更新时间:2012-5-16 16:32
来源:http://www.cegui.org.uk/wiki/index.php/CEGUI_In_Practice_-_Managing_input

CEGUI 实践3

再次欢迎回来!我们将通过这篇教程学会如何在运行时与CEGUI 系统交互--图形用户接口最重要的特性!

CEGUI 被设计用于许多系统,支持不同的渲染器。鉴于此,它没有与特定的输入系统绑定。然而对于演示如何与CEGUI 交互,我们必须选择一种输入系统来使用,不是吗?因此,我决定采用OIS [1] 。尽管我一直尝试保持OIS 特定功能和结构的分离,在一些依赖特定平台的地方还是会引用到我们使用的系统。

输入注入

首先,应当意识到在CEGUI 中处理用户输入动作很简单,这毫无疑问。当一个动作发生时(用户输入,敲击Esc 键退出,点击按钮),CEGUI 需要接到通知。

CEGUI::System::injectKeyDown(uint key_code); // Tells CEGUI Key has been Pressed
CEGUI::System::injectKeyUp(uint key_code); // Tells CEGUI Key has been Released
CEGUI::System::injectChar(utf32 code_point);

Inject keydown/up 一般用于发送Shift ,控制,回车键等,而injectChar 从字面理解是注入一个字符。

鼠标采用类似的处理方式,下面的代码展示了如何通知CEGUI 鼠标左键按下动作:

CEGUI::System::injectMouseButtonDown(CEGUI::MouseButton::LeftButton);

如你所想,应该会有injectMouseButtonUp() 通知CEGUI 鼠标松开。

鼠标移动时又会如何呢?当然会考虑到:

CEGUI::System::injectMouseMove(float delta_x, float delta_y);

delta 是自上次CEGUI 更新后鼠标在屏幕移动的像素。

截至目前为止,我们尚未给CEGUI 任何处理时间。如果我们正在移动鼠标或者点击按钮,我们想要CEGUI 显示动作的反馈,因此需要告诉CEGUI 自上次渲染后经历的时间。

CEGUI::System::injectTimePulse(float timeElapsed);

我们用经过的秒数来注入一次时间脉冲。更新CEGUI 的频率取决于个人,然而多数游戏都会在每次更新周期中同步的注入时间脉冲来更新GUI 。如果你正在构建的只是一个工具应用,频繁的更新可能没有必要。

继续前行!

我假定大家正在使用OIS 且知道如何使用它。有2个函数允许你与CEGUI 交互。下面的函数传递输入按键,注入键盘按下、弹起动作(键码)以及相关的按键字符:

void InjectOISKey(bool bButtonDown, OIS::KeyEvent inKey){
        if (bButtonDown)
        {
                CEGUI::System::getSingleton().injectKeyDown(inKey.key);
                CEGUI::System::getSingleton().injectChar(inKey.text);
        }
        else
        {
                CEGUI::System::getSingleton().injectKeyUp(inKey.key);
        }}

下面的函数处理OIS 的鼠标按键按下和弹起动作:

void InjectOISMouseButton(bool bButtonDown, OIS::MouseButtonID inButton){
        if (bButtonDown == true)
        {
                switch (inButton)
                {
                case OIS::MB_Left:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::LeftButton);
                        break;
                case OIS::MB_Middle:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::MiddleButton);
                        break;
                case OIS::MB_Right:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::RightButton);
                        break;
                case OIS::MB_Button3:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::X1Button);
                        break;
                case OIS::MB_Button4:
                        CEGUI::System::getSingleton().injectMouseButtonDown(CEGUI::X2Button);
                        break;
                default:    
                        break;
                }
        }
        else // bButtonDown = false
        {
                switch (inButton)
                {
                case OIS::MB_Left:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::LeftButton);
                        break;
                case OIS::MB_Middle:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::MiddleButton);
                        break;
                case OIS::MB_Right:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::RightButton);
                        break;
                case OIS::MB_Button3:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::X1Button);
                        break;
                case OIS::MB_Button4:
                        CEGUI::System::getSingleton().injectMouseButtonUp(CEGUI::X2Button);
                        break;
                default:    
                        break;
                }
        }
}

上面的函数会把OIS 鼠标输入转换到CEGUI 的。目前来讲这段代码是正确的,如果它们有变动的话,我将会提供一个示例讲解如何在CEGUI 中使用OIS。


总结

本篇教程有点短小,但它提供了如何与CEGUI 交互的信息。下一篇教程将讲解如何真正的处理按键来实现一些有趣的事情。敬请期待!


CEGUI 实践4:下压按钮(PushButton)

创建于:2012-5-7 14:19
更新时间:2012-5-8 17:28
来源:http://www.cegui.org.uk/wiki/index.php/CEGUI_In_Practice_-_A_push_button

CEGUI 实践4

嗨,嗨,通过前几节教程你已经有了很大进步!嗯,上一节我们简单的看了一下如何发送输入给CEGUI 。这非常有用,因为我们今天打算制作一个按钮。一个能触发事件的按钮!我知道,这令人兴奋。

一些基础工作

首先我们需要了解CEGUI 如何处理事件。

CEGUI 是基于事件/订阅方式来实现的。这意味着一些部件(按钮,编辑框,列表框)会触发事件(鼠标点击,接收文本,选择),其它对象(类对象,其它CEGUI 部件等)能够接收通知,通过下面的方法订阅事件:

CEGUI::Window::subscribeEvent(const String& name,Event::Subscriber subscriber)

第一个参数是希望订阅的事件名,都是静态常量字符串。你可以在CEGUI的各种Widget对应的头文件中找到这些事件,比如CEGUIPushButton 包含CEGUI::PushButton::EventClicked 。

第二个参数是事件触发时被调用的订阅者。

这里给出一个例子,演示如何注册一个在按钮点击时进行跳跃的对象。首先创建这个能触发对象跳跃的按钮,例子中使用全局变量只是为了简单起见,尽管是非常糟糕的编码风格:

CEGUI::Window *gJumpBtnWindow = NULL;
void CreateJumpButton(){
  gJumpBtnWindow = CEGUI::WindowManager::getSingleton().createWindow("TaharezLook/Button","JumpPushButton");  // Create Window
  gJumpBtnWindow->setPosition(CEGUI::UVector2(CEGUI::UDim(0.75,0),CEGUI::UDim(0.50,0)));
  gJumpBtnWindow->setSize(CEGUI::UVector2(CEGUI::UDim(0,50),CEGUI::UDim(0,50)));
  gJumpBtnWindow->setText("Jump!");
  CEGUI::System::getSingleton().getGUISheet()->addChildWindow(gJumpBtnWindow);  
}

上面代码创建的按钮是"TaharezLook/Button" 类型,存在于.scheme 文件中。接着设置按钮名字为"JumpPushButton" 并设置大小和位置,之后添加到GUI 的root 窗口中。这是对第一、二节教材的温习,但setText 是新内容。我想望文生义也能知道这个方法是设置窗口文本。PushButton ,EditBoxes ,FrameWindow 等都可通过它来设置显示文本。

创建好按钮之后,新建一个类来接受这个按钮的事件。请注意这只是一个示例,你自己的应用程序中很可能不会这么简单!

class OurPlayer
{
   public:
    OurPlayer()
    {
     RegisterForEvents();   // Call our Register function
    };
    bool Jump(const CEGUI::EventArgs& /*e*/){};        // Jump for joy
   private:
    RegisterForEvents()
    {
       gJumpBtnWindow->subscribeEvent(CEGUI::PushButton::EventClicked,CEGUI::Event::Subscriber(&OurPlayer::Jump,this));
    };}

嗯,这是一个很小的类,不是吗?我不知道你打算如何让对象跳跃,所以你得自己填充那些内容。我只是想演示如何与GUI 交互!

嗯,真正有有意义的是RegisterForEvents() 函数。你之前在其它应用中可能没有这样处理过事件订阅,所以我简单的来介绍一下。

一般来讲,它们通过创建一个结构体提供给CEGUI ,带有调用函数以及该函数的对象实例。第一个参数&OurPlayer::Jump 表明使用OurPlayer 的成员函数Jump 。问题是我们无法知道该使用哪个OurPlayer ?假如屏幕被一分为二,左右各一个OurPlayer 该怎么办?所以指定一个OurPlayer ,这就是第二个参数。在C++ 里,this 返回当前对象指针,因此事件的订阅者就是调用RegisterForEvents() 函数的类对象。

如果我们这样创建:

OurPlayer *leftPlayer;
OurPlayer *rightPlayer;

那么可以这样调用调用函数:

gJumpBtnWindow->subscribeEvent(CEGUI::PushButton::EventClicked,CEGUI::Event::Subscriber(&OurPlayer::Jump,leftPlayer));

可以看出有多种使用方式。还有一些其它的事件:

  • MouseClicked,
  • MouseEnters,
  • MouseLeaves,
  • EventActivated,
  • EventTextChanged,
  • EventAlphaChanged,
  • EventSized

这里只列出了一些,所有窗口都继承的事件列表很大,在CEGUIWindow.h 中。

总结

希望本篇教程使你明白如何在CEGUI 中实现事件。这是一种通用机制,一旦掌握了如何使用事件,GUI 其余的东西就变得简单了,因为大部分用户交互都是事件驱动的。甚至还有窗口的旋转和拖放事件,尽管你现在可能不会全部都用到。只有想不到,没有做不到!下一篇再见!

posted on 2012-05-16 17:09 万连文 阅读(3338) 评论(1)  编辑 收藏 引用

FeedBack:
# re: CEGUI0.7 实践
2012-06-05 17:38 | 七星重剑
你真有激情啊  回复  更多评论
  

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


简历下载
联系我

<2012年6月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

常用链接

留言簿(66)

随笔分类

随笔档案

相册

搜索

  •  

最新评论

阅读排行榜

评论排行榜