攻撃システム:ヘルスとダメージ第X章ゲームの勝ち負け第11章。敵AIリポジトリのdrawing-grid-2ブランチに自由に切り替えてください。これには、以前の投稿の作業結果が含まれており、この投稿の出発点として最適です。優れたコード移行ノードの構造Vector2Dのご紹介グリッド、ノード、描画のすべてが一緒になりました。ノードエンティティのテストグリッドエンティティのテスト結論freepikによって作成された矢印ベクトルシステム内で明確な一連の責任を定義しました。Gridへの参照を保持しNodes、これらのノードは独自の特定のロジックを持っている場合があります。描画機能はそれらの中にカプセル化されていNodeDrawComponentます。現在のコードはアーキテクチャ設計に違反しています。この時点で、Gameすべてのノードをレンダリングし、ある意味で、システム内の「神オブジェクト」になります。そのループをに移動する必要がありますGrid。私たちの計画によれば、Gridノードも描画せず、単にインスタンス化します。目覚めたInitNodesときにノードエンティティを構築するプライベートメソッドを定義しましたGrid。描画コードはNodeDrawComponent:に行きますTSコンパイラは、変数の欠落について私たちに叫びます:xとy。これらの変数はグリッドのループで定義されているため、何らかの方法でここに渡す必要があります。明らかな解決策は、これらの変数をノードのフィールドとして設定し、それらをコンポーネントに渡すことです。私たちはコードに飛び込む前に、しかし、私たちは時間を割いて、これらが何について少し審議しましょうxとy、実際に表しています。これまで、長方形を描画するためだけにそれらを使用してきました。以前の投稿で確立したように、ノードエンティティは画面上の単なる灰色の長方形ではありません。Nodeあるエンティティこれは私たちが達成しようとしているゲームプレイのデモですそれをエンティティにすることで、重要な情報をたくさん保存して計算することができます。このパワーを使用して、たとえば、特定のノードのみを強調表示して、プレイヤーが船を移動できる場所を示すことができます。そのためには、特定のノードがプレーヤーの現在の位置からどれだけ離れているかを知る必要があります。次に、それを実行できるようにするには、それぞれの場所を追跡する必要がありますNode。ノードの構造ポジションはさまざまな方法で考えることができます。キャンバス上の位置でありx、y座標によって決定されます。index、グリッド内のノードによって決定できます。この画像では、黄色の円が付いたノードにインデックス(2,1)があることがわかります。これは、行「2」と列「1」にあることを意味します(カウントはゼロから始まります)。座標はもう少し複雑な話です。どれだけ正確に数えたいのか、さまざまな方法があります。座標はNode左上隅であると仮定できます。キャンバスの左上隅から位置を数えるので、合理的に聞こえます。次のように言うこともできます。座標Nodeはその中心の位置です。このゲームではNode、境界の観点からの位置を考えるのが最も便利だと思います。境界を2つの点として定義しますStart。左上隅の座標でNodeありEnd、は右下隅の座標です。スクリーンショットでは、それぞれ赤と青のドットでマークされています。これらの点の間の領域はに属しNodeます。これにより、特定のノードの境界を決定できます。各点は2つの数字のセットです:xそしてyそれは関連する軸に対応します:しかし、境界NodeDrawComponentはNodeそれ自体ではなく、の特徴であると考えられていませんか?それは描画コンポーネントの責任ではありませんか?うーん、ダメ。境界は、必ずしもの視覚的表現とは関係ありませんNode。実際と目に見える境界が異なる場合があります。グリッド内の任意のノードが占める実際の領域は、そのコア特性NodeDrawComponentですが、任意の方法で自由に表すことができます。正方形ではなく円でノードを描画するとします。実際の領域はNodeこの灰色の正方形になりますが、表示されるのは黄色の円だけです。ご覧のとおり、それらは異なります。将来の章で見るように、他の多くのノードのコンポーネントは、描画コンポーネントだけでなく、実際の境界に依存します。コードに戻りましょう。numberここで構造を数回繰り返していることに気付くかもしれません。私たちはそれを私たちのゲームで何度も使用するつもりです。専用のクラスまたは少なくともそのためのインターフェースを定義する必要があります。IPointまたはのようなものである可能性がありますICoordinate:今のところはそれで十分ですが、よくあることですが、それだけではありません。このような狭いデータ構造をのように定義する代わりにPoint、の例に従い、Unity3d別のデータ構造を再利用しますVector。小規模から始めて基盤を定義し、必要に応じて徐々に機能を追加していきます。私たちは、美しく強力なものをすべて実装するつもりはありませんVectorが、現在必要なものだけに焦点を当てます。このアプローチは、ソフトウェアの設計において最も強力なものの1つだと思います。必要でない限り、機能を定義しません(「あなたはそれを必要としない」という原則を利用します)。同時に、私はコードの潜在的なより広い使用法を考え、将来の拡張のためにそれをオープンにしようとしています。数学では、ベクトルは大きさと方向の両方を持つオブジェクトです。ここでは、この構造を使用してポイントまたは座標に関する情報を保持し、他のすべては無視します。Vector2D、ECSまたはライフサイクルメソッドとして、ユーティリティです。ゲームに依存しません。それを念頭に置いて、utilsモジュールの下に配置しましょう:そして、バレルを忘れないでください:以上です!これは文字通りVector私たちが今私たちのために行う必要があるすべてです。将来的に機能を追加するために、オプションを開いたままにします。すべて一緒に:グリッド、ノード、描画pikisuperstarによって作成されたビジネスベクトルあるVector2D場所で、私たちは今の中にそれを使用することができますNode:驚くばかり!しかし今Grid、Node署名が間違っているという不満があります。それは、理にかなってNode需要Start、EndおよびIndexインスタンス化のために。DirtyDrawの開始位置と終了位置を特定するために行ったのと同じ計算を使用しNodeます。そのインデックスはループ変数を使用して定義できます。NodeDrawComponentそれでも変数を見逃しています。今、私たちは医者が注文したものとまったく同じStartフィールドを持っていますNode:すごい!また、Nodeそのサイズについての唯一の真実の源を作りましょう。コンポーネントの設定に直接アクセスする代わりに、エンティティに次の情報を提供するように依頼します。公開計算値を定義しましたNode。境界を分析しSizeてNode、のを計算するだけです。このプロパティを配置すると、サイズとオフセットをNodeDrawComponent:から削除できます。すごい!現在Node、その境界とサイズに関するデータのみを提供しています。これらすべてが整ったので、ついに(ついに!)DirtyDraw:を取り除くことができます:ただし、コードはまだコンパイルされません。Node単体テストでインスタンス化する方法を更新する必要があります。Size計算という新しい機能がいくつかあります。テストノード署名の更新は非常に簡単です。いくつかの偽のベクトルを定義するだけです。start、エラーなしでコンパイルされます。ブラウザを開くと、グリッドが再びレンダリングされますが、今回は「ダーティ」な描画ではなくなりました。tも合格する必要があります。驚くばかり!私たちの勝利を確固たるものにするために、Gridエンティティもテストでカバーしましょう。グリッドエンティティのテストまだコンポーネントはありませんが、子エンティティがたくさんあります。これは、グリッドがそれらを起動して更新する機能を確認する必要があることを意味します。子供のGameスパイをすることで、私たちが行ったのと同じ方法でそれを行うことができますprototype。この場合、それはNode:のプロトタイプです。ここのすべては見覚えがあるはずです。私は、ノードの上のスパイによって開始Awakeし、Updateそしてそれらがまだ呼び出されていませんでしたことを確認しました。その後、グリッドのを呼び出したAwakeとUpdateし、それぞれのスパイが実際に呼ばれてきた確認。正確に言うと、これらのライフサイクルイベントが呼び出された回数を確認し、ノードの数と比較しました。使用してエラーなしでコンパイルされるはずです。t:この投稿の完全なソースコードdrawing-grid-3は、リポジトリのブランチにあります。涼しい!それはたくさんのコーディングでした!この投稿の冒頭では、すべての描画機能がGameエンティティにハードコーディングされており、「神オブジェクト」のようになっています。コードをリファクタリングし、すべての図面をそれが属する場所に移動しましたNodeDrawComponent。道に沿って、我々は、署名を識別Nodeし、使用してその位置を決定することでありStart、End座標、およびIndex内の位置Grid。Vector2D、私たちが導入した新しい構造は、2次元のプロパティを処理するのに役立ちます。私たちが縛らなければならないいくつかのルーズエンドがまだあります。1つではなくいくつかのキャンバス要素を作成していることに気付くかもしれません。この時点では描画はかなり静的であり、頻繁な変更にはあまり適していません。この章の次のパートでは、これらすべてについて説明します。コメント、提案、質問、またはその他のフィードバックがある場合は、私にプライベートメッセージを送信するか、下にコメントを残してください!読んでいただきありがとうございます。次回お会いしましょう!これは、一連のチュートリアル「TypeScriptを使用したゲームの構築」の第III章です。他の章はここで利用できます:前書き第I章エンティティコンポーネントシステム第II章。ゲームループ(パート1、パート2)第III章。描画グリッド(パート1、パート2、パート3、パート4、パート5)第IV章。船(パート1、パート2、パート3、パート4)第V章相互作用システム第VI章。パスファインディング第7章。