1、一个游戏不能没有胜利和失败,当达到某个条件时,判断游戏胜利。或者说,当违反某个游戏规则既定次数时,游戏结束。本游戏有一个简单的场景切换来显示游戏的胜负,譬如当用户操控的小人当前的得分小于零时,游戏结束,切换到另一场景,显示一行文字:“You Lost!”;当分数达到某个值时(譬如20)显示“You Win!”。
2、显然,You Win 和 You Lost这两个场景只是文字不同,完全可以只写一个场景的类,调用时根据游戏情况,传入要显示的文字就行了。下面开始实现这个场景:
(1)新建两个类,分别别GameOverScene和GameOverLayer;
(2)在GameOverLayer的init函数中创建CCLabelTTF并设置位置为窗口正中,添加至布景层中;
(3)在GameOverLayer中为Label写一个函数,用于设置文字;
(4)因为在一个场景中不能直接访问到另一个场景的布景层,所以需要在另一个场景中写一个函数,返回另一个场景中创建的布景层的指针,这样就可以通过这个指针访问布景层了;
(5)在游戏场景中判断游戏的胜利与失败,并分别设置文字切换场景;
(6)前面的文章中有一个注释写错了,即子弹精灵数组应为_projectiles,而_targets是敌人,本文已改正;
3、贴出两个新类的代码:
GameOverLayer.h
#pragma once
#include "cocos2d.h"
class GameOverLayer : public cocos2d::CCLayerColor
{
public:
GameOverLayer(void);
~GameOverLayer(void);
bool init();
CREATE_FUNC(GameOverLayer);
// 用于显示文字的Label
cocos2d::CCLabelTTF *_pLabel;
// 设置文字函数
void SetGameOverString(const char *str);
};
GameOverLayer.cpp
#include "GameOverLayer.h"
USING_NS_CC;
GameOverLayer::GameOverLayer(void)
:_pLabel(NULL)
{
}
GameOverLayer::~GameOverLayer(void)
{
}
bool GameOverLayer::init()
{
bool bRct = false;
do
{
// 必须先调用父类初始化
CC_BREAK_IF(!CCLayerColor::initWithColor(ccc4(0, 0, 0, 255)));
// 创建Label
_pLabel = CCLabelTTF::create("", "Arial", 34);
// 检查
CC_BREAK_IF(!_pLabel);
// 设置Label位置为窗口正中
_pLabel->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width/2, CCDirector::sharedDirector()->getWinSize().height/2) );
// 将Label添加至布景Layer
this->addChild(_pLabel);
bRct = true;
} while (0);
return bRct;
}
void GameOverLayer::SetGameOverString(const char *str)
{
_pLabel->setString(str);
}
GameOverScene.h
#pragma once
#include "cocos2d.h"
#include "GameOverLayer.h"
class GameOverScene : public cocos2d::CCScene
{
public:
GameOverScene(void);
~GameOverScene(void);
bool init();
CREATE_FUNC(GameOverScene);
// 游戏结束布景层指针
GameOverLayer *_pGameOverLayer;
// 返回布景层的指针
GameOverLayer* GetGameOverLayerPointer();
};
GameOverScene.cpp
#include "GameOverScene.h"
USING_NS_CC;
GameOverScene::GameOverScene(void)
:_pGameOverLayer(NULL)
{
}
GameOverScene::~GameOverScene(void)
{
}
bool GameOverScene::init()
{
bool bRct = false;
do
{
CC_BREAK_IF(!CCScene::init());
// 创建GameOverLayer对象
_pGameOverLayer = GameOverLayer::create();
// 检查
CC_BREAK_IF(!_pGameOverLayer);
// 将布景添加至场景中
this->addChild(_pGameOverLayer);
bRct = true;
} while (0);
return bRct;
}
GameOverLayer* GameOverScene::GetGameOverLayerPointer()
{
return _pGameOverLayer;
}
另外,在ShooterLayer中判断胜负,并切换场景:
ShooterLayer.h
#pragma once
#include "cocos2d.h"
class ShooterLayer : public cocos2d::CCLayerColor
{
public:
ShooterLayer(void);
~ShooterLayer(void);
// 初始化函数
bool init();
// new一个对象,如果成功的话则调用初始化函数
CREATE_FUNC(ShooterLayer);
// 关闭菜单的回调函数
void menuCloseCallback(CCObject *pObj);
// 计时器的回调函数
void update(float dt);
// 随机创建敌方精灵
void addTarget();
// 敌方精灵飞出边界的回调函数
void SpriteMoveFinished(CCNode *pObj);
// 触摸操作(即Win32中的鼠标点击操作)响应函数
void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);
// 保存射击精灵的大小
cocos2d::CCSize _szPlayer;
// 碰撞检测
void bulletCrashTest();
// 敌人精灵数组
cocos2d::CCArray *_targets;
// 子弹精灵数组
cocos2d::CCArray *_projectiles;
// 显示击中次数(得分)
cocos2d::CCLabelTTF* _pLabel;
// 保存击中次数
int _score;
// 定义一个常量,用于设置初始的分数
const int constInitialScore;
};
ShooterLayer.cpp
#include "ShooterLayer.h"
#include "GameOverScene.h"
USING_NS_CC;
// 标记精灵
enum
{
TARGET,
PROJECT
};
ShooterLayer::ShooterLayer(void)
:_score(0)
,constInitialScore(10)
{
_score = constInitialScore;
_targets = NULL;
_projectiles = NULL;
}
ShooterLayer::~ShooterLayer(void)
{
if (_targets)
{
delete _targets;
_targets = NULL;
}
if (_projectiles)
{
delete _projectiles;
_projectiles = NULL;
}
}
bool ShooterLayer::init()
{
bool bRct = false;
// 初始化数组
_targets = new CCArray;
_projectiles = new CCArray;
do
{
// 1、初始化布景
CC_BREAK_IF(!CCLayerColor::initWithColor(ccc4(255, 227, 132, 255)));
// 2、创建窗口右下角的关闭菜单
// 创建菜单项
CCMenuItemImage *pCloseItem = NULL;
pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(ShooterLayer::menuCloseCallback)); // 回调函数
// 检测创建是否成功
CC_BREAK_IF(!pCloseItem); // create失败则返回
// 设置菜单项位置
pCloseItem->setPosition(CCDirector::sharedDirector()->getWinSize().width - 20, 20);
// 创建菜单
CCMenu *pMenu = CCMenu::create(pCloseItem, NULL);
// 设置菜单位置
pMenu->setPosition(CCPointZero);
// 将菜单添加至布景
this->addChild(pMenu);
//
// 创建精灵
CCSprite *pSprite = CCSprite::create("Player.png");
// 获取窗口大小
CCSize sz = CCDirector::sharedDirector()->getWinSize();
// 设置精灵位置
pSprite->setPosition(CCPointMake(pSprite->getContentSize().width/2, sz.height/2));
// 将精灵添加至布景
this->addChild(pSprite);
// 保存精灵大小
_szPlayer = pSprite->getContentSize();
//
// 显示分数的Label
// Create a label and initialize with string "0".
_pLabel = CCLabelTTF::create("Your Score:0", "Arial", 24);
// 设置颜色
_pLabel->setColor(ccc3(0, 0, 0));
CC_BREAK_IF(! _pLabel);
// Get window size and place the label upper.
CCSize size = CCDirector::sharedDirector()->getWinSize();
_pLabel->setPosition(ccp(size.width - 100, size.height - 50));
// Add the label to HelloWorld layer as a child layer.
this->addChild(_pLabel, 1);
//
//
// 设置计时器
this->scheduleUpdate();
//
// 允许触摸(注意:很重要)
this->setTouchEnabled(true);
//
bRct = true;
} while (0);
return bRct;
}
void ShooterLayer::menuCloseCallback(CCObject *pObj)
{
// 退出程序
CCDirector::sharedDirector()->end();
}
void ShooterLayer::update(float dt)
{
static int tmpTimes = 0;
static int tmpCount = 20;
if (tmpTimes++ > tmpCount)
{
addTarget();
tmpCount = rand() % 60 + 20;
tmpTimes = 0;
}
bulletCrashTest();
}
void ShooterLayer::addTarget()
{
// 创建精灵
CCSprite *target = CCSprite::create("Target.png");
// 计算敌方精灵可能出现的位置范围
CCSize sz = CCDirector::sharedDirector()->getWinSize();
int x = sz.width + target->getContentSize().width/2;
int nMax = sz.height - target->getContentSize().height/2;
int nMin = target->getContentSize().height/2;
// 产生随机数
int f = rand() % (nMax - nMin);
int y = nMin + f;
// 根据随机数,设置起始精灵位置
target->setPosition(CCPointMake(x, y));
// 利用计时器确定执行序列动作的速度
float nDurMin = 2.0;
float nDurMax = 4.0;
float nDur = float(rand() % 100) / 100.0 + nDurMin;
nDur = nDur > nDurMax ? nDurMax : nDur;
// 定义两个动作(平移动作, 以及移出边界的响应)
CCFiniteTimeAction *action = CCMoveTo::create(nDur, ccp(-target->getContentSize().width/2, y));
CCFiniteTimeAction *actionDone = CCCallFuncN::create(this,
callfuncN_selector(ShooterLayer::SpriteMoveFinished)); // 这里需要一个移出边界的响应函数
// 添加序列动作
CCAction *pseq = CCSequence::create(action, actionDone, NULL);
// 将序列动作添加至精灵,使其执行
target->runAction(pseq);
// 将精灵添加至布景
this->addChild(target);
//
// 加入数组
_targets->addObject(target);
// 设置标记
target->setTag(TARGET);
}
void ShooterLayer::SpriteMoveFinished(CCNode *pObj)
{
// 获取移出精灵对象指针
CCSprite *pSprite = (CCSprite*)pObj;
// 清除
this->removeChild(pSprite, true);
//
// 根据标记,释放对象
if (pSprite->getTag() == TARGET)
{
_targets->removeObject(pSprite);
_score--;
// 通过Label显示碰撞次数(即得分)
CCString *str = CCString::stringWithFormat("Your Score:%d", _score);
_pLabel->setString(str->getCString());
// 游戏失败
if (_score < 0)
{
_score = constInitialScore; // 重置为初始值
// 创建游戏失败场景
GameOverScene *pGameOverScene = GameOverScene::create();
pGameOverScene->GetGameOverLayerPointer()->SetGameOverString("You Lost!");
// 切换场景
CCDirector::sharedDirector()->replaceScene(pGameOverScene);
}
}
else if (pSprite->getTag() == PROJECT)
{
_projectiles->removeObject(pSprite);
}
}
void ShooterLayer::ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent)
{
// 获取CCTouch指针对象
CCTouch *pTouch = (CCTouch*)pTouches->anyObject();
// 获取视图位置
CCPoint location = pTouch->getLocationInView();
// 转化为OpenGL坐标
location = CCDirector::sharedDirector()->convertToGL(location);
// 创建子弹精灵
CCSprite *project = CCSprite::create("Projectile.png");
// 窗口大小
CCSize sz = CCDirector::sharedDirector()->getWinSize();
// 子弹起点
CCPoint pt = ccp(_szPlayer.width + project->getContentSize().width/2, sz.height/2);
// 设置子弹起点
project->setPosition(pt);
// 添加精灵至布景
this->addChild(project);
//
// 添加至数组
_projectiles->addObject(project);
// 添加标记
project->setTag(PROJECT);
//
// 求出子弹终点坐标
int x = sz.width + project->getContentSize().width/2;
float yDistance = (location.y - pt.y) / (location.x - pt.x) * (sz.width - _szPlayer.width);
// 子弹动作
CCFiniteTimeAction *action = CCMoveTo::create(1.0, ccp(x, yDistance + sz.height/2));
CCFiniteTimeAction *actionDone = CCCallFuncN::create(this, callfuncN_selector(ShooterLayer::SpriteMoveFinished));
project->runAction(CCSequence::create(action, actionDone, NULL));
}
void ShooterLayer::bulletCrashTest()
{
// 定义两个数组用于存储待删除的精灵
CCArray * projectilesToDelete = new CCArray;
CCArray * targetsToDelete = new CCArray;
CCObject* it = NULL;
CCObject* jt = NULL;
CCARRAY_FOREACH(_projectiles, it)
{
CCSprite *projectile = dynamic_cast<CCSprite *>(it);
CCRect projectileRect = CCRectMake(projectile->getPosition().x -
(projectile->getContentSize().width/2), projectile->getPosition().y -
(projectile->getContentSize().height/2), projectile->getContentSize().width, projectile->getContentSize().height);
CCARRAY_FOREACH(_targets, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
CCRect targetRect = CCRectMake(target->getPosition().x - (target->getContentSize().width/2),
target->getPosition().y - (target->getContentSize().height/2),target->getContentSize().width, target->getContentSize().height);
// 碰撞检测
if (/*CCRect::CCRectIntersectsRect(projectileRect, targetRect)*/projectileRect.intersectsRect(targetRect))
{
_score ++; // 碰撞次数
// 将精灵移入待删除数组
targetsToDelete->addObject(target);
projectilesToDelete->addObject(projectile);
// 通过Label显示碰撞次数(即得分)
CCString *str = CCString::stringWithFormat("Your Score:%d", _score);
_pLabel->setString(str->getCString());
// 胜利条件
if (_score > 20)
{
_score = constInitialScore; // 重置为初始值
// 创建游戏结束场景
GameOverScene *pGameOverScene = GameOverScene::create();
pGameOverScene->GetGameOverLayerPointer()->SetGameOverString("You Win!");
// 切换场景
CCDirector::sharedDirector()->replaceScene(pGameOverScene);
}
}
}
}
// 释放资源
CCARRAY_FOREACH(targetsToDelete, jt)
{
CCSprite *target = dynamic_cast<CCSprite*>(jt);
_targets->removeObject(target);
this->removeChild(target, true);
}
CCARRAY_FOREACH(projectilesToDelete, it)
{
CCSprite* projectile = dynamic_cast<CCSprite*>(it);
_projectiles->removeObject(projectile);
this->removeChild(projectile, true);
}
projectilesToDelete->release();
targetsToDelete->release();
}
4、游戏胜利:
5、游戏失败:
6、待续