cocos2dx CrystalCraze 宝石消除游戏 解析(二)

本文是cocos2dx游戏CrystalCraze宝石消除系列的第二部分,主要关注GameScene的详细解析。文章深入探讨了GameScene.js的1000行代码,涵盖了游戏界面的控制逻辑。从onDidLoadFromCCB方法开始,作者逐步展开讲解游戏界面的实现过程。
摘要由CSDN通过智能技术生成

上一篇: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
::::
89....
01..7

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值