浏览代码

Adding Flappy Bird

Simon 5 年之前
父节点
当前提交
3d19e48d47
共有 4 个文件被更改,包括 325 次插入0 次删除
  1. 二进制
      assets/bird.png
  2. 二进制
      assets/pipe.png
  3. 二进制
      assets/sky.png
  4. 325 0
      index.html

二进制
assets/bird.png


二进制
assets/pipe.png


二进制
assets/sky.png


+ 325 - 0
index.html

@@ -0,0 +1,325 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="https://cdn.jsdelivr.net/npm/phaser/dist/phaser.js"></script>
+</head>
+<body>
+
+<style>
+    body {
+      overflow: hidden;
+      padding: 0px;
+      margin: 0px;
+    }
+</style>
+
+<script>
+
+const _GRAVITY = 800;
+const _MAX_UPWARDS_VELOCITY = -300;
+const _UPWARDS_ACCELERATION = -450;
+const _TERMINAL_VELOCITY = 400;
+const _PIPE_SPACING_Y = 100;
+const _TREADMILL_SPEED = -125;
+
+const _CONFIG_WIDTH = 960;
+const _CONFIG_HEIGHT = 540;
+const _GROUND_Y = _CONFIG_HEIGHT;
+
+
+class FlappyBirdObject {
+  constructor(scene) {
+    this._sprite = scene.add.sprite(50, 100, 'bird');
+    this._velocity = 0;
+  }
+
+  Destroy() {
+    this._sprite.destroy();
+  }
+
+  Update(timeElapsed, keyboard) {
+    // Apply gravity and upward impulses
+    this._ApplyGravity(timeElapsed)
+    this._HandleInput(timeElapsed, keyboard)
+    this._velocity = Math.min(Math.max(this._velocity, _MAX_UPWARDS_VELOCITY), _TERMINAL_VELOCITY);
+
+    this._sprite.y += this._velocity * timeElapsed;
+
+    const v = new Phaser.Math.Vector2(-1 * _TREADMILL_SPEED * timeElapsed, 0);
+    v.add(new Phaser.Math.Vector2(0, this._velocity));
+    v.normalize();
+
+    const rad = Math.atan2(v.y, v.x);
+    const deg = (180.0 / Math.PI) * rad;
+
+    this._sprite.angle = deg * 0.75;
+  }
+
+  get Bounds() {
+    return this._sprite.getBounds();
+  }
+
+  _ApplyGravity(timeElapsed) {
+    this._velocity += _GRAVITY * timeElapsed;
+  }
+
+  _HandleInput(timeElapsed, keys) {
+    if (!Phaser.Input.Keyboard.JustDown(keys.up)) {
+      return;
+    }
+
+    this._velocity += _UPWARDS_ACCELERATION;
+  }
+}
+
+class PipePairObject {
+  constructor(scene, x) {
+    const height = _CONFIG_HEIGHT * (0.25 + 0.5 * Math.random());
+    this._sprite1 = scene.add.sprite(x, height + _PIPE_SPACING_Y * 0.5, 'pipe');
+    this._sprite1.displayOriginX = 0;
+    this._sprite1.displayOriginY = 0;
+
+    this._sprite2 = scene.add.sprite(x, height - _PIPE_SPACING_Y * 0.5, 'pipe');
+    this._sprite2.displayOriginX = 0;
+    this._sprite2.displayOriginY = 0;
+    this._sprite2.displayHeight = -1 * this._sprite2.height;
+  }
+
+  Destroy() {
+    this._sprite1.destroy();
+    this._sprite2.destroy();
+  }
+
+  Update(timeElapsed) {
+    this._sprite1.x += timeElapsed * _TREADMILL_SPEED;
+    this._sprite2.x += timeElapsed * _TREADMILL_SPEED;
+  }
+
+  Intersects(aabb) {
+    const b1 = this._sprite1.getBounds();
+    const b2 = this._sprite2.getBounds();
+    b2.y -= this._sprite2.height;
+    return (
+        Phaser.Geom.Intersects.RectangleToRectangle(b1, aabb) ||
+        Phaser.Geom.Intersects.RectangleToRectangle(b2, aabb));
+  }
+
+  Reset(x) {
+    const height = _CONFIG_HEIGHT * (0.25 + 0.5 * Math.random());
+    this._sprite1.x = x;
+    this._sprite1.y = height + _PIPE_SPACING_Y * 0.5;
+    this._sprite2.x = x;
+    this._sprite2.y = height - _PIPE_SPACING_Y * 0.5;
+  }
+
+  get X() {
+    return this._sprite1.x;
+  }
+
+  get Width() {
+    return this._sprite1.width;
+  }
+}
+
+class FlappyBirdGame {
+  constructor() {
+    this._game = this._CreateGame();
+    this._previousFrame = null;
+    this._bird = null;
+    this._gameOver = false;
+    this._score = 0;
+    this._scoreText = null;
+    this._gameOverText = null;
+    this._pipes = [];
+  }
+
+  _Destroy() {
+    this._bird.Destroy();
+    for (let p of this._pipes) {
+      p.Destroy();
+    }
+    this._scoreText.destroy();
+    if (this._gameOverText !== null) {
+      this._gameOverText.destroy();
+    }
+    this._bird = null;
+    this._pipes = [];
+    this._previousFrame = null;
+  }
+
+  _Init() {
+    for (let i = 0; i < 5; i+=1) {
+      this._pipes.push(new PipePairObject(this._scene, 500 + i * 250));
+    }
+
+    this._bird = new FlappyBirdObject(this._scene);
+    this._gameOver = false;
+    this._score = 0;
+  }
+
+  _CreateGame() {
+    const self = this;
+    const config = {
+        type: Phaser.AUTO,
+        scene: {
+            preload: function() { self._OnPreload(this); },
+            create: function() { self._OnCreate(this); },
+            update: function() { self._OnUpdate(this); },
+        },
+        scale: {
+          mode: Phaser.Scale.FIT,
+          autoCenter: Phaser.Scale.CENTER_BOTH,
+          width: _CONFIG_WIDTH,
+          height: _CONFIG_HEIGHT
+        }
+    };
+
+    return new Phaser.Game(config);
+  }
+
+  _OnPreload(scene) {
+    this._scene = scene;
+    this._scene.load.image('sky', 'assets/sky.png');
+    this._scene.load.image('bird', 'assets/bird.png');
+    this._scene.load.image('pipe', 'assets/pipe.png');
+  }
+
+  _OnCreate(scene) {
+    const s = this._scene.add.image(0, 0, 'sky');
+    s.displayOriginX = 0;
+    s.displayOriginY = 0;
+    s.displayWidth = _CONFIG_WIDTH;
+    s.displayHeight = _CONFIG_HEIGHT;
+
+    this._keys = {
+      up: this._scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP),
+      f: this._scene.input.keyboard.addKey('F'),
+      r: this._scene.input.keyboard.addKey('R'),
+    }
+
+    this._keys.f.on('down', function () {
+      if (this._scene.scale.isFullscreen) {
+        this._scene.scale.stopFullscreen();
+      } else {
+        this._scene.scale.startFullscreen();
+      }
+    }, this);
+
+    this._keys.r.on('down', function () {
+      this._Destroy();
+      this._Init();
+      this._DrawScore();
+    }, this);
+
+    this._Init();
+    this._DrawScore();
+  }
+
+  _OnUpdate(scene) {
+    if (this._gameOver) {
+      return;
+    }
+
+    const currentFrame = scene.time.now;
+    if (this._previousFrame == null) {
+      this._previousFrame = currentFrame;
+    }
+
+    const timeElapsedInS = (currentFrame - this._previousFrame) / 1000.0;
+
+    this._bird.Update(timeElapsedInS, this._keys);
+
+    this._UpdatePipes(timeElapsedInS);
+    this._CheckGameOver();
+
+    this._previousFrame = currentFrame;
+  }
+
+  _CheckGameOver() {
+    // Ground check
+    const birdAABB = this._bird.Bounds;
+    birdAABB.top += 10;
+    birdAABB.bottom -= 10;
+    birdAABB.left += 10;
+    birdAABB.right -= 10;
+
+    if (birdAABB.top >= _GROUND_Y) {
+      this._GameOver();
+      return;
+    }
+
+    for (const p of this._pipes) {
+      if (p.Intersects(birdAABB)) {
+        let a = p.Intersects(birdAABB);
+        this._GameOver();
+        return;
+      }
+    }
+  }
+
+  _UpdatePipes(timeElapsed) {
+    const oldPipeX = this._pipes[0].X + this._pipes[0].Width;
+
+    for (const p of this._pipes) {
+      p.Update(timeElapsed);
+    }
+
+    const newPipeX = this._pipes[0].X + this._pipes[0].Width;
+
+    if (oldPipeX > 50 && newPipeX <= 50) {
+      this._score += 1;
+      this._scoreText.text = "Score: " + this._score;
+    }
+
+    if ((this._pipes[0].X + this._pipes[0].Width) <= 0) {
+      const p = this._pipes.shift();
+      p.Reset(this._pipes[this._pipes.length - 1].X + 200.0);
+      this._pipes.push(p);
+    }
+  }
+
+  _GameOver() {
+    const text = "GAME OVER";
+    const style = {
+      font: "100px Roboto",
+      fill: "#FFFFFF",
+      align: "center",
+      fixedWidth: _CONFIG_WIDTH,
+      shadow: {
+        offsetX: 2,
+        offsetY: 2,
+        color: "#000",
+        blur: 2,
+        fill: true
+      }
+    };
+
+    this._gameOverText = this._scene.add.text(0, _CONFIG_HEIGHT * 0.25, text, style);
+    this._gameOver = true;
+  }
+
+  _DrawScore() {
+    const text = "Score: 0";
+    const style = {
+      font: "40px Roboto",
+      fill: "#FFFFFF",
+      align: "center",
+      shadow: {
+        offsetX: 2,
+        offsetY: 2,
+        color: "#000",
+        blur: 2,
+        fill: true
+      }
+    };
+
+    this._scoreText = this._scene.add.text(0, 0, text, style);
+  }
+}
+
+_GAME = new FlappyBirdGame()
+
+</script>
+
+</body>
+</html>