http://codingnow.cn/cocos2d-x/1295.html
马上就要放假回家了,最近几天也比较闲,所以抽空来学习一下cocos2d-x 3.0,目前我使用的是最新版:cocos2d-x-3.0alpha1,开发环境为win7+vs2012。这个游戏demo是在网上看到的,觉得挺有意思,网上也已经有很多了文章和例子了,不过基本上都是用cocos2d-x较早的版本实现的,本文使用cocos2d-x 3.0重新实现了一遍。cocos2d-x 3.0更新了一些API,加入了c++ 11特性。我们将要学习精灵动作切换和保存、碰撞检测、简单的机器人AI、3.0新功能的使用等等,最终效果如下图:
项目源码:http://download.csdn.net/detail/zhoujianghai/6962541
废话少说,现在就开始吧。
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
|
GameLayer.h
class
GameLayer :
public
cocos2d::Layer
{
public
:
GameLayer();
~GameLayer();
virtual
bool
init();
CREATE_FUNC(GameLayer);
private
:
cocos2d::TMXTiledMap *m_pTiledMap;
};
GameLayer.cpp
GameLayer::GameLayer()
:m_pTiledMap(NULL)
{
}
GameLayer::~GameLayer()
{
}
bool
GameLayer::init()
{
bool
ret =
false
;
do
{
CC_BREAK_IF( !Layer::init());
m_pTiledMap = TMXTiledMap::create(
"pd_tilemap.tmx"
);
this
->addChild(m_pTiledMap, -10);
ret =
true
;
}
while
(0);
return
ret;
}
|
这里设置z-order的值为-10,可以设置为其他值,因为地图一般是显示在最底层,所以添加其他元素的时候可以设置z-order的值大于该值即可。
接着创建场景类GameScene:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
GameScene.h
class
GameScene
{
public
:
static
cocos2d::Scene* createScene();
};
GameScene.cpp
Scene* GameScene::createScene()
{
auto scene = Scene::create();
auto gameLayer = GameLayer::create();
scene->addChild(gameLayer, 0);
return
scene;
}
|
然后修改AppDelegate.cpp的applicationDidFinishLaunching函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
bool
AppDelegate::applicationDidFinishLaunching() {
// initialize director
auto director = Director::getInstance();
auto eglView = EGLView::getInstance();
director->setOpenGLView(eglView);
eglView->setDesignResolutionSize(480, 320, ResolutionPolicy::SHOW_ALL);
// turn on display FPS
director->setDisplayStats(
false
);
// set FPS. the default value is 1.0/60 if you don't call this
director->setAnimationInterval(1.0 / 60);
// create a scene. it's an autorelease object
auto scene = GameScene::createScene();
// run
director->runWithScene(scene);
return
true
;
}
|
setDesignResolutionSize这个设置资源分辨率尺寸,为了适配多个分辨率的。
因为这里资源分辨率是480×320,所以在main.cpp中设置宽高为480×320效果最好。
编译运行项目会看到地图已经显示出来了,效果如下:
现在地图上啥也没有,太单调了,那就创造一个英雄吧。
4. 添加英雄
英雄的资源文件是pd_sprites.pvr.ccz,可以使用TexturePacker打开,如下图:
英雄有5种状态:
1. 空闲(站着不动时)
2. 行走
3. 攻击
4. 被攻击
5. 死亡
每个状态都对应一组动画,因为英雄不可能同时处于多种状态下,所以我们要考虑的是如何实现状态切换,然后播放状态对应的动画。
这里当英雄处于空闲状态时,只能切换到行走、攻击、受伤状态,不会还没受伤就死亡了,当英雄死亡后,就不能切换到其他四种状态了。
创建BaseSprite类,作为精灵的基类:
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
|
BaseSprite.h
typedef
enum
{
ACTION_STATE_NONE = 0,
ACTION_STATE_IDLE,
ACTION_STATE_WALK,
ACTION_STATE_ATTACK,
ACTION_STATE_HURT,
ACTION_STATE_DEAD
}ActionState;
class
BaseSprite :
public
cocos2d::Sprite
{
public
:
BaseSprite();
~BaseSprite();
void
runIdleAction();
void
runWalkAction();
void
runAttackAction();
void
runHurtAction();
void
runDeadAction();
CC_SYNTHESIZE_RETAIN(cocos2d::Action*, m_pIdleAction, IdleAction);
CC_SYNTHESIZE_RETAIN(cocos2d::Action*, m_pWalkAction, WalkAction);
CC_SYNTHESIZE_RETAIN(cocos2d::Action*, m_pAttackAction, AttackAction);
CC_SYNTHESIZE_RETAIN(cocos2d::Action*, m_pHurtAction, HurtAction);
CC_SYNTHESIZE_RETAIN(cocos2d::Action*, m_pDeadAction, DeadAction);
CC_SYNTHESIZE(ActionState, m_currActionState, CurrActionState);
cocos2d::CallFunc* createIdleCallbackFunc();
protected
:
static
cocos2d::Animation* createAnimation(
const
char
* formatStr,
int
frameCount,
int
fps);
private
:
bool
changeState(ActionState actionState);
};
BaseSprite.cpp
BaseSprite::BaseSprite():
m_pIdleAction(NULL),
m_pWalkAction(NULL),
m_pAttackAction(NULL),
m_pHurtAction(NULL),
m_pDeadAction(NULL),
m_currActionState(ACTION_STATE_NONE)
{
}
BaseSprite::~BaseSprite()
{
CC_SAFE_RELEASE_NULL(m_pIdleAction);
CC_SAFE_RELEASE_NULL(m_pWalkAction);
CC_SAFE_RELEASE_NULL(m_pAttackAction);
CC_SAFE_RELEASE_NULL(m_pHurtAction);
CC_SAFE_RELEASE_NULL(m_pDeadAction);
}
void
BaseSprite::runIdleAction()
{
if
(changeState(ACTION_STATE_IDLE))
{
this
->runAction(m_pIdleAction);
}
}
void
BaseSprite::runWalkAction()
{
if
(changeState(ACTION_STATE_WALK))
{
this
->runAction(m_pWalkAction);
}
}
void
BaseSprite::runAttackAction()
{
if
(changeState(ACTION_STATE_ATTACK))
{
this
->runAction(m_pAttackAction);
}
}
void
BaseSprite::runHurtAction()
{
if
(changeState(ACTION_STATE_HURT))
{
this
->runAction(m_pHurtAction);
}
}
void
BaseSprite::runDeadAction()
{
if
(changeState(ACTION_STATE_DEAD))
{
this
->runAction(m_pDeadAction);
}
}
Animation* BaseSprite::createAnimation(
const
char
* formatStr,
int
frameCount,
int
fps)
{
Array *pFrames = Array::createWithCapacity(frameCount);
for
(
int
i = 0; i < frameCount; ++ i)
{
const
char
* imgName = String::createWithFormat(formatStr, i)->getCString();
SpriteFrame *pFrame = SpriteFrameCache::getInstance()->getSpriteFrameByName(imgName);
pFrames->addObject(pFrame);
}
return
Animation::createWithSpriteFrames(pFrames, 1.0f / fps);
}
bool
BaseSprite::changeState(ActionState actionState)
{
if
(m_currActionState == ACTION_STATE_DEAD || m_currActionState == actionState)
{
return
false
;
}
this
->stopAllActions();
this
->m_currActionState = actionState;
return
true
;
}
CallFunc* BaseSprite::createIdleCallbackFunc()
{
return
CallFunc::create(CC_CALLBACK_0(BaseSprite::runIdleAction,
this
));
}
|
ActionState枚举表示精灵的各种状态,runXXXAction表示切换状态以及播放各状态对应的动画。变量m_currActionState表示当前精灵的状态。createIdleCallbackFunc函数后面需要用到,表示当精灵切换到攻击或者受伤状态后立即回到空闲状态。createAnimation函数是一个工具函数,根据图片路径、帧数、每秒显示的帧数来创建动画。changeState函数就是状态切换函数,当精灵死亡的时候就GameOver了。
现在该创建咱们的英雄类Hero了:
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
|
Hero.h
class
Hero :
public
BaseSprite
{
public
:
Hero();
~Hero();
bool
init();
CREATE_FUNC(Hero);
};
Hero.cpp
Hero::Hero()
{}
Hero::~Hero()
{}
bool
Hero::init()
{
bool
ret =
false
;
do
{
CC_BREAK_IF( !
this
->initWithSpriteFrameName(
"hero_idle_00.png"
) );
Animation *pIdleAnim =
this
->createAnimation(
"hero_idle_%02d.png"
, 6, 12);
this
->setIdleAction(RepeatForever::create(Animate::create(pIdleAnim)));
Animation *pWalkAnim =
this
->createAnimation(
"hero_walk_%02d.png"
, 7, 14);
this
->setWalkAction(RepeatForever::create(Animate::create(pWalkAnim)));
Animation *pAttackAnim =
this
->createAnimation(
"hero_attack_00_%02d.png"
, 3, 20);
this
->setAttackAction(Sequence::create(Animate::create(pAttackAnim), BaseSprite::createIdleCallbackFunc(), NULL));
Animation *pHurtAnim =
this
->createAnimation(
"hero_hurt_%02d.png"
, 3, 12);
this
->setHurtAction(Sequence::create(Animate::create(pHurtAnim), BaseSprite::createIdleCallbackFunc(), NULL));
Animation *pDeadAnim =
this
->createAnimation(
"hero_knockout_%02d.png"
, 5, 12);
this
->setDeadAction(Sequence::create(Animate::create(pDeadAnim), Blink::create(3, 9), NULL));
ret =
true
;
}
while
(0);
return
ret;
}
|
Hero类比较简单,就是初始化各种动作。
我们来修改GameLayer类,加载精灵图片所需的资源。
在GameLayer.h中添加:
1
2
3
4
5
6
|
CC_SYNTHESIZE_READONLY(Hero*, m_pHero, Hero);
float
m_fScreenWidth;
float
m_fScreenHeight;
cocos2d::Point m_origin;
cocos2d::SpriteBatchNode *m_pSpriteNodes;
|
在GameLayer.cpp的init函数中添加:
1
2
3
4
5
6
7
8
9
10
11
12
|
auto visibleSize = Director::getInstance()->getVisibleSize();
this
->m_origin = Director::getInstance()->getVisibleOrigin();
this
->m_fScreenWidth = visibleSize.width;
this
->m_fScreenHeight = visibleSize.height;
SpriteFrameCache::getInstance()->addSpriteFramesWithFile(
"pd_sprites.plist"
);
m_pSpriteNodes = SpriteBatchNode::create(
"pd_sprites.pvr.ccz"
);
this
->addChild(m_pSpriteNodes);
m_pHero = Hero::create();
m_pHero->setPosition( m_origin + Point(100, 100) );
m_pHero->runIdleAction();
m_pHero->setZOrder(m_fScreenHeight - m_pHero->getPositionY());
m_pSpriteNodes->addChild(m_pHero);
|
这里添加了一个英雄,然后切换到空闲状态,设置ZOrder值跟Y坐标相关,这样后期可以解决英雄和怪物的遮挡问题,关于SpriteFrameCache和SpriteBatchNode这里就不介绍了,不明白的可以查看http://codingnow.cn/cocos2d-x/795.html
现在编译运行项目,就可以看到一个骚包在屏幕上晃来晃去了:
ok,这篇就到此为止吧,文章太长会让人看着累。下一篇来实现控制英雄,让英雄动起来。
参考资料:
http://philon.cn/post/cocos2d-x-3.0-zhi-zuo-heng-ban-ge-dou-you-xi