Cocos2d-x跑酷游戏项目教程 Cocos2d-x跑酷游戏项目教程 cocos2d-x特性 cocos2d-x采用MVC三层架构 流程控制( flow control ):方便管理不同的场景之间的流程 精灵(Sprite) 方便快速的显示控制一切可见的元素 节点(Node) 采用树状的继承关系,方便管理不同层次的游戏元素 动作( Action) 应用于节点的各种动画效果,动作元素 特效,平面地图,菜单,用户输入,文档,MIT许可,基于OpenGL的深度优化 简单跑酷游戏项目内容: 1.模型,视图,控制之间的关系 2.实现触摸事件的加入 3.镜头移动的实现 4.碰撞以及碰撞修复 5.吃金币以及胜利,失败条件的实现 项目源码上线扩展功能预留: 1. 物理世界的加入 2. 加入声音,背景,粒子效果 3. 加入最好成绩存储 4. 引入firefly4手机网络游戏服务器,实现注册用户天梯比较排名 项目教程 Chapter1 屏幕适配 CCEGLView::sharedOpenGLView()->setDesignResolutionSize()//设计分辨率大小及模式CCDirector::sharedDirector()->setContentScaleFactor()//内容缩放因子deprecatedCCFileUtils::sharedFileUtils()->setSearchPaths()//资源搜索路径 CCEGLView::sharedOpenGLView()->getFrameSize()//屏幕分辨率CCDirector::sharedDirector()->getWinSize()//设计分辨率 CCDirector::sharedDirector()->getVisibleSize()//设计分辨率可视区域大小CCDirector::sharedDirector()->getVisibleOrigin()//设计分辨率可视区域起点 资源分辨率,设计分辨率,屏幕分辨率 资源分辨率,可以通过不同的设备,在程序中选择不同的图片包 从资源分辨率来说,通过接口setContentScaleFactor()决定了图片显示到屏幕的缩放因子 从屏幕分辨率来说,cocos2d-x也提供了对应的解决方案来解决这件事 kResolutionFixedHeight保持传入的设计分辨率高度不变,根据屏幕分辨率修正设计分辨率的宽度。 kResolutionFixedWidth保持传入的设计分辨率宽度不变,根据屏幕分辨率修正设计分辨率的高度。 在我的游戏里面,背景图的高需要全部显示,而宽方向可以裁减。 要实现这个目的,需要保证两个过程都是在宽方向裁减。 第一过程选择setContentScaleFactor(RH/DH) 第二过程有两个选择:kResolutionNoBorder和kResolutionFixedHeight Chapter2 模型,视图,控制 在跑酷游戏中我们建立多个碰撞对象,保证我们游戏的真实性,可玩性更高,抽离控制层,使用触摸事件完成对游戏的控制 MVC三层架构之间的关系 通过这样的逻辑关系设计游戏的整体逻辑,对于这样逻辑关系复杂,迭代层次比较多的软件系统,那么最好在设计之初就把游戏逻辑关系设计好,避免后面出现接口不足和函数之间耦合性太强,不利于维护。 Chapter3 跑酷游戏设计 跑酷游戏项目介绍 跑酷游戏是时下比较火爆的游戏类型,有多种设计方案,比如美国的基于U3D temple Run ,腾讯公司的天天酷跑等等,那么我们这里选择天天酷跑来说明整个游戏的设计: 首先跑酷游戏的游戏内容是, 1. 游戏精灵,在地图上跑,镜头视角移植定位在角色的3分之一处,随着精灵的移动而镜头移动。 2. 游戏精灵有站立,奔跑,跳起,落下基本状态,深入游戏开发,还有更多的状态,二次跳起,滑行等等。 3. 对于碰撞状态的设计来说,有前方,后方,上方,下方碰撞四种碰撞状态。 4. 通过碰撞状态我们能产生更多的游戏内容,比如掉入悬崖,吃金币等等丰富多彩的游戏内容 了解了这些基本设计我们就可以设计游戏整体架构和逻辑了: 1. 首先建立是主场景,作为游戏大部分显示界面的设计,绘制openGL游戏窗口,UI,精灵等等初始化和显示 2. 建立角色类,作为主要控制类,因为大部分的碰撞,跳起落下等等都是基于角色发生的,换句话说,没有角色这些都没有意义 3. 建立实体类,作为大部分其他游戏内容的设计,比如金币,怪物等等都可以继承实体类。 Chapter4 有限状态机 有限状态机(简称FSM)就是控制游戏对象在不同状态下该做什么事情的一个机制。 在这个游戏里我们定义了这样几个状态 首先是人物状态: 角色站立 ROLE_STAND_BY 角色跑动 ROLE_RUN 角色跳起 ROLE_JUMP 角色下落 ROLE_DOWN enumROLL_STATE{ ROLE_STAND_BY=0, ROLE_RUN, ROLE_JUMP, ROLE_DOWN, }; 还需要一个获取和设置角色状态的函数 setRoleCurrentState(ROLE_STATE_roleState); ROLE_STATE getRoleCurrentState(); 还有就是碰撞状态: 角色与下方的碰撞 ROLE_CURRENTBUTTOM 角色与上方的碰撞 ROLE_CURRENTTOP 角色与左方的碰撞 ROLE_CURRENTLEFT 角色与右方的碰撞ROLE_CURRENTRIGHT enumROLE_CURRENT_STATE{ ROLE_CURRENTBUTTOM=0, ROLE_CURRENTTOP, ROLE_CURRENTLEFT, ROLE_CURRENTRIGHT, }; 还需要一个获取和设置角色碰撞状态的函数 setRoleCurrentState(ROLE_CURRENT_STATE_roleCurrentState); ROLE_CURRENT_STATE getRoleCurrentState(); 通过定义状态我们就能在角色类里面轻松的完成角色状态之间的切换,通过切换角色之间的状态找到对应状态的函数,完成相应的事件 Chapter 5 TileMap工具的使用 在游戏世界里,我们需要绘制TileMap简化我们的操作,那么TileMap是一个免费的游戏砖块地图绘制工具,不仅仅是可以帮我们绘制地图还能帮助我们在地图中创建对象层,简化我们对对象的操作 通过建立砖块地图,然后建立对象层,然后在程序里加载建立的对象层,就能够完成对山体和物体的碰撞检测 主要获取流程: 建立砖块地图对象CCTMXTiledMap * m_map; 然后获取对象层,建立字典匹配砖块地图的属性,x,y坐标以及矩型框,产生碰撞事件 chapter6 镜头的移动和修复 让人物看起来运动有两种方案: 1. 人物不动,背景地图运动 2. 人物动,背景地图动,人物始终处于地图的某一个位置 第一种,优势在于非常简单,我们只需要两张背景图片,然后拼接移动就行了。劣势就是会使整个过程看起来非常生硬。 第二种,优势就是整个过程看起来非常流畅自然好用,但是实现较为附在,代码量高于第一种 那么在这个项目里,因为时参考的天天酷跑,所以并不怕麻烦,那么我们选择固定视角,移动地图的方式使人物移动 整个算法如下: 我们通过Tiledmap文件获取方块的数量 CCSizemapTiledNum= m_map->getMapSize(); 然后获取单个地图的大小 CCSizetiledSize =m_map->getTileSize(); 从而算出整个地图的总大小 CCSizemapSize =CCSize::CCSize(mapTiledNum.width * tiledSize.width, maTiledNum.height *tiledSize.height); 然后得到屏幕的大小: CCSizevisibleSize =CCDirector::shareDirector()->getVisibleSize(); 再得到角色的坐标 CCPointRole =m_Sprite->getPosition(); 如果主角的坐标小于屏幕的四分之一,就取屏幕的坐标,否则取主角的坐标 float x = rolePoint.x>visable.width/4?rolePoint.x:visable.width/4; x=x<mapsize.width-visable.width *="" .8?x:mapsize.width-visable.width="" .8;<="" div="" style="word-wrap: break-word; margin: 0px; padding: 0px;"> float y = rolePoint.y>visable.width/3?rolePoint.y:visable.height/3; y= y<mapsize.height 2?y:mapsize.height="" 2;<="" div="" style="word-wrap: break-word; margin: 0px; padding: 0px;"> 获取目标点 CCPointdesPoint = ccp(x,y); 算出屏幕四分之一点 CCPointcenterPoint = ccp(visable.width/5,visable.height/4); 计算屏幕的四分之一点和所要移动的目的点的距离 CCPointrltPoint = ccpSub(centerPoint, desPoint); 最后设置屏幕点的距离 m_map->setPosition(rltPoint); 最后实现的效果就是,当屏幕移动,主角也跟随场景移动,当到了地图边缘,场景不动主角动,最后知道移出屏幕,游戏结束 chapter 7山体碰撞检测 山体的碰撞需要我们从砖块地图上绘制出砖块地图的碰撞层 读取在TiledMap上的对象层 CCTMXObjectGroup* Group = getGamemap()->objectGroupNamed("coll"); CCObject * object = NULL; 遍历所有对象,获取山体的长宽高,建立山体的矩形框 CCARRAY_FOREACH(Group->getObjects(), object){ CCDictionary* dic = (CCDictionary*)object; float x = dic->valueForKey("x")->floatValue(); float y = dic->valueForKey("y")->floatValue(); float width = dic->valueForKey("width")->floatValue(); float height = dic->valueForKey("height")->floatValue(); CCRect box = CCRect(x, y, width,height); } 最后获取精灵的矩形框大小 CCRectmSpriteBox =m_sprite->boundingBox() 然后调用CCRect的方法看看是否发生碰撞 这里简介一些碰撞: 那么碰撞的产生其实就是看我们的精灵图片有没有相交当发上如上情况的时,就可以看作两个图片发生了碰撞,越是精细的碰撞条件就越需要多的矩型框去判断,比如格斗类游戏中,两个人物发生格斗,那么就需要对整个人物建立多个碰撞矩型框,拳头,头部,身体,腿部等,每一个部分都需要一个碰撞框,来确定到底是那一个部位发生了碰撞。 比较常用的碰撞算法就是AABB碰撞算法 矩形检测有着一项重要的优势,那就是速度快,尤其是在2D空间内。这里矩形特指AABB,即轴对齐包围盒。轴对齐包围盒的概念是:面法线皆平行于给定的坐标轴。在2D环境下如下图 关于AABB的储存有三种常规结构 1、取各坐标轴的最小值与最大值 2、最小顶点值与直径范围 3、中心点与各轴向半径 这三种表达有各自的适用范围,比如最小值与最大值储存虽然存在精度问题而且必须一次更新所有的参数但是在进行“排序扫掠算法”时有着数据结构上的优势。其余两种表达则在更新时有着只更新中心点/最小值点坐标即可的优势 当然还有更多的碰撞算法的方法,aabb就是比较常用的碰撞算法 chapter 8增强游戏可玩性 Box2D的引入 基本上大部分关于跑酷游戏的难点都已经抽离出来了,剩下的就是对跑酷游戏的丰富,和界面的优化等等,前面的内容我们基本上就能做出一个跑酷游戏,但是如果想要提高游戏的真实性,我们还可以加入Box2D物理引擎。 那么作为教程的最后一章,我们并不对这个程序本身加入物理引擎,因为物理引擎并不是所有游戏都存在的。 简介以下Box2D引擎,Box2D引擎是由美国暴雪公司的首席游戏开发师编写的,可见其专业程度,其次Box2D从公布以来,更新一直非常活跃,并且有C++版本,因为cocos2d-x 也是用C++写的,所以能够很好的对接。 下载地址就是www.box2d.org 我们把物理引擎下的环境,叫做物理世界,其规则都是遵守经典物理学定律的,你所有的操作都是在经典物理学体系下,所以运行在物理世界下的精灵并不受开发者控制,一切如真实的生活一样,当我们想要硬性的改变运行状态的时候,我们会发现,过程变得突兀了,莫名其妙的产生,游戏的过程也变得非常奇幻 用户通过对渲染世界的操作,将数据传递给物理世界,物理世界再将处理结果,返回给渲染世界产生动作 |
基于cocos2d-x的跑酷游戏项目教程