Cocos2d-x 3.2制作三消游戏《万圣大作战》4:添加时间与分数
完成前面的任务后,接下来我们要为游戏添加时间与分数机制。
1. 添加时间
在一般的消除类游戏中,通常会采用关卡制或者限制时间的方式来增加游戏的挑战性。然而,关卡设计较为繁琐,对于个人开发者而言,精力有限。因此,我们选择通过限制时间来提升游戏的可玩性。不过需要注意的是,这种方式可能会使玩家在反复挑战相同内容时容易产生疲劳。
1.1 时间变量的初始化与显示
在程序中添加时间功能,首先需要定义一个时间的全局变量,并在初始化游戏界面时将其显示在屏幕上。以下是具体的代码实现:
// 加载ttf字体
TTFConfig config("fonts/haibaoti.ttf", 30);
// 游戏时间的显示
auto labelTime = Label::createWithTTF(config, StringUtils::format("Time: %d ", m_time));
labelTime->setPosition(Vec2(labelTime->getContentSize().width, GAME_SCREEN_HEIGHT - labelTime->getContentSize().height));
labelTime->setTag(11);
this->addChild(labelTime);
1.2 时间减少函数的实现
为了实现时间每秒减少的功能,我们需要编写一个函数。代码如下:
void GameScene::myClock(float dt)
{
// 时间变化
--m_time;
// 如果时间归0,那么执行游戏结束动画,并跳到游戏结束界面
if (m_time == 0)
{
Label *labelTime = (Label *)this->getChildByTag(11);
labelTime->setScale(0);
// 游戏结束 动画
auto gmov = Sprite::create("pic_gameover.png");
gmov->setPosition(Point(GAME_SCREEN_WIDTH / 2, GAME_SCREEN_HEIGHT * 1.5));
this->addChild(gmov);
auto action = MoveTo::create(3.0f, Point(GAME_SCREEN_WIDTH / 2, GAME_SCREEN_HEIGHT / 2));
gmov->runAction(action);
// 跳转到游戏结束界面
return;
}
// 如果还有时间,那么更新时间显示
if (m_time > 0) {
Label *labelTime = (Label *)this->getChildByTag(11);
labelTime->setString(StringUtils::format("Time: %d ", m_time));
}
}
1.3 将倒计时函数加入计划任务
最后,我们需要将这个倒计时函数加入到计划任务中。与之前使用的 scheduleUpdate 不同,scheduleUpdate 会每一帧都执行,而这次我们使用 schedule 函数,自己指定要执行的函数,并设定间隔时间。代码如下:
schedule(schedule_selector(GameScene::myClock), 1.0f);
运行程序,就可以看到倒计时功能的实现。
1.4 游戏结束界面的创建
接下来,我们需要创建游戏结束界面,该界面包含返回游戏、返回主界面和退出三个按钮。具体代码可以参考文章末尾给出的源码。
2. 添加分数
仅有时间倒计时是不够的,添加分数机制可以让游戏更具趣味性。
2.1 分数的初始化与显示
和时间的显示类似,我们需要在初始化界面时将分数显示在游戏界面上。代码如下:
// 分数显示
auto labelScore = Label::createWithTTF(config, StringUtils::format("Score: %d ", m_score));
labelScore->setPosition(Vec2(GAME_SCREEN_WIDTH / 2, GAME_SCREEN_HEIGHT - labelScore->getContentSize().height * 2.6));
labelScore->setTag(10);
this->addChild(labelScore);
2.2 分数的实时更新
分数需要实时刷新,因此我们可以在 update 函数内更新分数的显示。代码如下:
// GameScene.cpp update()函数
// 分数变化
Label *labelScore = (Label *)this->getChildByTag(10);
labelScore->setString(StringUtils::format("Score: %d ", m_score));
2.3 分数的增加逻辑
在精灵下落处,每消除一个精灵我们增加30分。以下是修改后的 fillSprite 函数:
// GameScene.cpp 修改后的 fillSprite
// 填补空缺位置
void GameScene::fillSprite()
{
// 下落精灵总个数(方便后面分数的计算)
int sum = 0;
// 重置移动方向标志
isAction = true;
int *colEmptyInfo = (int *)malloc(sizeof(int) * COLS);
memset((void *)colEmptyInfo, 0, sizeof(int) * COLS);
// 将存在的精灵降落下来
SpriteShape *spr = NULL;
for (int c = 0; c < COLS; c++)
{
int removedSpriteOfCol = 0;
// 自底向上
for (int r = 0; r < ROWS; r++)
{
spr = map[r][c];
if (spr == NULL)
{
++removedSpriteOfCol;
}
else
{
if (removedSpriteOfCol > 0)
{
int newRow = r - removedSpriteOfCol;
map[newRow][c] = spr;
map[r][c] = NULL;
Point startPosition = spr->getPosition();
Point endPosition = positionOfItem(newRow, c);
float speed = (startPosition.y - endPosition.y) / GAME_SCREEN_HEIGHT * 3;
spr->stopAllActions();
spr->runAction(CCMoveTo::create(speed, endPosition));
spr->setRow(newRow);
}
}
}
// 记录相应列数移除的数量
colEmptyInfo[c] = removedSpriteOfCol;
sum += removedSpriteOfCol;
}
// 新建新的精灵,并降落
for (int c = 0; c < COLS; ++c)
{
for (int r = ROWS - colEmptyInfo[c]; r < ROWS; ++r)
{
createSprite(r, c);
}
}
m_score += sum * 30;
free(colEmptyInfo);
}
2.4 分数的传递
在游戏结束时,我们需要将分数传递到游戏结束界面。以下是相关代码:
// GameScene.cpp
// 游戏结束函数
void GameScene::gameOver(float dt)
{
auto scene = Scene::create();
auto layer = GameOver::create();
// 传递当前游戏获得的分数
layer->setScore(m_score);
scene->addChild(layer);
CCTransitionScene* reScene = CCTransitionFadeUp::create(1.0f, scene);
CCDirector::sharedDirector()->replaceScene(reScene);
}
// GameOverScene.cpp
void GameOver::setScore(int sc)
{
auto labelScore = (Label *)this->getChildByTag(13);
labelScore->setString(StringUtils::format(" %d ", sc));
}
现在,你可以运行游戏,体验添加时间与分数后的效果。目前游戏已经有了基本雏形,后续文章将对游戏进行进一步修饰,例如添加消除特效、实现消除4个精灵产生消除一列或一行的效果、添加音乐和音效以及记录最高分等。