原文在
http://blog.csdn.net/akof1314/article/details/8549150
本文实践自 Allen Tan 的文章《
How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 1
》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.0.4进行学习和移植。在这篇文章,将会学习到如何制作一个简单的横版格斗过关游戏。在这当中,学习如何跟踪动画状态、碰撞盒、添加方向键、添加简单敌人AI和更多其它的。
步骤如下:
1.新建Cocos2d-win32工程,工程名为”
PompaDroid
“,去除”
Box2D
“选项,勾选”
Simple Audio Engine in Cocos Den
shion
“选项;
2.添加游戏场景类
GameScene
,派生自
CCScene
类。添加
GameLayer
类和
HudLayer
类,派生自
CCLayer
类。删除
HelloWorldScene.h
和
HelloWorldScene.cpp
文件。
3.文件
GameScene.h
代码如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
#pragma once #include “cocos2d.h” #include “GameLayer.h” #include “HudLayer.h” CC_SYNTHESIZE(GameLayer*, _gameLayer, GameLayer); |
文件
GameScene.cpp
代码如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
#include “GameScene.h” using namespace cocos2d; GameScene::GameScene( GameScene::~GameScene( _gameLayer = GameLayer::create(); bRet = |
4.
HudLayer
类增加一个方法:
|
1
|
|
CREATE_FUNC(HudLayer);
|
和
GameLayer
类增加一个方法:
|
1
|
|
CREATE_FUNC(GameLayer);
|
5.修改
AppDelegate.cpp
文件,代码如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 |
|
//#include “HelloWorldScene.h” #include “GameScene.h” |
6.编译运行,此时只是空空的界面。
7.下载本游戏所需资源,将资源放置”
Resources
“目录下;
|
1
2 3 4 |
|
bool init(); void initTileMap(); cocos2d::CCTMXTiledMap *_tileMap; |
打开
GameLayer.cpp
,在构造函数,添加如下代码:
|
1
|
|
_tileMap =
NULL ; |
添加如下代码:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
bool GameLayer::init() { bool bRet = false ; do { CC_BREAK_IF(!CCLayer::init()); bRet = |
对所有图层进行
setAliasTexParameters
设置,该方法是关闭抗锯齿功能,这样就能保持像素风格。
10.编译运行,可以看到地图显示在屏幕上,如下图所示:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
|
#pragma once #include “cocos2d.h” #include “Defines.h” |
打开
ActionSprite.cpp
文件,构造函数如下:
|
1
2 3 4 5 6 7 8 |
|
ActionSprite::ActionSprite(
void ) { _idleAction = NULL ; _attackAction = NULL ; _walkAction = NULL ; _hurtAction = NULL ; _knockedOutAction = NULL ; } |
各个方法实现暂时为空。以上代码声明了基本变量和方法,可以分为以下几类:
-
Actions:这些是每种状态要执行的动作。这些动作是当角色切换状态时,执行精灵动画和其他触发的事件。
States:保存精灵的当前动作/状态,使用ActionState类型,这个类型待会我们将会进行定义。
Attributes:包含精灵行走速度值,受伤时减少生命点值,攻击伤害值。
Movement:用于计算精灵如何沿着地图移动。
Measurements:保存对精灵的实际图像有用的测量值。需要这些值,是因为你将要使用的这些精灵画布大小是远远大于内部包含的图像。
Action methods:不直接调用动作,而是使用这些方法触发每种状态。
Scheduled methods:任何事需要在一定的时间间隔进行运行,比如精灵位置和速度的更新,等等。
新建一个头文件
Defines.h
,代码如下:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
|
#pragma once #include “cocos2d.h” |
简要说明下:
①.定义了一些便利的宏,如直接使用SCREEN获取屏幕大小;
②.定义了一些便利的函数,随机返回整型或者浮点型;
③.定义ActionState类型,这个是ActionSprite可能处在不同状态的类型枚举;
④.定义BoundingBox结构体,将用于碰撞检测。
打开
GameLayer.h
文件,添加如下代码:
|
1
|
|
cocos2d::CCSpriteBatchNode *_actors;
|
打开
GameLayer.cpp
文件,在
init
函数里面添加如下代码:
|
1
2 3 4 |
|
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(
“pd_sprites.plist” ); _actors = CCSpriteBatchNode::create( “pd_sprites.pvr.ccz” ); _actors->getTexture()->setAliasTexParameters(); this ->addChild(_actors, – 5 ); |
加载精灵表单,创建一个CCSpriteBatchNode。这个精灵表单包含我们的所有精灵。它的z值高于CCTMXTiledMap对象,这样才能出现在地图前。
添加
Hero
类,派生自
ActionSprite
类,添加如下代码:
|
1
2 |
|
CREATE_FUNC(Hero);
bool init(); |
Hero
类的
init
函数的实现如下所示:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
bool Hero::init() { bool bRet = false ; do { CC_BREAK_IF(!ActionSprite::initWithSpriteFrameName( “hero_idle_00.png” )); bRet = |
我们用初始空闲精灵帧创建了英雄角色,配备了一个CCArray数组包含所有的属于空闲动画的精灵帧,然后创建一个CCAction动作播放来这个动画。以每秒12帧的速率进行播放。接下去,为英雄设置初始属性,包括精灵中心到边到底部的值。如下图所示:
|
1
|
|
#include “Hero.h” |
GameLayer
类添加如下代码:
|
1
|
|
Hero *_hero;
|
打开
GameLayer.cpp
文件,在构造函数添加如下代码:
|
1
|
|
_hero =
NULL ; |
在
init
函数
this->addChild(_actors, -5);
后面添加如下代码:
|
1
|
|
this ->initHero(); |
添加
initHero
方法,代码如下:
|
1
2 3 4 5 6 7 8 |
|
void GameLayer::initHero() { _hero = Hero::create(); _actors->addChild(_hero); _hero->setPosition(ccp(_hero->getCenterToSides(), 80 )); _hero->setDesiredPosition(_hero->getPosition()); _hero->idle(); } |
创建了一个英雄实例,添加到了精灵表单,并设置了设置。调用
idle
方法,让其处于空闲状态,运行空闲动画。返回到
ActionSprite.cpp
文件,实现
idle
方法,代码如下:
|
1
2 3 4 5 6 7 8 9 10 |
|
void ActionSprite::idle() { if (_actionState != kActionStateIdle) { this ->stopAllActions(); this ->runAction(_idleAction); _actionState = kActionStateIdle; _velocity = CCPointZero; } } |
这个
idle
方法只有当ActionSprite不处于空闲状态才能调用。当它触发时,它会执行空闲动作,改变当前状态到
kActionStateIdle
,并且把速度置零。
13.编译运行,可以看到英雄处于空闲状态。如下图所示:
|
1
2 3 4 5 6 7 8 9 |
|
//attack animation CCArray *attackFrames = CCArray::createWithCapacity( 3 ); for (i = 0 ; i < 3 ; i++) { CCSpriteFrame *frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat( “hero_attack_00_%02d.png” , i)->getCString()); attackFrames->addObject(frame); } CCAnimation *attackAnimation = CCAnimation::createWithSpriteFrames(attackFrames, 1 . 0 / 24 . 0 ); this ->setAttackAction(CCSequence::create(CCAnimate::create(attackAnimation), CCCallFunc::create( this , callfunc_selector(Hero::idle)), NULL )); |
打开
ActionSprite.cpp
文件,实现
attack
方法,代码如下:
|
1
2 3 4 5 6 7 8 9 |
|
void ActionSprite::attack() { if (_actionState == kActionStateIdle || _actionState == kActionStateAttack || _actionState == kActionStateWalk) { this ->stopAllActions(); this ->runAction(_attackAction); _actionState = kActionStateAttack; } } |
英雄只有在空闲、攻击、行走状态才能进行出拳。确保英雄正在受伤时、或者死亡时不能进行攻击。为了触发
attack
方法,打开
GameLayer.cpp
文件,在
init
函数添加如下代码:
|
1
|
|
this ->setTouchEnabled( true ); |
重载
ccTouchesBegan
方法,代码如下:
|
1
2 3 4 |
|
void GameLayer::ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent) { _hero->attack(); } |
15.编译运行,点击屏幕进行出拳,如下图所示:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
#pragma once #include “cocos2d.h” CC_SYNTHESIZE(SimpleDPadDelegate*, _delegate, Delegate); |
对以上的一些声明,解释如下:
-
radius:圆形方向键的半径。
direction:当前所按下的方向。这是一个矢量,(-1.0, -1.0)是左下方向,(1.0, 1.0)是右上方向。
delegate:方向键的委托,后续进行介绍。
isHeld:布尔值表示玩家触摸着方向键。
对于
SimpleDPad
类,使用了委托模式。意味着一个委托类(并非SimpleDPad),将会处理由被委托类(SimpleDPad)启动的任务。在某些你指定的点上,主要是当涉及到处理任何游戏相关的东西,SimpleDPad将会将职责传递给委托类。这使得SimpleDPad无需知道任何游戏逻辑,从而允许你在开发任何其他游戏时,可以进行重用。如下图所示:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
|
#include “SimpleDPad.h” using namespace cocos2d; SimpleDPad::SimpleDPad( SimpleDPad::~SimpleDPad( SimpleDPad* SimpleDPad::dPadWithFile(CCString *fileName, _radius = radius; bRet = |
以上方法中,
onEnterTransitionDidFinish
注册SimpleDPad委托类,
onExit
移除SimpleDPad委托类,
update
方法是当方向键被触摸时,传递方向值到委托类。
ccTouchBegan
方法检测触摸位置是否在方向键圆内,如果是,则将isHeld置为true,并更新方向值,返回true以拥有触摸事件优先权。
ccTouchMoved
当触摸点移动时,更新方向值。
ccTouchEnded
将isHeld置为false,重置方向,并通知委托触摸结束。
updateDirectionForTouchLocation
方法计算触摸点到方向键中心距离值,转换成角度,得到正确的方向值,然后传递值到委托。
打开
HudLayer.h
文件,添加头文件声明:
|
1
|
|
#include “SimpleDPad.h” |
添加如下代码:
|
1
2 |
|
bool init(); CC_SYNTHESIZE(SimpleDPad*, _dPad, DPad); |
打开
HudLayer.cpp
文件,添加如下代码:
|
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
HudLayer::HudLayer(
void ) { _dPad = NULL ; } _dPad = SimpleDPad::dPadWithFile(CCString::create( bRet = |
以上代码实例化
SimpleDPad
,并且添加到
HudLayer
上。现在GameScene同时控制GameLayer和HudLayer,但有时候想直接通过HudLayer访问GameLayer。打开
GameLayer.h
文件,添加头文件声明:
|
1
2 |
|
#include “SimpleDPad.h” #include “HudLayer.h” |
将
GameLayer
类声明修改成如下:
|
1
|
|
class GameLayer : public cocos2d::CCLayer, public SimpleDPadDelegate |
并添加以下声明:
|
1
2 3 4 5 |
|
virtual void didChangeDirectionTo(SimpleDPad *simpleDPad, cocos2d::CCPoint direction); virtual void isHoldingDirection(SimpleDPad *simpleDPad, cocos2d::CCPoint direction); virtual void simpleDPadTouchEnded(SimpleDPad *simpleDPad); CC_SYNTHESIZE(HudLayer*, _hud, Hud); |
以上方法的实现暂时为空。这样我们就在
GameLayer
中添加了
HudLayer
的引用,同时还让
GameLayer
遵循
SimpleDPad
所创建的协议。打开
GameScene.cpp
文件,在
init
函数
this->addChild(_hudLayer, 1);
后面,添加如下代码:
|
1
2 |
|
_hudLayer->getDPad()->setDelegate(_gameLayer);
_gameLayer->setHud(_hudLayer); |
17.编译运行,可以看到左下角的虚拟方向键,如下图所示:
别试着压下方向键,英雄不会有任何反应,因为还未实现协议方法,这在
第二部分
将完成。
参考资料:
1.How To Make A Side-Scrolling Beat ‘Em Up Game Like Scott Pilgrim with Cocos2D – Part 1
http://www.raywenderlich.com/24155/how-to-make-a-side-scrolling
2.如何使用cocos2d制作类似Scott Pilgrim的2D横版格斗过关游戏part1(翻译)
http://blog.sina.com.cn/s/blog_4b55f6860101a9b7.html
3.如何使用Cocos2d-x做一DNF类的游戏-part1
http://blog.csdn.net/jyzgo/article/details/8471306
非常感谢以上资料,本例子源代码附加资源
下载地址
:
http://download.csdn.net/detail/akof1314/5038013
如文章存在错误之处,欢迎指出,以便改正。
