如何制作一个类似Tiny Wings的游戏(2) Cocos2d-x 2.1.4
在第二篇《如何制作一个类似Tiny Wings的游戏》基础上,增加添加主角,并且使用Box2D来模拟主角移动,原文《How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 2》,在这里继续以Cocos2d-x进行实现。有关源码、资源等在文章下面给出了地址。
步骤如下:
1.使用上一篇的工程;
2.创建Box2D世界,并且添加一些代码来进行调试绘制,同时添加一些形状。打开HelloWorldScene.h文件,添加如下定义:
1 |
#define PTM_RATIO
. |
然后,添加如下变量:
1 |
b2World *_world;
|
这里声明了一个像素到米的转换比率,为32.0。这个是用来进行Box2D单位(米)和Cocos2D单位(点)之间的转换。另外,还定义了一个世界对象_world变量。打开HelloWorldScene.cpp文件,添加如下方法:
1 |
void HelloWorld::setupWorld()
{ b2Vec2 gravity = b2Vec2( .0f, - .0f); bool doSleep = true; _world = new b2World(gravity); _world->SetAllowSleeping(doSleep); } void HelloWorld::createTestBodyAtPostition(CCPoint position) b2CircleShape testBodyShape; |
这里setupWorld方法创建了一个有重力的世界,略小于地球标准-9.8米/秒2。createTestBodyAtPostition方法创建了一个测试对象,即一个25点半径的圆。在每次触摸屏幕时,都将使用这个方法来创建测试对象,但这仅是用来测试。在onEnter方法里面,最上面添加如下代码:
1 |
this->setupWorld();
|
修改_terrain = Terrain::create();为如下代码:
1 |
_terrain = Terrain::createWithWorld(_world);
|
这里调用了创建重力世界的方法,还修改了创建地形类的方法,传递了Box2D世界对象。这样山丘就可以使用这个对象来创建Box2D物体。在update方法里面,最上面添加如下代码:
1 |
static
double UPDATE_INTERVAL = .0f / .0f; static double MAX_CYCLES_PER_FRAME = ; static double timeAccumulator = ; timeAccumulator += dt; int32 velocityIterations = |
这里每次更新,调用了_world->Step,来运行Box2D的物理仿真。注意,这里使用固定时间步长来实现运行物理仿真,而这优于可变时间步长。在ccTouchesBegan方法里面,最后面添加如下代码:
1 |
CCTouch *anyTouch =
static_cast<CCTouch*>(pTouches->anyObject()); CCPoint touchLocation = _terrain->convertTouchToNodeSpace(anyTouch); this->createTestBodyAtPostition(touchLocation); |
这个方法用来当触摸屏幕的时候,创建一个Box2D物体。另外,这仅仅是为了测试Box2D是否能够正常运行。注意,这里得到的触摸坐标是在地形坐标内。这是因为地形会滚动,而所想要的是地形内的位置,而不是屏幕上的位置。打开Terrain.h文件,添加头文件声明,代码如下:
1 |
#include
"Box2D/Box2D.h" #include "GLES-Render.h" |
GLES-Render用来实现Box2D的可调试绘制,此文件位于“\cocos2d-x-2.1.4\samples\Cpp\TestCpp\Classes\Box2DTestBed”,拷贝GLES-Render.h、GLES-Render.cpp文件到工程目录,添加进工程。在Terrain.h文件里,添加如下代码:
1 |
static Terrain *createWithWorld(b2World *world);
b2World *_world; |
打开Terrain.cpp文件,添加头文件声明,代码如下:
1 |
#include
"HelloWorldScene.h" |
在构造函数里面,添加如下代码:
1 |
_world =
NULL; _body = NULL; _debugDraw = NULL; |
添加如下方法:
1 |
void Terrain::resetBox2DBody()
{ if (_body) { return; } CCPoint p0 = _hillKeyPoints[ b2BodyDef bd; b2EdgeShape shape; |
这仅仅是一个辅助方法,用来创建沿着山丘底部的一个Box2D物体,代表“地面”。因此可以让所添加的物体与“地面”物体进行碰撞,而不是无休止地掉落。这只是一个临时的方法,稍后会修改成山丘模型。就目前而言,它所做的只是将第一个和最后一个顶峰点的x轴坐标,用一条边连接起来。接下来,在resetHillVertices方法里面,在代码prevToKeyPointI = _toKeyPointI;之后,添加如下代码:
1 |
this->resetBox2DBody();
|
每当山丘顶点被重置的时候,调用resetBox2DBody方法来创建Box2D物体,此物体代表可见部分的山丘。眼下,这个物体没有进行改变(它只是添加了一条线,当作地面),但后面会进行修改这个物体为可见部分山丘的模型。继续添加如下方法:
1 |
Terrain * Terrain::createWithWorld(b2World *world)
{ Terrain *pRet = new Terrain(); if (pRet && pRet->initWithWorld(world)) { pRet->autorelease(); } else { CC_SAFE_DELETE(pRet); } return pRet; } bool Terrain::initWithWorld(b2World *world) _world = world; bRet = return bRet; void Terrain::setupDebugDraw() |
然后在draw方法里面,最后面添加如下代码:
1 |
_world->DrawDebugData();
|
setupDebugDraw方法和draw方法里面调用DrawDebugData方法,是设置可调试绘制Box2D对象所必需的。另外,将可调试绘制代码放在Terrain.cpp文件中,而不是HelloWorldScene.cpp文件中,是因为这个游戏的滚动效果是通过移动地形来实现的。因此,要将Box2D中的坐标系与屏幕的可见坐标系匹配起来,就需要将可调试绘制代码放在Terrain.cpp文件中。最后,编译运行,触摸屏幕,就可以看到圆形的物体落入场景里面,如下图所示:
3.Box2D中塑造山丘。现在已经有一个Box2D的形状来代表屏幕底部的地面,但真正想要的是一个代表山丘的形状。幸运的是,这很容易办到,因为已经有足够的先前条件:
一个山丘顶点数组(_borderVertices)。之前的文章中,在resetHillVertices方法里面已经创建了这个数组。
一个方法(resetBox2DBody)。每当顶点变化的时候,都会调用这个方法。
所以只需要修改resetBox2DBody方法,为_borderVertices中的每相邻的点创建一个边,修改代码为如下:
1 |
void Terrain::resetBox2DBody()
{ if (_body) { _world->DestroyBody(_body); } b2BodyDef bd; b2EdgeShape shape; |
这种新的实现方式,首先检查是否已经存在Box2D物体,如果是的话,就销毁原来的物体。然后,创建一个新的物体,循环遍历山丘顶点数组,对于每相邻的两个点,创建一条边进行连接起来。编译运行,就可以看到沿着山丘斜坡,有一个Box2D物体,如下图所示:
4.添加海豹。下载本游戏所需的资源,将资源放置"Resources"目录下。添加英雄类Hero,派生自CCSprite类。文件Hero.h代码如下:
1 |
#pragma once
#include "cocos2d.h" #include "Box2D/Box2D.h" class Hero : static Hero *createWithWorld(b2World *world); void update( CC_SYNTHESIZE_READONLY(bool, _awake, Awake); private: |
这里只是声明了Box2D头文件,然后定义了一些方法和变量。文件Hero.cpp代码如下:
1 |
#include
"Hero.h" #include "HelloWorldScene.h" using namespace cocos2d; Hero::Hero( Hero::~Hero( Hero * Hero::createWithWorld(b2World *world) bool Hero::initWithWorld(b2World *world) _world = world; bRet = return bRet; void Hero::update( void Hero::createBody() CCPoint startPosition = ccp( b2BodyDef bd; b2CircleShape shape; b2FixtureDef fd; _body->CreateFixture(&fd); |
这个createBody方法创建了一个圆形的形状来代表海豹。这与之前写的createTestBodyAtPosition方法几乎完全一样,除了半径是基于海豹大小(实际上小于海豹大小,这样碰撞检测会更好)。此外,摩擦设置为0.2(因此海豹是非常滑的),恢复设置为0(使得海豹撞击地面时,不会反弹)。还设置了物体的一些线性阻尼,所以海豹随着时间推移会慢慢减缓速度,并且设置了物体为固定旋转,因为这个游戏并不需要它进行旋转。initWithWorld方法初始化精灵为特定的精灵帧(seal1.png),保存世界对象指针,然后调用createBody方法。update方法以Box2D物体的位置来更新海豹精灵的位置,同时根据物体的速度来更新海豹的旋转。打开Terrain.h文件,添加如下代码:
1 |
CC_SYNTHESIZE_RETAIN(cocos2d::CCSpriteBatchNode*, _batchNode, BatchNode);
|
打开Terrain.cpp文件,在initWithWorld方法里面,最后面添加如下代码:
1 |
_batchNode = CCSpriteBatchNode::create(
"TinySeal.png"); this->addChild(_batchNode); CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile( "TinySeal.plist"); |
这里只是简单地为TinySeal.png精灵表单创建批处理节点,然后从TinySeal.plist加载精灵帧定义,放入精灵帧缓存。打开HelloWorldScene.h文件,添加头文件声明:
1 |
#include
"Hero.h" |
然后,添加如下变量:
1 |
Hero *_hero;
|
打开HelloWorldScene.cpp文件,在onEnter方法里面,最后面添加如下代码:
1 |
_hero = Hero::createWithWorld(_world);
_terrain->getBatchNode()->addChild(_hero); |
在update方法里面,在代码float PIXELS_PER_SECOND = 100;开始,修改为如下代码:
1 |
//float PIXELS_PER_SECOND = 100;
//static float offset = 0; //offset += PIXELS_PER_SECOND * dt; _hero->update(dt); float offset = _hero->getPosition().x; |
编译运行,现在可以看到一只海豹在屏幕左边,如下图所示:
但是,有一些奇怪,海豹的一部分在屏幕之外。将它往右边移动一些,将会更好。打开Terrain.cpp文件,修改setOffsetX方法为如下:
1 |
void Terrain::setOffsetX(
float newOffsetX) { CCSize winSize = CCDirector::sharedDirector()->getWinSize(); _offsetX = newOffsetX; |
这里增加屏幕宽度1/8的偏移,使得海豹可以出现在右边一些。编译运行,现在可以看到海豹的样子了,如下图所示:
5.使海豹移动。采取的策略如下:
第一次触摸屏幕,让海豹往右边跳起来一些,让它开始运动。
每当触摸不放,给海豹施加一个外力,推它下来。遇到下坡时,会使它的速度更快,这将使它到达下一个山头时能够飞起来。
添加一些代码,确保海豹至少移动固定的距离,不希望海豹被卡住。
打开Hero.cpp文件,添加如下方法:
1 |
void Hero::wake()
{ _awake = true; _body->SetActive( true); _body->ApplyLinearImpulse(b2Vec2( , ), _body->GetPosition()); } void Hero::dive() void Hero::limitVelocity() const |
wake方法对物体应用了一个右上角的冲量,使得海豹开始移动。dive方法对物体应用了一个强烈的向下冲量,和一个稍微向右的冲量。向下的冲量会导致海豹撞上山丘,山丘斜坡越大的,一旦到达下一个山头,海豹将会飞得更高。limitVelocity方法确保海豹的移动速度,在x轴方向至少5m/s2,在y轴方向不小于-40m/s2。最后,打开HelloWorldScene.h文件,添加如下变量:
1 |
bool _tapDown;
|
打开HelloWorldScene.cpp文件,在构造函数里面,添加如下代码:
1 |
_tapDown =
false; |
在update方法里面,最上面添加如下代码:
1 |
if (_tapDown)
{ if (!_hero->getAwake()) { _hero->wake(); _tapDown = false; } else { _hero->dive(); } } _hero->limitVelocity(); |
修改ccTouchesBegan方法,为如下代码:
1 |
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent)
{ this->genBackground(); _tapDown = true; } |
添加如下方法:
1 |
void HelloWorld::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{ _tapDown = false; } void HelloWorld::ccTouchesCancelled(CCSet *pTouches, CCEvent *pEvent) |
编译运行,现在可以看到海豹飞了起来,如下图所示:
6.修复摇晃的海豹。可能会注意到,当海豹沿着地面滑动的时候,会出现身体摇晃的情况。解决问题的一种方法是采用之前的线性速度与当前速度的加权平均值。打开Hero.h文件,添加如下代码:
1 |
#define NUM_PREV_VELS
|
添加如下变量:
1 |
b2Vec2 _prevVels[NUM_PREV_VELS];
int _nextVel; |
打开Hero.cpp文件,在构造函数里面,添加如下代码:
1 |
memset(_prevVels,
, NUM_PREV_VELS * sizeof(b2Vec2)); _nextVel = ; |
修改update方法为如下:
1 |
void Hero::update(
float dt) { this->setPosition(ccp(_body->GetPosition().x * PTM_RATIO, _body->GetPosition().y * PTM_RATIO)); b2Vec2 vel = _body->GetLinearVelocity(); b2Vec2 weightedVel = vel; for ( float angle = ccpToAngle(ccp(weightedVel.x, weightedVel.y)); |
这里采用之前的5个速度做加权平均值,而不是仅仅使用线性速度。编译运行,现在可以看到一个滑动更加顺畅的海豹了,如下图所示:
7.缩小画面。Tiny Wings游戏中有一个很酷的效果,就是飞的越高,屏幕画面就会越小。这不仅可以使英雄一直在画面中,而且在真正移动的时候,有一种酷酷的感觉。打开HelloWorldScene.cpp文件,在update方法里面,在代码_hero->update(dt);后面添加如下代码:
1 |
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
float scale = (winSize.height * / ) / _hero->getPosition().y; if (scale > ) { scale = ; } _terrain->setScale(scale); |
如果英雄在winSize.height的3/4处的话,拉伸比例为1。如果小于的话,拉伸比例增大。如果大于的话,拉伸比例减小,就有画面缩小的感觉。编译运行,现在飞高就能看到画面的缩放了,如下图所示:
如果不容易达到飞那么高,可以将Hero.cpp文件里的dive方法,增大向下冲量,就比较容易飞高起来。
8.动画和音乐。打开Hero.h文件,添加如下变量:
1 |
cocos2d::CCAnimation *_normalAnim;
cocos2d::CCAction *_normalAnimate; |
打开Hero.cpp文件,在构造函数里面,添加如下代码:
1 |
_normalAnim =
NULL; _normalAnimate = NULL; |
在析构函数里面添加如下代码:
1 |
CC_SAFE_RELEASE_NULL(_normalAnim);
|
添加如下方法:
1 |
void Hero::nodive()
{ this->runNormalAnimation(); } void Hero::runForceAnimation() void Hero::runNormalAnimation() |
在initWithWorld方法里面,最后面添加如下代码:
1 |
_normalAnim = CCAnimation::create();
_normalAnim->retain(); _normalAnim->addSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( "seal1.png")); _normalAnim->addSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName( "seal2.png")); _normalAnim->setDelayPerUnit( .1f); |
这里为海豹的正常移动创建了动画,以及一个运行该动画的方法。俯冲的动画其实只是一个精灵帧,所以添加一个辅助方法来进行设置。打开HelloWorldScene.cpp文件,在onEnter方法里面,最后面添加如下代码:
1 |
CocosDenshion::SimpleAudioEngine::sharedEngine()->playBackgroundMusic(
"TinySeal.mp3", true); |
在update方法里面,代码_hero->limitVelocity();上面,添加如下代码:
1 |
else
{ _hero->nodive(); } |
在ccTouchesBegan方法里面,最后面添加如下代码:
1 |
_hero->runForceAnimation();
|
在ccTouchesEnded方法和ccTouchesCancelled方法里面,最后面添加代码:
1 |
_hero->runNormalAnimation();
|
最后,打开Terrain.cpp文件,在draw方法里面,注释掉_world->DrawDebugData();代码。编译运行,最终如下图所示:
参考资料:
1.How To Create A Game Like Tiny Wings with Cocos2D 2.X Part 2 http://www.raywenderlich.com/32958/how-to-create-a-game-like-tiny-wings-with-cocos2d-2-x-part-2
2.(译)如何制作一个类似tiny wings的游戏:第二部分(完) http://www.cnblogs.com/andyque/archive/2011/07/02/2095527.html
非常感谢以上资料,本例子源代码附加资源下载地址:http://download.csdn.net/detail/akof1314/5800479
如文章存在错误之处,欢迎指出,以便改正。
如何制作一个类似Tiny Wings的游戏(2) Cocos2d-x 2.1.4的更多相关文章
- 如何制作一个类似Tiny Wings的游戏 Cocos2d-x 2.1.4
在第一篇<如何使用CCRenderTexture创建动态纹理>基础上,增加创建动态山丘,原文<How To Create A Game Like Tiny Wings with Co ...
- 怎样制作一个相似Tiny Wings的游戏 Cocos2d-x 2.1.4
在第一篇<怎样使用CCRenderTexture创建动态纹理>基础上,添加�创建动态山丘,原文<How To Create A Game Like Tiny Wings with C ...
- 制作一个类似苹果VFL的格式化语言来描述UIStackView
在项目中总是希望页面上各处的文字,颜色,字体大小甚至各个视图控件布局都能够在发版之后能够修改以弥补一些前期考虑不周,或者根据统计数据能够随时进行调整,当然是各个版本都能够统一变化.看到这样的要求后,第 ...
- [0]尝试用Unity3d制作一个王者荣耀(持续更新)->游戏规划
太得闲了于是想写个农药,虽然可能会失败但是还是要试一试. 因为是自学的不是Unity专业的可能表达语言会有些不标准!望见谅! 结构: 以组件式(比如说摇杆控制和玩家部分的编写是分离的,可以自由拼装)作 ...
- 怎样制作一个横版格斗过关游戏 Cocos2d-x 2.0.4
本文实践自 Allen Tan 的文章<How To Make A Side-Scrolling Beat 'Em Up Game Like Scott Pilgrim with Coco ...
- three.js 制作一个三维的推箱子游戏
今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏.在线案例请点击博客原文 ...
- 教你如何用python和pygame制作一个简单的贪食蛇游戏,可自定义
1.效果图 2.完整的代码 #第1步:导出模块 import pygame, sys, random from pygame.locals import * # 第2步:定义颜色变量,在pygame中 ...
- 如何制作一个类似jquery插件的vue插件
vue拿来写插件,会不会太那啥? 请跟我念,"不会,符合业务需求才是你的老板最想要的." 如何封装一个可以全局调用的vue插件 其原理其实相当简单,通过new Vue(vueCom ...
- 使用CocosSharp制作一个游戏 - CocosSharp中文教程
注:本教程翻译自官方<Walkthrough - Building a game with CocosSharp>,官方教程有很多地方说的不够详细,或者代码不全,导致无法继续,本人在看了G ...
随机推荐
- Struts2 注解(转)
转自:http://blog.csdn.net/wwwqvod/article/details/6214431 也叫Zero Configuration(零配置),它省去了写xml文件的麻烦,可以直接 ...
- Realview MDK 中不用手动开中断的原因
startup.s启动代码文件: ; Enter Supervisor Mode and set its Stack Pointer MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR ...
- perl 继承写法
use base (Critter); 和 BEGIN{ require Critter; @ISA=qw/Critter/; } 这两种写法是等价
- jQuery粘性跟随滚动条滚动的导航栏源代码下载
jQuery粘性跟随滚动条滚动的导航栏源代码下载 作者:网页模板 大小:0.005MB 点击次数:3494 发布时间:2014-03-07 12:55 分享到:0 特效介绍 jQuery粘性跟随滚动条 ...
- xrdp的rdp前端无法连接Windows 2008的问题
xrdp使用rdp前端,无法连接2008,但连接2003是可以的.连接2008的时候,会在客户端发送Client Info PDU后主动RST掉连接.如下图 开始以为是客户端发送Client Info ...
- poj2909 || poj2262
#include <stdio.h> #include <stdlib.h> #include<math.h> int isPri(int a, int b) { ...
- 【Stackoverflow好问题】Java += 操作符实质
问题 直到今天,我都一直以为: i += j 等同于 i = i + j; 但如果有: int i = 5; long j = 8; 这时 i = i + j不能编译.但i += j却能够编译.这说明 ...
- poj - 4045 - Power Station
题意:一棵有n个结点的树,要取其中的一个结点,使得该结点到其他所有结点的距离和dis最小,即损耗I * I * R * dis最小,输出最小损耗和该结点(有多个的话按结点编号从小到大输出)(3 < ...
- 配置启动挂载:fstab文件具体解释
fstab文件介绍 fstab文件包括了你的电脑上的存储设备及其文件系统的信息.它是决定一个硬盘(分区)被如何使用或者说整合到整个系统中的文件. 详细来说:用fstab能够自己主动挂载各种文件系统格式 ...
- XCode常用快捷键(转)
刚开始用Xcode是不是发现以前熟悉的开发环境的快捷键都不能用了?怎么快捷运行,停止,编辑等等.都不一样了.快速的掌握这些快捷键,能提供开发的效率. 其实快捷键在Xcode的工具栏里都标注有,只是有的 ...