网站地图    收藏   

主页 > canvas引擎 > Phaser游戏引擎 >

Phaser实现2048

来源:未知    时间:2017-11-15 14:25 作者:xxadmin 阅读:

[导读] 作者:channingbreeze 日期:2016-12-11 2048作为前段时间十分火爆的小游戏,以其简单的操作,有趣的玩法,一下子从小游戏的角逐中脱颖而出。本教程通过phaser来实现了一个标准版的2048,可...






作者:channingbreeze

日期:2016-12-11

2048作为前段时间十分火爆的小游戏,以其简单的操作,有趣的玩法,一下子从小游戏的角逐中脱颖而出。本教程通过phaser来实现了一个标准版的2048,可以在桌面和移动端运行,一起来看看其中的关键代码吧。

效果就不在这里截图了,给一个地址,大家自己体验一下先。http://game.webxinxin.com/2048。

本教程主要介绍一些思路,技术关键点以及实现过程中遇到的一些坑!



开始界面呀,结束界面呀,这些就不介绍了,非常简单,如果还不会的话,可以看前面的入门教程和一些实例。本案例比较难的就是游戏的逻辑以及数据结构的组织方式。

第一个技术点是上面的两个计分的框,由于本案例的图片资源比较少,很多东西都是由js去画出来的,两个计分框也不例外。其实每个计分框可以看成一个大的sprite,这个大的sprite由几部分组成,框子、标题和分数,分别用graphic和两个text就能实现,把这些都放到sprite里面,就组成了一个计分框。


//score
var scoreSprite = game.add.sprite(10, 10);
var scoreGraphics = game.add.graphics(0, 0);
scoreGraphics.lineStyle(5, 0xA1C5C5);
scoreGraphics.beginFill(0x308C8C);
scoreGraphics.drawRoundedRect(0, 0, 70, 50, 10);
scoreGraphics.endFill();
scoreSprite.addChild(scoreGraphics);
var scoreTitle = game.add.text(0, 5, "SCORE", titleStyle);
scoreTitle.setTextBounds(0, 0, 70, 50);
scoreSprite.addChild(scoreTitle);
this.scoreText = game.add.text(0, 20, this.score, scoreStyle);
this.scoreText.setTextBounds(0, 0, 70, 50);
scoreSprite.addChild(this.scoreText);


        其实有了计分框的实现思路,不难想象,2048游戏中的方块也是这种方式,一个框加上一个分数,组成一个sprite,也就是一个方块。但是方块仅仅实现了显示的需求,逻辑需求要怎么实现呢?所以其实我们需要一个4x4的数组,然后根据上下左右的滑动来做不同的遍历,决定怎么合并和移动方块。而每一个方块对象,除了有一个sprite属性外,还有一些标识去记录一些状态,表示逻辑的处理结果。

在滑动逻辑的处理中,我们总是从前面开始,从后往前遍历,如果碰到一个非零的方块,就检查是否能够合并,能够合并就进行合并,交换方块的位置及sprite等属性,并且直接开始执行动画(这种方式比记录下来动画再在最后全部执行来得简单)。如果不能合并,就需要移动当前方块了,这时候判断遍历的终点是否是空的,来决定移动的距离。

其实计算出最终的结果是很简单的,难的是计算出每一个方块的动画,每一个方块,可能会移动,可能会合并。具体的逻辑还需要大家自己细细琢磨。


// swipe的公共逻辑抽出
this.swipeCommon = function(i, j, arrNode, posJson, condition, nextArrNode, nextPosJson) {
  var that = this;
  var duration = 100;
  // 遇到了可以合并的
  if(!arrNode.newNode && arrNode.value == this.array[i][j].value) {
    arrNode.value = arrNode.value * 2;
    arrNode.newNode = true;
    this.array[i][j].value = 0;
    this.score = this.score + arrNode.value;
    this.scoreText.text = this.score;
    if(this.score > this.best) {
      this.best = this.score;
      this.bestText.text = this.best;
    }
    // 渐渐透明后被kill掉
    var t1 = game.add.tween(arrNode.sprite).to({alpha: 0}, duration, Phaser.Easing.Linear.None, true);
    t1.onComplete.add(function() {
      this.sprite.kill();
      that.placeSquare(this.x, this.y, this.value);
      if(!that.canSwipe) {
        that.canSwipe = true;
        that.generateSquare();
      }
    }, arrNode);
    var t2 = game.add.tween(this.array[i][j].sprite).to({alpha: 0}, duration, Phaser.Easing.Linear.None, true);
    t2.onComplete.add(function() {
      this.kill();
      if(!that.canSwipe) {
        that.canSwipe = true;
        that.generateSquare();
      }
    }, this.array[i][j].sprite);
    game.add.tween(this.array[i][j].sprite).to(posJson, duration, Phaser.Easing.Linear.None, true);
    arrNode.sprite = this.array[i][j].sprite;
    this.array[i][j].sprite = undefined;
  } else if(arrNode.value == 0) {
    arrNode.value = this.array[i][j].value;
    this.array[i][j].value = 0;
    var t = game.add.tween(this.array[i][j].sprite).to(posJson, duration, Phaser.Easing.Linear.None, true);
    t.onComplete.add(function() {
      if(!that.canSwipe) {
        that.canSwipe = true;
        that.generateSquare();
      }
    });
    arrNode.sprite = this.array[i][j].sprite;
    this.array[i][j].sprite = undefined;
  } else if(condition) {
    nextArrNode.value = this.array[i][j].value;
    this.array[i][j].value = 0;
    var t = game.add.tween(this.array[i][j].sprite).to(nextPosJson, duration, Phaser.Easing.Linear.None, true);
    t.onComplete.add(function() {
      if(!that.canSwipe) {
        that.canSwipe = true;
        that.generateSquare();
      }
    });
    nextArrNode.sprite = this.array[i][j].sprite;
    this.array[i][j].sprite = undefined;
  }
};

其实在做滑动逻辑的时候,最好是先分别做完上下左右的逻辑,然后,再将公共的逻辑抽出,变成一个函数,这样会稍微简单一些。

另一个需要解决的问题,就是如何判断手势,也就是怎么判断上滑,下滑,左滑和右滑。这里我找到了网上的一个插件,使用起来还是很方便的。但是假如不用插件,我们其实也能做,无非就是判断一下按下和抬起的时候,点的相对位置而已。大家可以去看看插件的源码,也是这么做的,但是插件原来对滑动的判断并不敏感,我对参数做了一些改动,想省事的同学可以直接拿去用。关键代码就是:


// add swipe check
this.swipe = new Swipe(this.game, this.swipeCheck);



// swipe检测
this.swipeCheck = {
  up: this.swipeUp.bind(this),
  down: this.swipeDown.bind(this),
  left: this.swipeLeft.bind(this),
  right: this.swipeRight.bind(this)
};


其实把滑动检测和滑动逻辑做完之后,游戏基本就可以了。但是我还在方块的生成动画这里跌了一下,我希望方块生成的时候,从中心开始往外缩放。但是一开始设置了anchor竟然无效。一开始还以为无法实现,但是其实只是自己的几个精灵的组织关系没有写好,调整之后,很轻松就实现了。


// 在x,y位置放置一个值为value的方块
this.placeSquare = function(x, y, value) {
  var squareStyle = { font: "bold 20px Arial", fill: "#FFFFFF", boundsAlignH: "center", boundsAlignV: "middle" };
  var square = game.add.sprite();
  square.reset(this.transX(x), this.transY(y));
  var squareBackground = game.add.graphics(-45/2, -45/2);
  squareBackground.beginFill(this.colors[value]);
  squareBackground.drawRoundedRect(0, 0, 45, 45, 5);
  squareBackground.endFill();
  square.addChild(squareBackground);
  var squareText = game.add.text(-45/2, -45/2, value, squareStyle);
  squareText.setTextBounds(0, 0, 45, 45);
  square.addChild(squareText);
  this.array[x][y].value = value;
  this.array[x][y].sprite = square;
  square.anchor.setTo(0.5, 0.5);
  square.scale.setTo(0.0, 0.0);
  var tween = game.add.tween(square.scale).to({x:1.0, y:1.0}, 100, Phaser.Easing.Sinusoidal.InOut, true);
  tween.onComplete.add(function() {
    if(this.checkGameover()) {
      this.gameOver();
    }
  }, this);
};


好了,2048的讲解到这里就结束了,期待下一个小游戏!


转载请注明出处:http://www.phaser-china.com/tutorial-detail-6.html



自学PHP网专注网站建设学习,PHP程序学习,平面设计学习,以及操作系统学习

京ICP备14009008号-1@版权所有www.zixuephp.com

网站声明:本站所有视频,教程都由网友上传,站长收集和分享给大家学习使用,如由牵扯版权问题请联系站长邮箱904561283@qq.com

添加评论