アイソメトリックタイルを描画するための最良のテクニック


8

HTML5 Canvasを使用してシンプルなアイソメトリックゲームを作成することを考えており、タイルをレンダリングする最速の方法は何だろうと思います。

タイルはひし形ですが、drawImageは四角形を描画するので、角(以下の黒い部分)を除外する必要があります。

サンプルダイヤモンドタイル

私には3つの選択肢があると思います:

  1. アルファチャネル(.png)を持つ画像オブジェクトを使用します。パフォーマンスが低下する可能性があります。
  2. クリッピングパスを使用します。レンダラーが最適化されている場合、これはかなり高速になる可能性があります。
  3. このような正方形のタイルをプリレンダリングします:

事前レンダリングされた正方形のタイル

私は黒い四角をメモリ内の1つの実際のタイルとして使用し、そのようなタイルを緑のフィールドとその隣、または上下にあるすべてのフィールドに描画します。対角線(青)のフィールドは、長方形のタイルの角で構成されます。これはクリッピングやアルファチャネルを回避しますが、タイルの可能なすべての組み合わせを事前レンダリングする必要があり、やり過ぎのようです。

アイソメトリックタイルを行うための最良または最速の方法は何ですか?FarmVilleなどの他のゲームは何を使用しますか?


1
「ベストテクニック」は一種のロードであり、ゲームの要件、使用されるテクノロジー、およびゲームを作成する開発者に依存すると思います。
MichaelHouse

1
実際にどんな問題がありますか?質問文の中にあなた自身の質問に対する3つの回答を提供したようですか?
Trevor Powell

1
これには十分な票がありません:BEST BEST BESSSSSSST!あれ?
ジミー

1
ここにあるアドバイスに従ってください:meta.gamedev.stackexchange.com/a/638/7191
MichaelHouse

1
これが実際にパフォーマンスの問題であることを知らないのですか?あなたはそれが書かれる前にコードを最適化しようとしているだけですか?その場合は、-1と投票して終了します。
Trevor Powell

回答:


11

透明度(アルファチャネル)を使用する方法をお勧めします。

これは、次のようなタイルに垂直オブジェクトが必要な場合を意味します。

木のタイル

次に、レンダラーがタイルを後ろから前へ、つまりペインターのアルゴリズムで描画すれば、簡単にそれを行うことができます。

画像クレジット:ライナーのタイルセット。


9

swsとMarkRで説明されている方法も私が好む方法ですが、別の方法を紹介したいと思います。

最小限の労力で等角投影の外観を作成するためのハッキーなオプションは、実際に直交タイルを使用し、context.transformを使用して、マップを等角投影に見えるようにする投影行列を設定することです(または、context.rotateとcontext.scaleを組み合わせた場合)。射影行列がどのように機能するかを知る)。

詳細については、キャンバス変換メソッドの仕様を参照しください。

タイル画像:

ベースタイル画像

描画コード:

    for (var x = 0; x < 5; x++) {
        for (var y = 0; y < 5; y++) {
            ctx.drawImage(img, x * img.width, y * img.height);
        }
    }

マトリックス適用前の結果:

変換行列が適用されていない

この変換行列を適用した後の同じタイルイメージの同じコード:

    ctx.transform(  1,   0.5,
                   -1,   0.5,
                  160,   0    );

変換行列あり

破線のグリッドをタイル画像から削除し、描画コードのタイルオフセットをimg.width - 1およびimg.height - 1変換によって生じるギャップを取り除くために。突然タイルが醜く半分に見えます:

マトリックスといくつかの微調整

この方法の主な欠点は、グラフィックエディターでタイルをデザインするときに、実際に見たとおりのタイルにならないことです。また、床ではなく直立しているオブジェクトを描画する場合にも問題が発生します。これらについては、描画する前に変換行列をオフにすることができますが、その場合は自分で位置を計算する必要があります。そのためにこれらの式を使用できます。

var xScreen = xWorld * 1   + yWorld * -1  + 160;
var yScreen = xWorld * 0.5 + yWorld * 0.5 + 0;  

(変換行列の数値がこれらの数式でどのように再現されるかに注意してください-ここで行列の乗算を自分で行っています)。

では、なぜこれを行う必要があるのでしょうか。

この方法は、次の場合に適しています。

  1. アイソメトリックタイルの設計は経験がありませんが、直交するタイルがあります
  2. 直交グラフィックエンジンよりもやや難しいアイソメトリックグラフィックエンジンの開発に多くの時間を費やしたくない。

もう1つの興味深い機能は、マトリックス計算の方法がわかっている場合、フレーム間の投影マトリックスを変更して、マップをリアルタイムでズーム、チルト、回転して、素敵な偽の3Dエフェクトを作成できることです(等角タイルを使用してみてください)。 。

しかし、芸術的にも技術的にもアイソメトリックタイルを処理する方法を知っていて、偽の視点のトリックを必要としない場合は、透明度のあるダイヤモンド型のタイルを使用することをお勧めします。


これを行うことは確かに可能です。このようにして、「フラット」または「トップダウン」のタイルを作成できます。ただし、タイル内の「3D」要素は簡単に作成できないことを意味します。私はこの変換を事前に行うことを好みます。
MarkR 2013年

専門家の説明だけでなくても+1!よくやった。
Luceos 2013年

@MarkR:コメントを書き込んだ後、回答を大幅に拡大しましたが、それでもまだ当てはまる場合は、調べてみてください。
フィリップ2013年

1
詳細な書き込みをありがとう!これは非常に興味深いテクニックで、以前は考えていませんでした。ただし、私は最初から(そして等尺性タイルと正方形タイルの描画が同じように苦手です;-))、本当の等尺性タイルを使用します。また、これはあまり高速ではないようです。私は小さなベンチマークを行い、整数ピクセル位置への変換行列なしでタイルをブリットすることは、私のPCでの変換よりも約3倍高速です。それでも、これをいくつかの特殊効果に使用する場合があります。
jdm 2013年

このマップにzレベルを追加する方法はありますか?
Tarion 2014年

3
  1. context.drawImageを使用して、あるソース(Image、またはオフスクリーンキャンバス)から別のソース(オフスクリーンまたはオンスクリーンキャンバス)にピクセルデータをコピーします。キャンバスはハードウェアアクセラレーションされているため、テストを行うと、アルファピクセルと不透明ピクセルのレンダリング速度に識別可能な違いがあるかどうかを判断できます。

  2. クリッピングでは、タイルごとに1回クリッピングパスを定義するときにコンテキストの状態をプッシュ/ポップする必要があります。これは、アイソメトリックのオーバードローを考慮すると、コストのかかる操作になる可能性があります。

  3. あなたが述べるように、事前レンダリングされたタイルは、膨大な数の「コネクタ」タイルを描画する必要がありますが、これは実現可能かもしれませんし、可能でないかもしれません。(私は「そうでないかもしれない」にもっと傾いています。)

4番目のソリューションは、事前にレンダリングされたタイルの「チャンク」(PRC)を採用し、一度オフスクリーンキャンバスに生成してから、フレームごとに1回PRCで画面を覆います。それでもオーバードローはありますが、PRCを一度構築し、PRCに対するプレーヤーキャラクター(またはビューカメラの)の位置によって決定されるオフセットでレンダリングすることは、比較的単純な操作でなければなりません。これにより、レンダリングをオプション#1と組み合わせることができます。これは、パフォーマンスが考慮されなかった場合にIMOが最良のオプションです(実装が最も簡単であるため)。


3

小さなアルファチャネルはそれほど害にはなりませんが、2つの4分の1タイルの使用を検討したくない場合は、これにより、多数の異なる画像を実行せずにタイルを適切に遷移させる余地が与えられます。これがおそらく最大の利点です。

長方形のサブタイルパターン

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.