VUEはFlappy Birdゲームのサンプルコードを実装します

VUEはFlappy Birdゲームのサンプルコードを実装します

Flappy Bird は、誰もがアプリでプレイしたことがある非常にシンプルな小さなゲームです。ここでは、娯楽用に VUE を使用して Flappy Bird のシンプルな PC バージョンを実装します~~~~~

このゲームを実現するには、まずゲーム インターフェースのどの部分をアニメーション化する必要があるかを分析しましょう。

1. 最初のものはもちろん上下に動く鳥です。

2. 背景画像が水平に動くため、鳥が水平に飛んでいるように見えます。

3. 画面右側から入ってくるパイプの列。

これは非常に明確です。上記の 3 つの部分をルールに従って動かし、ルールの境界判断とスコアリングを追加すれば、完全なゲームが得られます。では一つずつ解決していきましょう。

まず、いくつかの定数と変数を定義しましょう。

let rafId = null; // requestAnimationFrame ID
startSpeed ​​= 1とします。
const SPEED = 0.04; // 加速度 const UP = 5.0; // 速度累積の上限 const UP_SUM = 30; // 1回のジャンプの高さ const BIRD_WIDTH = 50; // 鳥の絵の幅 50px
const PIPE_DISTANCE = 350; // パイプ間の水平距離 let id = 0; // パイプの一意の ID。カウントは 0 から始まります...
 
データ() {
    戻る {
      開始: false、
      クライアント幅: 0,
      クライアントの高さ: 0,
      spaceHeight: [240, 200, 160], // 上部のパイプと下部のパイプの間の距離pipeArr: [], // パイプ配列score: 0, // ScorejumpSum: 0, // 現在のジャンプの相対高さjumpFlag: false, // true-スペースバーを押すと上昇ステージでジャンプします。false-自由落下ステージdropBirdImg: require("@/assets/img/bird/bird0_0.png"),
      flyBirdImg: require("@/assets/img/bird/bird0_2.png"),
      gameOver: false, // ゲームの失敗を示すフラグ。アニメーション フレームを停止するために使用されます};
},

1. 上下に動く鳥

鳥とパイプの位置をそれぞれ制御するために、要素はposition: absoluteを使用して配置されます。

鳥自体は div + 背景画像であり、インターフェース内での初期位置を定義します。

<div class="bird" id="bird" ref="bird"></div>
 
 #鳥 {
      高さ: 50px;
      幅: 50px;
      境界線の半径: 50%;
      背景: url("~assets/img/bird/bird0_1.png") 繰り返しなし center/contain;
      // 鳥の初期位置 position: absolute;
      左: 300ピクセル;
      上: 300px;
}

その後、何も操作しなくても、鳥は最初の位置から「落下」を始めます。鳥はどんどん速く落下します。ここでは、重力加速の物理的な式は使用せず、単に曲線加速プロセスをシミュレートしました。これは連続アニメーションなので、このアクションをアニメーションフレーム、つまりrequestAnimationFrameに配置し、各フレームの関数はloop()として定義します。

そのため、ループ関数では、親要素の offsetTop と clientHeight を使用して、鳥が画面の上限と下限に触れたかどうかを判断します。触れた場合はゲームが終了し、触れていない場合は style.top を増やして鳥を落下させます。

ループ() {
      _this = this とします。
      if (_this.jumpFlag) {
        // 鳥はジャンプします_this.jump();
      }
      top = _this.$refs.bird.offsetTop とします。
      _this.clientHeight がトップ > BIRD_WIDTH の場合、トップ <= 0 になります。
        //境界に達するとゲームが終了します_this.resetGame();
      } それ以外の場合 (!_this.jumpFlag) {
        _this.$refs.bird.style.background = `url('${_this.dropBirdImg}') 繰り返しなし center/contain`;
        _this.$refs.bird.style.top = top + startSpeed ​​* startSpeed ​​+ "px"; // 加速落下をシミュレートする if (startSpeed ​​< UP) {
          開始速度 + = 速度;
        }
      }
      _this.pipesMove(); // パイプラインの移動}

ゲーム内でプレイヤーがスペースバーを押すと、鳥が一定距離ジャンプします。この状態はthis.jumpFlag[true/false]で記録されます。押されたらtrueに設定されます。ループ関数内で鳥がジャンプします()。一定距離ジャンプした後、jumpFlagがfalseに設定され、鳥が落下し始めます。

したがって、ジャンプ関数は簡単に実装できます。

ジャンプ() {
      _this = this とします。
      _this.$refs.bird.style.background = `url('${_this.flyBirdImg}') 繰り返しなしの center/contain`;
      _this.jumpSum > UP_SUMの場合{
        // top_this.jumpFlag に到達すると落下します。false;
        _this.jump合計 = 0;
        開始速度 = 1;
      } それ以外 {
        _this.$refs.bird.style.top = _this.$refs.bird.offsetTop - 8 + "px";
        _this.jump合計+=8;
      }
}

2. 水平方向に移動する背景画像

これは比較的簡単です。無限ループで background-position を切り替えるだけです。位置は、ダウンロードした背景画像素材の幅によって決まります。

アニメーション: bgMove 8s 線形無限;
      @keyframes bgMove {
        0% {
          背景位置: 805px 0;
        }
        100% {
          背景位置: 0 0;
        }
}

これら 2 つの手順を実行すると、飛んでいる鳥が表示されます。以下に示すように、document.onkeydown を使用してスペース バーをリッスンし、jumpFlag を切り替えます。

3. 右から左へ移動してパイプに入る

パイプラインは 2 つの div で構成されており、それぞれの div の中央には、異なる top: -xx と bottom: -yy のギャップがあります。

まず、pipeArr オブジェクト配列に格納されるランダムギャップパイプを生成する関数を実装します。

パイプIDを追加します
      obj = {} とします。
      top_num = this.sum(10, 170) とします。
      高さを this.spaceHeight[ とする
        Math.floor(Math.random() * this.spaceHeight.length)
      ]; // ギャップ値をランダムに選択 let bottom_num = height - top_num;
      obj.top = トップ番号;
      obj.id = id;
      obj.right = -(PIPE_DISTANCE / 2);
      obj.bottom = ボトム番号;
      this.pipeArr.push(obj);
},
合計(m,n) {
      // nm 間の乱数 return Math.floor(Math.random() * (m - n) + n);
}

次に、パイプを移動する必要があります。つまり、loop() 内のパイプ移動関数 pipesMove() です。関数全体は次のように実装されます。

パイプ移動() {
      _this = this とします。
      _this.pipeArr.length === 0 の場合 {
        戻る;
      }
      right0 = _this.pipeArr[0].right;とします。
      右0 > this.clientWidth + 300の場合{
        シフト関数
      }
      right_last = _this.pipeArr[_this.pipeArr.length - 1].right とします。
      (右端 >= PIPE_DISTANCE / 2)の場合{
        id++;
        this.addPipe(id);
      }
      (i = 0 とします; i < _this.pipeArr.length; i++) {
        // 鳥がパイプに触れたかどうかを判定します。鳥は 50*50、左: 300px、パイプの幅は 100px、パイプの進入範囲の右は width-450 から width-300 です。
        もし (
          _this.pipeArr[i].right >= _this.clientWidth - 450 &&
          _this.pipeArr[i].right <= _this.clientWidth - 300
        ){
          // パイプが鳥の接触範囲に入りました。let bird_top = _this.$refs.bird.offsetTop;
          // 12は、上下に空白がある鳥の画像素材です(
            bird_top <= _this.clientHeight / 2 - _this.pipeArr[i].top - 12 ||
            鳥トップ >=
              _this.clientHeight / 2 + _this.pipeArr[i].bottom - BIRD_WIDTH + 12
          ){
            // pipe_this.resetGame() を実行します。
            戻る;
          }
        }
        if (_this.pipeArr[i].right === _this.clientWidth - 300 && _this.pipeArr[i].right === _this.clientWidth - 301) { // パイプが鳥のすぐ左側にある場合、鳥はパイプを通過したことがわかります。パイプの ID に基づいて鳥のスコアを計算します _this.score = _this.pipeArr[i].id + 1;
        }
        _this.pipeArr[i].right = _this.pipeArr[i].right + 2; // パイプはフレームごとに2ピクセル移動します
      }
}

ここでは 5 つのことが行われます:

(1)パイプが左画面から出た後、左端のパイプをshift()する。

(2)右端のパイプが画面右側から一定の距離を離れた後、新たなパイプが追加される。

(3)ループ中に、鳥が特定のパイプの範囲内に入ったかどうか、また鳥の上部が上下のパイプに触れているかどうかを判定します。触れている場合は負けです。

(4)鳥の左側にパイプがある場合は、鳥は無事に通過したことを意味し、得点は+1となる。

(5)各チャンネルは2pxピクセルずつ移動し、その値が右属性に記録されます。

DOM:style を正しく設定することで、パイプを水平方向に移動できるようになります。

<section class="pipes-wrap" ref="パイプ">
          <div
            クラス="パイプアイテム"
            v-for="(item, index) in pipeArr"
            :key="アイテムID"
            :id="'パイプ' + インデックス"
            :style="'right:' + item.right + 'px;'"
          >
            <div
              class="pipe パイプトップ"
              :style="'top:-' + item.top + 'px;'"
            </div>
            <div
              クラス="パイプ パイプ下部"
              :style="'bottom:-' + item.bottom + 'px;'"
            </div>
          </div>
</セクション>
 
.パイプラップ{
        位置: 相対的;
        高さ: 100%;
        オーバーフロー: 非表示;
        .パイプアイテム{
          位置: 絶対;
          高さ: 100%;
          幅: 100ピクセル;
          .パイプ{
            幅: 100%;
            高さ: 50%;
            位置: 相対的;
          }
          .パイプトップ{
            背景: url('"~assets/img/bird/pipe_down.png') 繰り返しなし;
            背景サイズ: 100px;
            背景位置: 下;
          }
          .パイプボトム{
            背景: url('"~assets/img/bird/pipe_up.png') 繰り返しなし;
            背景サイズ: 100px;
            背景の位置: 上;
          }
        }
} 

上記は、合計 200 行を超えるコードを含む、Vue による Flappy Bird の実装のアイデアとコア コードです。私の意見では、難しさは主にパイプの動き、タッチの決定、スコアの計算にあります。もちろん、コードにはまだ最適化できる欠点がたくさんあります。お互いに励まし合いましょう~~

以下もご興味があるかもしれません:
  • フラッピーバードゲームのサンプルコードを実装するための純粋なJavaScript
  • シンプルな Flappy Bird の C++ バージョン
  • フラッピーバードゲームのC言語簡易版
  • Python が Flappy Bird のソースコードを実装
  • C言語でフラッピーバードゲームを実装する
  • Unity が Flappy Bird ゲーム開発手法を実装
  • Flappy Bird ゲームのソースコードの Java 実装
  • C言語でFlappy Birdゲームを実装する
  • Pygame を使って Flappy Bird ゲームを書く
  • Pygame オブジェクト指向の飛ぶ鳥の実装 (Flappy bird)

<<:  CentOS7 systemdにカスタムシステムサービスを追加する方法

>>:  MySQL 5.7 における部分テーブルのデュアルマスター同期の実装プロセスの詳細な説明

推薦する

Centos8 で Apache httpd2.4.37 を使用して Web サーバーをインストールする詳細な手順

ステップ 1: yum install httpd -y #httpd サービスをインストールします...

表示しているページのスナップショットを Baidu が保存できないように設定する方法

今日、Baidu でページを検索したところ、ページが削除されていたため、当然 Baidu スナップシ...

MySQL の WriteSet 並列レプリケーションの簡単な分析

【歴史的背景】私は 3 年間 MySQL-DBA として働いてきましたが、MySQL が「基本的に利...

href をクリックした後にページがジャンプしないようにするための空のリンクの正しい書き方 # 問題

リンクを使用する必要がある場合もありますが、リンクする必要はありません。onclick イベントを処...

Linux オペレーティング システムに Apache サービスをインストールする方法

ダウンロードリンク:動作環境VMware 仮想マシンの CentOS 7.6セキュアCRT Xftp...

Windows で Nginx を使用して https サーバーとリバース プロキシを構成する際の問題

リクエストロジックフロントエンド --> https経由でnginxをリクエストnginx -...

DockerにTensorFlow環境を素早くインストールする方法

Docker に TensorFlow 環境をすばやくインストールし、TensorFlow を使用し...

MySQL でインデックス構造として B+ ツリーを使用する利点は何ですか?

序文MySQL では、Innodb と MyIsam の両方がインデックス構造として B+ ツリーを...

MySQLクエリの冗長インデックスと未使用のインデックス操作

MySQL 5.7 以降のバージョンでは、冗長インデックス、重複インデックス、およびインデックスを使...

Dockerコンテナの起動失敗を解決する方法

質問: コンピュータを再起動した後、docker の mysql コンテナを再起動できません。原因が...

VMware Esxi のルート パスワードを忘れた後に正常に取得する方法

CentOS6 インストール ディスク (任意のバージョン) を準備するか、別の pnux インスト...

React で Antd の Form コンポーネントを使用してフォーム機能を実装する方法

1. 構造部品1. フォームには、入力コントロール、標準フォーム フィールド、ラベル、ドロップダウン...

Ajax リクエストにおけるクロスドメイン問題の原因と解決策

目次1. クロスドメインはどのように形成されるのでしょうか? 2. クロスドメインの根本的な原因3....

React+TypeScriptプロジェクト構築事例解説

React プロジェクトの構築は非常に簡単ですが、Typescript と組み合わせると、実際にはそ...

MySQLの関連ロックについての簡単な理解

この記事は主にInnoDBのロックに関する知識を素早く理解してもらうことを目的としています。 Roc...