本文是解析跑酷victorian(维多利亚人)的代码,使用的coco2dx引擎。
设计上:
代理控制程序的开关、设置帧率、创建和运行场景,游戏层初始化时进入游戏主循环。游戏界面渲染在游戏层上。地形是个空白图精灵,控制所有的街区块精灵。
游戏含游戏状态机(在游戏主循环),控制游戏状态。角色有角色状态机,控制角色状态。
运行图:
目录:
1、游戏代理
2、游戏层
2.1、游戏层和初始化
2.2、游戏界面
2.3、游戏控制
2.4、游戏层主循环
3、角色
3.1、角色循环
4、地形
4.1、地形精灵初始化
4.2、地形精灵操作
4.3、地形控制街区块
5、街区块
5.1、街区块定义和初始化
5.2、设置街区块参数,包括屋顶和陷阱
5.3、街区块突出景象(烟囱)
5.4、街区块随机模式
6、自定义精灵
内容:
1、游戏代理
代理运行场景对象
bool AppDelegate::applicationDidFinishLaunching()
{
// initialize director
CCDirector *pDirector = CCDirector::sharedDirector();
pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
// turn on display FPS
pDirector->setDisplayStats(true);
// set FPS. the default value is 1.0/60 if you don't call this
pDirector->setAnimationInterval(1.0 / 60);
// create a scene. it's an autorelease object
CCScene *pScene = GameLayer::scene();
// run
pDirector->runWithScene(pScene);
return true;
}
代理控制动画
应用进入后台时停止动画
// This function will be called when the app is inactive. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground()
{
CCDirector::sharedDirector()->stopAnimation();
}
应用重新启动时开始动画
// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground()
{
CCDirector::sharedDirector()->startAnimation();
}
2、游戏层
2.1、游戏层和初始化
创建场景对象和层,并把层添加到场景对象
CCScene* GameLayer::scene()
{
CCScene * scene = NULL;
do
{
// 'scene' is an autorelease object
scene = CCScene::create();
CC_BREAK_IF(! scene);
// 'layer' is an autorelease object
GameLayer *layer = GameLayer::create();
CC_BREAK_IF(! layer);
// add layer as a child to scene
scene->addChild(layer);
} while (0);
// return the scene
return scene;
}
游戏层初始化
bool GameLayer::init()
{
bool bRet = false;
do
{
//
// super init first
//
CC_BREAK_IF(! CCLayer::init());
//获取屏幕大小
_screenSize = CCDirector::sharedDirector()->getWinSize();
//创建游戏界面
createGameScreen();
//重新开始游戏
resetGame();
//listen for touches
this->setTouchEnabled(true);
//create main loop
this->schedule(schedule_selector(GameLayer::update));
bRet = true;
} while (0);
return bRet;
}
2.2、游戏界面
创建游戏界面,加载资源到内存,设置背景和装饰精灵和角色精灵
void GameLayer::createGameScreen () {
//设置游戏背景
CCSprite * bg = CCSprite::create("bg.png");
bg->setPosition(ccp(_screenSize.width * 0.5f, _screenSize.height * 0.5f));
this->addChild(bg, kBackground);
//CCSpriteFrameCache(精灵帧缓存)主要用来存放CCSpriteFrame,它没有提供特别的属性,而是提供一系列用于管理CCSpriteFrame的方法
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("sprite_sheet.plist","sprite_sheet.png");
_gameBatchNode = CCSpriteBatchNode::create("sprite_sheet.png", 200);
this->addChild(_gameBatchNode, kMiddleground);
CCSprite * repeat;
_background = CCSprite::createWithSpriteFrameName("background.png");
_background->setAnchorPoint(ccp(0,0));
_gameBatchNode->addChild(_background, kBackground);
repeat = CCSprite::createWithSpriteFrameName("background.png");
repeat->setAnchorPoint(ccp(0,0));
repeat->setPosition(ccp(repeat->getContentSize().width - 1, 0));
_background->addChild(repeat, kBackground);
repeat = CCSprite::createWithSpriteFrameName("background.png");
repeat->setAnchorPoint(ccp(0,0));
repeat->setPosition(ccp(2 * (repeat->getContentSize().width - 1), 0));
_background->addChild(repeat, kBackground);
//设置路灯
_foreground = CCSprite::createWithSpriteFrameName("lamp.png");
_foreground->setAnchorPoint(ccp(0,0));
_gameBatchNode->addChild(_foreground, kForeground);
repeat = CCSprite::createWithSpriteFrameName("lamp.png");
repeat->setAnchorPoint(ccp(0,0));
repeat->setPosition(ccp(repeat->getContentSize().width * 4, 0));
_foreground->addChild(repeat, kBackground);
repeat = CCSprite::createWithSpriteFrameName("lamp.png");
repeat->setAnchorPoint(ccp(0,0));
repeat->setPosition(ccp(repeat->getContentSize().width * 8, 0));
_foreground->addChild(repeat, kBackground);
//加入精灵云
CCSprite * cloud;
_clouds = CCArray::createWithCapacity(4);
_clouds->retain();
float cloud_y;
for (int i = 0; i < 4; i++) {
//云位置:一低一高一低一高显示
cloud_y = i % 2 == 0 ? _screenSize.height * 0.7f : _screenSize.height * 0.8f;
cloud = CCSprite::createWithSpriteFrameName("cloud.png");
cloud->setPosition(ccp (_screenSize.width * 0.15f + i * _screenSize.width * 0.25f, cloud_y));
_gameBatchNode->addChild(cloud, kBackground);
_clouds->addObject(cloud);
}
//创建地形类
_terrain = Terrain::create();
_gameBatchNode->addChild(_terrain, kMiddleground);
//创建主角类
_player = Player::create();
_gameBatchNode->addChild(_player, kBackground);
//加入logo
_intro = CCSprite::createWithSpriteFrameName("logo.png");
_intro->setPosition(ccp(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
_gameBatchNode->addChild(_intro, kForeground);
//加入重试
_tryAgain = CCSprite::createWithSpriteFrameName("label_tryagain.png");
_tryAgain->setPosition(ccp(_screenSize.width * 0.5f, _screenSize.height * 0.7f));
_tryAgain->setVisible(false);
this->addChild(_tryAgain, kForeground);
//加入积分板
_scoreDisplay = CCLabelBMFont::create("000000", "font.fnt", _screenSize.width * 0.3f, kCCTextAlignmentCenter);
_scoreDisplay->setAnchorPoint(ccp(1,0));
_scoreDisplay->setPosition(ccp(_screenSize.width * 0.95f, _screenSize.height * 0.88f));
this->addChild(_scoreDisplay, kBackground);
//加入帽子
_hat = CCSprite::createWithSpriteFrameName("hat.png");
_hat->setVisible(false);
_gameBatchNode->addChild(_hat, kMiddleground);
//加入一个竞技团队(车队动画和车队移动动画)
_jam = CCSprite::createWithSpriteFrameName("jam_1.png");
//创建车队动画
CCAnimation* animation;
animation = CCAnimation::create();
CCSpriteFrame * frame;
int i;
for(i = 1; i <= 3; i++) {
char szName[100] = {0};
sprintf(szName, "jam_%i.png", i);
frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(szName);
animation->addSpriteFrame(frame);
}
animation->setDelayPerUnit(0.2f / 3.0f);
animation->setRestoreOriginalFrame(false);
animation->setLoops(-1);
_jamAnimate = CCAnimate::create(animation);
_jamAnimate->retain();
_gameBatchNode->addChild(_jam, kBackground);
_jam->setPosition(ccp(_screenSize.width * 0.19f, _screenSize.height * 0.47f));
_jamMove = CCMoveTo::create(6.0f, ccp(-_screenSize.width * 0.3f, _jam->getPositionY()));
_jamMove->retain();
//add menu
CCSprite * menuItemOn;
CCSprite * menuItemOff;
//菜单有两个状态,平时展示的样子和点击的样子
menuItemOn = CCSprite::createWithSpriteFrameName("btn_new_on.png");
menuItemOff = CCSprite::createWithSpriteFrameName("btn_new_off.png");
//New Game 菜单
CCMenuItemSprite * starGametItem = CCMenuItemSprite::create(
menuItemOff,
menuItemOn,
this,
//这个最重要,点击菜单调用系统哪个方法
menu_selector(GameLayer::startGame));
menuItemOn = CCSprite::createWithSpriteFrameName("btn_howto_on.png");
menuItemOff = CCSprite::createWithSpriteFrameName("btn_howto_off.png");
//How to Play 菜单
CCMenuItemSprite * howToItem = CCMenuItemSprite::create(
menuItemOff,
menuItemOn,
this,
menu_selector(GameLayer::showTutorial));
_mainMenu = CCMenu::create(howToItem, starGametItem, NULL);//创建菜单
_mainMenu->alignItemsHorizontallyWithPadding(120);//设置两个菜单的水平间距(水平方向以padding间隙排序)
//_mainMenu->alignItemsVerticallyWithPadding(50);
_mainMenu->setPosition(ccp(_screenSize.width * 0.5f, _screenSize.height * 0.54));
this->addChild(_mainMenu, kForeground);
_tutorialLabel = CCLabelTTF::create("", "Times New Roman", 25);
_tutorialLabel->setPosition(ccp (_screenSize.width * 0.5f, _screenSize.height * 0.6f) );
this->addChild(_tutorialLabel, kForeground);
_tutorialLabel->setVisible(false);
}
2.3、游戏控制
展示教程状态
void GameLayer::showTutorial (CCObject* pSender) {
_tutorialLabel->setString("Tap the screen to make the player jump.");
_state = kGameTutorialJump;
_jam->runAction(_jamMove);
_intro->setVisible(false);
_mainMenu->setVisible(false);
//SimpleAudioEngine::sharedEngine()->playEffect("start.wav");
_tutorialLabel->setVisible(true);
}
开始游戏状态
void GameLayer::startGame (CCObject* pSender) {
_tutorialLabel->setVisible(false);
_intro->setVisible(false);
_mainMenu->setVisible(false);
_jam->runAction(_jamMove);
// SimpleAudioEngine::sharedEngine()->playEffect("start.wav");
_terrain->setStartTerrain ( true );
_state = kGamePlay;
}
重新开始游戏
void GameLayer::resetGame () {
//初始化配置
_score = 0;
_speedIncreaseInterval = 15;
_speedIncreaseTimer = 0;
//显示积分
char szValue[100] = {0};
sprintf(szValue, "%i", (int) _score);
_scoreDisplay->setString (szValue);
_scoreDisplay->setAnchorPoint(ccp(1,0));
_scoreDisplay->setPosition(ccp(_screenSize.width * 0.95f, _screenSize.height * 0.88f));
_state = kGameIntro;
_intro->setVisible(true);
_mainMenu->setVisible(true);
_jam->setPosition(ccp(_screenSize.width * 0.19f, _screenSize.height * 0.47f));
_jam->setVisible(true);
_jam->runAction(_jamAnimate);
_running = true;
}
2.4、游戏层主循环
游戏层主循环,游戏状态机
void GameLayer::update(float dt) {
//CCLog("_running:%d",_running);
if (!_running)
return;
//获取主角位置
if (_player->getPositionY() < -_player->getHeight() ||
_player->getPositionX() < -_player->getWidth() * 0.5f) {//死亡判断
if (_state == kGamePlay) {
_running = false;
//create GAME OVER state
_state = kGameOver;//状态为结束状态
_tryAgain->setVisible(true);//出现重试图标
_scoreDisplay->setAnchorPoint(ccp(0.5f, 0.5f));//分数位置修改
_scoreDisplay->setPosition(ccp(_screenSize.width * 0.5f,
_screenSize.height * 0.88f));
_hat->setPosition(ccp(_screenSize.width * 0.2f, -_screenSize.height * 0.1f));
_hat->setVisible(true);//出现帽子
//旋转精灵,以度为单位
CCAction * rotate = CCRotateBy::create(2.0f, 660);
CCAction * jump = CCJumpBy::create(2.0f, ccp(0,10), _screenSize.height * 0.8f, 1);
//主角死亡,帽子旋转着跳起
_hat->runAction(rotate);
_hat->runAction(jump);
//SimpleAudioEngine::sharedEngine()->stopBackgroundMusic();
//SimpleAudioEngine::sharedEngine()->playEffect("crashing.wav");
}
}
//更新主角状态,角色状态机
_player->update(dt);
_terrain->move(_player->getVector().x);
//检查碰撞冲突
if (_player->getState() != kPlayerDying)
_terrain->checkCollision(_player);
//放置主角
_player->place();
if (_player->getNextPosition().y > _screenSize.height * 0.6f) {
_gameBatchNode->setPositionY( (_screenSize.height * 0.6f - _player->getNextPosition().y) * 0.8f);
}
else {
_gameBatchNode->setPositionY ( 0 );
}
//更新视差 update paralax
if (_player->getVector().x > 0) {
//一直往左移
_background->setPositionX(_background->getPosition().x - _player->getVector().x * 0.25f);
float diffx;
//移完一个宽度时,重新把位置设置为接近0的位置
if (_background->getPositionX() < -_background->getContentSize().width) {
diffx = fabs(_background->getPositionX()) - _background->getContentSize().width;
_background->setPositionX(-diffx);
}
_foreground->setPositionX(_foreground->getPosition().x - _player->getVector().x * 4);
if (_foreground->getPositionX() < -_foreground->getContentSize().width * 4) {
diffx = fabs(_foreground->getPositionX()) - _foreground->getContentSize().width * 4;
_foreground->setPositionX(-diffx);
}
int count = _clouds->count();
CCSprite * cloud;
for (int i = 0; i < count; i++) {
cloud = (CCSprite *) _clouds->objectAtIndex(i);
cloud->setPositionX(cloud->getPositionX() - _player->getVector().x * 0.15f);
if (cloud->getPositionX() + cloud->boundingBox().size.width * 0.5f < 0 )
cloud->setPositionX(_screenSize.width + cloud->boundingBox().size.width * 0.5f);
}
}
if (_jam->isVisible()) {
if (_jam->getPositionX() < -_screenSize.width * 0.2f) {
_jam->stopAllActions();
_jam->setVisible(false);
}
}
if (_terrain->getStartTerrain() && _player->getVector().x > 0)
{
_score += dt * 50;
char szValue[100] = {0};
sprintf(szValue, "%i", (int) _score);
_scoreDisplay->setString (szValue);
_speedIncreaseTimer += dt;
if (_speedIncreaseTimer > _speedIncreaseInterval) {
_speedIncreaseTimer = 0;
_player->setMaxSpeed (_player->getMaxSpeed() + 4);
}
}
if (_state > kGameTutorial) {//教程模式状态
if (_state == kGameTutorialJump) {
if (_player->getState() == kPlayerFalling && _player->getVector().y < 0) {
_player->stopAllActions();
_jam->setVisible(false);
_jam->stopAllActions();
_running = false;
_tutorialLabel->setString("While in the air, tap the screen to float.");
_state = kGameTutorialFloat;
}
} else if (_state == kGameTutorialFloat) {
if (_player->getPositionY() < _screenSize.height * 0.95f) {
_player->stopAllActions();
_running = false;
_tutorialLabel->setString("While floating, tap the screen again to drop.");
_state = kGameTutorialDrop;
}
}else {
_tutorialLabel->setString("That's it. Tap the screen to play.");
_state = kGameTutorial;
}
}
}
3、角色
3.1、角色循环
角色循环,处理状态机(0.017s)
void Player::update (float dt) {
//加速度到最大,然后匀速
if (_speed + ACCELERATION <= _maxSpeed) {
_speed += ACCELERATION;
} else {
_speed = _maxSpeed;
}
_vector.x = _speed;
//CCLog("play state:%d",_state);
switch (_state) {
case kPlayerMoving:
_vector.y -= GRAVITY;
if (_hasFloated)
_hasFloated = false;
break;
case kPlayerFalling:
if (_floating ) {
_vector.y -= FLOATNG_GRAVITY;//(减去重力加速度0.4f,乘以空气阻力0.98f)
_vector.x *= FLOATING_FRICTION;
} else {
_vector.y -= GRAVITY;//减去重力加速度(1.3f),乘以空气阻力(0.99f)
_vector.x *= AIR_FRICTION;
_floatingTimer = 0;
}
break;
case kPlayerDying:
_vector.y -= GRAVITY;//y减去重力加速度(1.3f),x减去速度4
_vector.x = -_speed;
this->setPositionX(this->getPositionX() + _vector.x);
break;
}
if (_jumping) {//按了跳起,就处于下落状态
_state = kPlayerFalling;
_vector.y += PLAYER_JUMP * 0.25f;//跳高速度跟跳跃高度(15)的1/4
if (_vector.y > PLAYER_JUMP )
_jumping = false;
}
if (_vector.y < -TERMINAL_VELOCITY) //y速度需要大于-70
_vector.y = -TERMINAL_VELOCITY;
_nextPosition.y = this->getPositionY() + _vector.y;//设置下一点的y值
if (_vector.x * _vector.x < 0.01)
_vector.x = 0;
if (_vector.y * _vector.y < 0.01)
_vector.y = 0;
if (_floating) {//漂浮定时器
_floatingTimer += dt;
if (_floatingTimer > _floatingTimerMax) {
_floatingTimer = 0;
this->setFloating(false);
}
}
}
//主角位置的真正实现
inline virtual void place () {
this->setPositionY( _nextPosition.y );
if (_vector.x > 0 && this->getPositionX() < _screenSize.width * 0.2f) {
this->setPositionX(this->getPositionX() + _vector.x);
if (this->getPositionX() > _screenSize.width * 0.2f) {
this->setPositionX(_screenSize.width * 0.2f);
}
}
};
4、地形
地形精灵初始化,地形精灵本身是个空白图的精灵
4.1、地形精灵初始化
void Terrain::initTerrain () {
_increaseGapInterval = 5000;
_increaseGapTimer = 0;
//陷阱宽初始为2
_gapSize = 2;
//街区块池,设计一个街区块pool用数组存起来
_blockPool = CCArray::createWithCapacity(20);
_blockPool->retain();
//init object pools
Block * block;
for (int i = 0; i < 20; i++) {
//初始化街区块
block = Block::create();
this->addChild(block);
_blockPool->addObject(block);
}
_blocks = CCArray::createWithCapacity(20);
_blocks->retain();
_minTerrainWidth = _screenSize.width * 1.5f;
random_shuffle(_blockPattern.begin(), _blockPattern.end());
random_shuffle(_blockWidths.begin(), _blockWidths.end());
random_shuffle(_blockHeights.begin(), _blockHeights.end());
this->addBlocks(0);
}
4.2、地形精灵操作
移动地形精灵
void Terrain::move (float xMove) {
if (xMove < 0)
return;
if (_startTerrain) {
if (xMove > 0 && _gapSize < 5)
_increaseGapTimer += xMove;//增加空隙定时器
if (_increaseGapTimer > _increaseGapInterval) {
_increaseGapTimer = 0;
_gapSize += 1;
}
}
this->setPositionX(this->getPositionX() - xMove);
Block * block;
block = (Block *) _blocks->objectAtIndex(0);
if (this->getPositionX() + block->getWidth() < 0) {
_blocks->removeObjectAtIndex(0);
_blocks->addObject(block);
this->setPositionX(this->getPositionX() + block->getWidth());
float width_cnt = this->getWidth() - block->getWidth() - ((Block *) _blocks->objectAtIndex(0))->getWidth();
this->initBlock(block);
this->addBlocks(width_cnt);
}
}
//地形精灵重置
void Terrain::reset() {
this->setPosition(ccp(0,0));
_startTerrain = false;
int count = _blocks->count();
Block * block;
int i = 0;
int currentWidth = 0;
for (i = 0; i < count; i++) {
block = (Block *) _blocks->objectAtIndex(i);
this->initBlock(block);
currentWidth += block->getWidth();
}
while (currentWidth < _minTerrainWidth) {
block = (Block *) _blockPool->objectAtIndex(_blockPoolIndex);
_blockPoolIndex++;
if (_blockPoolIndex == _blockPool->count()) {
_blockPoolIndex = 0;
}
_blocks->addObject(block);
this->initBlock(block);
currentWidth += block->getWidth();
}
this->distributeBlocks();
_increaseGapTimer = 0;
_gapSize = 2;
}
检查碰撞
void Terrain::checkCollision (Player * player) {
//检查主角状态,死亡则跳出
if (player->getState() == kPlayerDying)
return;
//街区总块数
int count = _blocks->count();
Block * block;
//是否在空中
bool inAir = true;
int i;
for (i = 0; i < count; i++) {
block = (Block *) _blocks->objectAtIndex(i);
if (block->getType() == kBlockGap)
continue;
//if within x, check y (bottom collision)
//当主角和房子在X轴上时
if (player->right() >= this->getPositionX() + block->left()
&& player->left() <= this->getPositionX() + block->right()) {
if (player->bottom() >= block->top() && player->next_bottom() <= block->top()
&& player->top() > block->top()) {
player->setNextPosition(ccp(player->getNextPosition().x, block->top() + player->getHeight()));
player->setVector ( ccp(player->getVector().x, 0) );
// Sets the rotation (angle) of the node in degrees
player->setRotation(0.0);
inAir = false;
break;
}
}
}
for (i = 0; i < count; i++) {
block = (Block *) _blocks->objectAtIndex(i);
if (block->getType() == kBlockGap)
continue;
//now if within y, check x (side collision)
//右侧房子突然高起
if ((player->bottom() < block->top() && player->top() > block->bottom())
|| (player->next_bottom() < block->top() && player->next_top() > block->bottom())) {
if (player->right() >= this->getPositionX() + block->getPositionX()
&& player->left() < this->getPositionX() + block->getPositionX()) {
player->setPositionX( this->getPositionX() + block->getPositionX() - player->getWidth() * 0.5f );
player->setNextPosition(ccp(this->getPositionX() + block->getPositionX() - player->getWidth() * 0.5f, player->getNextPosition().y));
player->setVector ( ccp(player->getVector().x * -0.5f, player->getVector().y) );
//主角装墙上了
if (player->bottom() + player->getHeight() * 0.2f < block->top()) {
player->setState(kPlayerDying);
return;
}
break;
}
}
}
if (inAir)
{
//设置下降状态
player->setState(kPlayerFalling);
}
else
{
//设置一直移植状态
player->setState(kPlayerMoving);
player->setFloating (false);
}
}
4.3、地形控制街区块
地形初始化街区块,地形移动和重置时会触发
void Terrain::initBlock(Block * block) {
int blockWidth;
int blockHeight;
//根据现在的街区类型索引找
int type = _blockTypes[_currentTypeIndex];
_currentTypeIndex++;
if (_currentTypeIndex == _blockTypes.size()) {
_currentTypeIndex = 0;
}
//地形设计开始
if (_startTerrain) {
//是否设置陷阱
if (_showGap) {
int gap = rand() % _gapSize;
if (gap < 2)
gap = 2;
//设置陷阱块
block->setupBlock (gap, 0, kBlockGap);
_showGap = false;
} else {
blockWidth = _blockWidths[_currentWidthIndex];
_currentWidthIndex++;
if (_currentWidthIndex == _blockWidths.size()) {
random_shuffle(_blockWidths.begin(), _blockWidths.end());
_currentWidthIndex = 0;
}
if (_blockHeights[_currentHeightIndex] != 0) {
//change height of next block
blockHeight = _blockHeights[_currentHeightIndex];
//if difference too high, decrease it
if (blockHeight - _lastBlockHeight > 2 && _gapSize == 2) {
blockHeight = 1;
}
} else {
blockHeight = _lastBlockHeight;
}
_currentHeightIndex++;
if (_currentHeightIndex == _blockHeights.size()) {
_currentHeightIndex = 0;
random_shuffle(_blockHeights.begin(), _blockHeights.end());
}
block->setupBlock (blockWidth, blockHeight, type);
_lastBlockWidth = blockWidth;
_lastBlockHeight = blockHeight;
//select next block series pattern
_currentPatternCnt++;
if (_currentPatternCnt > _blockPattern[_currentPatternIndex]) {
_showGap = true;
//start new pattern
_currentPatternIndex++;
if (_currentPatternIndex == _blockPattern.size()) {
random_shuffle(_blockPattern.begin(), _blockPattern.end());
_currentPatternIndex = 0;
}
_currentPatternCnt = 1;
}
}
} else {//地形没有改变 terrain is not being changed yet
_lastBlockHeight = 2;
_lastBlockWidth = rand() % 2 + 2;
block->setupBlock (_lastBlockWidth, _lastBlockHeight, type);
}
}
地形上添加新的街区块(之前需要去掉旧的)
void Terrain::addBlocks(int currentWidth) {
Block * block;
while (currentWidth < _minTerrainWidth)
{
block = (Block *) _blockPool->objectAtIndex(_blockPoolIndex);//从内存池获取街区块
_blockPoolIndex++;
if (_blockPoolIndex == _blockPool->count()) {
_blockPoolIndex = 0;
}
this->initBlock(block);//初始化街区块
currentWidth += block->getWidth();
_blocks->addObject(block);
}
this->distributeBlocks();
}
地形上分布街区块
void Terrain::distributeBlocks() {
int count = _blocks->count();
Block * block;
Block * prev_block;
int i;
for (i = 0; i < count; i++) {
block = (Block *) _blocks->objectAtIndex(i);
if (i != 0) {
prev_block = (Block *) _blocks->objectAtIndex(i - 1);
block->setPositionX( prev_block->getPositionX() + prev_block->getWidth());
}
else
{
block->setPositionX ( 0 );
}
}
}
5、街区块
街区块存在于一个从左到右的块数组(_blocks)。每个块是个精灵。
5.1、街区块定义和初始化
街区块定义
//街区类
class Block : public GameSprite {
//屏幕宽高
CCSize _screenSize;
int _tileWidth;
int _tileHeight;
int _puffIndex;
CCSpriteFrame * _tile1;
CCSpriteFrame * _tile2;
CCSpriteFrame * _tile3;
CCSpriteFrame * _tile4;
CCSpriteFrame * _roof1;//屋顶1
CCSpriteFrame * _roof2;//屋顶2
CCArray * _wallTiles;
CCArray * _roofTiles;
CCAction * _puffSpawn;
CCAction * _puffMove;
CCAction * _puffFade;
CCAction * _puffScale;
CCAction * _puffAnimation;
void initBlock(void);
void createPuff(void);
public:
CC_SYNTHESIZE(int, _type, Type);
//声明一个成员变量_puffing以及getfunName函数,没有set函数。getfunName函数的实现要自己做
CC_SYNTHESIZE_READONLY(bool, _puffing, Puffing);
//声明成员变量数组:烟囱
CC_SYNTHESIZE(CCArray *, _chimneys, Chimneys);
Block();
~Block();
static Block * create();
void setupBlock (int width, int height, int type);
void setPuffing (bool value);
void hidePuffs();
inline virtual int left() {
return this->getPositionX();
}
inline virtual int right() {
return this->getPositionX() + _width;
}
inline virtual int top() {
return this->getHeight();
}
inline virtual int bottom() {
return 0;
}
};
街区块初始化
//初始化街区配置信息
void Block::initBlock(){
_tile1 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("building_1.png");
_tile2 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("building_2.png");
_tile3 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("building_3.png");
_tile4 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("building_4.png");
_roof1 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("roof_1.png");
_roof2 = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName ("roof_2.png");
int i;
_wallTiles = CCArray::createWithCapacity(20);
_wallTiles->retain();
_roofTiles = CCArray::createWithCapacity(5);
_roofTiles->retain();
CCSprite * tile;
for (i = 0; i < 5; i++) {
tile = CCSprite::createWithSpriteFrameName("roof_1.png");
tile->setAnchorPoint(ccp(0, 1));
tile->setPosition(ccp(i * _tileWidth, 0));
tile->setVisible(false);
this->addChild(tile, kMiddleground, kRoofTile);
_roofTiles->addObject(tile);
for (int j = 0; j < 4; j++) {
tile = CCSprite::createWithSpriteFrameName("building_1.png");
tile->setAnchorPoint(ccp(0, 1));
tile->setPosition(ccp(i * _tileWidth, -1 * (_tileHeight * 0.47f + j * _tileHeight)));
tile->setVisible(false);
this->addChild(tile, kBackground, kWallTile);
_wallTiles->addObject(tile);
}
}
_chimneys = CCArray::createWithCapacity(5);
_chimneys->retain();
CCSprite * chimney;
CCSprite * puff;
for (i = 0; i < 5; i++) {
chimney = CCSprite::createWithSpriteFrameName("chimney.png");
chimney->setVisible(false);
this->addChild(chimney, kForeground, kChimney);
_chimneys->addObject(chimney);
for (int j = 0; j < TOTAL_PUFFS; j++) {
puff = CCSprite::createWithSpriteFrameName("puff_1.png");
puff->setAnchorPoint(ccp(0,-0.5));
puff->setVisible(false);
chimney->addChild(puff, -1, j);
}
}
CCAnimation* animation;
animation = CCAnimation::create();
CCSpriteFrame * frame;
for(i = 1; i <= 4; i++) {
char szName[100] = {0};
sprintf(szName, "puff_%i.png", i);
frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(szName);
animation->addSpriteFrame(frame);
}
animation->setDelayPerUnit(0.75f / 4.0f);
animation->setRestoreOriginalFrame(false);
animation->setLoops(-1);
_puffAnimation = CCAnimate::create(animation);
_puffAnimation->retain();
_puffSpawn = CCRepeat::create(CCSequence::create(CCDelayTime::create(0.5f),
CCCallFunc::create( this, callfunc_selector(Block::createPuff) ),
NULL), TOTAL_PUFFS);
_puffSpawn->retain();
_puffMove = CCMoveBy::create(1.0f, ccp(-100,80));
_puffMove->retain();
_puffFade = CCFadeOut::create(2.0f);
_puffFade->retain();
_puffScale = CCScaleBy::create(1.5f, 1.5);
_puffScale->retain();
_puffIndex = 0;
}
5.2、设置街区块参数,包括屋顶和陷阱
//设置街区:入参:宽、高、类型
void Block::setupBlock (int width, int height, int type) {
this->setPuffing(false);
_type = type;
_width = width * _tileWidth;
_height = height * _tileHeight + _tileHeight * 0.49f;
this->setPositionY(_height);
//这是屋顶
CCSpriteFrame * wallFrame;
//随机生成屋顶
CCSpriteFrame * roofFrame = rand() % 10 > 6 ? _roof1 : _roof2;
int num_chimneys;
float chimneyX[] = {0,0,0,0,0};
switch (type) {
//这是一个坑(看不见)
case kBlockGap:
this->setVisible(false);
return;
//街区1
case kBlock1:
wallFrame = _tile1;
chimneyX[0] = 0.2f;
chimneyX[1] = 0.8f;
num_chimneys = 2;
break;
case kBlock2:
wallFrame = _tile2;
chimneyX[0] = 0.2f;
chimneyX[1] = 0.8f;
chimneyX[2] = 0.5f;
num_chimneys = 3;
break;
case kBlock3:
wallFrame = _tile3;
chimneyX[0] = 0.2f;
chimneyX[1] = 0.8f;
chimneyX[2] = 0.5f;
num_chimneys = 3;
break;
case kBlock4:
wallFrame = _tile4;
chimneyX[0] = 0.2f;
chimneyX[1] = 0.5f;
num_chimneys = 2;
break;
}
int i;
GameSprite * chimney;
int count;
count = _chimneys->count();
for (i = 0; i < count; i++) {
chimney = (GameSprite *) _chimneys->objectAtIndex(i);
if (i < num_chimneys) {
chimney->setPosition( ccp (chimneyX[i] * _width, 0) );
chimney->setVisible(true);
} else {
chimney->setVisible(false);
}
}
this->setVisible(true);
CCSprite * tile;
count = _roofTiles->count();
for (i = 0; i < count; i++) {
tile = (CCSprite *) _roofTiles->objectAtIndex(i);
if (tile->getPositionX() < _width) {
tile->setVisible(true);
//显示相应的屋顶
tile->setDisplayFrame(roofFrame);
} else {
tile->setVisible(false);
}
}
//
count = _wallTiles->count();
for (i = 0; i < count; i++) {
tile = (CCSprite *) _wallTiles->objectAtIndex(i);
if (tile->getPositionX() < _width && tile->getPositionY() > -_height) {
tile->setVisible(true);
tile->setDisplayFrame(wallFrame);
} else {
tile->setVisible(false);
}
}
}
5.3、街区块突出景象(烟囱)
定时出现街区块突出的烟囱。在街区块初始化(Block::initBlock())时设置定时器。
void Block::createPuff () {
int count = _chimneys->count();
CCSprite * chimney;
CCSprite * puff;
for (int i = 0; i < count; i++) {
chimney = (CCSprite * ) _chimneys->objectAtIndex(i);
if (chimney->isVisible()) {
puff = (CCSprite *) chimney->getChildByTag(_puffIndex);
puff->setVisible(true);
puff->stopAllActions();
puff->setScale(1.0);
puff->setOpacity(255);
puff->setPosition(ccp(0,0));
puff->runAction((CCAction *) _puffAnimation->copy()->autorelease());
puff->runAction((CCAction *) _puffMove->copy()->autorelease());
//puff->runAction((CCAction *) _puffFade->copy()->autorelease());
puff->runAction((CCAction *) _puffScale->copy()->autorelease());
}
}
_puffIndex++;
if (_puffIndex == TOTAL_PUFFS)
_puffIndex = 0;
}
5.4、街区块随机模式
街区随机设计,地形精灵初始化时会打乱顺序
//随机的模式、宽、高、及类型
int patterns[] = {1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,3,3,3};
int widths[] = {2,2,2,2,2,3,3,3,3,3,3,4,4,4,4,4,4};
int heights[] = {0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,3,3,3,3,3,3,4};
int types[] = {1,2,3,4,1,3,2,4,3,2,1,4,2,3,1,4,2,3,1,2,3,2,3,4,1,2,4,3,1,3,1,4,2,4,2,1,2,3};
//街区模式
vector<int> _blockPattern (patterns, patterns + sizeof(patterns) / sizeof(int));
//街区宽度
vector<int> _blockWidths (widths, widths + sizeof(widths) / sizeof(int));
//街区高度
vector<int> _blockHeights (heights, heights + sizeof(heights) / sizeof(int));
//街区类型
vector<int> _blockTypes (types, types + sizeof(types) / sizeof(int));
6、自定义精灵
class GameSprite : public CCSprite {
protected:
CCSize _screenSize;
public:
//定义变量,并且直接定义默认的get/set方法
CC_SYNTHESIZE(CCPoint, _nextPosition, NextPosition);
CC_SYNTHESIZE(float, _width, Width);
CC_SYNTHESIZE(float, _height, Height);
CC_SYNTHESIZE(CCPoint, _vector, Vector);
GameSprite(void);
~GameSprite(void);
//内联虚函数,只是一个接口
inline virtual void place () { this->setPosition(_nextPosition); };
inline virtual float radius () {
return _width * 0.5f;
}
inline void setSize() {
_width = this->boundingBox().size.width;
_height = this->boundingBox().size.height;
}
};