更好的阅读体验请前往《萝莉快跑》开发教程


配置:win7+Cocos2d-x.2.0.3+VS2012

目标读者:已经了解图形显示、动作、回调函数、定时器的用法。



前言:

《萝莉快跑》这个游戏demo资源由bill_man提供,demo版权归其所有。由于教程是按照笔者自己的游戏实现思路写的,代码跟bill_man提供的不同,读者可前往此处下载源码,如果只需看运行效果前往此处下载。本来想做个gif的,方便无法成功运行源码的童鞋看,无奈快做完的时候电脑崩溃,就不再重做了。此外,笔者所写demo有多处bug,比如英雄动作衔接不连贯,卷屏出现裂缝,如果读者排除了bug,还请留言告知,笔者会及时更正,方便大家学习。


一、基本知识点

1、动画

动画(CCAnimate)是动作(CCAction)的子类,以下为动画的使用流程:

  1. CCAnimation *anim = CCAnimation::create(); // 创建动画对象
  1. for (int i = 1; i <= 6; i++) //添加精灵帧
  2. {
  3. char path[20] = {0};
  4. sprintf(path,"s_%d.png",i);
  5. CCLOG(path);
  6. anim->addSpriteFrameWithFileName(path);
  7. }
  1. anim->setDelayPerUnit(0.1f); //设置帧率//参数为两帧的间隔时间
  1. anim->setRestoreOriginalFrame(true); //设置动画完成后是否恢复为初始帧
  1. CCAction *action = CCAnimate::create(anim); //把动画对象转为动画动作对象
  1. pNode->runAction(action); //执行动画
  1. pNode->stopAction(action); //停止动画

还有一些其他的对动作的操作,详见CCNode类。

2、CCLabelAtlas | 自定义字体

自定义字体在使用时,对提供字体的图片有一定的要求,图片中字符需按照ASCII码的顺序排列,并且每个字符占据同样的大小,如图:



也可以是这种:

自定义字体的使用方法如下:

  1. CCLabelAtlas* label = CCLabelAtlas::create("0000", "fonts.png", 48, 64, ' ');//CCLabelAtlas::create(初始字符串,图片路径,单个字符的宽,单个字符的高,图片的起始字符);
  1. pNode->addChild(label); //添加到结点树
  1. label->setString("23"); //设置显示的值

3、SimpleAudioEngine | 音乐播放

SimpleAudioEngine负责简单的音乐播放,使用流程:

  1. #include "SimpleAudioEngine.h" //添加头文件和命名空间,(确保解决方案已经包含libCocosDenshion.lib)
  2. using namespace CocosDenshion
  1. SimpleAudioEngine::sharedEngine()->preloadBackgroundMusic("background.mp3"); //预加载
  1. SimpleAudioEngine::sharedEngine()->playBackgroundMusic("background.mp3",true); //播放

还有暂停、结束等使用方式就不一一列举。

3、CCMenuItemToggle | 开关按键

关于开关按钮的使用详见wq右边的博文《Cocos2d-x开关按钮CCMenuItemToggle的使用》

4、其他

零碎知识点放这里。

 4.1 FlipX 是绕y轴水平翻转(左右翻转),注意是绕y轴,而不是绕x轴左右翻转。

 4.2 实现翻页效果用CCTransitionXXX来包装CCScene对象就可以了。

 4.3 CCSequence类create得到的不是CCSequence类对象,而是 CCFiniteTimeAction对象,不过这个问题好像是由于我的引擎版本过低所致。

 4.4 static 和 const 语法

 static const 是可以进行一次初始化

 const 是只能在声明的时候初始化

 static 声明的对象在程序生命周期内一直存在

二、基本问题解决

1、Z序控制

初学者往往在给图层定义z-order参数的时候随意设置数值,这种做法不利于插入新的图层,而且也不方便记忆。比较科学的方法是使用一个枚举类型来进行控制。这时只要在枚举变量设置中保证有序性,便可高枕无忧了。

例子:

  1. const enum Z_ORDER
  2. {
  3. Z_BACKGROUND = 1, //从上往下Z序增大排列
  4. Z_ANIMATION,
  5. Z_ATTRIBUTE,
  6. Z_GAME_OVER,
  7. };

2、卷屏

卷屏是由两张图片交替循环移动来实现的。除了第一次移动,图片都是以长度2W为周期循环移动,一个周期的移动情况如下图:



只有背景图1在第一次移动时不满一个周期,如下图:



因此,只要对图1的第一次区分处理,其他情况重复处理即可。

代码:

  1. // 在addVista()中写入
  2. //背景图1的第一次移动
  3. CCSprite *vista1 = CCSprite::create("back_5.png");
  4. vista1->setAnchorPoint(ccp(0,0));
  5. vista1->setPosition(ccp(0,0));
  6. vista1->runAction(
  7. CCSequence::create
  8. (
  9. CCMoveBy::create(size.width / BG_SPEED,ccp(-size.width,0)) //以速度BG_SPEED移动一个W的宽度
  10. ,CCCallFuncN::create(this,callfuncN_selector(BackgroundLayer::onBackgroudEnd)) //跳转到循环移动
  11. ,NULL
  12. )
  13. );
  14. this->addChild(vista1,0,TAG::TAG_VISTA1);
  1. //在scrollForever()中写入
  2. /*循环移动*/
  3. //屏幕右侧位置
  4. CCPlace *pos = CCPlace::create(ccp(size.width,0));
  5. //移动2W的距离
  6. float dY = 2 * size.width;
  7. CCMoveBy *move = CCMoveBy::create(dY / BG_SPEED ,ccp(-dY,0));
  8. //把图片放到屏幕右侧后向左移动2W距离
  9. CCFiniteTimeAction *seq = CCSequence::create(pos,move,NULL);
  10. //循环
  11. pSprite->runAction(CCRepeatForever::create((CCActionInterval *)seq));

注意:为了避免图片衔接缺少一个像素点的位置,应该让背景图的宽度比设计尺寸宽度大1个像素。而在处理时按照设计尺寸处理。

3、音乐播放

本例中主界面和场景界面音乐播放同步。音乐的设置放在主界面中。

用一个bPlaySound全局变量管理声音是否打开关闭。本来想直接用声量是否为零来代替bPlaySound,结果引擎在pc端没有实现对音量的控制,也就是说无法改变音量的值。

声音控制按键可以更改bPlaySound的值。而在主界面初始化时,根据bPlaySound来控制按键的现实状态。

三、实现流程

  1. /*基本代码文件
  2. -main
  3. -AppDelegate
  4. -heads.h
  5. -MainScene //主界面
  6. -GameScene //游戏界面
  7. ·BackgroundLayer //背景层
  8. ·HeroLayer //英雄层
  9. ·ScoreLayer //分数层
  10. ·GameOverLayer //游戏结束层

实现流程主要讲写代码的大体流程,先写哪些东西,再写哪些东西。至于每个代码文件具体写了些什么,参见下面第四节。

列出步骤:

1、主界面正确显示

第一步当然是设置实际尺寸与设计尺寸。

第二步书写heads头文件。

第三步便是将游戏主界面展示出来。保留界面跳转函数先不写,做个//TODO的标记。

2、游戏界面正确显示

第四步将游戏界面的壳写出来,保留init函数,实现create、scene等界面创建函数。

第五步将背景层的壳写出来,再让背景层实现卷屏。

第六步将背景层添加到游戏界面层,在AppDelegate文件中将启动界面设置为游戏界面。使得可以正确运行卷屏。

第七步在背景层添加道路和星星。背景层基本到此写完。

第八步开始写英雄层,先实现英雄层的run动作,之后添加Touch事件,再实现jump动作。再添加两者间的切换。dead动作先不管。

3、游戏界面碰撞处理

第九步在游戏界面处理英雄与星星的碰撞。在背景层添加对应用于处理碰撞的变量、函数等。

第十步在游戏界面处理英雄与道路的碰撞。

第十一步添加英雄层的dead动作。

第十二步添加游戏界面的死亡处理。

4、游戏失败的处理

第十三步实现游戏结束层。

第十四步在游戏界面添加对游戏结束层的控制。

5、游戏分数的处理

第十五步实现分数层。

第十六步在游戏界面添加对分数层的控制。

6、游戏音效添加

第十七步完成游戏音效的添加和声音开关按键。

四、各个类的具体设计

1、main.cpp | 实际尺寸

实际尺寸是指游戏在电脑上显示的尺寸。

  1. eglView->setFrameSize(480, 320);

2、AppDelegate.cpp | 设计尺寸

游戏所有资源以设计尺寸为基准,比如设计尺寸(960,640),那么大小960*640的背景图正好铺满。资源摆放的相对位置也是以设计尺寸为基准。

在AppDelegate.cpp文件里面添加设计尺寸,并进行场景的创建。下面是添加设计尺寸的方法:

  1. //在函数applicationDidFinishLaunching()中写入
  2. pDirector->getOpenGLView()->setDesignResolutionSize(960,640,kResolutionShowAll);

注意:先确保已经调用setOpenGLView,再对setDesignResolutionSize进行调用,完成设置。否则会报错。

3、heads.h | 头文件管理

heads.h可以方便地对头文件进行管理。

使用步骤:

  1. 在heads.h中包含所有会用到的类的头文件以及命名空间等
  2. 在自定义类的实现文件中直接包含heads.h。

注意:在自定义类的头文件中不使用heads.h,而是直接写上需要用到的头文件,并且避免在头文件中使用命名空间。

举例:

  1. // heads.h
  2. #ifndef _HEADS_H_ //确保头文件只被编译一次
  3. #define _HEADS_H_
  4. #include "cocos2d.h" //包含要用到的头文件
  5. #include "MainScene/MainScene.h" //包含自己写的类的头文件,放在子目录下的文件加上路径
  6. USING_NS_CC; //用到的命名空间
  7. #endif //_HEADS_H_
  8. -------------------------------------------------------
  9. // MainScene.h
  10. #include "cocos2d.h" //需要用到的头文件
  11. -------------------------------------------------------
  12. // MainScene.cpp
  13. #include "heads.h" //只需要加入这个头文件即可

这样,每次创建一个类,就只要在heads.h中包含一下就好了。不需要在每个需要用到这个类的文件中再添加一遍。谨遵此法,方便,安全,用过都说好_

4、MainScene类 | 主界面

主界面实现十分简单,按照Z序画出图片,再处理下按键的回调函数即可。

具体点说,在初始化时分别添加背景和按键,Z序用枚举变量控制。按键的回调函数中进行场景切换。

5、GameScene类 | 游戏界面

游戏界面用于管理游戏中的各个图层以及图层见的交互。本例中将游戏界面分出四个层:背景层、英雄层、分数层、游戏结束层。游戏界面在init中把四个层依次画出来,并在update中进行碰撞检测和处理,当游戏结束时,停止背景层的移动,英雄转为死亡动作,调出游戏结束层。

 5.1 碰撞检测

 碰撞检测的主逻辑在update函数中完成,具体的碰撞检测放在isCollisionWithRoad()和getCollisionStar()这两者函数中。

  1. // 在update()中写入
  2. //碰撞检测
  3. CCRect heroRect = getHeroRect();
  4. //道路检测
  5. if (!isCollisionWithRoad(heroRect))
  6. {
  7. gameOver();
  8. }
  9. //星星检测
  10. CCSprite *star = getCollisionStar(heroRect);
  11. if (star != NULL)
  12. {
  13. //消除星星
  14. ...
  15. //添加分数
  16. ...
  17. }
  1. /*检测是否与道路碰撞*/
  2. bool GameScene::isCollisionWithRoad(cocos2d::CCRect heroRect)
  3. {
  4. //在英雄run状态才进行检测
  5. if (((HeroLayer *)heroLayer)->getState() != HeroLayer::ACTION_STATE::RUN) return true;
  6. //遍历每一列道路,将其方块与英雄进行碰撞检测
  7. for (int i = 0; i < ((BackgroundLayer *)backgroundLayer)->getRoadBlockNum(); i++)
  8. {
  9. //识别砖块依附在哪一张背景图上,得到砖块对象
  10. CCSprite *block = NULL;
  11. block = (CCSprite *)backgroundLayer->getChildByTag(BackgroundLayer::TAG::TAG_VISTA1)->getChildByTag(i);
  12. float dx = backgroundLayer->getChildByTag(BackgroundLayer::TAG::TAG_VISTA1)->getPosition().x;
  13. if (block == NULL)
  14. {
  15. block = (CCSprite *)backgroundLayer->getChildByTag(BackgroundLayer::TAG::TAG_VISTA2)->getChildByTag(i);
  16. dx = backgroundLayer->getChildByTag(BackgroundLayer::TAG::TAG_VISTA2)->getPosition().x;
  17. }
  18. //获取砖块的矩形
  19. CCPoint blockPos = ccp(block->getPosition().x + dx,block->getPosition().y);
  20. CCSize blockSize = block->getContentSize();
  21. blockPos = ccp(blockPos.x - blockSize.width/2,blockPos.y - blockSize.height/2);
  22. CCRect blockRect = CCRectMake(blockPos.x,blockPos.y,blockSize.width,blockSize.height);
  23. CCLOG("blockRect:(%f,%f,%f,%f)",blockPos.x,blockPos.y,blockSize.width,blockSize.height);
  24. //检测
  25. if (blockRect.intersectsRect(heroRect))
  26. {
  27. return true;
  28. }
  29. }
  30. return false;
  31. }
  1. /*获取被英雄碰到的星星*/
  2. CCSprite *GameScene::getCollisionStar(cocos2d::CCRect heroRect)
  3. {
  4. //获取星星管理列表
  5. CCArray *starAry = ((BackgroundLayer *)backgroundLayer)->getStarArray();
  6. CCObject *starObj = NULL;
  7. //遍历星星列表
  8. CCARRAY_FOREACH(starAry,starObj)
  9. {
  10. CCSprite *star = (CCSprite *)starObj;
  11. //获得星星的矩阵
  12. CCSize size = star->getContentSize();
  13. float scale = star->getScale();
  14. size = CCSizeMake(size.width * scale,size.height * scale);
  15. CCPoint point = star->getPosition();
  16. point = ccp(point.x - size.width/2,point.y - size.height/2);
  17. CCRect starRect = CCRectMake(point.x,point.y,size.width,size.height);
  18. //碰撞检测
  19. if (starRect.intersectsRect(heroRect))
  20. {
  21. return star;
  22. }
  23. }
  24. return NULL;
  25. }

 5.2 游戏结束层的处理

 由于游戏结束层是直到游戏结束的时候才出现的,本例的做法是,在初始化的时候先创建游戏结束层并添加到游戏界面层。当游戏结束时,调用游戏结束层的show函数。具体的可见性以及Touch事件屏蔽的控制由游戏结束层自行处理。

6、BackgroundLayer类 | 背景层 ☆

背景层分为远景、星星、和道路三个部分。背景层的处理虽然逻辑清晰,但是具体的处理相对来说有些混乱,只能是差强人意。原因恐怕在笔者对引擎动作的处理还未能深入所致。看官凑个热闹就是了。

 6.1 远景

 远景由两张一模一样的首尾能无缝衔接的图构成。循环执行引擎的MoveBy动作完成卷屏的效果。具体实现参见卷屏

 6.2 星星

 星星利用schedule函数每隔固定时间产生。利用CCMoveBy函数移动星星,当移动结束后利用回调函数销毁星星。

于此同时,星星的产生和销毁用CCArray列表进行记录。便于在碰撞检测时遍历所有星星。CCArray管理对象的用法参见Cocos2d-x游戏实例《忍者飞镖》之对象管理

 6.3 道路

 《萝莉快跑》中道路是由许多个小方框拼接而成的。先以列为单位,将不同的地形分为不同的列。再将图形一列一列地拼到远景图上,是的,就是拼到远景图上。这种处理方式下只需在初始时进行道路的设置即可。

为了能够对道路进行碰撞检测,用计数器,给每一列地形都做了编号。

7、HeroLayer类 | 英雄层

英雄层主要是对英雄不同的动作进行管理。英雄有三个动作:奔跑、跳跃、死亡。

英雄是个CCSprite对象,在初始化的时候添加至图层。之后只要执行runAction(action)中不同的action即可改变动作状态。

动作转换 转换条件
行走 >> 跳跃 Touch事件
跳跃 >> 行走 跳跃结束
行走 >> 死亡 踩空

为了避免在一个动作没有做完的时候发生状态转换,在改变状态前进行判断。仅当目标状态与当前状态不同时才进行转换。

8、ScoreLayer类 | 分数层

分数层管理分数显示与分数变更,提供addScore函数来添加分数。分数的显示用CCLabelAtlas来实现。CCLabelAtlas的使用参见CCLabelAtlas | 自定义字体

9、GameOverLayer类 | 游戏结束层

游戏结束层就一个GameOver的图片标题和一个返回主界面的按键。分别在init和show中改变了图层的显示状态和按键的使能状态。


转载请注明出处!

唐衣可俊的——Cocos2d-x开发教程——《萝莉快跑》

http://www.cnblogs.com/tangyikejun/p/3903366.html

Cocos2d-x开发教程——《萝莉快跑》的更多相关文章

  1. Cocos2dx系列笔记7:一个简单的跑酷游戏《萝莉快跑》的消化(附下载)

    懒骨头(http://blog.csdn.com/iamlazybone) 或许有天 我们羡慕和崇拜的人 因为我们的努力 也会来了解我们 说不定 还会成为好友 骨头喜欢这样与哲哲共勉 多少个夜晚 一张 ...

  2. 【python游戏编程之旅】第九篇---嗷大喵快跑小游戏开发实例

    本系列博客介绍以python+pygame库进行小游戏的开发.有写的不对之处还望各位海涵. 前几期博客我们一起学习了,pygame中的冲突检测技术以及一些常用的数据结构. 这次我们来一起做一个简单的酷 ...

  3. 设计师和开发人员更快完成工作需求的20个惊人的jqury插件教程(上)

    [转] 设计师和开发人员更快完成工作需求的20个惊人的jqury插件教程(上) jquery的功能总是那么的强大,用他可以开发任何web和移动框架,在浏览器市场,他一直是占有重要的份额,今天,就给大家 ...

  4. Unity 2D游戏开发教程之使用脚本实现游戏逻辑

    Unity 2D游戏开发教程之使用脚本实现游戏逻辑 使用脚本实现游戏逻辑 通过上一节的操作,我们不仅创建了精灵的动画,还设置了动画的过渡条件,最终使得精灵得以按照我们的意愿,进入我们所指定的动画状态. ...

  5. php支付宝在线支付接口开发教程【转】

    php支付宝在线支付接口开发教程 这篇文章主要为大家详细介绍了php支付宝在线支付接口开发教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下   1.什么是第三方支付 所谓第三方支付,就是一些和各 ...

  6. .Net魔法堂:史上最全的ActiveX开发教程——部署篇

    一.前言 接<.Net魔法堂:史上最全的ActiveX开发教程——发布篇>,后我们继续来部署吧! 二. 挽起衣袖来部署   ActiveX的部署其实就是客户端安装ActiveX组件,对未签 ...

  7. 微信公众账号开发教程(四)自定义菜单(含实例源码)——转自http://www.cnblogs.com/yank/p/3418194.html

    微信公众账号开发教程(四)自定义菜单 请尊重作者版权,如需转载,请标明出处. 应大家强烈要求,将自定义菜单功能课程提前. 一.概述: 如果只有输入框,可能太简单,感觉像命令行.自定义菜单,给我们提供了 ...

  8. Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门

    Easyui + asp.net mvc + sqlite 开发教程(录屏)适合入门 第一节: 前言(技术简介) EasyUI 是一套 js的前端框架 利用它可以快速的开发出好看的 前端系统 web ...

  9. canvas小程序-快跑程序员

    canvas不用说html5带来的好东西,游戏什么的,么么哒 记得有一天玩手机游戏,就是一个跳跃过柱子那种,其实元素很简单啊,app能开发,借助html5 canvas也可以啊, 于是就开始了. -- ...

随机推荐

  1. C# 利用反射更改父类公开对象

    需求 : 有一个保存数据库字段的基础类,现在要加个状态返回给前端,但是又不能改基础类: class BaseA { public string Name { get; set; } } class A ...

  2. centos7下ldap+kerberos实现单点登陆

    一. LDAP概念 http://wiki.jabbercn.org/index.php/OpenLDAP2.4%E7%AE%A1%E7%90%86%E5%91%98%E6%8C%87%E5%8D%9 ...

  3. git输错用户名和密码报错

    最近在使用git clone命令操作时一直报错,报错消息如下: remote: Coding 提示: Authentication failed! 认证失败,请确认您输入了正确的账号密码 fatal: ...

  4. 移动端Vue图片获取,压缩,预览组件-upload-img(H5+获取照片,压缩,预览)

    组件示例图 组件代码 upload_img.vue <div id="uploadImg"> <div class="upload_image_box& ...

  5. poj 2513 Colored Sticks (trie树+并查集+欧拉路)

    Colored Sticks Time Limit: 5000MS   Memory Limit: 128000K Total Submissions: 40043   Accepted: 10406 ...

  6. .get的取值特点:.get只起到取值的作用 不能对原值修改

    #银行支付接口 def pay_interface(username,cost): user_dic=db_handler.select(username) if user_dic.get('bala ...

  7. 《Java Spring框架》基于IDEA搭建Spring源码

    第一步: IDEA :IntelliJ IDEA 2018.1.4    :JDK安装(必须1.8或者以上),IDEA安装(过程省略). 第二步: Gradle:下载地址:https://servic ...

  8. css重点知识和bug解决方法

    1.图片向下撑大3像素问题 在一个盒子里面放一张图片,默认情况下,图片会向下撑大3像素,有以下几种解决方法: 1.1 给图片添加display:block: 1.2 给图片添加 float:left: ...

  9. python的memory_profiler模块使用

    本文主要介绍了python内存分析工具: memory_profiler,可以展示每一行代码执行所增加的内存,方便做内存调优和排除bug memory_profiler是第三方模块,需要安装才能使用 ...

  10. python探索微信朋友信息

    一.itchat itchat是一个开源的微信个人号接口,这一次就用它来来玩玩. 在使用之前,先下载,老规矩通过 pip install itchat 即可安装. 想要获取朋友圈信息,只需要几行代码就 ...