网站地图    收藏   

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

Phaser实现水果忍者

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

[导读] 作者:channingbreeze 日期:2016-11-23 作为一个游戏引擎,不能仅仅实现简单的游戏,这次挑战了水果忍者。水果忍者,游戏玩起来简单,实现起来有一定难度,因为很多效果,很多逻辑,...

作者:channingbreeze

日期:2016-11-23

作为一个游戏引擎,不能仅仅实现简单的游戏,这次挑战了水果忍者。水果忍者,游戏玩起来简单,实现起来有一定难度,因为很多效果,很多逻辑,很多元素,很多代码,不用面向对象的方法还真不行。





先来看一个效果吧!

怎么样?和真正的水果忍者有点像吧?再给一个体验地址,自己先体验一下:http://game.webxinxin.com/fruit/

好了,才实现基本功能,还有很多可以扩展的功能,如果你有兴趣可以在我的这个版本基础上继续开发。下面简单讲解一下代码设计思路,具体的还是自己撸吧。

先看下主菜单页面。

主菜单上面的部分还是比较简单的,主要是几个开场动画,把元素定好位后,做一个带次序的tween动画就可以了。可以用tweenchain来做,也可以在tween结束时onComplete回调启动下一个动画。下面的几个水果和炸弹要想一想,尤其是炸弹。炸弹是会冒火星的,这里我简单用方块来代替了,而且使用了粒子系统。

左边桃子和DOJO的圆圈,可以组成一个group,这两个元素都是旋转,但是他们的旋转方向和速度是不一样的。中间的西瓜和NEWGAME的圆圈也一样,还有右边的炸弹和QUIT圆圈。但是炸弹应该怎么来做呢,其实它本身也是三部分组成,一个炸弹图片,一个烟雾图片,一个火花的粒子发射器。一开始,我也想把它们三组成一个group,但是后来想一想,其实整个炸弹应该是一个sprite。在phaser中,groupsprite都可以去addChild。至于是一个group还是一个sprite,主要看内部元素的关系,如果这些内部元素自己是一个个体,可以有不用的速度,位置,旋转方向等,那么它们应该组成一个group,如果内部元素位置相互固定,速度,旋转都一致,而且本来就是某个物体的组成部分,那么它们就组成一个sprite。组成一个sprite的好处就是,物理属性共享。

sprite = game.add.sprite(env.x || 0, env.y || 0);
var bombImage = game.add.sprite(0, 0, 'bomb');
bombImage.anchor.setTo(0.5, 0.5);
// 烟雾
var bombSmoke = game.add.sprite(-55, -55, 'smoke');
// 粒子发射器
var bombEmit = game.add.emitter(-30, -30, 20);
// 设置粒子,使用我们自定义的粒子
bombEmit.particleClass = FlameParticle;
bombEmit.makeParticles();
// 设置属性
bombEmit.setScale(1, 0.8, 1, 0.8, 1500);
bombEmit.setAlpha(1, 0.1, 1500);
// 发射
bombEmit.start(false, 500, 50);
// 什么时候用Group,什么时候用sprite,一个炸弹,是一个sprite,刚体,速度,旋转都一致。group里面的东西可以速度不一致。
sprite.addChild(bombImage);
sprite.addChild(bombEmit);
sprite.addChild(bombSmoke);
// 物理属性
game.physics.enable(sprite, Phaser.Physics.ARCADE);
sprite.enableBody = true;

接下来的一个难点就是鼠标划过屏幕时的刀光怎么来做。这块确实想了很久,后来决定用图形来画,graphics,把鼠标划过的点连成折线,往周围延伸,形成一个刀光的模样。这里面涉及到一些数学的计算,具体的逻辑都封装在了mathTool里面。算法最后得到的就是组成刀光轮廓的一系列点,而输入就是鼠标滑过的点。再记录一下鼠标点的时间,设置一个超时,超时后移除,这个刀光效果就完成了。

// 形成刀光点
var res = [];
if(points.length <= 0) {
  return;
} else if(points.length == 1) {
  var oneLength = 6;
  res.push(new Phaser.Point(points[0].x - oneLength, points[0].y));
  res.push(new Phaser.Point(points[0].x, points[0].y - oneLength));
  res.push(new Phaser.Point(points[0].x + oneLength, points[0].y));
  res.push(new Phaser.Point(points[0].x, points[0].y + oneLength));
} else {
  var tailLength = 10;
  var headLength = 20;
  var tailWidth = 1;
  var headWidth = 6;
  res.push(this.calcParallel(points[0], points[1], tailLength));
  for(var i=0; i0; i--) {
    res.push(this.calcVertical(points[i], points[i-1], Math.round((headWidth - tailWidth) * (i - 1) / (points.length - 1) + tailWidth), false));
  }
}
return res;

还有很多人会好奇,一刀把水果切成两半是怎么实现的?其实你看一下图片资源就知道了。图片中每一个水果都配有两个半片的水果,只要计算一下切过去的角度,然后把原来水果消失,把两半的水果贴上去,再把速度和加速度调整一下,就实现了水果切成两半的效果。大概像这样:

// 两半
halfOne = game.add.sprite(sprite.body.x + sprite.width/2, sprite.body.y + sprite.height/2, env.key + '-1');
halfOne.anchor.setTo(0.5, 0.5);
halfOne.rotation = deg + 45;
game.physics.enable(halfOne, Phaser.Physics.ARCADE);
halfOne.body.velocity.x = 100 + sprite.body.velocity.x;
halfOne.body.velocity.y = sprite.body.velocity.y;
halfOne.body.gravity.y = 2000;
halfOne.checkWorldBounds = true;
halfOne.outOfBoundsKill = true;
halfTwo = game.add.sprite(sprite.body.x + sprite.width/2, sprite.body.y + sprite.height/2, env.key + '-2');
halfTwo.anchor.setTo(0.5, 0.5);
halfTwo.rotation = deg + 45;
game.physics.enable(halfTwo, Phaser.Physics.ARCADE);
halfTwo.body.velocity.x = -100 + sprite.body.velocity.x;
halfTwo.body.velocity.y = sprite.body.velocity.y;
halfTwo.body.gravity.y = 2000;
halfTwo.checkWorldBounds = true;
halfTwo.outOfBoundsKill = true;
sprite.kill();

其实到这里,都还没有意识到,需要做一点封装了,知道做到下一个场景。

在这个场景中,很多东西其实和主菜单场景类似。比如刀光,水果被切成两半,炸弹。所以这个时候,我开始去封装一些东西,我把水果,炸弹,刀光代码都抽出来,封装成一些类。这样用起来确实方便很多了。

其实在这个场景中,基本动画就不说了,最关键的就是随机产生水果和炸弹,还要注意产生的范围,上抛的速度和方向,这些东西,就要大量用到随机数,熟练了之后也很简单,具体可看代码。

最后一个点就是炸弹爆炸时的动画,这个动画是用tweenchain功能来实现的。先随机一个初始角度,然后每隔45度增加一个光线,先把graphic画出来,但是设置透明度为0,通过tween动画将透明度设为1,就实现了光线一道一道出现的效果。当然光线数组设置好了之后,还要shuffle一下。

var explode = function(onWhite, onComplete) {
  var lights = [];
  var startDeg = Math.floor(Math.random() * 360);
  for(var i=0; i<8; i++) {
    var light = game.add.graphics(sprite.body.x, sprite.body.y);
    var points = [];
    points[0] = new Phaser.Point(0, 0);
    points[1] = new Phaser.Point(Math.floor(800*mathTool.degCos(startDeg + i*45)), Math.floor(800*mathTool.degSin(startDeg + i*45)));
    points[2] = new Phaser.Point(Math.floor(800*mathTool.degCos(startDeg + i*45 + 10)), Math.floor(800*mathTool.degSin(startDeg + i*45 + 10)));
    light.beginFill(0xffffff);
    light.drawPolygon(points);
    light.endFill();
    light.alpha = 0;
    lights.push(light);
  }
  lights = mathTool.shuffle(lights);
  var firstTween;
  var lastTween;
  for(var i=0; i<8; i++) {
    var light = lights[i];
    var tween = game.add.tween(light).to({alpha: 1}, 100, "Linear", false);
    if(i == 0) {
      firstTween = tween;
    }
    if(lastTween) {
      lastTween.chain(tween);
    }
    lastTween = tween;
    if(i == 7) {
      tween.onComplete.add(function() {
        var whiteScreen = game.add.graphics(0, 0);
        whiteScreen.beginFill(0xffffff);
        whiteScreen.drawRect(0, 0, game.width, game.height);
        whiteScreen.endFill();
        whiteScreen.alpha = 0;
        var tween = game.add.tween(whiteScreen).to({alpha: 1}, 100, "Linear", true);
        // 开始和结束的回调
        tween.onComplete.add(function() {
          onWhite();
          for(var i=0; i<8; i++) {
            var light = lights[i];
            light.kill();
          }
          var tweenBack = game.add.tween(whiteScreen).to({alpha: 0}, 100, "Linear", true);
          tweenBack.onComplete.add(function() {
            onComplete();
          });
        });
      });
    }
  }
  firstTween.start();
};

最后还有一个坑要注意,在GAMEOVER之后,我们点一下鼠标又能回到主菜单场景,但是这时候并不是开了一个新的对象,而是用的原来内存里的对象,也就是主菜单场景里面的一些属性,还会是跳转到play场景时的值,所以在跳转前,需要reset一下。否则可能会有意想不到的结果。

好了,整个水果忍者游戏大概就介绍到这里,有兴趣的朋友可以去翻看源码。当然,这个游戏和原版的还是有一些区别的,有的功能还不完善,期待你来改吧。

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

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

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

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

添加评论