Cocos2dx项目实战(3)——抢红包

这一次让我们来结合动画的知识,做一个抢红包的小游戏吧。

本文仅供个人记录和复习,不用于其他用途

游戏效果展示

游戏思路

我们需要一个背景层,用于加载背景图片和回应点击事件。然后就是我们的红包类,用于生成随机位置的红包,并且加载相应的动画。

本篇文章同样是借助了雷过就跑的教程,可以在慕课网上搜索到他的视频,本文所用的资源也可以在课程页面下载。

HelloWorldScene

HelloWorldScene.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
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "RedNode.h"
USING_NS_CC;
class HelloWorld : public cocos2d::Layer
{
public:
HelloWorld();
~HelloWorld();
static cocos2d::Scene* createScene();
virtual bool init();
void menuCloseCallBack(cocos2d::Ref *pSender);
void update(float dt);
CREATE_FUNC(HelloWorld);
public:
void setBackGround(const char *filename);
private:
int state;
Sprite *backImg;
RedNode *redNode;
};
#endif

setBackGround()用于加载背景图片,其他的已经很熟悉了,不再赘述。

HelloWorldScene.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
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
#include "HelloWorldScene.h"
Scene* HelloWorld::createScene()
{
auto scene = Scene::create();
auto layer = HelloWorld::create();
scene->addChild(layer);
return scene;
}
HelloWorld::HelloWorld()
{
backImg = 0;
state = 0;
}
HelloWorld::~HelloWorld()
{
backImg = 0;
state = 0;
}
void HelloWorld::setBackGround(const char *filename)
{
if (backImg == 0)
{
backImg = Sprite::create(filename);
backImg->setAnchorPoint(Point::ZERO);
backImg->setPosition(Point::ZERO);
this->addChild(backImg);
return;
}
backImg->setTexture(filename);
}
bool HelloWorld::init()
{
if ( !Layer::init() )
{
return false;
}
Size visablesize = Director::sharedDirector()->getVisibleSize();
Vec2 origin = Director::sharedDirector()->getVisibleOrigin();
auto listener = EventListenerTouchOneByOne::create();
listener->setSwallowTouches(true);
listener->onTouchBegan = [=](Touch *touch, Event *event)
{
if (state == 0)
{
Vec2 touchPoint = touch->getLocation();
Size s = redNode->getContentSize();
Rect rect = Rect(redNode->getPositionX(), redNode->getPositionY(), s.width, s.height);
if (rect.containsPoint(touchPoint))
{
state = 1;
unscheduleUpdate();
redNode->setPosition(Vec2(320, 480));
redNode->playAnimate();
return true;
}
}
else
{
state = 0;
redNode->setDefault();
scheduleUpdate();
}
return false;
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
setBackGround("res/BackGround.jpg");
auto item = MenuItemFont::create("Click", CC_CALLBACK_1(HelloWorld::menuCloseCallBack, this));
item->setFontSizeObj(120);
item->setColor(Color3B(255, 0, 0));
auto menu = Menu::create(item, NULL);
menu->setPosition(Vec2::ZERO);
menu->setPosition(visablesize / 2);
this->addChild(menu, 1);
redNode = RedNode::create();
redNode->loadAnimate();
redNode->setVisible(false);
this->addChild(redNode, 1);
return true;
}
void HelloWorld::update(float dt)
{
static float time = 0;
if (time > 1.0f)
{
time = 0;
}
else
{
time += dt;
return;
}
float pos_x = std::rand() % 640;
float pos_y = std::rand() % 960;
redNode->setVisible(true);
redNode->setPosition(ccp(pos_x, pos_y));
}
void HelloWorld::menuCloseCallBack(Ref *pSender)
{
static_cast<Node *> (pSender)->setVisible(false);
scheduleUpdate();
}

首先我们创建一个监听器,用于监听鼠标的单点事件。至于为什么要设置吞噬,那么为了防止事件被其他的监听器使用。随后,使用Lambda表达式快速编写onTouchBegan()回调函数,那么这里我们用state来标记状态。当鼠标点击时,如果state为0,我们就获取触摸点,并且在红包上创建一个矩形区域。如果触摸点在这个矩形区域内,那么就判定我们点中了红包。然后我们将state变为1,关闭定时器,并且将红包设置在中央,开始播放动画。这里要说明一下,当我们再次点击时,由于state为1,那么我们就会将它改为0,同时将红包初始化,并且开启定时器。这样,我们的红包就又开始四处飘动,成为一个循环。

剩下的工作都很好理解,比如将监听器加到层上,然后创建一个菜单和一个红包,不过这里我们得先将红包隐藏。update()其实就是随机改变红包的位置,同时将红包设置为可见。而menuCloseCallBack()就是当你在点击了Click之后,把菜单隐藏,并且开启定时器。

RedNode

RedNode.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
#ifndef __RedNode__H__
#define __RedNode__H__
#include <iostream>
#include "cocos2d.h"
USING_NS_CC;
class RedNode:public Node
{
public:
CREATE_FUNC(RedNode);
virtual bool init();
RedNode();
~RedNode();
public:
void loadAnimate();
void playAnimate();
void aniCallBack();
void setDefault();
void addTitle(const char *text);
private:
Animate *animate;
Sprite *backImg;
Label *title;
};
#endif

红包类用于创建红包,并且播放红包打开的动画。

RedNode.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
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
#include "RedNode.h"
std::vector<std::string> context = { "1,000,000$","0.1$","23333" };
bool RedNode::init()
{
backImg = Sprite::create("res/default.png");
backImg->setAnchorPoint(Point::ZERO);
this->addChild(backImg);
setContentSize(backImg->getContentSize());
return true;
}
RedNode::RedNode()
{
backImg = 0;
animate = 0;
title = 0;
}
RedNode::~RedNode()
{
backImg = 0;
animate = 0;
title = 0;
}
void RedNode::loadAnimate()
{
SpriteFrameCache *spriteFrameCache = SpriteFrameCache::getInstance();
int index = 1;
SpriteFrame *frame = NULL;
Vector<SpriteFrame *>frameArray;
do
{
frame = spriteFrameCache->getSpriteFrameByName(__String::createWithFormat("%d.png", index)->getCString());
if (frame == 0)
{
break;
}
frameArray.pushBack(frame);
index++;
} while (true);
Animation *animation = Animation::createWithSpriteFrames(frameArray);
animation->setLoops(1);
animation->setRestoreOriginalFrame(false);
animation->setDelayPerUnit(0.1);
animate = Animate::create(animation);
animate->retain();
}
void RedNode::playAnimate()
{
if (backImg != 0 && animate != 0)
{
backImg->setAnchorPoint(ccp(0.5, 0.5));
CallFunc *callFunc = CallFunc::create(CC_CALLBACK_0(RedNode::aniCallBack, this));
Sequence *seq = Sequence::create(animate, callFunc, NULL);
backImg->runAction(seq);
}
}
void RedNode::aniCallBack()
{
std::string str = context[std::rand() % context.size()];
printf("%s", str.c_str());
addTitle(str.c_str());
}
void RedNode::setDefault()
{
if (backImg != 0)
{
backImg->removeFromParent();
backImg = Sprite::create("res/default.png");
backImg->setAnchorPoint(Point::ZERO);
this->addChild(backImg);
if (title != 0)
{
title->setVisible(false);
}
}
}
void RedNode::addTitle(const char *text)
{
if (title == 0)
{
title = Label::createWithSystemFont(text, "", 40);
title->setPosition(Point::ZERO);
title->setColor(Color3B::RED);
this->addChild(title, 1);
return;
}
title->setString(text);
title->setVisible(true);
}

loadAnimate()用于加载动画,我们获取了精灵帧缓存,并且创建了一个Vector用于存储精灵帧。随后我们将图片载入精灵帧缓存,并且加入到frameArray中。接下来,我们使用这个容器来创建一个动画,设置相应的参数。不过,如果要播放动画的话需要动画动作,这里一定要给动画动作加一个计数,因为动作并没有在当前使用,而是被playAnimate()所使用。

playAnimate()这里要说的就是CallFuncSequence这两个,CallFunc是回调函数,而Sequence代表一个序列,按顺序执行里面的内容。那么这里就是执行完红包动画后,在红包上显示一串数字,也就是所得的钱数。CallFuncSequence之后会详细介绍。

aniCallBack()addTitle()很简单,setDefault()用于红包的初始化。首先将背景图去除,然后重新加载,设置相应的参数。这里要注意的是,如果不是第一次打开红包,那么title中已经有了上一次的内容,所以应该隐藏。

总结

这一次将动画的内容实地的演练了一遍,加强了动画相关的运用。动画的加载与播放在实际中是分开的,这一点与我们之前加载完就马上用是不一样的。