开发第一个Phaser3游戏

1. 前言

开发第一个Phaser3游戏_第1张图片

这是我们第一个用Phaser3开发游戏的教程。我们将学习如何创建一个小游戏,包括玩家在平台上跑和跳,收集星星并避免一些不好的行为。

什么是Phaser?

Phaser是一个HTML5旨在帮助开发者创建强大、跨浏览器的HTML5游戏的框架。它是利用现代浏览器(桌面版和移动版)的优势而专门创建的。 对浏览器的唯一要求是对Canvas标签的支持。

每步的完整代码和资源可以在此处下载。在继续之前,建议先看看Phaser3入门

在我们的编辑器中打开part1.html页面,代码主要结构如下所示:

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

var game = new Phaser.Game(config);

function preload(){
}

function create(){
}

function update(){
}

config对象表示我们想如何配置Phaser游戏。可以在此对象中设置许多选项,随着我们对Phaser了解加深将其进一步拓展。上述代码中我们设置了渲染器,尺寸和默认Scene。

config对象被传给Phaser.Game对象的一个实例并赋值给game这个局部变量。这是Phaser游戏生命周期的开始。

type属性可以是Phaser.CANVASPhaser.WEBGL或者Phaser.AUTO

这是要用于游戏的渲染上下文。 推荐值为Phaser.AUTO,它会自动尝试使用WebGL,但是如果浏览器或设备不支持WebGL,它将使用Canvas。 Phaser创建的Canvas元素将在调用脚本时添加到HTML文档中,但也可以根据需要在游戏配置中指定一个父容器。

widthheight属性用于设置Phaser创建的Canvas元素的尺寸。上面代码是800x600像素,可以设置成任何想要的尺寸,也代表游戏的分辨率。

配置对象的scene属性将在之后的内容更加详细的介绍。

2. 加载资源

让我们加载游戏所需要的资源。这个可以通过放在称为preload的Scene函数中来实现。 Phaser将在启动时自动寻找该函数中并加载其中定义的所有内容。

之前的preload函数是空的,我们做如下更改:

function preload(){
    this.load.image('sky', 'assets/sky.png');
    this.load.image('ground', 'assets/platform.png');
    this.load.image('star', 'assets/star.png');
    this.load.image('bomb', 'assets/bomb.png');
    this.load.spritesheet('dude', 'assets/dude.png',
        {frameWidth: 32, frameHeight: 48}
    );
}

这将加载5个资源:4张图像和1个精灵表。load.image函数的第一个参数是个字符串,相当于资源的键值。

显示图像

为了显示一张我们已经加载过的图像资源,我们可以将下列代码放到create函数中:

this.add.image(400,300,'sky');

在浏览器中访问part3.html文件,可以看到有个蓝色的天空背景:

开发第一个Phaser3游戏_第2张图片

400和300是图像的x和y坐标。 为什么是400和300? 这是因为在 Phaser3中,默认情况下所有游戏对象都基于其中心位置。 背景图像的尺寸为800x600像素,因此,如果我们将其设置为0x0为中心,只会看到其右下角。如果我们设置为400x300,将会看到整个图像。

提示:我们可以使用 setOrigin函数改变这种设置。比如, this.add.image(0,0,'sky').setOrigin(0,0)

游戏对象的显示顺序与创建它们的顺序一致。 因此,如果想在背景上放置一个星星,只要将创建星星的代码放在天空之后:

function create(){
    this.add.image(400, 300, 'sky');
    this.add.image(400, 300, 'star');
}

开发第一个Phaser3游戏_第3张图片

3. 平台

让我们给游戏添加一些平台,

function create(){
    this.add.image(0, 0, 'sky').setOrigin(0,0);
    platforms = this.physics.add.staticGroup();
    platforms.create(400, 568, 'ground').setScale(2).refreshBody();
    platforms.create(600, 400, 'ground');
    platforms.create(50,250,'ground');
    platforms.create(750,220,'ground');
}

上述代码用了this.physics,我们将使用Arcade物理系统,在使用之前需要通过配置对象告诉Phaser我们需要用它。

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    physics: {
        default: 'arcade',
        arcade: {
            gravity: {y: 300},
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

在浏览器中访问part4.html文件可以看到,

开发第一个Phaser3游戏_第4张图片

我们用

platforms = this.physics.add.staticGroup();

创建了一个静态物理组,并将其赋值给platforms。 在Arcade物理系统中,有两种类型的物理实体:动态和静态。 动态物体是可以由于受到速度或加速度之类的力而移动的物体。它可能会由于和其它物体碰撞而弹开。

而静态物体仅具有位置和大小。 它不会受到重力的影响,我们无法在其上设置速度,并且当物体与其碰撞时,它不会因此而移动。由于这个特性,它非常适合我们游戏中需要的地面和平台。

那什么是组呢?顾名思义,它将类似的对象组合在一起并将它们作为一个整体加以控制。比如,我们可以检查组和其他游戏对象是否发生碰撞。

创建了平台组后,我们就以用其创建平台了:

platforms.create(400, 568, 'ground').setScale(2).refreshBody();
platforms.create(600, 400, 'ground');
platforms.create(50,250,'ground');
platforms.create(750,220,'ground');

现在,我们可以看到上图所示的内容了。代码的第一行我们创建了地面,之后将其放大两倍。因为原始的ground图像是400x32像素的,而我们的游戏界面的尺寸是800x600,所以我们需要将图像放大两倍,也就是变成800x64,否则玩家就会从地面掉下去。

4. 玩家

创建个名为player的变量,并将下列代码写在create函数中:

player = this.physics.add.sprite(100,450,'dude');
player.setBounce(0.2);
player.setCollideWorldBounds(true);

this.anims.create({
    key: 'left',
    frames: this.anims.generateFrameNumbers('dude', {start:0, end:3}),
    frameRate: 10,
    repeat: -1
});

this.anims.create({
    key: 'turn',
    frames: [{key: 'dude', frame: 4}],
    frameRate: 20
});

this.anims.create({
    key: 'right',
    frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8}),
    frameRate: 10,
    repeat: -1
});

主要做了两件事:创建物理精灵和创建可以使用的一些动画。

物理精灵

代码的第一部分创建了精灵:

player = this.physics.add.sprite(100,450,'dude');
player.setBounce(0.2);
player.setCollideWorldBounds(true);

我们创建了一个精灵并赋值给player,并让其显示在游戏的底部(100x450)。精灵创建是通过Physics Game Object Factory(this.physics.add),意味着它是动态物体。

创建精灵后,将反弹值设为0.2。 这意味着当它跳下后着陆时,它会反弹得比较小。 然后将精灵设置会与游戏边界碰撞。 默认情况下,游戏边界在游戏尺寸的之外。 当我们将游戏尺寸设置为800x600时,玩家将无法在该区域之外活动,将避免玩家跑出屏幕边缘或跳出顶部。

动画

回头看一下preload函数,会看到dude是作为精灵表而不是图像加载的。 那是因为它包含动画帧。 完整的精灵表如下所示:

总共有9帧,4帧向左跑,1帧面向镜头,4帧向右跑。我们定义了两个动画leftright。这是left动画:

this.anims.create({
    key: 'left',
    frames: this.anims.generateFrameNumbers('dude', {start:0, end:3}),
    frameRate: 10,
    repeat: -1
});

left动画使用帧0、1、2和3,以每秒10帧的速度运行。repeat: -1指的是让动画循环运行。

查看一下现在游戏的运行情况,我们会发现玩家会从地面掉下来……

开发第一个Phaser3游戏_第5张图片

为了让玩家与平台碰撞,我们可以创建一个Collider对象。 让该对象监视两个物理对象(可以包括组),并检测它们之间是否存在碰撞或重叠。 若发生这种情况,则可以选择调用相应的函数,仅仅为了检测与平台碰撞,我们并不需要调用什么函数:

this.physics.add.collider(player, platforms);

开发第一个Phaser3游戏_第6张图片

5. 键盘控制

现在我们开始给我们的游戏加入一些键盘控制。Phaser有内置的键盘管理器,一大好处就是我们可以用下面这样方便的函数:

cursors = this.input.keyboard.createCursorKeys();

cursors对象有四个属性:updownleftright,它们都是Key对象的实例。我们需要做的就是在update函数中进行处理:

cursors = this.input.keyboard.createCursorKeys();
if (cursors.left.isDown){
    player.setVelocityX(-160);
    player.anims.play('left', true);
} else if (cursors.right.isDown){
    player.setVelocityX(160);
    player.anims.play('right', true);
} else {
    player.setVelocityX(0);
    player.anims.play('turn');
}

if (cursors.up.isDown && player.body.touching.down){
    player.setVelocityY(-330);
}

当我们按下键盘<-键时,玩家的运动速度设为-160,并播放left动画,其它按键也进行类似处理。值得注意的是,我们在最后的if语句中加入的player.body.touching.down这个条件,若不加的话,玩家将可以上演“纵云梯”,我们不希望这样,所以加入了这个条件。详情请看Phaser.Physics.Arcade.Body

6. 收集星星

是时候给我们的游戏定个小目标了!我们将在游戏场景中添加一些星星,并让玩家可以去收集它们。在create函数中添加如下代码:

stars = this.physics.add.group({
    key: 'star',
    repeat: 11,
    setXY: {x: 12, y: 0, stepX: 70}
});

stars.children.iterate(function(child){
    child.setBounceY(Phaser.Math.FloatBetween(0.4,0.8));
});

与创建平台组类似,只不过这次我们创建的是动态物理组,而不是静态物理组。

代码将迭代组中的所有元素,并为他们提供介于0.4和0.8之间的随机Y反弹值。 反弹范围介于0(完全没有反弹)和1(完整反弹)之间。 所有的星星都是在y=0处产生的,由于重力它们会掉落,直到它们与平台或地面碰撞。 反弹值表示它们将随机反弹,直到最终稳定下来。

但如果我们现在运行代码,星星会掉到游戏的底部而不见了。 为此,我们需要检查它们是否与平台发生碰撞。 我们可以用另一个Collider对象来做到这点:

this.physics.add.collider(stars, platforms);

我们游戏的目标是让玩家可以收集星星,为此,需要检测玩家是否和星星发生重叠:

this.physics.add.overlap(player, stars, collectStar, null, this);

以上代码告诉Phaser,若玩家和星星重叠了就调用collectStar函数。

function collectStar(player, star){
    star.disableBody(true, true);
}

collectStar函数让与玩家重叠了的星星不可见。

开发第一个Phaser3游戏_第7张图片

7. 计分

我们可能还希望让游戏计分,并在游戏中显示。

创建两个变量:

var score = 0;
var scoreText;

score用来记录分数,scoreText将是个Text对象,用于在游戏中显示分数。在create函数中创建Text对象的一个实例,并将其赋值给scoreText

scoreText = this.add.text(16, 16, 'score: 0', { fontSize: '32px', fill: '#000' });

每次我们收集到星星,分数就会增加,于是我们可以将其代码加入到之前的collectStar函数中。

score += 10;
scoreText.setText('Score: ' + score);

开发第一个Phaser3游戏_第8张图片

8. 炸弹

为了完善我们的游戏,是时候给游戏增加一点挑战了。想法是这样的:当我们收集完所有星星时,它将放出一颗炸弹,所有星星都会重现。 炸弹只会在水平方向随机反弹,玩家触碰到炸弹就会死。而我们可以再次收集星星,收集完它将释放另一枚炸弹。 所以,要在不死的情况下获得尽可能高的分数。

首先创建一个炸弹组和两个Collider对象:

bombs = this.physics.add.group();

this.physics.add.collider(bombs, platforms);
this.physics.add.collider(player, bombs, hitBomb, null, this);

玩家碰到炸弹,将调用hitBomb函数。这里我们做的是让玩家变成红色,并结束游戏。

function hitBomb(player, bomb){
    this.physics.pause();
    player.setTint(0xff0000);
    player.anims.play('turn');
    gameOver = true;
}

然后,我们修改一下hitStar函数,以便释放炸弹:

function collectStar(player, star){
    star.disableBody(true, true);
    score += 10;
    scoreText.setText('Score: ' + score);

    if (stars.countActive(true) === 0){
        stars.children.iterate(function(child){
            child.enableBody(true, child.x, 0, true, true);
        });
        var x = (player.x < 400) ? Phaser.Math.Between(400, 800) : Phaser.Math.Between(0, 400);
        var bomb = bombs.create(x, 16, 'bomb');
        bomb.setCollideWorldBounds(true);
        bomb.setVelocity(Phaser.Math.Between(-200,200), 20);
    }
}

我们用了GroupcountActive方法来查看还剩多少星星。如果为零,表示玩家已收集了所有星星,因此我们迭代地启用所有星星,并将其y坐标置为零。这将使所有星星再次从屏幕顶部掉落。

接下来的代码将创建炸弹。 首先,我们选择一个随机的x坐标,总是在玩家相对的另一侧。 然后创建炸弹,将其设置为与游戏边界碰撞,具有随机速度。

开发第一个Phaser3游戏_第9张图片

总结

本文我们学习了如何使用Phaser3开发第一个游戏,主要包括加载资源、创建精灵、键盘控制、物体碰撞等等一些知识点,之后我们可以参考PHASER 3 EXAMPLES将这个简单实例丰富起来。

参考

你可能感兴趣的:(javascript,phaser)