ホームページ > 記事 > ウェブフロントエンド > Javascriptの高模倣レジェンド・オブ・ブラッドゲームのコード共有
ゲームの最初のバージョンは 2014 年に開発されました。ブラウザは html+css+js を使用し、サーバーは asp+php を使用し、通信は ajax を使用し、データ ストレージは access+mySql を使用します。ただし、いくつかの問題がありました (当時はノードの使い方を知らなかったので、ASP で複雑なロジックを記述するのは非常に困難でした。当時は Canvas 上での記述がほとんどなく、DOM レンダリングは簡単にパフォーマンスのボトルネックに達する可能性がありました)。 、放棄されました。その後、キャンバスを使用したバージョンがリメイクされました。
この記事では主に JavaScript の模倣性の高い Legend of Legend ゲームの実装コードを紹介します。必要な方は参考にしていただければ幸いです。
1. 開発前の準備
比較的複雑なPCゲームを実装するのになぜJavaScriptを使うのか
1.js PCオンラインゲームを実装することが可能です。 PC や携帯電話のハードウェア構成のアップグレード、ブラウザの更新、さまざまな H5 ライブラリの開発により、js でオンライン ゲームを実装することはますます困難になってきています。ここでの難しさは主に 2 つの側面にあります。1 つはブラウザのパフォーマンス、もう 1 つは非常に複雑なロジックを含むゲームの反復を満足させるほど簡単に JS コードを拡張できるかどうかです。 2. 現在のjsゲームの中には大規模なものはほとんどありませんので、参考にしてください。マルチプレイヤー接続、サーバー側のデータ ストレージ、複雑なインタラクションを伴うほとんど (ほぼすべて) のゲームは、Flash を使用して開発されています。しかし、flash は結局衰退しつつありますが、js は急速に発展しており、ブラウザさえあれば実行できます。なぜ私が 2001 年のレジェンド オブ ブラッド ゲームを選んだのか
最初の理由は、もちろん古いゲームに対する私の感情ですが、もう 1 つのより重要な理由は、他のゲームのプレイ方法を知らないか、または遊び方は知っていますが、素材(写真、効果音など)がありません。ゲームのマップ、キャラクターやモンスターのモデル、アイテムや装備図を収集し、それらを処理して解析してから js 開発に使用するのに多大な労力を費やすのは時間の無駄だと思います。 私は以前に Legend ゲームのマテリアルをいくつか収集しており、幸運にも Legend of Legend クライアント リソース ファイル (github アドレス) を抽出する方法を見つけたので、コードを直接書き始めることができ、準備時間をいくらか節約できます。考えられる問題点
1. ブラウザの動作パフォーマンス: これが最も難しい点です。ゲームが 40 フレームを維持したい場合、js が計算するために各フレームに残る時間は 25 ミリ秒のみです。また、レンダリングは通常、計算よりも多くのパフォーマンスを消費するため、js に残される実際の時間はわずか約 10 ミリ秒です。 2. 不正行為防止: ユーザーがインターフェイスを直接呼び出したり、ネットワーク リクエスト データを改ざんしたりすることを防ぐにはどうすればよいですか?目標は、js を使用してより複雑なゲームを実装することであり、オンライン ゲームではこれを考慮する必要があるため、比較的成熟したソリューションが必要です。これはこの記事の焦点ではありません。 2. 全体的なデザインブラウザ側
画面レンダリングはキャンバスを使用します。 dom(p)+css と比較して、canvas はより複雑なシーンのレンダリングとイベント管理を処理できます。たとえば、次のシーンには、プレーヤー、動物、地面のアイテム、および一番下のマップの画像の 4 つの画像が含まれています。 (実際には、地面の影、文字、動物、物体をマウスでポイントしたときに表示される対応する名前、地面の影もあります。読みやすさのため、内容についてはあまり考慮しません。 )この時点で、「動物をクリックすると動物を攻撃し、アイテムをクリックするとアイテムを拾う」という効果を実現するには、動物とアイテムのイベントを監視する必要があります。 dom メソッドを使用する場合、対処が難しいいくつかの問題が発生します: a. レンダリングの順序とイベント処理の順序が異なります (場合によっては、小さな Z インデックスが最初にイベントを処理する必要がある)。追加の処理。例えば上記の例では、モンスターやアイテムをクリックする際にキャラクターをクリックしやすいため、キャラクターに対して「クリックイベント貫通」処理を行う必要があります。さらに、イベント処理の順序は固定されていません。キャラクターの解放を必要とするスキル (ゲーム内の治療など) を持っている場合、この時点でキャラクターはイベント監視を行う必要があります。したがって、要素がイベントを処理する必要があるかどうか、およびイベントが処理される順序はゲームの状態によって異なり、DOM のイベント バインディングではニーズを満たすことができなくなります。 b. プレーヤーのモデル、プレーヤーの名前、プレーヤーのスキル効果などの関連する要素を同じ DOM ノードに配置するのは困難です。これらは e388a4556c0f65e1904146cc1a846bee または 2f8332c8dcfd5c7dec030a070bf652c3 に配置する必要があります。コンテナ。管理が簡単です (この方法では、位置を個別に処理することなく、複数の要素の位置を親要素から継承できます)。しかし、この方法では、z-index を扱うのが難しくなります。たとえば、プレーヤー A がプレーヤー B の上にある場合、A は B によって隠されます。 したがって、A の z-index を小さくする必要がありますが、プレーヤー A の名前が B の名前や影によって隠されてはなりませんが、これは達成できません。 。簡単に言うと、DOM 構造の保守性により画面表示の効果が犠牲になり、その逆も同様です。 c. パフォーマンスの問題。たとえ効果が犠牲になったとしても、レンダリングに DOM を使用すると、必然的に多くのネストされた関係が生じ、すべての要素のスタイルが頻繁に変更され、ブラウザーの再描画やリフローが継続的にトリガーされます。
キャンバスのレンダリングロジックはプロジェクトロジックから分離されています
キャンバスのさまざまなレンダリング操作(drawImage、fillTextなど)をプロジェクトコードにまとめてしまうと、後々プロジェクトをメンテナンスできなくなることは避けられません。いくつかの既存のキャンバス ライブラリを検討し、Vue のデータ バインディングとデバッグ ツールを組み合わせた後、新しいキャンバス ライブラリ Easycanvas (github アドレス) を作成しました。これは、Vue と同様に、プラグインを介してキャンバス内の要素のデバッグをサポートします。
この方法では、ゲーム全体のレンダリング部分がはるかに簡単になります。必要なのは、ゲームの現在の状態を管理し、サーバーによってソケットから返されたデータに基づいてデータを更新することだけです。 Easycanvas は、「データの変更によりビューが変更される」というリンクを担当します。たとえば、下の図のアイテムをラッピングするプレーヤーの実装では、ラッピング コンテナの位置とバックパック内の各要素の配置規則を指定するだけで、ラップされた各アイテムを配列にバインドします。この配列を管理します。はい (Easycanvas は、データを画面にマッピングするプロセスを担当します)。
たとえば、5 行 8 列の合計 40 個のアイテムのスタイルは、次の形式で Easycanvas に渡すことができます (index はアイテムのインデックス、アイテムの x 方向の間隔は 36、 y 方向の間隔は 32)。そして、このロジックは、項目の配列がどのように変更されたり、パッケージがどこにドラッグされたりしても、各項目の相対位置は固定されます。 Canvas 上でのレンダリングに関しては、プロジェクト自体を考慮する必要がないため、メンテナンス性が向上します。
style: { tw: 30, th: 30, tx: function () { return 40 + index % 8 * 36; }, ty: function () { return 31 + Math.floor(index / 8) * 32; } }
キャンバスレイヤードレンダリング
前提: ゲームは40フレームを維持する必要があり、ブラウザは幅800、高さ600、面積480,000(以下、画面領域として480,000と呼びます) 。
同じキャンバスをレンダリングに使用する場合、このキャンバスのフレーム数は 40 で、1 秒あたり少なくとも 40 の画面領域を描画する必要があります。ただし、複数の要素が同じ座標点で重なっている可能性があります。たとえば、下部の UI、ヘルス バー、ボタンが重なっており、それらが結合してシーン マップをブロックしています。したがって、これらを合計すると、ブラウザの 1 秒あたりの描画量は、ゆうに 100 画面領域を超える可能性があります。
この描画は、キャンバス全体のあらゆる場所でビューが更新されるため、最適化するのが困難です。それは、プレイヤーや動物の動きである可能性があり、ボタンの特殊効果である可能性があり、特定のスキル効果の変化である可能性があります。 。この場合、プレイヤーが動かなくても、服が「風になびく」効果(実際にはスプライトアニメーションが次の絵に再生されます)やポーションの瓶が現れるなどの影響でキャンバス全体が再描画されます。地面。ゲームのあるフレームが前のフレームと区別できないことはほとんど不可能であるため、ゲーム画面の一部であっても変化しないことは困難です。ゲーム画面全体が常に更新されます。
ゲームの特定のフレームが前のフレームと区別できないことはほとんど不可能であり、画面は常に更新されるからです。
ということで、今回は3枚のキャンバスを重ねる配置を採用しました。 Easycanvasのイベント処理は配信に対応しているため、先頭のキャンバスがクリックされても、クリックを終了する要素がなければ後続のキャンバスもイベントを受け取ることができます。 3 つのキャンバスは、UI、地面 (マップ)、およびエルフ (キャラクター、動物、スキルの特殊効果など) を担当します。
この階層化の利点は、1 つあたりの最大フレーム数必要に応じてレイヤーを調整できます:
たとえば、UI レイヤーでは、多くの UI は通常動かず、たとえ動いたとしても、あまり厳密な描画を必要としないため、フレーム数を適切に減らすことができます。 20まで。このようにして、プレイヤーの体力が 100 から 20 に減少した場合、ビューは 50 ミリ秒以内に更新され、50 ミリ秒の切り替えはプレイヤーには感じられません。体力などのUIレイヤーのデータの変化は短時間に複数回連続して変化することが難しく、また50msの遅延は人間が認識しにくいため、頻繁な描画が必要ないためです。 1 秒あたり 20 フレームを保存すると、おそらく 10 画面領域の描画を保存できるでしょう。
再び地面と同様に、マップはプレイヤーが移動したときにのみ変化します。このようにして、プレーヤーが動いていない場合、フレームごとに 1 つの画面領域を保存できます。プレーヤーが移動する際の滑らかさを確保する必要があるため、地上での最大フレーム レートは低すぎてはなりません。地上フレームが 30 フレームの場合、プレイヤーが静止している場合、1 秒あたり 30 の画面領域を保存できます (このプロジェクトでは、マップはほぼ画面いっぱいに描画されます)。また、他のプレイヤーや動物の動きによって地面が変化することはなく、地面レイヤーを再描画する必要もありません。
スプライトレイヤーの最大フレームレートを下げることはできません。このレイヤーはキャラクターの動きなどゲームの核となる部分を表示するため、最大フレームレートを40に設定します。
このように、1秒あたりに描画される面積は、プレイヤーがエリアを移動するときは 80 ~ 100 画面になる可能性がありますが、移動していないときは 50 画面エリアしかない場合があります。ゲームでは、プレイヤーは立ち止まったままモンスターと戦い、入力し、アイテムを整理し、スキルを解放するため、長時間にわたって地面の描画がトリガーされず、パフォーマンスが大幅に節約されます。
サーバー側
目標はマルチプレイヤーオンラインゲームをjsで実装することなので、サーバーはノードとソケットを使用してブラウザと通信します。このもう 1 つの利点は、マップ上の特定の座標点に障害物があるかどうかを判断するなど、一部の共通ロジックを両端で再利用できることです。
ノード上のプレーヤーやシーンなどのゲーム関連データはすべてメモリに保存され、定期的にファイルと同期されます。 Node サービスが開始されるたびに、データがファイルからメモリに読み込まれます。このように、プレーヤーの数が増えると、ファイルの読み取りと書き込みの頻度が急激に増加し、パフォーマンスの問題が発生します。 (その後、安定性を向上させるために、読み取りおよび書き込みプロセス中のサーバーの再起動によって引き起こされるファイルの損傷を避けるために、「メモリ ファイル バックアップ」方式を使用して、ファイルの読み取りおよび書き込み用のバッファが追加されました)。
Node側はインターフェース、データ、インスタンスなど複数のレイヤーに分かれています。 「インターフェイス」はブラウザとの対話を担当します。 「データ」とは、薬の名前や効果、モンスターの速さや体力などの静的なデータであり、ゲームルールの一部です。 「インスタンス」とは、例えばあるプレイヤーの薬が「薬データ」のインスタンスです。別の例として、「鹿インスタンス」は属性「現在の血液量」を持ち、鹿 A は 10、鹿 B は 14 で、「鹿」自体は「初期血液量」のみを持ちます。
3. シーン マップの実装
マップ シーン
まずは、レンダリングに Easycanvas に依存するマップ シーンの部分から始めましょう。
思考
プレイヤーは常に画面中央に固定されているため、プレイヤーの動きは実際にはマップの動きとなります。たとえば、プレイヤーが左に走ると、マップは右に移動します。先ほども述べたように、プレイヤーは 3 つのキャンバスの中間層にいて、マップは最下層に属しているため、プレイヤーはマップをブロックする必要があります。
これは合理的なように思えますが、マップ内に木がある場合、「プレイヤーのレベルは常に木よりも高い」は間違いです。現時点では、
地図の階層化と、「地上」と「地上」の分離という 2 つの大きな解決策があります。プレイヤーを2つのレイヤーの間に配置し、例えば下の図では左側が地面、右側が地面で、真ん中のキャラクターを挟むように重ねて描画します。
問題を解決するには、実際には 2 つの新しい問題が発生します。 1 つ目は、プレイヤーが「地上」のもの (木など) によってブロックされる場合があり、場合によっては「地上」のものをブロックできる必要があることです。 」(木の下に立っていると、頭が木を覆うなど)。もう 1 つの問題は、レンダリングのパフォーマンス コストが増加することです。プレイヤーは常に変更されるため、「地面」レイヤーは頻繁に再描画する必要があります。これにより、大きな地上地図のレンダリングをできるだけ節約するため、元のデザインが崩れ、キャンバスの階層化がより複雑になります。
地図は階層化されておらず、「地上」と「地上」が一緒に描かれています。プレーヤーが木の後ろにいるときは、次の図のようにプレーヤーの透明度を 0.5 に設定します。
これには 1 つだけ欠点があります。プレーヤーの体は不透明か半透明です (マップ上を歩いているモンスターは透明です)。もこの影響を及ぼします)、それは完全に真実ではありません。理想的な効果は、プレイヤーの体の一部が隠れるシーンがあることだからです。ただし、これはパフォーマンスに優れており、コードの保守も簡単です。現在、このソリューションを使用しています。
それでは、「地図」画像のどの部分が木であるかをどのように判断するのでしょうか?通常、ゲームには大きなマップ記述ファイル (実際には配列) があり、0、1、2 などの数字を使用して、通過できる場所、障害物がある場所、乗り換えポイントなどを識別します。熱血伝説の「説明ファイル」は48×32を最小単位として記述されているため、熱血伝説におけるプレイヤーのアクションは「チェス盤」のような感覚になります。ユニットが小さいほどスムーズになりますが、ユニットが占める体積は大きくなり、この記述を生成するプロセスに時間がかかります。
本題に取り掛かりましょう。
実装
私は友人に、Legend of Legend クライアントで「ビーチ州」のマップをエクスポートするのを手伝ってもらいました。幅 33,600、高さ 22,400 で、私のコンピューターの数百倍のサイズです。コンピューターの爆発を防ぐには、コンピューターを複数のチャンクに分割して読み込む必要があります。凡例の最小単位は 48x32 であるため、マップを 480x320 の 4900 (70x70) の画像ファイルに分割します。
キャンバスのサイズを 800x600 に設定したので、プレイヤーは合計 3x3 の画像をロードするだけで済み、キャンバス全体をカバーできます。 800/480=1.67 なので、2x2 ではないでしょうか?なぜなら、プレイヤーの現在位置によっては、たまたま一部の画像の一部しか表示されない可能性があるからです。以下に示すように:
関連する推奨事項:
2 時間で HTML5 パズル ゲーム コードのグラフィック紹介を完了
JavaScript もぐらたたきゲーム コードの説明_ゲーム エンターテイメント
以上がJavascriptの高模倣レジェンド・オブ・ブラッドゲームのコード共有の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。