上一篇:cocos2dx CrystalCraze 宝石消除游戏 解析(一)
上一篇介绍了 mainScene界面,下面进入正题,GameScne界面!会比较长,毕竟1000行代码啊,耐心看下吧
2 GameScene.js
这才是游戏界面的主要控制代码,一看1000行代码,啥也不说了,慢慢看吧...
界面还是从onDidLoadFromCCB 开始
//开始游戏
GameScene.prototype.onDidLoadFromCCB = function()
{
// 初始化面板 各种数组
setupBoard();
//游戏是否结束 是否显示提示
gIsGameOver = false;
gIsDisplayingHint = false;
//浏览器设置可以 鼠标控制
if( sys.platform == 'browser')
this.rootNode.setMouseEnabled( true );
// 触摸事件 跟鼠标事件
this.rootNode.onTouchesBegan = function( touches, event) {
this.controller.onTouchesBegan(touches, event);
return true;
};
this.rootNode.onMouseDown = function( event) {
this.controller.onMouseDown(event);
return true;
};
// Setup timer 进度条
this.sprtTimer.setVisible(false);
gTimer = cc.ProgressTimer.create(cc.Sprite.create("gamescene/timer.png"));
gTimer.setPosition(this.sprtTimer.getPosition());
gTimer.setPercentage(100);
gTimer.setType(cc.PROGRESS_TIMER_TYPE_BAR);
gTimer.setMidpoint(cc.p(0, 0.5));
gTimer.setBarChangeRate(cc.p(1, 0));
this.sprtHeader.addChild(gTimer);
//这是最后的点击时间,连击的个数,是否是炸弹,是否快没有时间
var dNow = Date.now();
gStartTime = dNow + kIntroTime;
gLastMoveTime = dNow;
gNumConsecutiveGems = 0;
gIsPowerPlay = false;
gEndTimerStarted = false;
gScore = 0;
// 没帧刷新onUpdate方法
this.rootNode.onUpdate = function(dt) {
this.controller.onUpdate();
};
this.rootNode.schedule(this.rootNode.onUpdate);
//判断平台,设置GameLayer
if ("opengl" in sys.capabilities && "browser" != sys.platform)
{
cc.log("OpenGL rendering");
gParticleLayer = cc.ParticleBatchNode.create("particles/taken-gem.png", 250);
gGameLayer = cc.SpriteBatchNode.create("crystals.pvr.ccz");
}
else
{
cc.log("WebGL or Canvas rendering");
gParticleLayer = cc.Node.create();
gGameLayer = cc.Node.create();
}
gGameLayer.setContentSize(this.gameLayer.getContentSize());
//gParticleLayer = cc.ParticleBatchNode.create("particles/taken-gem.png", 250);
//gParticleLayer = cc.Node.create();
gHintLayer = cc.Node.create();
gShimmerLayer = cc.Node.create();
gEffectsLayer = cc.Node.create();
//设置各界面层级关系
this.gameLayer.addChild(gShimmerLayer, -1);
this.gameLayer.addChild(gParticleLayer, 1);
this.gameLayer.addChild(gGameLayer, 0);
this.gameLayer.addChild(gHintLayer, 3);
this.gameLayer.addChild(gEffectsLayer, 2);
//gGameLayer = this.gameLayer;
// 设置完成动画后回调
this.rootNode.animationManager.setCompletedAnimationCallback(this, this.onAnimationComplete);
//背景上面 那个条半透明 紫色条状图片 的旋转,移动 淡入淡出效果
setupShimmer();
//setupSparkle();
// 分数控件
gScoreLabel = this.lblScore;
};
function setupBoard()
{
//游戏面板 存放宝石种类 初始化[80] var kBoardWidth = 8;
//var kBoardHeight = 10;
//var kNumTotalGems = kBoardWidth * kBoardHeight;
gBoard = new Array(kNumTotalGems);
for (var i = 0; i < kNumTotalGems; i++)
{
gBoard[i] = -1;
}
//游戏精灵面板
gBoardSprites = new Array(kNumTotalGems);
//竖着的8列宝石数组
gNumGemsInColumn = new Array(kBoardWidth);
//当前时间每一列的位置
gTimeSinceAddInColumn = new Array(kBoardWidth);
var x;
for (x = 0; x < kBoardWidth; x++)
{ //初始化为0
gNumGemsInColumn[x] = 0;
gTimeSinceAddInColumn[x] = 0;
}
// 初始化下落宝石二维数组
gFallingGems = new Array(kBoardWidth);
for (x = 0; x < kBoardWidth; x++)
{
gFallingGems[x] = new Array(0);
}
//评估是否有可以消除的宝石 和默认可以消除的宝石位置
gBoardChangedSinceEvaluation = true;
gPossibleMove = -1;
}
下面是整个游戏的重点 onUpdate方法
// Game main loop
GameScene.prototype.onUpdate = function(dt)
{
//如果游戏没有结束
if (!gIsGameOver)
{
//删除各列已被消除连接着的宝石 并把它上面加入到gFallingGems
removeMarkedGems();
var x;
var gem;
// Add falling gems
for (x = 0; x < kBoardWidth; x++)
{
//当这一列中的个数+要下落的个数<10 && 在当前列的位置>=8
if (gNumGemsInColumn[x] + gFallingGems[x].length < kBoardHeight &&
gTimeSinceAddInColumn[x] >= kTimeBetweenGemAdds)
{
//每一列增加一个随机宝石位置在界面的顶部,但yPos设为kBoardHeight = 10 并加入到gFallingGems var kGemSize = 40;
var gemType = Math.floor(Math.random()*5);
var gemSprite = cc.Sprite.createWithSpriteFrameName("crystals/"+gemType+".png");
gemSprite.setPosition(x * kGemSize, kBoardHeight * kGemSize);
gemSprite.setAnchorPoint(0,0);
gem = {gemType: gemType, sprite: gemSprite, yPos: kBoardHeight, ySpeed: 0};
gFallingGems[x].push(gem);
gGameLayer.addChild(gemSprite);
gTimeSinceAddInColumn[x] = 0;
}
gTimeSinceAddInColumn[x]++;
}
// Move falling gems
var gemLanded = false;
for (x = 0; x < kBoardWidth; x++)
{
var column = gFallingGems[x];
var numFallingGems = gFallingGems[x].length;
//遍历这一列中需要下降的宝石
for (var i = numFallingGems-1; i >= 0; i--)
{
gem = column[i];
//每次宝石 下降一定高度
gem.ySpeed += 0.06;
gem.ySpeed *= 0.99;
gem.yPos -= gem.ySpeed;
//如果宝石y坐标<=该列宝石个数时,证明已经到达了底部或碰到了宝石
if (gem.yPos <= gNumGemsInColumn[x])
{
// 随机播放一个音效
if (!gemLanded)
{
gAudioEngine.playEffect("sounds/tap-"+Math.floor(Math.random()*4)+".wav");
gemLanded = true;
}
//从gFallingGems中删除该宝石
column.splice(i, 1);
//插入该宝石到面板中 宝石类型到gBoard srite到gBoardSprites
var y = gNumGemsInColumn[x];
if (gBoard[x + y*kBoardWidth] != -1)
{
cc.log("Warning! Overwriting board idx: "+x + y*kBoardWidth+" type: "+gBoard[x + y*kBoardWidth]);
}
gBoard[x + y*kBoardWidth] = gem.gemType;
gBoardSprites[x + y*kBoardWidth] = gem.sprite;
//更新宝石位置 和该列中宝石的个数
gem.sprite.setPosition(x*kGemSize, y*kGemSize);
gNumGemsInColumn[x] ++;
//全部插满可以消除
gBoardChangedSinceEvaluation = true;
}
else
{
// 更新宝石下落位置
gem.sprite.setPosition(x*kGemSize, gem.yPos*kGemSize);
}
}
}
// Check if there are possible moves and no gems falling
var isFallingGems = false;
for (x = 0; x < kBoardWidth; x++)
{
if (gNumGemsInColumn[x] != kBoardHeight)
{
isFallingGems = true;
break;
}
}
//当全部下落完毕
if (!isFallingGems)
{
//遍历所有位置上的宝石,检查是否存在3个以上相连的宝石,并返回坐标
var possibleMove = findMove();
if (possibleMove == -1)
{
// 如果没有可以消除的,随机选各位置,并把它右面和上面的变成相同图案
createRandomMove();
}
}
// Update timer
var currentTime = Date.now();
var elapsedTime = (currentTime - gStartTime)/kTotalGameTime;
var timeLeft = (1 - elapsedTime)*100;
if (timeLeft < 0) timeLeft = 0;
if (timeLeft > 99.9) timeLeft = 99.9;
gTimer.setPercentage(timeLeft);
// 更新两次消除的间隔时间 如果大于1秒 重置
if (currentTime - gLastMoveTime > kMaxTimeBetweenConsecutiveMoves)
{
gNumConsecutiveGems = 0;
}
//更新炸弹粒子状态
updatePowerPlay();
//随机在宝石上增加闪耀的星星
updateSparkle();
// 时间小于6.6播放倒计时音效
if (timeLeft < 6.6 && !gEndTimerStarted)
{
gAudioEngine.playEffect("sounds/timer.wav");
gEndTimerStarted = true;
}
//没有时间则游戏结束
if (timeLeft === 0)
{
//界面内剩余的全部宝石 x,y随机一个速度
createGameOver();
//执行动画 (会有个回调,前面设置的onAnimationComplete)
this.rootNode.animationManager.runAnimationsForSequenceNamed("Outro");
gIsGameOver = true;
//gAudioEngine.stopAllEffects();
cc.log("stopAllEffects not working!");
gAudioEngine.playEffect("sounds/endgame.wav");
gLastScore = gScore;
}
else if (currentTime - gLastMoveTime > kDelayBeforeHint && !gIsDisplayingHint)
{
displayHint();//大于3秒提示相连的宝石
}
}
else
{
//结束宝石数组更新位置
updateGameOver();
}
};
这样游戏一开始就可以一排排的落下来,直到填满整个游戏,序列大概如图所示:
: | : | : | 79 |
: | : | : | : |
8 | 9 | .. | .. |
0 | 1 | .. | 7 |