2011年4月9日
#
介绍:
关于warning C4715:not all control paths return a value (不是所有的控件路径都返回值).
问题:
一个函数,不是所有路径都有返回值,如下:
1) 基本数据类型
对于函数的并不是每个分支都有返回值,那么这样警告会提示不是所有路径都有返回值。
int test( int value )
{
if ( value > 0 ) return value;
}
调用 int value = test( -1 );
看下test的汇编代码.
关于8个程序寄存器一般只有esp寄存器作为入栈,出栈,调用和返回指令作为栈指针,其余
比如eax,exc等寄存器都没有固定的含义和固定值.
看下面test汇编代码.
int test( int value )
{
; 4个寄存器入栈
; ebp 用于存放函数栈的栈顶指针
; esp 用于存放函数栈的栈底指针
004113A0 push ebp ;将寄存器ebp的内容压入程序栈
004113A1 mov ebp,esp ;保留esp寄存器
004113A3 sub esp,0C0h ;为该函数留出临时存储区
004113A9 push ebx
004113AA push esi
004113AB push edi
; 用0CCCCCCCCh初始化堆栈
004113AC lea edi,[ebp-0C0h] ;lea直接寻址
004113B2 mov ecx,30h ;利用编译器的offset立即寻址
004113B7 mov eax,0CCCCCCCCh ;eax=0CCCCCCCCh
004113BC rep stos dword ptr es:[edi] ;根据edi的大小来重复指令执行次数
; 如果 cmp为真则把value的值保存到eax寄存器中
; 否则跳转到地址4113C7h,并没有对eax做处理
if ( value > 0 ) return value;
004113BE cmp dword ptr [value],0
004113C2 jle test+27h (4113C7h)
004113C4 mov eax,dword ptr [value]
}
;各指针出栈,对应前面3条push
004113C7 pop edi ;弹出edi
004113C8 pop esi ;弹出esi
004113C9 pop ebx ;弹出ebx
004113CA mov esp,ebp ;把esp重新指向ebp(函数栈的栈顶指
针,test函数栈顶)
004113CC pop ebp ;ebp重新指向test调用函数返回地址
004113CD ret
调用汇编代码
int value = test ( 1 );
004113FE push 1
00411400 call test (4110AFh)
00411405 add esp,4 ;Call test 函数时将压入栈数据,
由于只有一个参数,所以只有4字节
00411408 mov dword ptr [value],eax
当test 调用小于0时最后value指向的eax是一个0CCCCCCCCh,而对于基本数据类型大多value得到的是0CCCCCCCCh值.
如果我们的test函数:
int test( int value )
{
if ( value > 0 ) return value;
return 0;
}
那么汇编代码会如下:
if ( value > 0 ) return value;
004113EE cmp dword ptr [value],0
004113F2 jle test+29h (4113F9h)
004113F4 mov eax,dword ptr [value]
004113F7 jmp test+2Bh (4113FBh)
return 0;
004113F9 xor eax,eax ;将eax清零,作为返回值
2 )如果返回的是一个引用对象
obj & test( type value )
{
if( type2 ) return obj;
}
obj & ob = test( type1 );
如果ob是个空引用的话,就出出错,关于这种出错是否可以通过什么方式避免呢?
我觉得warning C4715就应该是error C4715.让开发者从最开始就避免这种错误的发生。
2011年3月23日
#
date: 3/23/2011
介绍:
利用Win32 来处理MyGui 3.0.1的中文输入。
实现:
配置还是参考网上的配置,主要再加中文字体.
如下:
□ 更改配置文件MyGUI3.0\Media\MyGUI_Media下
☆ core_font.xml添加
<Resource type="ResourceTrueTypeFont" name="font_Simhei">
<Property key="Source" value="simhei.ttf"/>
<Property key="Size" value="19"/>
<Property key="Resolution" value="50"/>
<Property key="Antialias" value="false"/>
<Property key="SpaceWidth" value="4"/>
<Property key="TabWidth" value="8"/>
<Property key="CursorWidth" value="2"/>
<Property key="Distance" value="6"/>
<Property key="OffsetHeight" value="0"/>
<Codes>
<Code range="33 126"/>
<Code range="19969 40869"/>
<Code hide="128"/>
<Code hide="1026 1039"/>
<Code hide="1104"/>
</Codes>
</Resource>
☆ simhei.ttf要从系统目录下的Fonts拷贝到当前目录。
☆ core_settings.xml中将默认字体改成
<MyGUI type="Font">
<Property key="Default" value="font_Simhei"/>
</MyGUI>
运行Demo解决方案: solution_directx。
给BaseManager添加Win32消息响应函数void ProcIO(UINT messgae, WPARAM wParam, LPARAM lParam ).
case WM_CHAR:
case WM_KEYDOWN:
case WM_KEYUP:
{
base::BaseManager *baseManager = (base::BaseManager*)GetWindowLongPtr(hWnd, GWL_USERDATA);
if ( baseManager )
baseManager->ProcIO( uMsg , wParam , lParam );
break;
}
ProcIO主要是对
WM_CHAR 字符响应
WM_KEYDOWN/WM_KEYUP 按键响应
在处理字符响应的时候需要区分输入法状态和非输入法状态的字符响应。
case WM_CHAR:
{
if ( ImmIsIME( GetKeyboardLayout(0) ))
ProcChar( wParam , lParam );
else
{
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(scan_code), code_point);
};
}
break;
ProcChar函数主要处理中文。因为汉字为8个字节会响应WM_CHAR2次。需要进行组合一次。
WM_KEYDOWN:主要处理一些Widget字符输入。同时还可以做全局键盘信息监控(快捷键).
判断一个Widget是否可以进行字符输入:
\MyGUIEngine\include\MyGUI_InputManager.cpp
bool InputManager::isKeyInputCapture()
{
if( !mWidgetKeyFocus ) return false;
std::string strName = mWidgetKeyFocus->getTypeName();
if ( strName == "ComboBox" ||
strName == "Edit" ||
strName == "Message" ||
strName == "List")
{
return true;
}
return false;
}
由于采用Win32键盘消息,应该屏蔽自带OIS的。
Input\OIS\InputManager.cpp
void InputManager::captureInput()
{
if (mMouse) mMouse->capture();
//mKeyboard->capture();
}
在Demo中监控按键消息( DemoKeeper功能是UIManager )。
void DemoKeeper::injectKeyPress(MyGUI::KeyCode _key, MyGUI::Char _text)
{
if (_key == MyGUI::KeyCode::Grave)
{
mConsole->setVisible(!mConsole->isVisible());
return;
}
else if (_key == MyGUI::KeyCode::F2 )
{
MyGUI::Message::createMessageBox("Message", "Info", "Press F2 ", MyGUI::MessageBoxStyle::Ok | MyGUI::MessageBoxStyle::IconInfo);
return;
}
else if (_key == MyGUI::KeyCode::F3 )
{
MyGUI::Message::createMessageBox("Message", "Info", "Press F3 ", MyGUI::MessageBoxStyle::Ok | MyGUI::MessageBoxStyle::IconInfo);
return;
}
base::BaseManager::injectKeyPress(_key, _text);
}
最后附上源码解决方案:
/Files/expter/MyGuiDemo.rar
图片:
2011年2月24日
#
摘要: 描述: 一个常见遇到的解决方案,下面记录下来。 1个功能模块,有一个简单的继承体系,基类假设为Base. 然后通过一个接口,如何访问子类的特有操作? /// 外部提供一个下面接口: virutal Base* ...
阅读全文
2011年1月18日
#
目的:
针对自己的一个内存池如何测试其性能.
介绍:
1.内存池测试用例的选取.
1.单线程的分配和释放.
2.内存回收.
3.性能关注.
关于内存池的设计和实现网上遍地都是,本文不具体介绍关于内存池的具体实现和方式,主要是介绍设计一个内存池怎样去测试其性能和安全处理,有一个开源的内存池项目tcmalloc也有介绍很多,但是为了满足多种需求,代码过于庞大,最后我用来测试分配性能测试。
1个内存池的测试用例应该包含:
1)该项目内存分配概率随机性.
2)同时保证释放的随机性.
3)可以支持多种分配方式(不同大小,不同对象参数等).
要达到上面要求则可以设计
1.一个数组来设定需要分配的大小。
long arr[ ] = { 16,32,64,128,256,512,1024,2048,5120,5130,7000,6000,10240,15000,20000};
2.根据需求来指定各个大小的分配几率,这好比有多少概率选中某个数(需特定的分配):
A.针对这个需求可以设定定一个概率数组Odds,数组值arrArr的索引。
B.针对Odds指定数组数据,使其数据分配达到arr需要分配概率。
/**//// 被分配的概率 long Odds[ ] = { 0,0,0,1,1,2,2,2,3,3,4,4,4,4,5,5,5,5,6,6,,7,7,7,8,8,8,8,9,9,9,10,10,11,12,12};
C.随机Odds数组,然后得到其值分配,其值则为Arr的索引。
long Asize= arr[ Odds[ rand()%size ] ] ;
3.释放保证随机性。
什么时候释放,以及分配了做什么用,都是又应用层决定的,所以需要把分配出来的内存通过一个容器来存储.
由于分配是随机性,那么释放的时候也保证了随机性。
4.支持多种分配方式。
A. 对象分配:
MemFactory Memory;
A* a = Memory.Alloc<A>( );
B* b = Memory.Alloc<B,int>( 2 );
Memory.FreeObj( a );
Memory.FreeObj( b );
B.直接分配
void* p1 = Memory.Alloc( Asize );
5.性能测试
为了测试性能,我选择了分配1000W次,其中用一个容器保存分配的数据,然后当容器到达100W的时候释放60W数据(保证数据正在使用,随机释放)。
下面的Alloc time 只是统计的Alloc时间累加,Free time只是统计的Free 时间累加,Total time记录这次测试总共花费时间。
1测试结果如下:
2MemPool Alloc time 3242 ms Free time: 2412 ms Total time 22535 ms
3System Alloc time 33616 ms Free time: 6676 ms Total time 55013 ms
4TCMalloc Alloc time 3451 ms Free time 1896 ms Toal time 21078 ms
5
6可以看到TCMalloc的分配和释放都比较快。。
7
8其中arr每个分配的大小命中概率。
9Count[ 1 ] = 2436395
10Count[ 2 ] = 1281728
11Count[ 3 ] = 1026009
12Count[ 4 ] = 769123
13Count[ 5 ] = 768911
14Count[ 6 ] = 769335
15Count[ 7 ] = 640757
16Count[ 8 ] = 640974
17Count[ 9 ] = 512378
18Count[ 10 ] = 384841
19Count[ 11 ] = 256135
20Count[ 12 ] = 257367
21Count[ 13 ] = 256047
PS:
1.内存池的使用:
一般情况下内存池,是整理一整块内存,然后通过一个list串连起来,然后分配的时候从链表中获取,释放也是插入到链表中。
为了方便多对象的多参数以及无参数的分配,可以一些列宏和模板来实现:
具体的可以参考后面附带的内存池实现的代码:
#define DEFINE_CALL_CON( paramcount ) template <class T, DP_STMP_##paramcount( typename, tp ) >\
inline T * Alloc(DP_MTMP_##paramcount( tp, p ) ){\
unsigned long lSize = sizeof(T);\
void* ptMem = Alloc(lSize);\
if( !ptMem) return NULL; \
T * pt = new(ptMem)T( LP_SNMP_##paramcount( p ) );\
return pt;\
}
A. 对象分配:
MemFactory Memory;
A* a = Memory.Alloc<A>( );
B* b = Memory.Alloc<B,int>( 2 );
C* c = Memory.Alloc<C,int,const char*>(1,"dd");
Memory.FreeObj( a );
Memory.FreeObj( b );
Memory.FreeObj( c );
B.直接分配
void* p1 = Memory.Alloc( Asize );
memset(p1,0,ASize);
2.内存池的代码:
1) 实现全是利用的freelist,减少内存开销,分配速度,直接定位。
2) 管理都是通过工厂类来同一的管理。
3) 指定分配策略.
源码为Vs2008版本...
/Files/expter/Pool.rar
关于实现有疑问和建议,可以提出宝贵的意见。。
2011年1月14日
#
问题描述:
1个容器有大量元素,需要进行erase大部分数据的时候,需要遍历这些元素,然后释放item的空间,还要erase删除其item。
一个库,为了测试其性能的时候,需要保存所有外部使用者的数据,这里选取了map,vector和list.
为了简化问题,我写了下面测试代码来测试各个操作:
数据节点:
struct node
{
node(int i){data = i;}
int data;
}
1int _tmain(int argc, _TCHAR* argv[])
2{
3 typedef std::map<long,node*> Mptable;
4 typedef std::vector<node*> Vec;
5 typedef std::list<node*> List;
6
7 Mptable mapnode;
8 Vec vecnode;
9 List listnode;
10
11 for(int i = 1 ; i <= 100000 ; i++ )
12 {
13 mapnode [ i ] = new node(i);
14 vecnode.push_back( new node(i) );
15 listnode.push_back( new node(i));
16 }
17
18 long time = timeGetTime( );
19
20 for( Mptable::iterator itr = mapnode.begin() ; itr != mapnode.end() ; )
21 {
22 delete itr->second;
23 mapnode.erase( itr++ );
24 }
25
26 std::cout <<"map : spend " << timeGetTime() - time << " msec " << std::endl;
27
28
29 time = timeGetTime( );
30
31 for( Vec::iterator itr = vecnode.begin() ; itr != vecnode.end() ; )
32 {
33 delete *itr;
34 itr = vecnode.erase( itr );
35 }
36
37 std::cout <<"vector : spend " << timeGetTime() - time << " msec " << std::endl;
38
39
40 time = timeGetTime( );
41
42 for( List::iterator itr = listnode.begin() ; itr != listnode.end() ; )
43 {
44 delete *itr;
45 itr = listnode.erase( itr );
46 }
47
48 std::cout <<"list : spend " << timeGetTime() - time << " msec" << std::endl;
49
50
51 return 0;
52}
Release下运行结果:
map : spend 31 msec
vector : spend 3734 msec
list : spend 35 msec
发现map的速度最快,vector最慢,list相当。
其实vector就是一个Array,只是Array是静态大小,vector可以扩展,然后查看vector的erase的源码:
iterator erase(const_iterator _Where)
{ // erase element at where
_Move(_VIPTR(_Where) + 1, this->_Mylast,
_VIPTR(_Where));
_Destroy(this->_Mylast - 1, this->_Mylast);
--this->_Mylast;
return (_Make_iter(_Where));
}
有一个move操作,原来把当前iterator+1的往前移了,这样的话会遍历iterator后面所有的元素。
关于map的erase原理可以查看map的实现源码:
由于map的erase后有一个维护过程,其实map是一个RB-Tree,删除算法相对比较麻烦,删除某个item会查找下一个item替换删除的节点,同时还要考虑红和黑的节点处理。同时还要保证map的erase后,平衡且有序。
所以map的erase主要做:
1.刪除item.
2.让树平衡,且有序。
list其实是一个双向链表:
关于删除其实是0(1)的操作,我们查看list的erase的操作:
iterator erase(const_iterator _Where)
{ // erase element at _Where
#if _ITERATOR_DEBUG_LEVEL == 2
if (_Where._Getcont() != this || _Where._Ptr == this->_Myhead)
_DEBUG_ERROR("list erase iterator outside range");
_Nodeptr _Pnode = (_Where++)._Mynode();
_Orphan_ptr(*this, _Pnode);
#else /* _ITERATOR_DEBUG_LEVEL == 2 */
_Nodeptr _Pnode = (_Where++)._Mynode();
#endif /* _ITERATOR_DEBUG_LEVEL == 2 */
if (_Pnode != this->_Myhead)
{ // not list head, safe to erase
this->_Nextnode(this->_Prevnode(_Pnode)) =
this->_Nextnode(_Pnode);
this->_Prevnode(this->_Nextnode(_Pnode)) =
this->_Prevnode(_Pnode);
_Dest_val(this->_Alnod, _Pnode);
this->_Alnod.deallocate(_Pnode, 1);
--this->_Mysize;
}
return (_Make_iter(_Where));
}
主要代码删除就是下面删除部分:
对prev和next节点进行处理即可。
关于list的移除竟然比map还要慢.
PS:测试为单线程。
当为100W数据的时候:
map : spend 300 msec
list : spend 385 msec
咋list比map容器还要慢?
还是上面的代码不能说明问题。
2010年7月31日
#
author:expter
date: 2010/07/31
上次写的超链接是重写了一个单独的超链接控件,主要实现文本的鼠标事件和文本下划线,静态图像的显示,主要写的渲染过程,这次为了解决动态图片(比如gif有多帧实现的解决方案).
注:主要针对当前CEGUI的最新库0.7.1。
目的:在游戏聊天框中可能有一个聊天表情,次表情是动态的,需要显示动态表情图片,同时可能有超链接等功能,我们约定一种解析格式,然后输入表情代码或者超链接内容即可显示我们需要的功能。
实现方式:
由于针对聊天窗口,所以这里聊天窗口内容采用CEGUI::ListBox,每条消息内容为ListBoxTextItem,由于ListBox有滚动horzScrollbar条,而一般聊天窗口没有horzScrollbar此功能,所以假设一段话过长我们需要才分内容,可能一条消息包括多条ListBoxTextItem。每行里面可能有文本,表情,图片,以及超链接等.
由于CEGUi不能直接解析Gif文件,我们需要把gif的每一帧全部到出来,然后实现一个当前表情anim控制类,表情管理变量,文件解析类TalkRenderedStringParser。
Anim主要记录当前表情应该为那帧的哪张图片。
表情管理主要记录当前所有的动态表情,每帧时间到达的时候开始渲染。
TalkRenderedStringParser还是一个文本解析类。
简单的ChatList的主要文本如下:
[C FFFF0000]StaticTxt test1![\\ 1] [\\ 3]
dasd[\\ 2] [\\ 3] [\\ 4] [\\ 1] [\\ 5]
其实是动态表情,只是切的图片不能显示了:
要实现一个与游戏相关的聊天窗口基本功能都具备了,这里包括实现超链接,表情图片,图片的功能。
然后只需要制定分频道等功能分别显示不同的聊天信息。
源码功能上相对比较简单,CEGUI库做了相应的修改。
2010年7月22日
#
基本上实现一个基于静态文本多任务的过程.
注:主要针对当前CEGUI的最新库0.7.1。
目的:游戏制作过程中一般打开NPC会弹出一个对话框,一般对话框就是显示一段话,有图片,超链接,文字,同时文本分别有不同的颜色!
那么只要我们输入一段文本,对话框的控件解析文本定义好的标签然后显示所有文本内容和图片即可。
实现方式:
1.超链接控件既要响应点击消息,又要有超级链接标记的下划线。实现方式主要参考了Button的Clicked事件,StaticText的render渲染过程,重新写的一个基于超级链接组件。
2.文本解析利用了当前CEGUI的版本的BasicRenderedStringParser类,我们只需要继承此类,然后设置系统默认的文本解析类为我们当前的类。
3.写超级链接组件的渲染过程主要方便支持CELayoutEditor的可视化编辑。
实现结果:
1.文本显示颜色。
2.换行操作。
3.支持超级链接的显示,以及事件响应和事件处理,事件响应为CEGUI::HyperText::EventClicked。
4.超链接的下划线绘制。
5.支持图片显示和支持图片事件响应。
假设我们的解析文本如下:
标签定义如下
[N]则是换行字符
[C]字体颜色
[A]超级链接
[M]图片
相对来说编写此文本比较简单.
具体用法
xxx 显示文本xxx
[C XX] xx表示32位的字体颜色
[A 1: XX] xx 超级链接显示内容。
[M xx] xx表示图片名字
colorTest:[N]
StaticTxt test1![N]
[C FFFF0000]StaticTxt test2! [N]
[C FFFFFF00]StaticTxt test3! [N] [N] [N]
[C FFFF0000]HyperLink Test: [N]
[A 1:this is the Hyperlink!this is the Hyperlink!][N]
[A 2:this is the second Hyperlink! this is hyperlink!!!][N]
[A 3:this is the third hyperlink!this is hyperlink!this is hyperlink!this is hyperlink!]
[N] [N] [N]
Image Text:[M 381] [M 286] [M 669]
具体过程:
后期目标支持动画的显示,比如GIF格式图片.
实现过程相对繁琐,而且涉及datafiles配置一些处理。
可能真正游戏界面上的实现可能会更丰富,其实也就是增加几个标签然后解析即可。
注:上次听盖老板说有本书专门介绍足球AI,然后专门去买了,看其介绍他实现上足球仿真AI专门实现比较智能,先学习学习他的在继续写我的了。。
2010年6月30日
#
author:expter
date 2010/06/30
介绍: 世界杯现在如此的备受瞩目和关注,本文介绍如何实现一个基于足球AI的实现,而作为程序员我们关注的不是目标用其赢得世界杯,而是创造一个把球踢好的智能体,加上最近上班轻松,晚上较闲,加上去年实现的一个AI模型与平时写的游戏智能算法,想组织起来完成一个足球模拟玩玩。
本文会首先介绍一种基于AI仿真机的实现流程图,后面我将会用大量的篇幅详细介绍各个实现细节,与具体足球战术,此足球AI主要是主动攻击性AI,所以还需要具体完善加强防御性的AI,所以具体代码现在将不会现在放出。以后实现完整过后会完整公开,现在主要设计描述如下。
足球的游戏规则不是很复杂,就是2个球队,然后每个球队一个守门员与几名球员,目的就是踢进对方的球门。简单的足球是没有傻子的,也就没有犯规,越位,头球,点球以及乌龙球。以后可能会增加上面几种。
一个简单的游戏的具体环境如下:
1.一个足球场(FootBallPitch)
2.一个足球 FootBall
3.二个球门 Goal
4.二个球队 FootBallTeam
5.场上12名足球队员(每队6名,期中5名为球员2名后卫3名前锋,还有1名守门员)
6.球员 FootBaller 守门员 GoalKeeper
然后只要理清上面的描述然后把具体的实现封装到每个类中,就实现了1个简单的足球仿真模拟,实现上面的功能代码还是简单,但是如何组织强大攻击性强大,防御性强的AI还是挺复杂的。
下面将给出具体UML实现类图:
由于是基于智能体的足球AI所以还是借鉴了FSM模型,我们可以把每个Player处于不同的状态进行不同的操作,具体把操作类型和事件处理都放在具体的状态中。
基于球场上运球的FootBaller有下列状态ChaseBall 追球状态,Dribble运球 Gohome 归位 KickBall 踢球 ReceviveBall传球
基于守门员GoalKeeper有InterceptBall 拦截 PutBallBackInPlay发球.
这里为了区分队员是前锋还是后卫,我们给队员增加一个行为Behaviors,让其根据自己的行为做相应的事情.。
其中所有的图像处理都是用的GDI的绘制,程序采用的Win32编写方式。
上面的设计基本是现在程序的设计方案和流程图。
后期完善部分:
1.引入基于事件响应,FootBaller 可以通知同队FootBaller 的接收响应的消息处理,比如A发现B的位置很好,A可以通知B我要传球到一个坐标点。
2.加强防御和攻击AI。
3.完成具体方案后,公布所有的方案设计和具体算法,后期引入脚本机制,通过外部编写脚本实现不同队伍AI模拟。
2010年4月14日
#
摘要: 一.XXX 1)概念说明 这里不再具体描述内存池的概念和作用,需要了解请看http://baike.baidu.com/view/2659852.htm?fr=ala0_1_1。 &nbs...
阅读全文
2010年3月5日
#
摘要: 对于一个游戏而言,定时器是必须的,而它一般作为一个游戏基本公共组件,而定时器在游戏逻辑中运用是非常明显的(比如吃药回血,每几秒回血多少),而对于游戏逻辑而言需要开发一个高效率高精度(毫秒级别)的定时器。
一:分析Ace库定时器实现方式 1.Ace种定...
阅读全文