怎样制作基于cocos2d x游戏-x的SLG游戏

如何制作一个类似Tiny Wings的游戏 Cocos2d-x 2.1.4 - 推酷
如何制作一个类似Tiny Wings的游戏 Cocos2d-x 2.1.4
& & & &在第一篇《
》基础上,增加创建动态山丘,原文《
》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.添加地形类
代码如下:
&cocos2d.h&
&kMaxHillKeyPoints&
<span style="color:#ff
&Terrain&:&
&cocos2d::CCNode
&&&&Terrain(
&&&&~Terrain(
&&&&CREATE_FUNC(Terrain);
&&&&CC_SYNTHESIZE_RETAIN(cocos2d::CCSprite*,&_stripes,&Stripes);
&_offsetX;
&&&&cocos2d::CCPoint&_hillKeyPoints[kMaxHillKeyPoints];
这里声明了一个
_hillKeyPoints
数组,用来存储每个山丘顶峰的点,同时声明了一个
代表当前地形滚动的偏移量。文件
Terrain.cpp
代码如下:&
&Terrain.h&
Terrain::Terrain(
&&&&_stripes&=&
&&&&_offsetX&=&0;
Terrain::~Terrain(
&&&&CC_SAFE_RELEASE_NULL(_stripes);
增加如下方法:&
&Terrain::generateHills()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&y&=&winSize.height&/&
&i&&&kMaxHillKeyP&++i)
&&&&&&&&_hillKeyPoints[i]&=&ccp(x,&y);
&&&&&&&&x&+=&winSize.width&/&
&&&&&&&&y&=&rand()&%&(
这个方法用来生成随机的山丘顶峰的点。第一个点在屏幕的左侧中间,之后的每一个点,x轴方向移动半个屏幕宽度,y轴方向设置为0到屏幕高度之间的一个随机值。添加以下方法:&
&Terrain::init()
&&&&&&&&CC_BREAK_IF(!CCNode::init());
-&generateHills();
&&&&&&&&bRet&=&
&Terrain::draw()
&&&&CCNode::draw();
&i&&&kMaxHillKeyP&++i)
&&&&&&&&ccDrawLine(_hillKeyPoints[i&-&
],&_hillKeyPoints[i]);
generateHills
方法创建山丘,
方法简单地绘制相邻点之间的线段,方便可视化调试。添加以下方法:&
&Terrain::setOffsetX(
&newOffsetX)
&&&&_offsetX&=&newOffsetX;
-&setPosition(ccp(-_offsetX&*&
-&getScale(),&
英雄沿着地形的x轴方法前进,地形向左滑动。因此,偏移量需要乘以-1,还有缩放比例。打开
HelloWorldScene.h
文件,添加头文件引用:&
&Terrain.h&
添加如下变量:&
Terrain&*_
HelloWorldScene.cpp
方法里,调用
genBackground
方法之前,加入如下代码:&
_terrain&=&Terrain::create();
-&addChild(_terrain,&
方法里,最后面添加如下代码:&
_terrain-&setOffsetX(offset);
genBackground
方法为如下:&
&HelloWorld::genBackground()
&(_background)
&&&&&&&&_background-&removeFromParentAndCleanup(
&&&&ccColor4F&bgColor&=&
-&randomBrightColor();
&&&&_background&=&
-&spriteWithColor(bgColor,&
<span style="color:#ff
<span style="color:#ff
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&&&&_background-&setPosition(ccp(winSize.width&/&
,&winSize.height&/&
&&&&ccTexParams&tp&=&{GL_LINEAR,&GL_LINEAR,&GL_REPEAT,&GL_REPEAT};
&&&&_background-&getTexture()-&setTexParameters(&tp);
-&addChild(_background);
&&&&ccColor4F&color3&=&
-&randomBrightColor();
&&&&ccColor4F&color4&=&
-&randomBrightColor();
&&&&CCSprite&*stripes&=&
-&spriteWithColor1(color3,&color4,&
<span style="color:#ff
<span style="color:#ff
&&&&ccTexParams&tp2&=&{GL_LINEAR,&GL_LINEAR,&GL_REPEAT,&GL_CLAMP_TO_EDGE};
&&&&stripes-&getTexture()-&setTexParameters(&tp2);
&&&&_terrain-&setStripes(stripes);
注意,每次触摸屏幕,地形上的条纹纹理都会随机生成一个新的条纹纹理,这方便于测试。此外,在
方法里_background调用setTextureRect方法时,可以将offset乘以0.7,这样背景就会比地形滚动地慢一些。编译运行,可以看到一些线段,连接着山丘顶峰的点,如下图所示:
当看到山丘滚动,可以想象得到,这对于一个Tiny&Wings游戏,并不能很好的工作。由于采用y轴随机值,有时候山丘太高,有时候山丘又太低,而且x轴也没有足够的差别。但是现在已经有了这些测试代码,是时候用更好的算法了。
3.更好的山丘算法。使用
的算法来进行实现。打开
Terrain.cpp
文件,修改
generateHills
方法为如下:&
&Terrain::generateHills()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
<span style="color:#ff
&rangeDX&=&
&rangeDY&=&
&x&=&-minDX;
&y&=&winSize.height&/&
//&+1&-&going&up,&-1&-&going&&down
&paddingTop&=&
&paddingBottom&=&
&i&&&kMaxHillKeyP&++i)
&&&&&&&&_hillKeyPoints[i]&=&ccp(x,&y);
&&&&&&&&&&&&x&=&
&&&&&&&&&&&&y&=&winSize.height&/&
&&&&&&&&}&
&&&&&&&&&&&&x&+=&rand()&%&rangeDX&+&minDX;
&&&&&&&&&&&&
&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&dy&=&rand()&%&rangeDY&+&minDY;
&&&&&&&&&&&&&&&&ny&=&y&+&dy&*&
&&&&&&&&&&&&&&&&
&(ny&&&winSize.height&-&paddingTop&&&&ny&&&paddingBottom)
&&&&&&&&&&&&&&&&{
&&&&&&&&&&&&&&&&&&&&
&&&&&&&&&&&&&&&&}
&&&&&&&&&&&&}
&&&&&&&&&&&&y&=&
&&&&&&&&sign&*=&-
这个算法执行的策略如下:
在范围160加上0-80之间的随机数进行递增x轴。
在范围60加上0-40之间的随机数进行递增y轴。
每次都反转y轴偏移量。
不要让y轴值过于接近顶部或底部(paddingTop,&paddingBottom)。
开始于屏幕外的左侧,硬编码第二个点为(0,&winSize.height/2),所以左侧屏幕外有一个山丘。
编译运行,现在可以看到一个更好的山丘算法,如下图所示:
4.一次只绘制部分。在更进一步之前,需要做出一项重大的性能优化。现在,绘制出了山丘的1000个顶峰点,即使每次都只有少数在屏幕上看得到。所以,可以根据屏幕区域来计算哪些顶峰点会被显示出来,然后只显示那些点,如下图所示:
文件,添加如下变量:&
&_fromKeyPointI;
&_toKeyPointI;
Terrain.cpp
文件,在构造函数里面添加如下代码:&
_fromKeyPointI&=&
_toKeyPointI&=&
添加如下方法:&
&Terrain::resetHillVertices()
&&&&CCSize&winSize&=&CCDirector::sharedDirector()-&getWinSize();
&prevFromKeyPointI&=&-
&prevToKeyPointI&=&-
//&key&points&interval&for&drawing
&(_hillKeyPoints[_fromKeyPointI&+&
].x&&&_offsetX&-&winSize.width&/&
-&getScale())
&&&&&&&&_fromKeyPointI++;
&(_hillKeyPoints[_toKeyPointI].x&&&_offsetX&+&winSize.width&*&9&/&
-&getScale())
&&&&&&&&_toKeyPointI++;
这里,遍历每一个顶峰点(从0开始),将它们的x轴值拿来做比较。无论当前对应到屏幕左边缘的偏移量设置为多少,只要将它减去winSize.width/8。如果顶峰点的x轴值小于结果值,那么就继续遍历,直到找到一个大于结果值的,这个顶峰点就是显示的起始点。对于
toKeypoint
也采用同样的过程。修改
方法,代码如下:&
&Terrain::draw()
&&&&CCNode::draw();
&i&=&MAX(_fromKeyPointI,&
);&i&&=&_toKeyPointI;&++i)
&&&&&&&&ccDrawColor4F(
&&&&&&&&ccDrawLine(_hillKeyPoints[i&-&
],&_hillKeyPoints[i]);
现在,不是绘制所有点,而是只绘制当前可见的点,这些点是前面计算得到的。另外,也把线的颜色改成红色,这样更易于分辨。接着,在
方法里面,最后面添加如下代码:&
-&resetHillVertices();
setOffsetX
方法里面,最后面添加如下代码:&
-&resetHillVertices();
为了更容易看到,打开
HelloWorldScene.cpp
方法,最后面添加如下代码:
-&setScale(
编译运行,可以看到线段出现时才进行绘制,如下图所示:
5.制作平滑的斜坡。山丘是有斜坡的,而不是这样直上直下的直线。一个办法是使用余弦函数让山丘弯曲。回想一下,余弦曲线就如下图所示:
因此,它是从1开始,每隔PI长度,曲线下降到-1。但怎么利用这个函数来创建一个漂亮的曲线连接顶峰点呢?先只考虑两个点的情况,如下图所示:
首先,需要分段绘制线,因此,需要每10个点创建一个区段。同样的,想要一个完整的余弦曲线,因此,可以将PI除以区段的数量,得到每个点的角度。然后,让cos(0)对应p0的y轴值,而cos(PI)对应p1的y轴值。要做到这一点,将调用cos(angle),乘以p1和p0之间距离的一半(图上的ampl)。由于cos(0)=1,而cos(PI)=-1,所以,ampl在p0,而-ampl在p1。将它加上中点坐标,就可以得到想要的y轴值。打开
文件,添加区段长度定义,如下代码:&
&kHillSegmentWidth&
然后,打开
Terrain.cpp
方法里面,
ccDrawLine
之后,添加如下代码:&
ccDrawColor4F(
CCPoint&p0&=&_hillKeyPoints[i&-&
CCPoint&p1&=&_hillKeyPoints[i];
&hSegments&=&floorf((p1.x&-&p0.x)&/&kHillSegmentWidth);
&dx&=&(p1.x&-&p0.x)&/&hS
&da&=&M_PI&/&hS
&ymid&=&(p0.y&+&p1.y)&/&
&ampl&=&(p0.y&-&p1.y)&/&
CCPoint&pt0,&pt1;
&j&&&hSegments&+&
&&&&pt1.x&=&p0.x&+&j&*&
&&&&pt1.y&=&ymid&+&ampl&*&cosf(da&*&j);
&&&&ccDrawLine(pt0,&pt1);
&&&&pt0&=&pt1;
HelloWorldScene.cpp
方法,设置scale为1.0,如下代码:&
-&setScale(
编译运行,现在可以看到一条曲线连接着山丘,如下图所示:
6.绘制山丘。用上一篇文章生成的条纹纹理来绘制山丘。计划是对山丘的每个区段,计算出两个三角形来渲染山丘,如下图所示:
还将设置每个点的纹理坐标。对于x坐标,简单地除以纹理的宽度(因为纹理重复)。对于y坐标,将山丘的底部映射为0,顶部映射为1,沿着条带的方向分发纹理高度。打开
文件,添加如下代码:&
&kMaxHillVertices&
<span style="color:#ff
&kMaxBorderVertices&
<span style="color:#ff
添加类变量,代码如下:&
cocos2d::CCPoint&_hillVertices[kMaxHillVertices];
cocos2d::CCPoint&_hillTexCoords[kMaxHillVertices];
&_nBorderV
cocos2d::CCPoint&_borderVertices[kMaxBorderVertices];
Terrain.cpp
resetHillVertices
方法里面,最后面添加如下代码:&
&(prevFromKeyPointI&!=&_fromKeyPointI&||&prevToKeyPointI&!=&_toKeyPointI)
//&vertices&for&visible&area
&&&&_nHillVertices&=&
&&&&_nBorderVertices&=&
&&&&CCPoint&p0,&p1,&pt0,&pt1;
&&&&p0&=&_hillKeyPoints[_fromKeyPointI];
&i&=&_fromKeyPointI&+&
&i&&&_toKeyPointI&+&
&&&&&&&&p1&=&_hillKeyPoints[i];
//&triangle&strip&between&p0&and&p1
&hSegments&=&floorf((p1.x&-&p0.x)&/&kHillSegmentWidth);
&dx&=&(p1.x&-&p0.x)&/&hS
&da&=&M_PI&/&hS
&ymid&=&(p0.y&+&p1.y)&/&
&ampl&=&(p0.y&-&p1.y)&/&
&&&&&&&&pt0&=&p0;
&&&&&&&&_borderVertices[_nBorderVertices++]&=&pt0;
&j&&&hSegments&+&
&&&&&&&&&&&&pt1.x&=&p0.x&+&j&*&
&&&&&&&&&&&&pt1.y&=&ymid&+&ampl&*&cosf(da&*&j);
&&&&&&&&&&&&_borderVertices[_nBorderVertices++]&=&pt1;
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt0.x,&
&&&&&&&&&&&&_hillTexCoords[_nHillVertices++]&=&ccp(pt0.x&/&
<span style="color:#ff
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt1.x,&
&&&&&&&&&&&&_hillTexCoords[_nHillVertices++]&=&ccp(pt1.x&/&
<span style="color:#ff
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt0.x,&pt0.y);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices++]&=&ccp(pt0.x&/&
<span style="color:#ff
&&&&&&&&&&&&_hillVertices[_nHillVertices]&=&ccp(pt1.x,&pt1.y);
&&&&&&&&&&&&_hillTexCoords[_nHillVertices++]&=&ccp(pt1.x&/&
<span style="color:#ff
&&&&&&&&&&&&pt0&=&pt1;
&&&&&&&&p0&=&p1;
&&&&prevFromKeyPointI&=&_fromKeyPointI;
&&&&prevToKeyPointI&=&_toKeyPointI;
这里的大部分代码,跟上面的使用余弦绘制山丘曲线一样。新的部分,是将山丘每个区段的顶点用来填充数组,每个条纹需要4个顶点和4个纹理坐标。在
方法里面,最上面添加如下代码:&
CC_NODE_DRAW_SETUP();
ccGLBindTexture2D(_stripes-&getTexture()-&getName());
ccGLEnableVertexAttribs(kCCVertexAttribFlag_Position&|&kCCVertexAttribFlag_TexCoords);
ccDrawColor4F(
glVertexAttribPointer(kCCVertexAttrib_Position,&
,&GL_FLOAT,&GL_FALSE,&
,&_hillVertices);
glVertexAttribPointer(kCCVertexAttrib_TexCoords,&
,&GL_FLOAT,&GL_FALSE,&
,&_hillTexCoords);
glDrawArrays(GL_TRIANGLE_STRIP,&
,&(GLsizei)_nHillVertices);
这里绑定条纹纹理作为渲染纹理来使用,传入之前计算好的顶点数组和纹理坐标数组,然后以
GL_TRIANGLE_STRIP
来绘制这些数组。此外,注释掉绘制山丘直线和曲线的代码。在
方法里面,调用
generateHills
方法之前,添加如下代码:&
-&setShaderProgram(CCShaderCache::sharedShaderCache()-&programForKey(kCCShader_PositionTexture));
HelloWorldScene.cpp
spriteWithColor1
方法里面,注释
// Layer&4:&Noise
里,更改混合方式,代码如下:&
ccBlendFunc&blendFunc&=&{GL_DST_COLOR,&CC_BLEND_DST};
编译运行,可以看到不错的山丘了,如下图所示:
7.还不完善?仔细看山丘,可能会注意到一些不完善的地方,如下图所示:
增加水平区段数量,可以提高一些质量。打开
文件,修改
kHillSegmentWidth
&kHillSegmentWidth&
通过减少每个区段的宽度,强制代码生成更多的区段来填充空间。编译运行,可以看到山丘看起来更好了。当然,代价是处理时间。效果如下图所示:
在第二部分,将会实现海豹飞翔。
参考资料:
1.How&To&Create&A&Game&Like&Tiny&Wings&with&Cocos2D&2.X&Part&1
2.(译)如何制作一个类似tiny&wings的游戏:第一部分
非常感谢以上资料,本例子源代码附加资源
如文章存在错误之处,欢迎指出,以便改正。
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见基于Cocos2d-x的手机游戏坦克大战的设计与实现--CNKI数字图书馆
[登录]&&[免费注册]
基于Cocos2d-x的手机游戏坦克大战的设计与实现
作&&&&者:
来&&&&源: 哈尔滨工业大学
摘&&&&要: 坦克大战是由北京炫火科技有限公司提出的跨平台手机游戏项目项目的开发目标是开发一款多场景的闯关类游戏,游戏中有多种坦克而且坦克可以升级,坦克配备有不同的主被动技能以及特殊的导弹,同时加入了一般游戏中玩家比较重视的成就系统和抽奖系统
本论文根据当今智能手机游戏市场现状,以及对主流的手机游戏引擎的总结,提出了相应的项目开发方案项目运用开源的手机游戏引擎Cocos2d-x,实现了Android和iOS的跨平台开发项目采用Cocos2d-x的游戏框架,结合本游戏的特征,形成了适用于本游戏开发的游戏系统架构,即底层采用Cocos2d-x引擎SQLite数据库引擎及其它底层库,上层通过各个模块整合实现系统功能,上层的主要模块包括界面逻辑管理模块数据管理模块地图模块碰撞检测模块人工智能模块跨平台模块数据和图片加密模块和音效模块
项目开发过程中对游戏中的重要模块进行了处理和优化,即碰撞检测模块人工智能模块图片管理模块以及扩展性优化在碰撞检测模块中主要运用了基于均匀空间划分的碰撞检测来提高碰撞检测效率人工智能模块主要是运用了模糊状态机和A*寻路算法实现图片采用TexturePacker进行打包处理,降低了图片的内存空间占用,同时便于对图片资源的管理在游戏扩展性上,主要是针对后期版本场景和坦克的添加,项目中的主要实现方式是将各个场景的资源分开存放,同时将坦克的信息参数化,以及坦克资源分开存放
通过项目的开发,实现了一款基于Android和iOS的手机游戏坦克大战坦克大战共设有5个场景,每个场景有15个关卡,不同的场景中地图风格不同,给玩家带来更好的视觉体验游戏中有10辆不同的玩家坦克,配有共计28种不同的技能和特殊导弹,而且坦克可以进行最高5次升级,丰富了玩家的游戏体验同时,游戏中有三十几个游戏成就和抽奖系统,满足玩家获取成就的心理游戏中每个场景都有一个Boss,使游戏的趣味性和挑战性更强通过游戏的测试以及上线发布,游戏较好的完成了项目的功能需求和非功能需求,在用户中获得了较好的评价
关&键&词:
学位级别:
所属学科馆:
数据库类型:
摘要4-5? Abstract5-9? 第1章 绪论9-16?
1.1 课题的来源及意义9?
1.2 与课题有关的行业发展现状9-14?
1.2.1 智能手机操作系统市场分析9-11?
1.2.2 手机游戏发展现状11?
1.2.3 主流的手机游戏引擎11-13?
1.2.4 手机 FPS 游戏开发中的关键技术13-14?
1.3 本论文的主要工作内容14-16? 第2章 项目需求及关键技术分析16-22?
2.1 项目需求分析16-18?
2.1.1 项目基本功能需求16-18?
2.1.2 非功能需求18?
2.2 关键技术分析18-21?
2.2.1 Cocos2d-x 游戏框架18-20?
2.2.2 Cocos2d-x 游戏引擎特性20-21?
2.3 本章小结21-22? 第3章 系统设计22-38?
3.1 游戏整体设计22-24?
3.1.1 游戏中的主要界面22-23?
3.1.2 游戏的系统架构23-24?
3.2 系统功能模块设计24-37?
3.2.1 数据管理模块设计24-28?
3.2.2 逻辑处理模块设计28?
3.2.3 游戏地图设计28-29?
3.2.4 图片及动画管理模块29-31?
3.2.5 碰撞检测模块设计31-32?
3.2.6 人工智能模块设计32-35?
3.2.7 跨平台管理模块设计35-36?
3.2.8 数据和图片加密模块36-37?
3.2.9 其它扩展模块的设计37?
3.3 本章小结37-38? 第4章 系统实现38-62?
4.1 游戏地图模块实现38-40?
4.2 图片及动画处理模块实现40-41?
4.3 碰撞检测模块实现41-45?
4.3.1 玩家坦克碰撞检测41-43?
4.3.2 敌方坦克碰撞检测43-45?
4.4 人工智能模块实现45-52?
4.4.1 敌方坦克的人工智能45-48?
4.4.2 敌方哨塔的人工智能48-49?
4.4.3 游戏中的 A*寻路算法49-52?
4.5 平台管理模块实现52?
4.6 数据和图片加密模块实现52-53?
4.7 其它扩展模块的实现53-55?
4.8 游戏中的运行界面55-61?
4.8.1 菜单界面55-56?
4.8.2 商城界面56-57?
4.8.3 场景及关卡选择界面57-59?
4.8.4 游戏主界面59?
4.8.5 得分抽奖界面59-60?
4.8.6 游戏成就界面60-61?
4.9 本章小结61-62? 第5章 游戏测试62-65?
5.1 测试设备62?
5.2 单元测试62-63?
5.3 性能测试63-64?
5.4 本章小结64-65? 结论65-66? 参考文献66-70? 致谢70-71? 个人简历71
中国学术期刊网络出版总库前10篇[1] 鱼丸;;电脑知识与技术;2004年09期[2] ;;电脑爱好者;2003年09期[3] ;;软件;2004年11期[4] SB;电脑应用文萃;2005年08期[5] ;;数字通信;2006年01期[6] ;;电脑技术-HELLO-IT;2006年07期[7] 陶建军;;;信息系统工程;2008年08期[8] 真真;;;移动信息;2008年08期[9] ;;计算机与网络;2008年01期[10] ;;无线互联科技;2005年01期
中国博士学位论文全文数据库[1] 林桂;;北京化工大学;2004年[2] 李华兵;;复旦大学;2004年[3] 管南异;;浙江大学;2007年[4] 王义敏;;西北工业大学;2006年[5] 奚声慧;;上海社会科学院;2007年[6] 郑岩;;吉林大学;2010年
中国硕士学位论文全文数据库[1] 夏志海;;哈尔滨工业大学;2013年[2] 王晨;;北京邮电大学;2010年[3] 赵勤;;电子科技大学;2010年[4] 柏步芳;;中南大学;2010年[5] 蒋剑忠;;厦门大学;2009年[6] 罗丽;;四川师范大学;2009年[7] 谭素菊;;西安电子科技大学;2008年[8] 杨光宏;;电子科技大学;2005年[9] 姚培荣;;青岛大学;2008年[10] 张琪;;北京邮电大学;2007年
中国重要会议论文全文数据库[1] 李惠;;;论北京文化产业发展——2009北京文化论坛文集[C];2009年[2] ;;四川省通信学会2007年学术年会论文集[C];2007年[3] 王三喜;朱军辉;杨建兵;屈洋;;;第四届中国青年运筹与管理学者大会论文集[C];2001年[4] 黄殿伟;杜立;;;疲劳与断裂2000——第十届全国疲劳与断裂学术会议论文集[C];2000年[5] 郑宝鑫;周雪松;李斌;唐宇;;;广东通信2010青年论坛优秀论文集[C];2010年[6] 居乃俊;;;第二届全国人—机—环境系统工程学术会议论文集[C];1995年[7] 孙成松;花传杰;;;第五届全国仿真器学术会论文集[C];2004年[8] 邓珍荣;黄文明;张敬伟;李丽芳;;;广西计算机学会2008年年会论文集[C];2008年[9] 许川;李兵仓;王建民;赖西南;康建毅;张波;;;第七届全国创伤学术会议暨2009海峡两岸创伤医学论坛论文汇编[C];2009年[10] 高会波;薛青;邓青;;;第13届中国系统仿真技术及其应用学术年会论文集[C];2011年
数字图书馆推荐 >>
相关学科馆&&
400-819-9993
800-810-6613
主办:中国知网
数字出版:《中国学术期刊(光盘版)》电子杂志社有限公司
互联网出版许可证 新出网证(京)字008号
地址:北京清华大学
84-48信箱 知识超市公司
京ICP证040431号
订购热线:400-819-82499
服务热线:800-810-91813
在线咨询:
传真:010-
京公网安备75号Sina Visitor System怎样制作基于Cocos2d-x的SLG游戏-第7章 | 泰然网
514 次浏览 |
文章目录原创:
本章我们将继续学习SLG游戏的制作,本章教程将是该系列游戏的最后一章。在开始之前,我先要对各位翘首以盼等候很久的童鞋们说声抱歉,由于此前事情太多,所以这一系列文章一拖再拖,迟迟没有更新,还望见谅啦!
下面我们回到正题,在上一篇文章中,我们已经讲到了各种操作界面的创建,接下来我们来创建其中相比下较难一点的计时面板,也可以说是计时层。最终我们要实现的游戏效果如下图所示:
计时面板类
这个计时面板是在播种的时候同未成熟幼苗一起创建的,每当播洒一颗种子时,该处坐标上就会创建一个隐藏的计时面板;当该处坐标再次处于选中状态,那么隐藏的计时面板就会显示出来;直到时间耗尽时,该计时面板会从场景中移除。
定义计时面板类
对于计时面板来说,需要注意以下几点:
首先,这里计时面板是一个用进度条表示时间进度的控件项;
其次,对于不同的农作物而言,它们的成熟时间,标题标示都是不相同的;
在一个计时面板的生命周期中,我们需要不停的更新面板状态:进度条不断的减短,时间不断的减少;
最后在时间耗尽时,移除该面板。
效果如下:
根据以上分析结果,计时面板的定义如下:
class TimingLayer: public Layer
virtual bool init()
static TimingLayer* create(Vec2 pos, CropsType type);
virtual void updateProgressBar(float dt);// 更新
CC_SYNTHESIZE(bool, timeOut, TimeOut);
// 记录时间是否有用
CC_SYNTHESIZE(Vec2, timingLayerPos, TimingLayerPos);// 标示TimingLayer的位置
void setParam(CropsType type);
// 设置TimingLayer属性
ProgressTimer* progressB
Label* nameL
// 显示标示了计时面板所属类型的文本项
Label* timeL
// 显示剩余时间的文本项
int growUpT
// 农作物成熟所需时间,也就是计时面板的生命周期
// 进度条的百分比
我们通过create(Vec2 pos, CropsType type)两个属性来创建计时面板,其一是坐标项,用它来标示计时面板的“位置”,每个地图位置上至多只能有一个计时面板;其二是农作物类型,通过它可以确定计时面板的时间和标题项。
计时面板的实现
首先我们先来看看TimingLayer的初始化,如下:
bool TimingLayer::init()
if (!Layer::init())
counter= 1;
percent = 100;
auto progressBarBg = Sprite::create(&progressBg.png&);
progressBarBg-&setAnchorPoint(Vec2(0, 0));
progressBarBg-&setPosition(Vec2(0,
addChild(progressBarBg);
progressBar = ProgressTimer::create(Sprite::create(&progressBar.png&));
progressBar-&setType(ProgressTimer::Type::BAR);
progressBar-&setMidpoint(Point(0, 0.5f));
progressBar-&setBarChangeRate(Point(1, 0));
progressBar-&setPercentage(percent);
progressBar-&setAnchorPoint(Point(0.5f, 0.5f));
progressBar-&setPosition(Point(progressBarBg-&getContentSize().width / 2,
progressBarBg-&getContentSize().height /2 ));
progressBarBg-&addChild(progressBar);
nameLabel = Label::createWithBMFont(&fonts/Font30.fnt&,& &);
nameLabel-&setPosition(Vec2(progressBarBg-&getContentSize().width / 2,
progressBarBg-&getContentSize().height ));
nameLabel-&setAnchorPoint(Vec2(0.5f, 0.0f));
progressBarBg-&addChild(nameLabel);
timeLabel = Label::createWithBMFont(&fonts/Font30.fnt&,&&);
timeLabel-&setPosition(Vec2(progressBarBg-&getContentSize().width / 2,
- progressBarBg-&getContentSize().height ));
timeLabel-&setAnchorPoint(Vec2(0.5f, 0.0f));
progressBarBg-&addChild(timeLabel);
this-&schedule(schedule_selector(TimingLayer::updateProgressBar), 1.0f);
创建并添加进度条的背景图片,这样进度条看着才更明显。
创建进度条并把它添加到背景图片上,这里ProgressTimer是Cocos2d-x自带的进度条类。
ProgressTimer有两种类型:一种是环形,一种是条形,使用ProgressTimer时我们需要通过setType方法指明它所属类型。
另外,setMidpoint方法设置进度条的起始点,(0,y)表示最左边,(1,y)表示最右边,(x,1)表示最上面,(x,0)表示最下面。
setBarChangeRate方法用来设置进度条变化方向的,如果不用变化的方向,则设置该方向为0,否则设置为1。所以(1,0)表示横方向,(0,1)表示纵方向。
ProgressTimer有一个非常最要的percentage属性。它代表了当前进度条的进度值,这也是为什么我们在类中定义了percent属性的原因。如果要让一个进度条正常的显示出来,那么percentage的值必须大于0。setPercentage方法能设置ProgressTimer的percentage值。
创建标示了计时面板所属类型的文本项,也就是用它来显示计时面板的标题。这里通过Label的createWithBMFont函数来创建,其中Font30.fnt字体是我通过Glyph Designer(一款Mac下的字库图集制作工具,Windows下可使用Hiero和BMFont)生成的位图字体,里面包含了本游戏所需的全部字符。
创建并添加显示剩余时间的文本项。
每秒更新一次updateProgressBar函数。
初始化TimingLayer的后,接下来我们来创建TimingLayer:
TimingLayer* TimingLayer::create(Vec2 pos, CropsType type)
TimingLayer *pRet = new TimingLayer();
if (pRet && pRet-&init())
pRet-&setTimingLayerPos(pos);
pRet-&setParam(type);
pRet-&autorelease();
pRet = NULL;
return NULL;
其中setTimingLayerPos方法设置TimingLayer所处的位置,setParam方法根据传入的CropsType类型设置TimingLayer的相关属性,如下代码所示:
void TimingLayer::setParam(CropsType type)
switch (type) {
case WHEAT:
nameLabel-&setString(&小麦&);// 标题标示
growUpTime = 60;
// 成熟时间
case CORN:
nameLabel-&setString(&玉米&);
growUpTime = 120;
case CARROT:
nameLabel-&setString(&胡萝卜&);
growUpTime = 180;
最后,我们来看看如何更新TimingLayer的进度条等状态,代码如下:
void TimingLayer::updateProgressBar(float dt)
counter++;
percent = 100 - float(counter) / float(growUpTime ) * 100; // 1
if(percent &= 100 && percent & 0 ) // 2
progressBar-&setPercentage(percent);
timeLabel-&setString(std::to_string(growUpTime - counter) + & 秒&);
this-&setTimeOut( true);
this-&unschedule(schedule_selector(TimingLayer::updateProgressBar));
this-&removeFromParent();
注意计算百分比时,计数器/成熟时间的值应该是一个“小数”类型,所以要将它们转换成float类型再进行计算。
当percent的值在0到100的范围内时,更新进度条的百分比,同时更新显示的剩余时间。
当percent超出范围时,设置timeOut属性为true,停止更新updateProgressBar函数,并且移除TimingLayer。
现在我们的计时面板就算创建好了,接下来我们就可以在播种时创建一个这样的计时器面板了。
添加计时面板
回到主场景,我们在GameScene中定义一个向量来保存游戏中所有的TimingLayer,这样遍历该向量可以控制所有计时面板的显示和隐藏。
Vector&TimingLayer *& timingV
在播种的时候,计时面板会同农作物幼苗一起“创建”,所以下面我们在播种的地方创建相应的计时面板,即像下面一样修改update函数:
switch (type)
case WHEAT:
map-&getLayer(&goodsLayer&)-&setTileGID(18, touchObjectPos);
createTimingLayer(WHEAT);
this-&removeChild(seedPanel);
case CORN:
map-&getLayer(&goodsLayer&)-&setTileGID(20, touchObjectPos);
createTimingLayer(CORN);
this-&removeChild(seedPanel);
case CARROT:
map-&getLayer(&goodsLayer&)-&setTileGID(22, touchObjectPos);
createTimingLayer(CARROT);
this-&removeChild(seedPanel);
其中createTimingLayer函数是用于创建计时面板的方法,其代码段如下所示:
void GameScene::createTimingLayer(CropsType type)
auto timingLayer = TimingLayer::create(touchObjectPos, type); // 1
auto screenPos = this-&convertToScreenCoord(touchObjectPos); // 2
timingLayer-&setPosition(screenPos);
timingLayer-&setVisible(false);
bgSprite-&addChild(timingLayer, 10); // 4
this-&timingVector.pushBack(timingLayer); // 5
在touchObjectPos位置上创建一个type类型的计时面板。
将touchObjectPos转换成场景坐标,并设置计时面板的位置到该处。
创建的计时面板初始状态下是不可以见的,所以要把它隐藏起来。
convertToScreenCoord方法转换的场景坐标是相对于整个地图的,所以我们把这个计时面板添加到地图的背景上。
把创建的计时面板压入向量中,便于统一管理。
显示隐藏计时面板
现在当我们“播种”的时候就可以为每一个幼苗一同创建对应的计时器了。这些计时面板都是隐藏的,那么在此之后,我们需要在再次选中幼苗时,让计时面板能显示出来。所以要在以下两个地方做些调整。
1、在触摸开始时,即onTouchesBegan函数中确定所有的计时面板是隐藏的:
for (TimingLayer* timingLayerTemp : timingVector)
timingLayerTemp-&setVisible(false);
2、在触摸结束时,即onTouchesEnded函数中加上以下判断:
else if(tileType == GROUD_CROP)
for (int i = 0; i & timingVector.size(); i++)
auto temp = timingVector.at(i);
auto pos = temp-&getTimingLayerPos();
if( pos == touchObjectPos)
temp-&setVisible(true);
更新成熟的农作物
到现在为止,运行游戏你会发现一切都差不多趋于完整,接下来当时间耗尽时,我们就该收获庄稼了。所以我们在Update函数中加入以下函来更新农作物的状态,便于后面可以收割。
void GameScene::updateRipeCrop()
for (int i = 0; i & timingVector.size(); i++)
auto temp = timingVector.at(i);
if( temp-&getTimeOut())
auto pos = temp-&getTimingLayerPos();
auto gid = map-&getLayer(&goodsLayer&)-&getTileGIDAt( pos);
switch (gid)
map-&getLayer(&goodsLayer&)-&setTileGID(19, pos);
tileType = TileType::CROP_HARVEST;
map-&getLayer(&goodsLayer&)-&setTileGID(21, pos);
tileType = TileType::CROP_HARVEST;
map-&getLayer(&goodsLayer&)-&setTileGID(23, pos);
tileType = TileType::CROP_HARVEST;
timingVector.eraseObject(temp);
updateRipeCrop函数遍历了计时面板向量timingVector,它通过得到每个计时面板的timeOut属性来判断农作物是否成熟。如果成熟则通过得到相应计时面板的位置来改变该位置处的瓦片块状态,从而实现将幼苗换成成熟的状态,这里18,20,22分别表示幼苗时期的农作物,19,21,23是成熟的农作物,这种写法当初没想太多,但请各位小伙们不要跟我学,:]嘻嘻,这样太不规范,久了会忘的,最好还是用标记标示,这里我动动嘴皮子就不改了。
收获农作物
最后,经过一番辛勤的劳作之后,是时候收获成果了。
所以此时我们就可以在检查到type等于CROP_HARVEST的时候时,创建收获界面了。这里收获农作物同移除瓦片(即RemoveLayer)的功能超级相似,相信大家依葫芦画瓢就可以实现,相关的代码也已上传,所以也就不在过多赘述了。
最终我们已经实现了之前所承诺的《全面农场》的功能,如下图所示:
播种、计时、收获整个操作的效果图如下:
目前,整个游戏已经有了大致的雏形,接下来需要做的逻辑原理大同小异,所以我就不再废话。我想对于一个游戏Demo来说,该SLG游戏教学的目的已经差不多达到了吧,感兴趣的同学请自行扩展,本游戏的源码我已上传到Github仓库,可以下载,同时也欢迎大家克隆、斧正、提交pr。
没有个人说明
5 4 2 2 2 1 1 1 1 1
9650 6974 6883 6307 5805 5804 5421 4694 3818 3499
115 麦田守望者}

我要回帖

更多关于 cocos2d x游戏开发 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信