Phaser3入门示例

# Phaser3 入门示例

# 星星收集者

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Making your first Phaser 3 Game - Part 10</title>
        <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 = {
                // 类型 canvas webgl
                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 bombs; // 炸弹
            var platforms; // 地面及平台
            var cursors; // 键盘控制
            var score = 0; // 得分
            var gameOver = false;
            var scoreText; // 得分文字

            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() {
                //  A simple background for our game
                // 添加游戏背景
                this.add.image(400, 300, "sky");

                //  The platforms group contains the ground and the 2 ledges we can jump on
                // 生成一个静态物理组(Group),并把这个组赋值给局部变量platforms。在Arcade物理系统中,有动态的和静态的两类物体(body)。
                // 动态物体可以通过外力比如速度(velocity)、加速度(acceleration),得以四处移动。
                // 它可以跟其他对象发生反弹(bounce)、碰撞(collide),此类碰撞受物体质量和其他因素影响。
                platforms = this.physics.add.staticGroup();

                //  Here we create the ground.
                //  Scale it to fit the width of the game (the original sprite is 400x32 in size)
                // 添加一张新的地面图像到400 x 568的位置(请记住,图像定位基于中心点)——问题是,我们需要这个平台撑满游戏的宽度。否则玩家就会掉出边界。
                // 要做到这一点,我们用函数setScale(2)把它按x2(两倍)缩放。现在它的尺寸是800 x 64了,恰好符合我们的要求。
                // 要调用refreshBody(),这是因为我们缩放的是一个 静态 物体,所以必须把所作变动告诉物理世界(physics world)。
                platforms.create(400, 568, "ground").setScale(2).refreshBody();

                //  Now let's create some ledges
                platforms.create(600, 400, "ground");
                platforms.create(50, 250, "ground");
                platforms.create(750, 220, "ground");

                // The player and its settings
                // 生成一个新的精灵,叫player(玩家),位于100 x 450像素,在游戏的下部。
                // 精灵是通过物理游戏对象工厂函数(Physics Game Object Factory,即this.physics.add)生成的,这意味着它默认拥有一个动态物体。
                player = this.physics.add.sprite(100, 450, "dude");

                //  Player physics properties. Give the little guy a slight bounce.
                // 精灵生成后,被赋予0.2的一点点反弹(bounce)值。这意味着,它跳起后着地时始终会弹起那么一点点。
                // 然后精灵设置了与世界边界(bound)的碰撞。——边界默认在游戏尺寸之外。
                player.setBounce(0.2);
                // 我们(通过player.setCollideWorldBounds(true))把游戏(的世界边界)设置为800 x 600后,玩家就不能不跑出这个区域了。
                // 这样会让玩家停下来,不能跑出画面边界,或跳出顶边。
                player.setCollideWorldBounds(true);

                //  Our player animations, turning, walking left and walking right.
                // 'left'动画使用0, 1, 2, 3帧,跑动时每秒10帧。'repeat -1'告诉动画要循环播放。
                this.anims.create({
                    key: "left",
                    frames: this.anims.generateFrameNumbers("dude", { start: 0, end: 3 }),
                    frameRate: 10,
                    repeat: -1,
                });

                // 动画键值用'turn'(转身)
                this.anims.create({
                    key: "turn",
                    frames: [{ key: "dude", frame: 4 }],
                    frameRate: 20,
                });

                // 反方向的动画把这些重复一下,键值用'right'。
                this.anims.create({
                    key: "right",
                    frames: this.anims.generateFrameNumbers("dude", { start: 5, end: 8 }),
                    frameRate: 10,
                    repeat: -1,
                });

                //  Input Events
                // Phaser有内置的键盘管理器,光标(cursor)对象有四个属性up, down, left, right(都是Key对象的实例)。
                cursors = this.input.keyboard.createCursorKeys();

                //  Some stars to collect, 12 in total, evenly spaced 70 pixels apart along the x axis
                // 首先,它设置纹理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个子项将完美地横跨着布满画面。
                stars = this.physics.add.group({
                    key: "star",
                    repeat: 11,
                    setXY: { x: 12, y: 0, stepX: 70 },
                });

                // 遍历组中所有子项,给它们的bounce.y赋予0.4到0.8之间的随机值,反弹范围在0(不反弹)到1之间(完全反弹)。
                // 因为星星都是在y等于0的位置产出的,重力将把它们往下拉,直到与平台或地面碰撞为止。反弹值意味着它们将随机地反弹上来,直到最终恢复安定为止。
                stars.children.iterate(function (child) {
                    //  Give each star a slightly different bounce
                    // 给每颗星一个稍微不同的反弹
                    child.setBounceY(Phaser.Math.FloatBetween(0.4, 0.8));
                });

                // 生成一个炸弹(bombs)组
                bombs = this.physics.add.group();

                //  The score
                // 16 x 16是显示文本的坐标位置。'score: 0'是要显示的默认字符串,接下来的对象包含字号、填充色。
                // 因为没有指定字体,实际上将用Phaser默认的,即Courier。
                scoreText = this.add.text(16, 16, "score: 0", { fontSize: "32px", fill: "#000" });

                //  Collide the player and the stars with the platforms
                // 碰撞器(Collider)是施魔法的地方。它接收两个对象,检测二者之间的碰撞,并使二者分开。
                this.physics.add.collider(player, platforms);
                this.physics.add.collider(stars, platforms);
                this.physics.add.collider(bombs, platforms);

                //  Checks to see if the player overlaps with any of the stars, if he does call the collectStar function
                // 检查玩家是否与任何星星重叠,如果检测到就调用 collectStar 函数
                this.physics.add.overlap(player, stars, collectStar, null, this);

                // 检查玩家是否碰到炸弹
                this.physics.add.collider(player, bombs, hitBomb, null, this);
            }

            function update() {
                if (gameOver) {
                    return;
                }

                // 键盘事件 左右移动
                if (cursors.left.isDown) {
                    // 设置水平速度
                    player.setVelocityX(-160);

                    // 播放left动画
                    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);

                //  Add and update the score
                // 玩家捡到一颗星星时分数会提高,文本会更新以反映出新状态
                score += 10;
                scoreText.setText("Score: " + score);

                // 使用一个组的方法countActive,看看有多少星星还活着。
                // 如果没有了,那么玩家把它们收集完了,于是我们使用迭代函数重新激活所有星星,重置它们的y位置为0。这将使所有星星再次从画面顶部落下。
                if (stars.countActive(true) === 0) {
                    //  A new batch of stars to collect
                    // 新一批要收集的星星
                    stars.children.iterate(function (child) {
                        child.enableBody(true, child.x, 0, true, true);
                    });

                    // 生成一个炸弹。首先取一个随机x坐标给它,始终在玩家的对侧画面,以便给玩家个机会。然后生成炸弹,设置它跟世界碰撞,反弹,拥有随机速度。
                    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);
                    // 禁用炸弹的重力效果,这样可以一直弹跳
                    bomb.allowGravity = false;
                }
            }

            // 玩家碰到炸弹的回调
            function hitBomb(player, bomb) {
                // 物理暂停
                this.physics.pause();

                // 让玩家变成红色
                player.setTint(0xff0000);

                player.anims.play("turn");

                gameOver = true;
            }
        </script>
    </body>
</html>