ここ数週間、私は、キャンバス に基づいた JavaScript と HTML5 によるビデオ ゲーム エンジンの作成という、話したら面白いかもしれないと思うプロジェクトに定期的に取り組んでいます。
おそらく、ビデオ ゲームを作成するために HTML5 と JavaScript を選択した理由を疑問に思っているのではないでしょうか?答えは質問ほどクールではありません。私の学校 (Zone01 Normandie) に必要なプロジェクトの競争であり、言語にはこのプロジェクトを実行するために必要なものがすべて揃っているという事実が、私がこれらのテクノロジーを選択した理由です。
しかし、実際には、これらは私がベースとして選択したであろう言語ではなく、この言語が完成した後は、別の言語でこの種の別の冒険に乗り出すことになるでしょう。
それで、私はビデオ ゲーム エンジンの設計に取り掛かりました。このエンジンは、少なくとも 2 つの主要なクラスを含むいくつかのクラスで構成されます。Game クラスはゲーム領域全体を管理し、GameObject クラスはオブジェクトを生成できるようにします。私たちのゲームを相互に対話させます。
これらのクラスに、すべてのオブジェクトのコリジョン ボックスを管理できるようにする CollideBox クラスを追加します。
Game クラスには、ゲームの各フレーム (画像) で実行される GameLoop メソッドと、各ゲーム ループ中に呼び出される Draw メソッドがあります。
GameObjectクラスにはStepメソッドとDrawメソッドがあります。
1 つ目はゲーム ループの各ラウンドを実行し、2 つ目は GameLoop クラスの Draw メソッドが呼び出されるたびに実行します。
これにより、理論的にはエンジン モジュールをプロジェクトにインポートすることでゲームを作成できます。
スプライトを表示するために、HTML5 に組み込まれている canva API を使用することにしました (組み込みとは、デフォルトで付属していることを意味します)
これにより、すべてのスプライトを表示したり、画像を再カットしてアニメーションを作成したりできるため、非常に便利です!
数日後、指定された速度でアニメーションを表示し、CollideBox 経由で衝突を検出できるようになりました。
他にもたくさんの素晴らしいものを以下に紹介します:
GameObject クラス
class GameObject{ constructor(game) { // Initialize the GameObject this.x = 0 this.y = 0 this.sprite_img = {file: undefined, col: 1, row: 1, fw: 1, fh: 1, step: 0, anim_speed: 0, scale: 1} this.loaded = false this.game = game this.kill = false this.collision = new CollideBox() game.gObjects.push(this) }; setSprite(img_path, row=1, col=1, speed=12, scale=1) { var img = new Image(); img.onload = () => { console.log("image loaded") this.sprite_img = {file: img, col: col, row: row, fw: img.width / col, fh: img.height / row, step: 0, anim_speed: speed, scale: scale} this.onSpriteLoaded() }; img.src = img_path } onSpriteLoaded() {} draw(context, frame) { // Draw function of game object if (this.sprite_img.file != undefined) { let column = this.sprite_img.step % this.sprite_img.col; let row = Math.floor(this.sprite_img.step / this.sprite_img.col); // context.clearRect(this.x, this.y, this.sprite_img.fw, this.sprite_img.fh); context.drawImage( this.sprite_img.file, this.sprite_img.fw * column, this.sprite_img.fh * row, this.sprite_img.fw, this.sprite_img.fh, this.x, this.y, this.sprite_img.fw * this.sprite_img.scale, this.sprite_img.fh * this.sprite_img.scale ); if (frame % Math.floor(60 / this.sprite_img.anim_speed) === 0) { // Mise à jour de step seulement à 12 fps if (this.sprite_img.step < this.sprite_img.row * this.sprite_img.col - 1) { this.sprite_img.step += 1; } else { this.sprite_img.step = 0; } } } } distance_to(pos) { return Math.sqrt((pos.x - this.x) ** 2 + (pos.y - this.y) ** 2) } collide_with(box) { if (box instanceof GameObject) { box = box.collision } return ( this.collision.x < box.x + box.w && this.collision.x + this.collision.w > box.x && this.collision.y < box.y + box.h && this.collision.y + this.collision.h > box.y ) } onStep() {}; }
ゲームクラス
class Game { constructor(width = 1400, height = 700) { this.gObjects = []; this.toLoad = []; this.timers = []; this.layers = []; this.canvas = document.getElementsByTagName("canvas")[0] this.canvas.width = width this.canvas.height = height this.context = this.canvas.getContext("2d") this.context.globalCompositeOperation = 'source-over'; this.inputs = {}; this.mouse = {x: 0, y: 0} document.addEventListener('keydown', (e) => { this.inputs[e.key] = true; }, false); document.addEventListener('keyup', (e) => { this.inputs[e.key] = false; }, false); document.addEventListener('mousemove', (e) => { this.mouse.x = e.x; this.mouse.y = e.y; }) document.addEventListener('mouseevent', (e) => { switch (e.button) { } }) } draw(frame) { this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); console.log( this.canvas.width, this.canvas.heigh) for(let i = 0; i < this.gObjects.length; i ++) { this.gObjects[i].draw(this.context, frame) } } gLoop() { let fps = Math.floor(1000 / 60) console.log(fps) let clock = 0 setInterval(() => { clock += 1 for(let i = 0; i < this.gObjects.length; i ++) { if (this.gObjects[i] != undefined) { if (this.gObjects[i].kill) { this.gObjects.splice(i, 1); continue; } this.gObjects[i].onStep(); } } this.draw(Math.floor(clock)) // context.l(); // console.log(clock) if (fps <= clock) { clock = 0 } }, fps) } keyboard_check(key) { return this.inputs[key] == true } }
確かに最適化やその他のエラーはたくさんありますが、すべて機能しています。
"完璧!"教えてもらえますか?
それはあまりにも単純すぎます。
これを完了し、このエンジンでゲームを作成するためのテストを開始した後、同僚との会話中に恐ろしいニュースを知りました。
テクノロジーの選択は、私の Zone01 学校の要件に対応するために行われたことを覚えていると思います…
確かに、選択された言語は良かったのですが、プロジェクトに重大な支障をきたすような指示があるとは知りませんでした…
Canva ライブラリの使用が禁止されました!
念のため言っておきますが、これは画像を表示するために使用するライブラリです。
このテキストを書きながら、Canva を使用せずにこのゲーム エンジンを完全に再設計し始めています。
この開発ブログは終了しました。このストーリーの残りの部分はすぐにご覧いただけます。ご心配なく。
次回の開発ブログでは、必ず新しいフォーマットを試してみます。
このコンテンツがあなたを助け、楽しませ、少なくともいくつかの主題について教育することになれば幸いです。一日の終わりとコーディングがうまくいくことを祈っています。
数か月前にビデオ ゲーム エンジンの作成を開始し、完成しました...かなり前に、Zone01 の数人の同僚の協力を得て、スーパー マリオ ブラザーズにインスピレーションを得たゲームを作成することにも成功しました。 itch.io ページ。
この開発ブログに適用する形式を決定するのに多くの時間がかかり、この開発ブログの執筆期限をわずかに遅らせたか、完全に遅らせたことは認めます。
このテーマに取り組めなかった自分の優柔不断の言い訳を辛抱強く受け入れ、今では発売予定日から 2 か月後、電車が欠航したためさらに 1 時間待たされる中、ルーアンのバス停の休憩所で原稿を書いていることに気づきました。
したがって、アーキテクチャの詳細はすべて無視しましょう。このアーキテクチャは、私の開発ブログの最初の部分から (キャンバスの使用を避けることによる調整を除けば) ほとんど変更されていません。
そこで、実行されたプロジェクト、チームとしての取り組み方、遭遇した問題についてお話します。
これをこのプロジェクトへのフィードバックとして捉えてください。この記事からいくつかの教訓を引き出して、あなたのプロジェクトに役立てていただければ幸いです。
このプロジェクトは、JavaScript でスーパー マリオ ブラザーズを再作成し、少なくともコードに関してはゼロから始めることでした。
仕様はシンプルで、マリオ ゲームにいくつかのレベルがあり、新しいレベルを簡単に作成できる方法が必要でした。
また、オプションを調整するためにスコアボードとメニューを作成する必要がありました。
このプロジェクトの難しさは次のとおりです:
すべての要素がプレーヤーの位置を基準にしてバックグラウンドでスクロールする必要があるため、スクロールします。
また、画面に表示されない要素を最適化すると、パフォーマンスを損なうことなくゲームを実行するために必要なリソースが削減されます。
これらの問題を解決した後、このゲームを私の itch.io ページに公開し、実際にテストしてみることもできます。
この開発ブログはこれで終了します。これで、他のプロジェクトや他のテーマについて書くことができるようになります。
私が話していることに少しでも興味がある場合は、github で私のさまざまなプロジェクト (この開発ブログ内のプロジェクトを含む) をご覧ください。
今日も素敵な一日をお過ごしください!
以上がDevlog - ゲームエンジンを作成しています!の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。