您的位置:首页 > 产品设计 > UI/UE

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

2016-04-09 20:13 357 查看
接触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 events
static const String EventNamespace;
//! Window factory name
static 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.scheme
SchemeManager::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.scheme
SchemeManager::getSingleton().createFromFile( "VanillaSkin.scheme" );
SchemeManager::getSingleton().createFromFile( "TaharezLook.scheme" );
//当加载完scheme的后面注册渲染窗口
WindowRendererManager::addFactory<TplWindowRendererFactory<FalgardTimerWindow> >();

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

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: