誰もがピンボールやレンガ崩しのゲームをプレイしたことがあるでしょう。左と右のキーを使用して、下にある小さな木の板の移動を制御して、落ちてくるボールをキャッチし、ボールを跳ね上げて画面上部のレンガの山を消します。 では、VUE + Canvas でそれをどのように実現するのでしょうか?アイデアは非常にシンプルです。まず、キャンバスに描画するコンテンツを分割しましょう。 (1)キーボードの左右のキーを使って木の板の移動を制御します。 (2)キャンバスの上で跳ね回る小さなボール (3)画面上部に固定され、ボールが当たると消えるレンガの山。 上記の 3 つのオブジェクトは、requestAnimationFrame() 関数を使用して変換し、さまざまな衝突チェックと組み合わせて最終結果を得ることができます。 まずは最終的な効果を見てみましょう。 1. 水平に動く木の板下部の木板は、板の y 座標が固定されているため、最も単純な部分です。板の幅、高さ、移動速度などの初期パラメータを設定し、板を描画する機能を実装します。 パネル: { x: 0, y: 0, 高さ: 8, 幅: 100, 速度: 8, 度: 0 }, .... パネルを描画する() { this.drawRoundRect() は、 this.pannel.x、 this.pannel.y、 this.pannel.width、 this.pannel.height、 5 ); }, drawRoundRect(x, y, width, height, radius) { // 角丸四角形を描画します this.ctx.beginPath(); this.ctx.arc(x + 半径, y + 半径, 半径, Math.PI, (Math.PI * 3) / 2); this.ctx.lineTo(幅 - 半径 + x, y); this.ctx.arc( 幅 - 半径 + x、 半径 + y、 半径、 (数学.円周率 * 3) / 2、 数学.PI * 2 ); this.ctx.lineTo(幅 + x、高さ + y - 半径); this.ctx.arc( 幅 - 半径 + x、 高さ - 半径 + y、 半径、 0, (数学.円周率 * 1) / 2 ); this.ctx.lineTo(半径 + x、高さ + y); this.ctx.arc( 半径 + x、 高さ - 半径 + y、 半径、 (数学.円周率 * 1) / 2、 数学.PI ); this.ctx.fillStyle = "#008b8b"; this.ctx.fill(); this.ctx.closePath(); } プログラムが初期化されると、キーボードの左矢印キーと右矢印キーが監視されてボードが移動し、その長さを使用してボードが左境界または右境界に移動したかどうかを判断して、それ以上画面から移動できないようにします。 document.onkeydown = 関数(e) { キーを window.event.keyCode とします。 (キー === 37) の場合 { //左 button_this.pannel.dx = -_this.pannel.speed; } そうでない場合 (キー === 39) { //右ボタン_this.pannel.dx = _this.pannel.speed; } }; document.onkeyup = 関数(e) { _this.pannel.dx = 0; }; .... パネルを移動します(){ this.pannel.x += this.pannel.dx; this.pannel.x > this.clientWidth - this.pannel.width の場合 { this.pannel.x = this.clientWidth - this.pannel.width; } それ以外の場合 (this.pannel.x < 0) { this.pannel.x = 0; } }, 2. 跳ねるボールと衝突検出ボールの動きはボードの動きと似ていますが、dx オフセットだけでなく dy オフセットもある点が異なります。 そして衝突検出が必要です: (1)上、右、左の壁や木の板にぶつかると跳ね返ります。 (2)衝突が盤外の下界に当たった場合は負けとなる。 (3)ボールがレンガにぶつかるとレンガが消えて得点が1増え、ボールは跳ね返ります。 そこで、木の板と同じように、ボール部分はボール描画関数drawBall()とボール移動関数moveBall()に分かれています。 ドローボール() { this.ctx.beginPath(); this.ctx.arc(this.ball.x, this.ball.y, this.ball.r, 0, 2 * Math.PI); this.ctx.fillStyle = "#008b8b"; this.ctx.fill(); this.ctx.closePath(); }, ボールを移動します(){ this.ball.x += this.ball.dx; this.ball.y += this.ball.dy; この.breaksHandle(); this.edgeHandle(); }, breaksHandle() { // ブロックタッチ検出 this.breaks.forEach(item => { if (item.show) { もし ( this.ball.x + this.ball.r > item.x && this.ball.x - this.ball.r < item.x + this.breaksConfig.width && this.ball.y + this.ball.r > item.y && this.ball.y - this.ball.r < item.y + this.breaksConfig.height ){ アイテムを表示 = false; this.ball.dy * = -1; this.score++; if (this.showBreaksCount === 0) { ゲームオーバー = true; } } } }); }, エッジハンドル() { // エッジ検出 // 上部に当たったときに跳ね返る if (this.ball.y - this.ball.r < 0) { this.ball.dy = -this.ball.dy; } もし ( // 左右の壁を打つ this.ball.x - this.ball.r < 0 || this.ball.x + this.ball.r > this.clientWidth ){ this.ball.dx = -this.ball.dx; } もし ( this.ball.x >= this.pannel.x && this.ball.x <= this.pannel.x + this.pannel.width && this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height ){ // ボールの x がボードの範囲内にあり、ボードに触れている this.ball.dy *= -1; } それ以外の場合 ( (this.ball.x < this.pannel.x || this.ball.x > this.pannel.x + this.pannel.width) && this.ball.y + this.ball.r >= this.clientHeight ){ // ボールが下端に当たる this.gameOver = true; 戻り値: } } 3. レンガの生成ブリックの生成も比較的簡単です。ここではいくつかのデータを初期化します。 ブレーク設定: row: 6, // 行数 height: 25, // レンガの高さ width: 130, // レンガの幅 radius: 5, // 長方形の角の丸め space: 0, // 間隔 colunm: 6 // 列数 } これらの構成項目とキャンバスの幅に基づいて、各ブリック間の水平方向のギャップを計算できます。 // レンガの隙間の幅を計算します this.breaksConfig.space = Math.floor( (this.clientWidth - this.breaksConfig.width * this.breaksConfig.colunm) / (this.breaksConfig.column + 1) ); キャンバス内の各レンガのx、y座標を取得できます(レンガの左上隅の座標を参照)。 (i = 0 とします; i < _this.breaksConfig.row; i++) { (j = 0 とします; j < _this.breaksConfig.colunm; j++) { _this.breaks.push({ x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j、 y: 10 * (i + 1) + this.breaksConfig.height * i、 表示: 真 }); } } レンガを描く機能も追加: 描画区切り() { _this = this とします。 _this.breaks.forEach(item => { if (item.show) { _this.drawRoundRect() 関数は、 アイテム.x、 アイテム.y、 _this.breaksConfig.width、 _this.breaksConfig.height、 _this.breaksConfig.半径 ); } }); } 4. 上記の3つの部分を動かす(関数animloop() { もし(!_this.gameOver) { _this.movePannel(); _this.moveBall(); _this.drawAll(); } それ以外 { _this.drawCrushBreaks(); } window.requestAnimationFrame(アニメーションループ); })(); .... すべて描画() { this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight); パネルを描画します。 this.drawBall(); this.drawScore(); this.drawBreaks(); } 5. ゲーム終了後の効果最初のアニメーション画像では、ゲーム終了後、レンガが砕けていくつかの小さなボールが落ちてくるのがわかります。これは実際には、個々の小さなボールを描くことに似ています。アイデアは、残りのレンガの中心座標に、さまざまなサイズ、移動軌道、色の小さなボールをいくつか生成し、アニメーションを続行することです。 getCurshBreaks() { _this = this とします。 this.breaks.forEach(item => { if (item.show) { アイテムを表示 = false; for (let i = 0; i < 8; i++) { // 各レンガは 8 つの小さなボールに砕かれます this.crushBalls.push({ x: item.x + this.breaksConfig.width / 2, y: item.y + this.breaksConfig.height / 2, dx: _this.getRandomArbitrary(-6, 6)、 dy: _this.getRandomArbitrary(-6, 6)、 r: _this.getRandomArbitrary(1, 4)、 色: _this.getRandomColor() }); } } }); }, クラッシュブレイクを描画する() { this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight); this.crushBalls.forEach(item => { this.ctx.beginPath(); this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI); this.ctx.fillStyle = item.color; this.ctx.fill(); this.ctx.closePath(); アイテム.x += アイテム.dx; アイテム.y += アイテム.dy; もし ( // 左右の壁を叩く item.x - item.r < 0 || item.x + item.r > this.clientWidth ){ アイテム.dx = -アイテム.dx; } もし ( // 上部と下部の壁にぶつかる item.y - item.r < 0 || item.y + item.r > this.clientHeight ){ アイテム.dy = -アイテム.dy; } }); }, 上記は、デスクトップ ピンボール ブロック破壊ゲームの実装アイデアとコードの一部です。実装は非常に簡単で、このゲームは 200 行から 300 行のコードで実現できます。ボールの動きを継続的に最適化でき、難易度オプションも増やすことができます。 最後に、参考のためにすべての vue ファイル コードを添付します。 <テンプレート> <div class="break-ball"> <canvas id="breakBall" width="900" height="600"></canvas> <div class="container" v-if="ゲームオーバー"> <div class="ダイアログ"> <p class="once-again">このラウンドのスコア: {{score}} ポイント</p> <p class="once-again">とても楽しいです! </p> <p class="once-again">もう一度〜〜</p> <el-button class="once-again-btn" @click="init">開始</el-button> </div> </div> </div> </テンプレート> <スクリプト> 定数ランダムカラー = [ "#FF95CA", "#00E3E3", "#00E3E3", "#6F00D2", "#6F00D2", "#C2C287", 「#ECFFFF」、 "#FFDC35", "#93FF93", 「#d0d0d0」 ]; エクスポートデフォルト{ 名前:「ブレイクボール」 データ() { 戻る { クライアント幅: 0, クライアントの高さ: 0, ctx: null、 クラッシュボール: [], パネル: { x: 0, y: 0, 高さ: 8, 幅: 100, 速度: 8, 度: 0 }, ボール: x: 0, y: 0, r: 8, 度数: -4, dy: -4 }, スコア: 0, ゲームオーバー: false、 改行: [], ブレーク設定: row: 6, // 行数 height: 25, // レンガの高さ width: 130, // レンガの幅 radius: 5, // 長方形の角の丸め space: 0, // 間隔 colunm: 6 // 列数 } }; }, マウント() { _this = this とします。 コンテナを document.getElementById("breakBall"); this.ctx = コンテナ.getContext("2d"); コンテナの高さを設定します。 コンテナの幅を設定します。 _this.init(); document.onkeydown = 関数(e) { キーを window.event.keyCode とします。 (キー === 37) の場合 { //左 button_this.pannel.dx = -_this.pannel.speed; } そうでない場合 (キー === 39) { //右ボタン_this.pannel.dx = _this.pannel.speed; } }; document.onkeyup = 関数(e) { _this.pannel.dx = 0; }; (関数animloop() { もし(!_this.gameOver) { _this.movePannel(); _this.moveBall(); _this.drawAll(); } それ以外 { _this.drawCrushBreaks(); } window.requestAnimationFrame(アニメーションループ); })(); }, 計算:{ 表示ブレークカウント(){ this.breaks.filter(item=>{ を返します item.show を返します。 })。長さ; } }, メソッド: { 初期化() { _this = this とします。 _this.gameOver = false; this.pannel.y = this.clientHeight - this.pannel.height; this.pannel.x = this.clientWidth / 2 - this.pannel.width / 2; this.ball.y = this.clientHeight / 2; this.ball.x = this.clientWidth / 2; this.スコア = 0; this.ball.dx = [-1,1][Math.floor(Math.random() * 2)]*4; this.ball.dy = [-1,1][Math.floor(Math.random() * 2)]*4; this.crushBalls = []; this.breaks = []; // レンガの隙間の幅を計算します this.breaksConfig.space = Math.floor( (this.clientWidth - this.breaksConfig.width * this.breaksConfig.colunm) / (this.breaksConfig.column + 1) ); (i = 0 とします; i < _this.breaksConfig.row; i++) { (j = 0 とします; j < _this.breaksConfig.colunm; j++) { _this.breaks.push({ x: this.breaksConfig.space * (j + 1) + this.breaksConfig.width * j、 y: 10 * (i + 1) + this.breaksConfig.height * i、 表示: 真 }); } } }, すべて描画() { this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight); パネルを描画します。 this.drawBall(); this.drawScore(); this.drawBreaks(); }, パネルを移動します(){ this.pannel.x += this.pannel.dx; this.pannel.x > this.clientWidth - this.pannel.width の場合 { this.pannel.x = this.clientWidth - this.pannel.width; } それ以外の場合 (this.pannel.x < 0) { this.pannel.x = 0; } }, ボールを移動します(){ this.ball.x += this.ball.dx; this.ball.y += this.ball.dy; この.breaksHandle(); this.edgeHandle(); }, breaksHandle() { // ブロックタッチ検出 this.breaks.forEach(item => { if (item.show) { もし ( this.ball.x + this.ball.r > item.x && this.ball.x - this.ball.r < item.x + this.breaksConfig.width && this.ball.y + this.ball.r > item.y && this.ball.y - this.ball.r < item.y + this.breaksConfig.height ){ アイテムを表示 = false; this.ball.dy * = -1; this.score++; if (this.showBreaksCount === 0) { ゲームオーバー = true; } } } }); }, エッジハンドル() { // エッジ検出 // 上部に当たったときに跳ね返る if (this.ball.y - this.ball.r < 0) { this.ball.dy = -this.ball.dy; } もし ( // 左右の壁を打つ this.ball.x - this.ball.r < 0 || this.ball.x + this.ball.r > this.clientWidth ){ this.ball.dx = -this.ball.dx; } もし ( this.ball.x >= this.pannel.x && this.ball.x <= this.pannel.x + this.pannel.width && this.ball.y + this.ball.r >= this.clientHeight - this.pannel.height ){ // ボールの x がボードの範囲内にあり、ボードに触れている this.ball.dy *= -1; } それ以外の場合 ( (this.ball.x < this.pannel.x || this.ball.x > this.pannel.x + this.pannel.width) && this.ball.y + this.ball.r >= this.clientHeight ){ // ボールが下端に当たる this.gameOver = true; 戻り値: } }, スコアを描画する(){ this.ctx.beginPath(); this.ctx.font="14px Arial"; this.ctx.fillStyle = "#FFF"; this.ctx.fillText("スコア: "+this.score,10,this.clientHeight-14); this.ctx.closePath(); }, クラッシュブレイクを描画する() { this.ctx.clearRect(0, 0, this.clientWidth, this.clientHeight); this.crushBalls.forEach(item => { this.ctx.beginPath(); this.ctx.arc(item.x, item.y, item.r, 0, 2 * Math.PI); this.ctx.fillStyle = item.color; this.ctx.fill(); this.ctx.closePath(); アイテム.x += アイテム.dx; アイテム.y += アイテム.dy; もし ( // 左右の壁を叩く item.x - item.r < 0 || item.x + item.r > this.clientWidth ){ アイテム.dx = -アイテム.dx; } もし ( // 上部と下部の壁にぶつかる item.y - item.r < 0 || item.y + item.r > this.clientHeight ){ アイテム.dy = -アイテム.dy; } }); }, ランダムカラーを取得する() { randomColor[Math.floor(Math.random() * randomColor.length)] を返します。 }, ランダムに取得(最小値, 最大値) { Math.random() * (max - min) + min を返します。 }, getCurshBreaks() { _this = this とします。 this.breaks.forEach(item => { if (item.show) { アイテムを表示 = false; (i = 0; i < 8; i++ とします) { this.crushBalls.push({ x: item.x + this.breaksConfig.width / 2, y: item.y + this.breaksConfig.height / 2, dx: _this.getRandomArbitrary(-6, 6)、 dy: _this.getRandomArbitrary(-6, 6)、 r: _this.getRandomArbitrary(1, 4)、 色: _this.getRandomColor() }); } } }); }, ドローボール() { this.ctx.beginPath(); this.ctx.arc(this.ball.x、this.ball.y、this.ball.r、0、2 * Math.PI); this.ctx.fillStyle = "#008b8b"; this.ctx.fill(); this.ctx.closePath(); }, パネルを描画する() { this.drawRoundRect() は、 this.pannel.x、 this.pannel.y、 this.pannel.width、 this.pannel.height、 5 ); }, drawRoundRect(x, y, 幅, 高さ, 半径) { this.ctx.beginPath(); this.ctx.arc(x + 半径, y + 半径, 半径, Math.PI, (Math.PI * 3) / 2); this.ctx.lineTo(幅 - 半径 + x, y); this.ctx.arc( 幅 - 半径 + x、 半径 + y、 半径、 (数学.円周率 * 3) / 2、 数学.PI * 2 ); this.ctx.lineTo(幅 + x、高さ + y - 半径); this.ctx.arc( 幅 - 半径 + x、 高さ - 半径 + y、 半径、 0, (数学.円周率 * 1) / 2 ); this.ctx.lineTo(半径 + x、高さ + y); this.ctx.arc( 半径 + x、 高さ - 半径 + y、 半径、 (数学.円周率 * 1) / 2、 数学.PI ); this.ctx.fillStyle = "#008b8b"; this.ctx.fill(); this.ctx.closePath(); }, 描画区切り() { _this = this とします。 _this.breaks.forEach(item => { if (item.show) { _this.drawRoundRect() 関数は、 アイテム.x、 アイテム.y、 _this.breaksConfig.width、 _this.breaksConfig.height、 _this.breaksConfig.半径 ); } }); } } }; </スクリプト> <!-- CSS をこのコンポーネントのみに制限するために "scoped" 属性を追加します --> <スタイル スコープ lang="scss"> .break-ball { 幅: 900ピクセル; 高さ: 600px; 位置: 相対的; #ブレイクボール { 背景: #2a4546; } 。容器 { 位置: 絶対; 上: 0; 右: 0; 下部: 0; 左: 0; 背景色: rgba(0, 0, 0, 0.3); テキスト配置: 中央; フォントサイズ: 0; 空白: ラップなし; オーバーフロー:自動; } .コンテナ:後{ コンテンツ: ""; 表示: インラインブロック; 高さ: 100%; 垂直位置合わせ: 中央; } .ダイアログ{ 幅: 400ピクセル; 高さ: 300px; 背景: rgba(255, 255, 255, 0.5); ボックスの影: 3px 3px 6px 3px rgba(0, 0, 0, 0.3); 表示: インラインブロック; 垂直位置合わせ: 中央; テキスト配置: 左; フォントサイズ: 28px; 色: #fff; フォントの太さ: 600; 境界線の半径: 10px; 空白: 通常; テキスト配置: 中央; .もう一度-btn { 背景: #1f9a9a; 境界線: なし; 色: #fff; } } } </スタイル> これで、デスクトップ ピンボールとブロック崩しゲームを実現するための VUE+Canvas に関するこの記事は終了です。VUE ピンボールとブロック崩しゲームに関するその他の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM を応援していただければ幸いです。 以下もご興味があるかもしれません:
|
<<: FTP環境設定ソリューション(vsftpd)の詳細な説明
>>: リモート接続を許可するようにMySQLを変更する方法
プロジェクトシナリオ: Vueプロジェクトを実行したらインデントエラーが出ました。ideaコンパイラ...
今日、MySQL の無料インストール版をデプロイしたところ、テーブル 'mysql.plug...
1. mysqldump バックアップ方法では論理バックアップが使用されます。最大の欠点は、バック...
エラーは次のとおりです:キャッチされない TypeError: 未定義のプロパティ 'mod...
1. ダウンロードApacheの公式サイトhttp://httpd.apache.org/にアクセス...
例えば: <link rel="スタイルシート" href="h...
目次1. はじめに2. 行き詰まった問題の分析3. 解決策(理論) 4. ソリューション(コード) ...
この記事の例では、ページング機能を実装するためのVueの具体的なコードを参考までに共有しています。具...
目次問題の説明MySQLオンラインDDLで列を追加する従来の方法01 コピー方法02 インプレースメ...
この記事では、例を使用して MySQL ビューの管理ビュー操作について説明します。ご参考までに、詳細...
1. レンダリング2. 操作手順1. テンセントマップキーを申請する - 住所2. ミニプログラムの...
例: VMware IOInsight は、VM のストレージ I/O 動作を理解するのに役立つツー...
以前のブログのログインページを作成していたときに、この問題に遭遇しました。突然、透明な背景画像と不透...
目次1. 開発前の準備2. 新しいプロジェクトIdea は Java 開発のための強力なツールであり...
mysql のデフォルトのストレージ ディレクトリは/var/lib/mysql/です。以下は、デフ...