来抓我啊!(C++)
原作者:http://discuss.cocos2d-x.org/t/cocos3-0-tutorial-game-catchme/14258#Torelli95
英文论坛原帖:http://discuss.cocos2d-x.org/t/cocos3-0-tutorial-game-catchme/14258
在本片教程中我们要制作一个简单的游戏,来抓我啊!Catch Me.
这游戏使用点击的方式来捕获一个在屏幕上移动的简单的动态标签
这款游戏将会在Linux上编译,在此仅说明一下。
这款游戏包括:
-自定义排版
-动作
-时间
-动画
-使用用户默认(保存玩家分数)
开始编码吧~
第一章
设置项目
创建一个新的cocos项目:
在终端里输入:
cocos new -l cpp -d /path/to/project CatchMe
替换/path/to/project为你要保存项目的位置
在你的编译环境中打开项目的时候,必须有4个类
HelloWorld.cpp
HelloWorld.h
AppDelegate.cpp
AppDelegate.h
添加一个空的类名为CatchMe.cpp还有对应的CatchMe.h。这个类会包含这个游戏最重要的元素,标签和处理程序。(监听器,动画等)
现在我们的HelloWorld看起来像默认的cocos HelloWorld:

建立我们的
CatchMe
标签
第一步:到网址http://www.dafont.com/给我们的标签下载一个新的字体,我下载了字体:NextGames.tff;
将文件保存到/path/to/CatchMe/Resources/fonts里。
你可以创建自己的tff字体也可以在其他地方下载。
第二步,我们要在CatchMe.h中声明下一个变量和程序:
///include cocos to the new class
#include "cocos2d.h"
///Extends LabelTTF because CatchMe will have _eventDispatcher for this must be a Node(Father of LabelTTF)
class CatchMe : cocos2d::LabelTTF
{
public:
///Constructor and Destructor of CatchMe class
CatchMe();
~CatchMe();
///Gives LabelCatchMe to others
cocos2d::LabelTTF* getLabel();
protected:
cocos2d::LabelTTF* LabelCatchMe;
};
```
在这之后,在CatchMe.cpp中声明constructor, destructor 和 getLabel方法。
CatchMe::CatchMe()
{
LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);
}
CatchMe::~CatchMe()
{
}
LabelTTF *CatchMe::getLabel()
{
return LabelCatchMe;
}
```
现在我们在HelloWorld中把HelloWold标签替换成新标签;为此我们首先需要在HelloWorld.cpp中的类CatchMe里创建一个新的实例。
然后在HelloWorld.h的类HelloWorld里声明CatchMe实例的属性。
protected:
CatchMe* Game;
cocos2d::LabelTTF* LabelCatchMe;
可能你已经注意到了其他的生命的变量,LabelCatchMe,这将被用来在HelloWorld里保持向后复制我们的标签。
并替换:
// add a label shows "Hello World"
// create and initialize a label
auto label = LabelTTF::create("Hello World", "Arial", 24);
// position the label on the center of the screen
label->setPosition(Point(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - label->getContentSize().height));
// add the label as a child to this layer
this->addChild(label, 1);
to:
// add a label shows "CatchMe"
Game = new CatchMe();
LabelCatchMe = Game->getLabel();
// position the label on the center of the screen
LabelCatchMe->setPosition(Point(origin.x + visibleSize.width/2,
origin.y + visibleSize.height - LabelCatchMe->getContentSize().height));
// add the label as a child to this layer
this->addChild(LabelCatchMe, 1);
```
别忘了在HelloWorld.h中添加 #include "CatchMe.h"
如果你现在运行项目,结果应该是这样的:
第二章
增加一些颜色
这些标签效果看起来不错,但如果换换颜色效果会更好。为此我们需要使用Glubyte类的变量。
在CatchMe.h中声明下一个变量:
GLubyte r,g,b;
r,g,b对应着RGB颜色模型,这些变量的值范围在0~255之间。
现在我们到CatchMe.cpp 里的tchMe结构体中添加下面几行代码:
CatchMe::CatchMe()
{
///Initializes the Label CatchMe
LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);
srand(time(nullptr));
r = rand()%255;
g = rand()%255;
b = rand()%255;
LabelCatchMe->setColor( Color3B(r,g,b) );
}
```
因为这个标签会以随机颜色的方式出现,但我们现在就要改变颜色。下一步我们就要处理这个问题:
到CatchMe.h里声明一下方法:
protected:
///return a small value to change the colors gradually
GLubyte newColor();
public:
///Change color label
void changeColor();
```
在CatchMe.cpp里:
GLubyte CatchMe::newColor()
{
GLubyte nuevoColor = rand() % 5;
if(rand() % 3 == 0)
nuevoColor = -nuevoColor;
return nuevoColor;
}
void CatchMe::changeColor()
{
r += newColor();
g += newColor();
b += newColor();
LabelCatchMe->setColor( Color3B(r,g,b) );
}
```
下一步就是更新游戏场景并引用我们在上面声明的方法。
在HelloWorld.h中
protected: void update(float df);
在HelloWorld.cpp中
void HelloWorld::update(
float df)
{
Game->changeColor();
LabelCatchMe = Game->getLabel();
}
在HelloWorld中引用:
schedule (schedule_selector(HelloWorld::
update));
注意到方法update需要一个schedule.
如果你运行了程序的话会得到如下场景:
标签的颜色不断地变化。
第三章
事件
在这个区域里当玩家碰触的时候标签能够做出反应。
为此我们将在CatchMe.h上声明下一个方法:
protected:
///Manage Events and Actions
void
setEventHandlers();
在CatchMe.cpp里:
void CatchMe::setEventHandlers()
{
//Create a "one by one" touch event listener (processes one touch at a time)
auto listener = EventListenerTouchOneByOne::create();
// When "swallow touches" is true, then returning 'true' from the onTouchBegan method will "swallow" the touch event, preventing other listeners from using it.
listener->setSwallowTouches(true);
// Example of using a lambda expression to implement onTouchBegan event callback function
listener->onTouchBegan = &](Touch* touch, Event* event){
// event->getCurrentTarget() returns the *listener's* sceneGraphPriority node.
auto target = static_cast(event->getCurrentTarget());
//Get the position of the current point relative to the button
Point locationInNode = target->convertToNodeSpace(touch->getLocation());
Size s = target->getContentSize();
Rect rect = Rect(0, 0, s.width, s.height);
//Check the click area
if (rect.containsPoint(locationInNode))
{
///The action that we want to run if the Label get touched
auto hide = Hide::create();
target->runAction(hide);
return true;
}
return false;
};
//Add listener
_eventDispatcher->addEventListenerWithSceneGraphPriority(listener, LabelCatchMe);
}
```
我们也需要在CatchMe结构体中引用这个方法:
CatchMe::CatchMe()
{
///Initializes the Label CatchMe
LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);
srand(time(nullptr));
r = rand()%255;
g = rand()%255;
b = rand()%255;
LabelCatchMe->setColor( Color3B(r,g,b) );
setEventHandlers();
}
```
现在如果你运行项目并点击标签,你就会看到神器的事情发生了!
移动和动画
现在我们要给游戏增加点难度,难道不是这样吗?
为了能够移动标签我们需要在屏幕上产生一个随机点,考虑到标签的大小,所以我们需要以下的方法:
In CatchMe.h:
在CatchMe.h中:
protected:
///Generate a random point on the screen, taking
into account the size of the label
cocos2d::Point
generatedRandomPoint(cocos2d::Node* node);
在CatchMe.cpp中:
Point CatchMe::generatedRandomPoint(Node* node)
{
srand(time(nullptr));
int randx;
int randy;
int contentSizeHeight = node->getContentSize().height;
int contentSizeWidht = node->getContentSize().width;
int visibleSizeHeight = Director::getInstance()->getVisibleSize().height - (contentSizeHeight * 0.5);
int visibleSizeWidth = Director::getInstance()->getVisibleSize().width - (contentSizeWidht * 0.5);
randx = rand() % visibleSizeWidth;
randy = rand() % visibleSizeHeight;
while((randx > (contentSizeWidht * 0.5) && randx < visibleSizeWidth && randy > (contentSizeHeight *0.5) && randy < visibleSizeHeight) == false ){
randx = rand() % visibleSizeWidth;
randy = rand() % visibleSizeHeight;}
return Point(randx,randy);
}
```
现在我们用以下的方法添加移动和动画。
在CatchMe.h中:
protected:
void animateGameTitle();
在CatchMe.cpp中:
void CatchMe::animateGameTitle()
{
///get the label size
int nodeWidth = LabelCatchMe->getContentSize().width;
int nodeHeight = LabelCatchMe->getContentSize().height;
///Animatios to use
auto moveTo = MoveTo::create(0.2f + rand() % 40 / 150.0f, generatedRandomPoint(LabelCatchMe));
auto rotateTo = RotateTo::create(0.5f + rand() % 40 / 150.0f, rand() % 360);
auto scaleTo = ScaleTo::create(0.2f + rand() % 40 / 150.0f,rand() % nodeWidth * 0.5/60,rand()%nodeHeight*0.5 /60);
///The animations will repeated many times,calls himself
auto callFunc = CallFunc::create( this, callfunc_selector(CatchMe::animateGameTitle) );
auto sequence = Sequence::create(moveTo,callFunc, nullptr);
///this Actions will run in parallel to MoveTo sequence.
LabelCatchMe->runAction(sequence);
LabelCatchMe->runAction(rotateTo);
LabelCatchMe->runAction(scaleTo);
}
```
Cocos2d-x有着很多其他可用的动画,这些能够在文档中找到。
最后在结构体重animateGameTitle引用:
CatchMe::CatchMe()
{
///Initializes the Label CatchMe
LabelCatchMe = LabelTTF::create("CatchMe", "fonts/NextGames.ttf", 72);
srand(time(nullptr));
r = rand()%255;
g = rand()%255;
b = rand()%255;
LabelCatchMe->setColor( Color3B(r,g,b) );
setEventHandlers();
animateGameTitle();
}
```
如果你运行程序的话,应该会得到如下结果:
伴随着标签在全屏幕的移动,旋转和尺寸更改。
但我们现在还有一个问题,如果点击了标签,他就不再出现了。
为了修复这个问题,我们到setEventHandlers里:
///The action that we want to run if the Label get touched
auto hide = Hide::create();
target->runAction(hide);
return true;
we raplace for:
///The action that we want to run if the Label get touched
auto hide = Hide::create();
auto show = Show::create();
auto delay = DelayTime::create(0.75f);
///make a sequence that hide and show a little delay to the label
///keep moving to another part of the screen without the player sees
auto sequenceTouch = Sequence::create(hide,delay,show, nullptr);
target->runAction(sequenceTouch);
return true;
```
第四章
分数!
一款游戏没有得分?如果不能超过最高分以及知道自己的得分那这还有玩的意义么?
我们需要声明3种新的变量,由于时间的关系我将它添加进类HelloWorld中,这可能不那么美观但它至少能运作。
HelloWorld.h:
在HelloWorld.h中:
public
cocos2d::LabelTTF* currentScore;
cocos2d::LabelTTF* bestScore;
接下来在CatchMe.h中:
public: int currentPoints;
HelloWorld.cpp: In HelloWorld::init;
在HelloWorld.cpp的HelloWorld::init中
currentScore = LabelTTF::create("0000","fonts/NextGames.ttf",25);
currentScore->setPosition(Point(origin.x + visibleSize.width-currentScore->getContentSize().width,
origin.y + visibleSize.height-currentScore->getContentSize().height));
addChild(currentScore,-1);
```
然后在CatchMe.cpp中:
///The action that we want to run if the Label get touched
auto hide = Hide::create();
auto show = Show::create();
auto delay = DelayTime::create(0.75f);
///make a sequence that hide and show a little delay to the label
///keep moving to another part of the screen without the player sees
auto sequenceTouch = Sequence::create(hide,delay,show, nullptr);
target->runAction(sequenceTouch);
```
添加:
currentPoints +=5; This will increment currentPoints value if the label get touched.
回到HelloWorld.cpp中在HelloWorld::update里添加:
char buffer;
sprintf(buffer, "%04lli", Game->currentPoints);
currentScore->setString(std::string(buffer));
这会获取’currentPoints’的现有值并将它转换为字符。
如果你先在运行项目现在就会有分数显示了。
但最高分呢?
为了完成这一步我们在HelloWorld.h里声明方法bestScoreUpdated。
void bestScoreUpdated();
and in the HelloWorld.cpp:
在HelloWorld.cpp里:
void HelloWorld::bestScoreUpdated()
{
int best = UserDefault::getInstance()->getIntegerForKey("Best_Score");
if(best < Game->currentPoints){
best = Game->currentPoints;
UserDefault::getInstance()->setIntegerForKey("Best_Score",best);
char buffer;
sprintf(buffer, "%04lli", best);
bestScore->setString("Best: " + std::string(buffer));
UserDefault::getInstance()->flush();
}
else{
char buffer;
sprintf(buffer, "%04lli", best);
bestScore->setString("Best: " + std::string(buffer));
}
}
```
现在我们引用HelloWorld::update里的方法:
void HelloWorld::update(float df)
{
Game->changeColor();
LabelCatchMe = Game->getLabel();
char buffer;
sprintf(buffer, "%04lli", Game->currentPoints);
currentScore->setString(std::string(buffer));
bestScoreUpdated();
}
```
最后我们在HelloWorld::init里把标签添加到场景:
bestScore = LabelTTF::create("Best: ","fonts/NextGames.ttf",25);
bestScore->setPosition(Point(origin.x + currentScore->getContentSize().width * 2.0f,
origin.y + visibleSize.height-currentScore->getContentSize().height));
addChild(bestScore,-1);
```
现在你得到了一个好玩的游戏了!
希望你喜欢这篇教程!
一些额外内容来改进游戏:
添加一个计时器、有趣的音乐。突破你的想象吧~
