CEGUI自定义控件的几个关键步骤

来源:互联网 发布:win10怎么还原网络设置 编辑:程序博客网 时间:2024/05/09 18:13

            接触cegui也不算太长的时间,后来特别想了解一下原理,以及弄懂为什么设计,读了 <<CEGUI深入详解>>这本书,受益颇多,但是也遇到了版本不一致导致的各种问题,尤其是当设计到自定义控件这种实际性的检验学习成果的时候



1.版本差别,该书作者使用的是0.6.x  我用的是 0.8.x,具体的小版本已经不记得了,本篇文章的目的并不是为了实现一个多么强大的控件,更多的是把我遇到的一些坑给大家讲解一下,大家一定要注意的是我实现的自定义控件并不编译到cegui库中,也就是只有我自己的exe能够用



2.首先我们用0.6来实现一下,这个  cegui深入详解已经讲得很明白了,不过难免令粘贴党感觉到有语焉不详的地方,我把完整的代码

//TimeWindow.h

namespace CEGUI{class TimeWindow:public Window{public://! Namespace for global eventsstatic const String EventNamespace;//! Window factory namestatic const String WidgetTypeName;TimeWindow(const String& type, const String& name);~TimeWindow(void);void setLastTime( float f ) ;float getLastTime( ) const ;protected:float d_lastTime; //定时器设置的时间,倒计时持续多久的属性变量};CEGUI_DECLARE_WINDOW_FACTORY(TimeWindow);}

//TimeWindow.cpp

namespace CEGUI{//CEGUI_DEFINE_WINDOW_FACTORY(TimeWindow) ;const String TimeWindow::WidgetTypeName = "CEGUI/TimeWindow";const String TimeWindow::EventNamespace = "TimeWindow";TimeWindow::TimeWindow(const String& type, const String& name):Window( type , name ){ const String& propertyOrigin = WidgetTypeName;CEGUI_DEFINE_PROPERTY(TimeWindow, float,"LastTime","Property to get/set the read-only setting for the Editbox.  Value is either \"true\" or \"false\".",&TimeWindow::setLastTime, &TimeWindow::getLastTime, 0.0f);}TimeWindow::~TimeWindow(void){}void TimeWindow::setLastTime( float f ){d_lastTime = f ;}float TimeWindow::getLastTime() const{return d_lastTime ;}}

以上是渲染类,大家请注意我为了省事,我是直接粘贴的自己的源代码,d_lastTime并没有任何作用,因为我开始是照着书本弄得,但是后来发现连基本流程都走不通,索性就实现一个绘制图片的按钮


//下面我们把渲染类实现出来

//FalgardTimerWindow.h

namespace CEGUI{class FalgardTimerWindow:public WindowRenderer{public:static const String TypeName;       //!< type name for this widget.FalgardTimerWindow(const String& type);~FalgardTimerWindow(void);void render();};}#define CEGUI_DEFINE_WR_FACTORY( className )\namespace CEGUI {\class className ## WRFactory : public WindowRendererFactory\{\public:\className ## WRFactory(void) : WindowRendererFactory(className::TypeName) { }\WindowRenderer* create(void)\{ return new className(className::TypeName); }\void destroy(WindowRenderer* wr)\{ delete wr; }\};\}\static CEGUI::className ## WRFactory s_ ## className ## WRFactory;CEGUI_DEFINE_WR_FACTORY(FalgardTimerWindow)

//FalgardTimerWindow.cpp

namespace CEGUI{const String FalgardTimerWindow::TypeName("Core/TimeWindow");FalgardTimerWindow::FalgardTimerWindow(const String& type):WindowRenderer(type){}FalgardTimerWindow::~FalgardTimerWindow(void){}void FalgardTimerWindow::render(){TimeWindow* w = (TimeWindow*)d_window ;const WidgetLookFeel& wlf = getLookNFeel() ;wlf.getImagerySection( "Text" ).render( *w , 0 ) ;}}

//以上这些还不够,我们需要添加到样式文件中,这些文件是cegui默认提供的

//VanillaSkin.scheme里添加一行

<FalagardMapping windowType="Vanilla/TimeWindow" targetType="CEGUI/TimeWindow" renderer="Core/TimeWindow" lookNFeel="Vanilla/TimeWindow" />

//Vanilla.looknfeel

<WidgetLook name="Vanilla/TimeWindow">
  <ImagerySection name="Text">
   <ImageryComponent>
    <Area>
    <Dim type="LeftEdge"><AbsoluteDim value="3" /></Dim>
    <Dim type="TopEdge"><AbsoluteDim value="3" /></Dim>
    <Dim type="Width"><UnifiedDim scale="1" offset="-3" type="Width" /></Dim>
    <Dim type="Height"><UnifiedDim scale="1" offset="-3" type="Height" /></Dim>
    </Area>
    <Image name="Vanilla-Images/FrameTopLeft"/>
    <VertFormat type="Stretched" />
    <HorzFormat type="Stretched" />
   </ImageryComponent>
  </ImagerySection>
 </WidgetLook>

接下来最重要的一步就是注册(说白了就是你得让你的样式xml文件能够和咱们的窗口类对应起来)

Direct3D9Renderer* render = &Direct3D9Renderer::bootstrapSystem(g_pd3dDevice); //WindowFactoryManager::getSingleton().addFactory( &(CEGUI_WINDOW_FACTORY(TimeWindow)) ) ;  WindowFactoryManager::addFactory<TplWindowFactory<TimeWindow> >() ;//注册窗口 WindowRendererManager& wfm =  WindowRendererManager::getSingleton(); wfm.addFactory( &s_FalgardTimerWindowWRFactory);//注册渲染窗口

以上就是在0.6种自定义一个控件的所有步骤,s_FalgardTimerWindowWRFactory这个可能大家有点疑惑,请看我的源代码中有一个宏CEGUI_DEFINE_WR_FACTORY


2.那么在0.8中有什么不同呢

其实并没有什么不同,当然我说的没有不同只针对自定义一个窗口的流程,至于CEGUI库有无不同,我们不做考虑,废话不多说,我们看一下0.8中如何实现

以下宏都去掉

CEGUI_DECLARE_WINDOW_FACTORY(TimeWindow);

CEGUI_DEFINE_WINDOW_FACTORY(TimeWindow) ;

#define CEGUI_DEFINE_WR_FACTORY( className )\
 namespace CEGUI {\
class className ## WRFactory : public WindowRendererFactory\
{\
public:\
 className ## WRFactory(void) : WindowRendererFactory(className::TypeName) { }\
 WindowRenderer* create(void)\
{ return new className(className::TypeName); }\
 void destroy(WindowRenderer* wr)\
{ delete wr; }\
};\
}\
 static CEGUI::className ## WRFactory s_ ## className ## WRFactory;


CEGUI_DEFINE_WR_FACTORY(FalgardTimerWindow)


以上全部不要,0.8为了使我们更方便的注册自定义控件,为我们添加了两个静态函数来完成注册

WindowFactoryManager::addFactory<TplWindowFactory<TimeWindow> >() ;

WindowRendererManager::addFactory<TplWindowRendererFactory<FalgardTimerWindow> >();

只需要 这两行就完成了我们上面坐的所有东西,具体的意义,大家看一下源代码,就知道了和0.6并无本质上的不同,相信不难看懂


3.请注意大坑来了

   1)用0.6的方式一样可以在0.8中完成窗口的注册

   2)当用0.6的方式时

 g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );    // Turn on the zbuffer    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );Direct3D9Renderer* render = &Direct3D9Renderer::bootstrapSystem(g_pd3dDevice);WindowFactoryManager::getSingleton().addFactory( &(CEGUI_WINDOW_FACTORY(TimeWindow)) ) ;WindowRendererManager& wfm =WindowRendererManager::getSingleton();wfm.addFactory( &s_FalgardTimerWindowWRFactory);DefaultResourceProvider*  resPro = static_cast<DefaultResourceProvider*>(System::getSingleton().getResourceProvider());resPro->setResourceGroupDirectory("schemes","schemes\\");resPro->setResourceGroupDirectory("imagesets", "imagesets\\");resPro->setResourceGroupDirectory("fonts", "fonts\\");resPro->setResourceGroupDirectory("layouts", "layouts\\");resPro->setResourceGroupDirectory("looknfeels", "looknfeel\\");resPro->setResourceGroupDirectory("lua_scripts", "lua_scripts\\");resPro->setResourceGroupDirectory("schemas", "xml_schemas\\");resPro->setResourceGroupDirectory("animations", "animations\\");AnimationManager::setDefaultResourceGroup("animations");ImageManager::setImagesetDefaultResourceGroup("imagesets");Font::setDefaultResourceGroup("fonts");Scheme::setDefaultResourceGroup("schemes");WidgetLookManager::setDefaultResourceGroup("looknfeels");WindowManager::setDefaultResourceGroup("layouts");ScriptModule::setDefaultResourceGroup("lua_scripts");XMLParser* parser = System::getSingleton().getXMLParser();if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))parser->setProperty("SchemaDefaultResourceGroup", "schemas");/*if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))parser->setProperty("SchemaDefaultResourceGroup", "schemas");*///加载方案//TaharezLook.schemeSchemeManager::getSingleton().createFromFile( "VanillaSkin.scheme" );SchemeManager::getSingleton().createFromFile( "TaharezLook.scheme" );

注册渲染窗口可以放在加载方案的前面,但是如果0.8将渲染窗口的注册放在前面的话,会崩溃,我看了一下调用堆栈,大概的原因就是 当cegui加载方案的时候会注册很多渲染类,例如button  editbox等,但是他会首先释放掉我们注册的工厂,释放动作是在dll中完成的(也就是cegui基础库dll),但是对象的创建是通过一个宏来完成,也就是new操作实在我们的exe中,这就会出问题,解决的方法就是将渲染窗口的注册放在加载scheme的后面进行,上代码

 g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, FALSE );    // Turn on the zbuffer    g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );Direct3D9Renderer* render = &Direct3D9Renderer::bootstrapSystem(g_pd3dDevice);WindowFactoryManager::addFactory<TplWindowFactory<TimeWindow> >() ;//注册窗口DefaultResourceProvider*  resPro = static_cast<DefaultResourceProvider*>(System::getSingleton().getResourceProvider());resPro->setResourceGroupDirectory("schemes","schemes\\");resPro->setResourceGroupDirectory("imagesets", "imagesets\\");resPro->setResourceGroupDirectory("fonts", "fonts\\");resPro->setResourceGroupDirectory("layouts", "layouts\\");resPro->setResourceGroupDirectory("looknfeels", "looknfeel\\");resPro->setResourceGroupDirectory("lua_scripts", "lua_scripts\\");resPro->setResourceGroupDirectory("schemas", "xml_schemas\\");resPro->setResourceGroupDirectory("animations", "animations\\");AnimationManager::setDefaultResourceGroup("animations");ImageManager::setImagesetDefaultResourceGroup("imagesets");Font::setDefaultResourceGroup("fonts");Scheme::setDefaultResourceGroup("schemes");WidgetLookManager::setDefaultResourceGroup("looknfeels");WindowManager::setDefaultResourceGroup("layouts");ScriptModule::setDefaultResourceGroup("lua_scripts");XMLParser* parser = System::getSingleton().getXMLParser();if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))parser->setProperty("SchemaDefaultResourceGroup", "schemas");/*if (parser->isPropertyPresent("SchemaDefaultResourceGroup"))parser->setProperty("SchemaDefaultResourceGroup", "schemas");*///加载方案//TaharezLook.schemeSchemeManager::getSingleton().createFromFile( "VanillaSkin.scheme" );SchemeManager::getSingleton().createFromFile( "TaharezLook.scheme" );
        //当加载完scheme的后面注册渲染窗口WindowRendererManager::addFactory<TplWindowRendererFactory<FalgardTimerWindow> >();

这样不出问题的原因就是不会去做释放操作,那为什么注册窗口就没问题,大家可以看一下注册窗口并没有这个释放动作所以不会出问题

4.还有一个 我偷懒了 我给出的样式文件中的xml块是0.8版本下的,如果有问题,大家请参照cegui深入详解上的片段






0 0
原创粉丝点击