【phaser】快速实现HTML5 2d小游戏

作者阿里云代理 文章分类 分类:linux图文教程 阅读次数 已被围观 916

前语

最近忙着看项目和写项目,在 github 上无意中发现了别人用 phaser 完成的2d小游戏,代码简略易懂,并且phaser结构自身便是十分的简略,十分适合想快速开发小游戏的开发者。但是国内关于 phaser3 的材料和教程甚少,于是笔者阅读官方学习文档简略完成了一个 2d 小游戏。由于官网自身有中文版的游戏完成教程,所以我在这儿也就不做具体解读了,更多的是从学习笔记的视点来进行书写。

游戏源码和在线demo

在线demo

源码以及图片资源下载

假如你直接 push 下来源码在本地双击 html 文件是无法运转出来游戏的,该h5游戏需求在服务器环境中运转。因而笔者推荐运用vscode打开源文件,然后运转 firstgame.html 文件即可。

笔者这儿主张能够优先把源码 clone 下来,tensteps文件夹是游戏开发中代码构建的过程,在看教程的过程中,能够参阅。

在线demo也能够帮助了解。

准备工作

开发环境:vscode。并在vscode中安装 liveserver 插件,以发明服务器环境,满意h5游戏运转的条件。

phaser是否需求下载.? 我自己的主张是:直接在 html 文件中运用 cdn 即可。这儿供给一下 phaser 的源码下载地址 ,供感兴趣的读者运用。

结构构建和解析

  • 新建文件夹 自定义称号
  • 将源文件中的资料文件 assets 文件夹复制到文件夹下。
  • 文件夹内新建 html 文件,这将是咱们接下来编写代码的地方。

首要建立整个游戏的结构。咱们之后的代码将会在此结构下不断拓展。

html> <html lang="en"> <head> <meta charset="UTF-8" /> <title>Making your first Phaser 3 Game - Part 1title> <script src="//cdn.jsdelivr.net/npm/phaser@3.11.0/dist/phaser.js">script> <style type="text/css"> body { margin: 0;
        } style> head> <body> <script type="text/javascript"> 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 () {
    } script> body> html>

config 目标中包括了游戏的各种装备:画布显现的长宽,游戏的场景装备,type 指游戏的整体烘托。能够是Phaser.CANVAS,或许Phaser.WEBGL,或许Phaser.AUTO。咱们在这儿运用 AUTO ,它将主动测验运用WebGL,假如浏览器或设备不支撑,它将回退为Canvas。

scene 装备包括了 preload,create 和 update 。

  • preload 办法可自定义。运用它来加载各种自定义资源(图片,音乐等)。此办法由场景管理器在 init() 之后和 create() 之前调用,条件是场景具有 LoaderPlugin。
  • create 办法可自定义。运用它来创立您的游戏目标。一般在在 init() 和 preload() 之后被调用。
  • update 办法需求自行重写,当游戏运转时,该办法在每个游戏步骤中被调用。(英文原句为:This method is called once per game step while the scene is running. 翻译很一般 请自行体会。)因而咱们能够考虑之后人物的走路判定办法编写在 update 办法中即完成游戏运转时其每一阶段都被调用一次。

资源加载

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张图(image)和一个精灵表单(sprite sheet)。图片用于游戏中的各种场景显现,布景以及人物等等。

精灵表单则用于人物在走路时的动态显现。

create

要显现咱们之前在 preload 中引进的各种资源的话,咱们需求在 create 函数中运用 this.add.image() 办法。

咱们先测验加载一片蓝天与一颗星星。

在这儿刺进图片描绘

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

只需求先参加add ’sky‘ 再add ’star‘ 即可。
400和300是图画坐标的x值和y值。为什么是400和300呢?这是由于,在Phaser 3 中,一切游戏目标的定位都默许根据它们的中心点。这个布景图画的尺度是800 x 600像素,所以,假如咱们显现它时将它的中心定在0 x 0,你将只能看到它的右下角。假如咱们显现它时定位在400 x 300,你能看到整体。

构建游戏国际

从现在开端咱们将开端构建游戏内的各种场景,依据在线 demo 可知,咱们需求几个渠道以及布景图片。因而咱们需求在create()函数中增加以下代码:

var platforms; function create () { this.add.image(400, 300, 'sky');//生成游戏的图片布景天空 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 因而之前的游戏装备 config 也需求修正。修正后的config:

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
    }
};

新增了 physics 特点。

这些代码修正之后,咱们的游戏场景就建立成功了。作用如图下:

在这儿刺进图片描绘

渠道建立代码详解

把目光再次确定再咱们刚刚修正过的create函数上,咱们新增了一张天空的照片用作游戏的布景。

然后咱们引进了platforms变量。

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

这一句生成一个静态物理组(Group),并把这个组赋值给局部变量platforms。在Arcade物理体系中,有动态的和静态的两类物体(body)。动态物体能够经过外力比方速度(velocity)、加速度(acceleration),得以四处移动。它能够跟其他目标产生反弹(bounce)、磕碰(collide),此类磕碰受物体质量和其他要素影响。

与此明显不同的是,静态物体只要方位和尺度。重力对它没有影响,你不能给它设置速度,有东西跟它磕碰时,它一点都不动。名副其实,彻底是静态的。因而将静态物体用作地上和渠道很完美,咱们计划让玩家在上面来回跑动。

那么什么是组呢?如其名所示,是把近似目标安排在一起的手法,操控目标整体就像操控一个统一的个体。你也能够查看组与其他游戏目标之间的磕碰。组能够生成自己的游戏目标,这是经过便当的辅佐函数如create完成的。物理组会主动生成现已开启物理体系的子项(children),免得你运用多余的重复代码去新建更多的渠道变量。

渠道组做好了,咱们现在能够用它生成渠道:

platforms.create(400, 568, 'ground').setScale(2).refreshBody();

platforms.create(600, 400, 'ground');
platforms.create(50, 250, 'ground');
platforms.create(750, 220, 'ground');

上述代码的榜首行,增加一张新的地上图画到400 x 568的方位(请记住,图画定位根据中心点)——问题是,咱们需求这个渠道撑满游戏的宽度。否则玩家就会掉出鸿沟。

要做到这一点,咱们用函数setScale(2)把它按x2(两倍)缩放。现在它的尺度是800 x 64了,宽度正好和咱们的游戏画布大小相同。要调用refreshBody(),这是由于咱们缩放的是一个 静态 物体,所以有必要把所作变动告知物理国际(physics world)。

关于 refreshbody 办法。官方 docs 中的原话是这样的:Syncs the Body's position and size with its parent Game Object. You don't need to call this for Dynamic Bodies, as it happens automatically. But for Static bodies it's a useful way of modifying the position of a Static Body in the Physics World, based on its Game Object.

将Body的方位和大小与它的父游戏目标同步。你不需求为Dynamic Bodies调用这个,由于它会主动改变。但对于静态物体来说,这是根据其游戏目标在物理国际中修正静态物体方位的有效办法。

剩余的渠道建立代码:

platforms.create(600, 400, 'ground');
platforms.create(50, 250, 'ground');
platforms.create(750, 220, 'ground');

这个步骤跟前面彻底相同,仅仅不需求缩放,由于他们的尺度本来就正好。

接下来咱们来增加玩家。

增加玩家

在咱们的html文件中参加新的变量 player 并在 create() 函数中参加player 的一些设置即可。

create() 中 player 相关的代码:

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 });

这段代码所做的工作便是生成一个精灵,然后生成精灵运动时的动画。

增加 sprite

player = this.physics.add.sprite(100, 450, 'dude');

player.setBounce(0.2);
player.setCollideWorldBounds(true);

这样生成一个新的精灵,叫player(玩家),坐落100 x 450像素,在游戏的下部,该 sprite 的称号叫做 dude。精灵是经过物理游戏目标工厂函数(Physics Game Object Factory,即this.physics.add)生成的,这意味着它默许拥有一个动态物体。

sprite 和 image 的区别:

sprite 目标用于显现游戏中的静态和动画图画。精灵能够有输入事情和物理实体。它们还能够进行突变、着色、翻滚和动画。

sprite 和 image 的主要区别在于,你不能把 image 变成动画。因而,由于动画组件,精灵的处理时间更长,API占用也更大。假如你不需求动画,那么你能够在一切情况下运用 image 来替换 sprite 。

sprite 生成后,其被赋予0.2的一点点反弹(bounce)值。这意味着,它跳起后着地时一直会弹起那么一点点。然后精灵设置了与国际鸿沟(bound)的磕碰。——鸿沟默许在游戏尺度之外。

setBounce(x [, y])

设置这个目标的反弹值。弹跳是物体与另一个物体磕碰时的康复量,或弹性。值为1表明它将在反弹后保持其悉数速度。值为0意味着它不反弹。

咱们(经过player.setCollideWorldBounds(true))把游戏(的国际鸿沟)设置为800 x 600后,玩家就不能不跑出这个区域了。这样会让玩家停下来,不能跑出画面鸿沟,或跳出顶边。

setCollideWorldBounds( [value] )

设置此物体是否与游戏国际鸿沟磕碰。

假如需求该目标与游戏鸿沟产生磕碰,则将 value 设为为 true,否则为false

动画完成

回忆一下preload函数,你会看到'dude'是作为精灵表单(sprite sheet)载入的,而非 image 。这是由于它包括了动画帧(frame)。完好的精灵表单是这个姿态的:

在这儿刺进图片描绘

总共有9帧,4帧向左跑动,1帧面向镜头,4帧向右跑动。

咱们定义两个动画,叫'left'和'right'。这是'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'告知动画要循环播放。

这是咱们的规范跑动周期。反方向的动画把这些重复一下,键值用'right'。最终一个动画键值用'turn'(回身)。

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 });
这儿的 anims 对应的是 AnimationState 类. 这个组件供给了将动画运用到游戏目标的功用。

物理体系

Phaser支撑多种物理体系,每一种都以插件形式运作,任何Phaser场景都能运用它们。针对本教程,咱们将给咱们的游戏运用Arcade物理体系,它简略,轻量,完美地支撑移动浏览器。

物理精灵在生成时,即被赋予body(物体)特点,这个特点指向它的Arcade物理体系的Body。它表明精灵是Arcade物理引擎中的一个物体。物体目标有许多特点和办法,咱们能够玩一下。

比方,在一个精灵上仿照重力作用,能够这么简略写:

player.body.setGravityY(300)

这是个随意的值,但逻辑讲,值越大你的目标感觉越重,下落越快。假如你把这些加到你的代码里,或许运转part5.html,你会看到玩家不停地往下落,彻底无视咱们先前生成的地上。

原因在于,咱们还没有测试地上和玩家之间的磕碰。

当玩家和渠道磕碰时,由于力的作用是相互的,玩家碰到它时,磕碰导致的力会作用于地上,因而两个物体交流互相的速度,地上也开端下落。

要想玩家能与渠道磕碰,且渠道不动。咱们能够生成一个磕碰目标。该目标监控两个物体(能够是组),检测二者之间的磕碰和重叠事情。假如产生事情,这时它能够随意调用咱们的回调函数。不过仅仅就与渠道间的磕碰而言,咱们没必要那么做:

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

磕碰器(Collider)接纳两个目标,检测二者之间的磕碰,并使二者分开。在本例中,咱们把玩家精灵和渠道组传给它。最终完成玩家成功站在渠道上的作用。
在这儿刺进图片描绘

键盘操控

现在咱们的游戏更像是一个动画,而非游戏,想要让它变的更像游戏一样,咱们就应该向其间增加键盘操控,让玩家能够走动起来。

Phaser有内置的键盘管理器,用它的一个好处体现在这样一个便利的小函数:

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

这儿把四个特点up, down, left, right(都是Key目标的实例),植入光标(cursor)目标。然后咱们要做的便是在update循环中做这样一些if判别:

//在update函数中编写 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);
}

它做的榜首件事,是查看方向左键是不是正被按下。假如是,咱们运用一个负的水平速度,开动奔跑动画'left'。假如是方向右键正被按下,咱们按字面意思做反向动作。经过铲除速度值,再如此设置,一帧一帧,构成一个“走走停停”(stop-start)式的运动。

玩家只要键被按下时才移动,抬起时立即中止。键盘检测的最终部分,假如没有键被按下,就设置动画为'turn',水平速度为0。

代码的最终部分增加了跳起功用。方向up键是跳起键,咱们查看它有没有被按下同时也检测玩家是不是正与地上触摸,否则在半空中还会往上跳。(当然你也能够测验完成二连跳的作用)

假如一切这些条件都契合,咱们运用一个笔直速度,330像素每秒。玩家会由于重力的影响主动落回地上,操控现已就位,咱们现在有了一个能够探究的游戏国际。请加载part7.html,或许运转自己的代码玩一玩。测验调整各个值,比方跳起值330,调低,调高,看看会有什么作用。

搜集星星

接下来咱们增加星星让玩家搜集,首要要做的便是让星星出现在游戏画面上。由于要批量生成星星,因而咱们生成一个新的组,叫'stars',再充分它。在create函数中,咱们参加如下代码(这些能够在part8.html中看到):

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));

});

这个过程跟咱们生成渠道组近似。但由于需求星星移动、反弹,咱们生成动态物理组,而不是静态的。

组能够接纳装备目标,以便于设置。在本例中,组装备目标有3个部分:

首要,它设置纹理key(键值)为星星图画。这意味着装备目标生成的一切子项,都将被默许地赋予星星纹理。然后,它设置重复值为11。由于它主动生成一个子项,重复11次就意味着咱们总共将得到12颗,这正好是咱们的游戏所需求的。

最终的部分是setXY——这用来设置组的12个子项的方位。每个子项都将如此放置:初始是x: 12,y: 0,然后x步进70。这意味着榜首个子项将坐落12 x 0;第二个离开70像素,坐落82 x 0;第三个在152 x 0,依次类推。'step'(步进)值用于组生成子项时加以排布,真是很便利的手法。选用值70是由于,这意味着一切12个子项将完美地横跨着布满画面。

下一段代码遍历组中一切子项,给它们的bounce.y赋予0.4到0.8之间的随机值,反弹范围在0(不反弹)到1之间(彻底反弹)。由于星星都是在y等于0的方位产出的,重力将把它们往下拉,直到与渠道或地上磕碰为止。反弹值意味着它们将随机地反弹上来,直到最终康复安靖为止。

假如现在咱们这样就运转代码,星星会落下并穿过游戏底边,消失不见了。要避免这个问题,咱们就要检测它们与渠道的磕碰。咱们能够再运用一个磕碰器目标来做这件事:

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);
}

简略来说,星星带着个已封闭的物体,其父级游戏目标被设置为不活动、不行见,行将它从显现中移除。现在运转一下游戏,咱们得到一个玩家,它左冲右突的,跳起,从渠道反弹,搜集头顶上落下的星星。不错,毕竟就这么几行、八成看起来还很好了解的代码.

在这儿刺进图片描绘

计分

搜集星星能够得分。接下来让咱们来完成计分功用。

运用 text 目标即可。

在此咱们生成两个新的变量,一个持有实际得分,一个文本目标自身:

var score = 0; var scoreText;

scoreText在create函数中构建:

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

16 x 16是显现文本的坐标方位。score: 0 是要显现的默许字符串,接下来的目标包括字号、填充色。由于没有指定字体,实际上将用 Phaser 默许的,即 Courier 。

下一步咱们要调整collectStar函数,以便玩家捡到一颗星星时分数会进步,文本会更新以反映出新状况:

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

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

这样一来,每颗星星加10分,scoreText将更新,显现出新的总分。假如运转part9.html,你能够看到星星掉下来,搜集星星时分数会进步。

在这儿刺进图片描绘

弹球

现在该增加一些坏蛋,以此给咱们的游戏收尾。

想法是这样的:你榜首次搜集到一切星星后,将放出一个炸弹。这个炸弹仅仅随机地在渠道上各处跳,假如触摸到它,你就死了。一切星星会从头产出,以便你能够再次搜集,假如你完成了,又会放出另一个炸弹。这将给玩家一个应战:别死掉,取得尽可能高的分数。

咱们首要需求的东西是给炸弹用的一个组,还有几个磕碰器:

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;
}

现在看来还不错,不过咱们要放出一个炸弹。要做到这一点,咱们改一下collectStar函数:

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.setBounce(1);
        bomb.setCollideWorldBounds(true);
        bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);

    }
}

咱们运用一个组的办法countActive,看看有多少星星还活着。假如没有了,那么玩家把它们搜集完了,于是咱们运用迭代函数从头激活一切星星,重置它们的y方位为0。这将使一切星星再次从画面顶部落下。

下一部分代码生成一个炸弹。首要,咱们取一个随机x坐标给它,一直在玩家的对侧画面,以便给玩家个时机。然后生成炸弹,设置它跟国际磕碰,反弹,拥有随机速度。

在这儿刺进图片描绘

最终附上咱们的一切源码:

html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Documenttitle> <script src="//cdn.jsdelivr.net/npm/phaser@3.11.0/dist/phaser.js">script> <style type="text/css"> body { margin: 0;
        } style> head> <body> <script type="text/javascript"> 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
            }
        }; var player; var stars; var platforms; var cursors; var score = 0; var scoreText; var bombs; var game = new Phaser.Game(config); 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 }
            );
        } function create() { this.add.image(400, 300, 'sky');

            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');

            player = this.physics.add.sprite(100, 450, 'dude'); this.physics.add.collider(player, platforms);

            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 });
            cursors = this.input.keyboard.createCursorKeys();

            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));
            }); this.physics.add.collider(stars, platforms); this.physics.add.overlap(player, stars, collectStar, null, this);

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

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

        } function update() { 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);
            }
        } 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.setBounce(1);
                bomb.setCollideWorldBounds(true);
                bomb.setVelocity(Phaser.Math.Between(-200, 200), 20);
            }
        } function hitBomb (player, bomb){ this.physics.pause();
            player.setTint(0xff0000);
            player.anims.play('turn');
            gameOver = true;
            
        } script> body> html>
本公司销售:阿里云新/老客户,只要购买阿里云,即可享受折上折优惠!>

我有话说: