
私を訪問した最初の考えは、「実際、これは何ですか?」
まあ、それは何もないようです:
•テクスチャの配列を作成し、
•カードのサイズを示し、
•配列をループして、オブジェクトを作成します。
それはまさに私が最初からやったことです...
小さな余談
タイルとは何なのかを詳しく説明したくありません。記事ではそのことについて説明していません。 読者は、ゲーム内のアイソメ図とは何か、タイルとは何か、それらはどのようなものか、どのように描画されるかについて既にある程度の考えを持っていると想定されています。 基本的な等尺性タイルは2対1の比率で作成されます。つまり、タイルの幅が2単位の場合、高さは1単位でなければなりません。
私のプロジェクトでは、寸法が1から1の擬似3Dタイルを使用することに注意してください。これらは次のようになります。

ただし、この「キューブ」の半分のみが使用されます(赤で強調表示)。 これまでのところ、クリップされたボトムの使用方法は思いつきませんでしたが、将来的には、マップの山、くぼみ、または平凡な崖に使用される可能性が高いでしょう。 次に、おそらくz-indexを使用する必要がありますが、それは別の話です
PS 記事の最後にプロジェクトソースがあります最初のステップ
これは、コードが私のパスの最初にどのように見えるかです:
property int mapcols: 4
以上です。 最小限の労力で、このような素晴らしいマップを作成できました。
Tile.qmlのコンテンツはペイントしません。将来的にはこのコンポーネントはまったく必要ないからです。 そして、そうすることは全く価値がないからです!
説明してみましょう:寸法4x12(
mapcols * maprows )の地図を描くことにより、48個のオブジェクトが作成されました。 しかし、そのような競技場は明らかに小さすぎます。 たとえば、幅20タイルの大きなフィールドを描画すると、その高さは60タイルになり、これは
1200のビジュアルオブジェクトになります ! 非常に多くのオブジェクトを格納するためにどれだけのメモリが使用されるかを想像することは難しくありません。 一言で言えば-たくさん。
反射
マップを作成するための新しい方法について長く考える必要はありませんでした。 まず、新しい方法で達成すべきマップの主なパラメーターを特定しました。
1.カードは移動可能でなければなりません(プレイヤーはカードを任意の方向にスクロールできます)。
2.ウィンドウの外側にあるオブジェクトは描画しないでください。
3.メソッドはできるだけシンプルに実装する必要があります%)
最初のウィッシュリストは、
Flickable要素を使用して非常に簡単に実装
できます。 どうして? スクロール、イベントのキャッチに
煩わされる必要はありません...一般的に、まったく気にする必要はありません。これは3番目のポイントを完全に満たします:-)要素の名前は
map_area -map_areaになります。
Flickableにマップを移動
する機能を付与するには、ピクセル単位のマップのフルサイズに等しいサイズで、フリックに要素を作成する必要があります。 このためには、通常の
Itemが適しています-この要素は視覚的ではないため、そのサイズは消費されるメモリの量に影響しません。 彼はキーネーム
マップ -マップを負います。
テクスチャを描画するには、追加の要素を使用する必要があります。これは、
マップ要素内に配置する必要があります。 同時に、そのサイズは
map_areaのサイズに対応する必要があり、この要素が常に「見える」ようにするには、マップスクロールの反対側に移動する必要があります。 つまり ユーザーがマップを左に移動した場合、この要素は右に移動して再描画する必要があります。
このアイデアを実装するには、
QQuickImageProviderを
備えた
Imageバンドルが適している可能性がありますが、その機能はかなり不足しているため、ダークサイド
-C ++に頼って独自のコンポーネントを作成する必要があります。 将来のアイテムは
QQuickPaintedItemの子孫となり、
MapProviderという名前に
なります。
シンプルから...シンプルへ
私の見解では、それは次のように見えました。

コードでは、次のようになります。
Window { id: root visible: true width: 600 height: 600
このコードは、今後の作業のスケルトンになります。 次のステップは、
MapProvider要素を作成すること
です 。 これを行うには、プロジェクトに新しいC ++クラスを作成します。
class MapProvider : public QQuickPaintedItem { Q_OBJECT public: MapProvider(QQuickItem *parent = 0); void paint(QPainter *painter) {
この要素をすぐに
QMLに登録します。そのためには、
main.cppを編集します。 その内容は次のようになります。
#include <QGuiApplication> #include <QQmlApplicationEngine> #include "mapprovider.h" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); // : qmlRegisterType<MapProvider>("game.engine", 1, 0, "MapProvider"); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
変更を保存した後、この要素を
QMLで使用できます。
これを行うには、モジュールインポートを
main.qmlに追加します。
import game.engine 1.0
そして、行を置き換えます
Item {
に
MapProvider {
メソッドがどのように機能するかを明確に示すために、フォーム上に2つの追加要素を作成しました。ウィンドウ内に特別な領域
game_areaを指定し、そこに
map_area要素を移動し
ました 。 ゲーム領域のサイズをフォームのサイズよりも意図的に小さくし、この領域の境界を表示するために、通常の
Rectangleを作成しました。
湿式計算-大量の水があるセクション
私たちはほぼ地図を描くことに近づいていますが、注意する価値のあるニュアンスがいくつかあります。 そして、考慮すべき最初の候補は、マップのエッジです。 私たちでは、彼らは「
歯が生える 」ことが判明しました。 これは前のプロジェクトで観察できましたが、新しいプロジェクトではそれを取り除く必要があります。 歯を左と上から隠すには、マップ(
Item:map )を左に移動し、タイルの幅と高さの半分を上に移動します。
Item { id: map width: mapcols * tilesizew height: maprows * tilesizeh x: -tilesizew / 2 y: -tilesizeh / 2

左右の歯を隠すには、
contentWidthおよび
contentHeightパラメーターを変更してスクロールを制限するだけです。 ここでは、カードを既に左に半分のサイズにシフトしているという事実を考慮する必要があります。つまり、コンテンツサイズをタイルサイズ全体で縮小する必要があります。
Flickable { id: maparea contentWidth: map.width - tilesizew contentHeight: map.height - tilesizeh
スクロール中に
MapProvider要素を移動する実装は次のようになります。
MapProvider { id: mapprovider width: gamearea.width + tilesizew * 2 height: gamearea.height + tilesizeh * 2 x: maparea.contentX + (tilesizew/2 - maparea.contentX%tilesizew + map.x) y: maparea.contentY + (tilesizeh/2 - maparea.contentY%tilesizeh + map.y)
不気味な:)ここで何が起こっているのかを説明します。
実際、私たちの地図は、菱形のタイルが刻まれた長方形のブロックで構成されています。 このため、わずかなスクロールでマップの表示領域を再描画する必要はありません。表示領域の外側にある「
保護ゾーン 」(適切な名前が出ていない)を選択するだけで、マップ全体とともに描画され、マップ全体を再描画する必要があるのは、スクロールはこのゾーンのサイズを超えます。 このため、マップの必要な再描画の回数は数百回減少します(タイルのサイズによって異なります)。
このコードでは、この「保護ゾーン」は、
MapProviderの幅と高さに2倍の
タイルサイズを追加して計算されます。 したがって、レンダリングされた領域を右と下にちょうど2タイル拡張します。 このエリアの半分を左上に
広げるには、
map_areaのコンテンツのサイズとマップ
mapのサイズを調整する必要があり
ます 。
Flickable { id: maparea contentWidth: map.width - tilesizew * 1.5 contentHeight: map.height - tilesizeh / 2 Item { id: map width: mapcols * tilesizew + tilesizew height: maprows * tilesizeh / 2
MapProvider要素の
Xおよび
Yを計算する式は、スクロールが「保護ゾーン」を超えた場合にのみ、
ジャンプのような動きを提供します。 将来的には、これらのジャンプにマップ再描画イベントが添付されます。
体に近い
したがって、QML側の計算は終了しました。
次に 、
MapProvider要素の「
body 」を正しくレンダリングするために必要な追加パラメーターのセットを決定する必要があります。
1.
map_areaのコンテンツの実際の位置-マップが
描画される列と行の数を計算するために必要になります(描画は左上から始まるため、左上のタイルのインデックスが見つかります)。 これらのパラメーターに
cxおよび
cyという名前を付けました。
2.タイルのサイズ-画像の描画に必要です。
3.マップサイズ-実際のタイルインデックスの計算に必要です。
4.実際には、テクスチャマップの説明。 リソースの名前を持つこの通常の1次元配列があります。
MapProvider { id: mapprovider width: gamearea.width + tilesizew*2 height: gamearea.height + tilesizeh*2 x: maparea.contentX + (tilesizew/2 - maparea.contentX%tilesizew + map.x) y: maparea.contentY + (tilesizeh/2 - maparea.contentY%tilesizeh + map.y) cx: maparea.contentX cy: maparea.contentY tilesize: root.tilesize tilesizew: root.tilesizew tilesizeh: root.tilesizeh mapcols: root.mapcols maprows: root.maprows mapdata: [ "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004", "0004","0004","0004","0004","0004","0004","0004","0004" ] }
PS ここで、「0004」は、拡張子のない画像リソースの名前です。もちろん、これらのパラメーターはすべてC ++側で宣言する必要があり、これはすべて
Q_PROPERTYマクロを使用して行われます。
class MapProvider : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(double tilesize READ tilesize WRITE setTilesize NOTIFY tilesizeChanged) Q_PROPERTY(double tilesizew READ tilesizew WRITE setTilesizew NOTIFY tilesizewChanged) Q_PROPERTY(double tilesizeh READ tilesizeh WRITE setTilesizeh NOTIFY tilesizehChanged) Q_PROPERTY(double mapcols READ mapcols WRITE setMapcols NOTIFY mapcolsChanged) Q_PROPERTY(double maprows READ maprows WRITE setMaprows NOTIFY maprowsChanged) Q_PROPERTY(double cx READ cx WRITE setCx NOTIFY cxChanged) Q_PROPERTY(double cy READ cy WRITE setCy NOTIFY cyChanged) Q_PROPERTY(QVariantList mapdata READ mapdata WRITE setMapdata NOTIFY mapDatachanged) public: }
QtCreator 'aのパワーにより、これらのパラメーターを数回クリックするだけで簡単に作成できます(知らない人のために:各行でコンテキストメニューを呼び出します
Q_PROPERTY->リファクタリング-> Q_PROPERTYメンバーの生成... )
ファイナル
最後に、
paintメソッドの実装に
到達しました 。 実際、画像キャッシュが追加されていることを除いて、前のプロジェクトの
createMap()関数とそれほど違いはありません。
void MapProvider::paint(QPainter *painter) {
キャッシングは毎回画像を再描画しないために必要ですが、元の画像のサイズがタイルサイズよりもはるかに大きいために再描画されます(これは将来スケーリングを実装するために行われました)。 特に
Qt :: SmoothTransformationは画像を変更するときに使用されるため、再
描画は多くのリソースを消費します。
ところで、理論的にスケーリングを実装できるようになりました。root.tilesizeパラメーターの増加係数を追加するだけですtileCache変数
は 、
MapProviderクラスで宣言されてい
ます 。
private: QMap<QString, QPixmap> tileCache;
最後の仕上げは、接続のペアを作成してマップ再描画イベントを追加することです。
MapProvider::MapProvider(QQuickItem *parent) : QQuickPaintedItem(parent) { connect(this, SIGNAL(xChanged()), this, SLOT(update())); connect(this, SIGNAL(yChanged()), this, SLOT(update())); }
発売日
さて、これですべてです。プロジェクトを実行して、次の図を見ることができます。

これは、最初のドラフトの写真と大差はありませんが、食いしん坊ではありません。
マップがどのように動いて描画されるかを確認するには、
root.mapcols変数の値を増やして、たとえば
8に設定する必要があります(この値に
root.maprowsを掛けた値は、
mapprovider.mapdata変数の要素数に対応し、大きな値の場合は要素を追加する必要があります) )
カーテンの後ろに「保護ゾーン」を非表示にして、マップの有用な部分のみを表示するには、
gamearea.clipパラメーターを
falseから
trueに変更するだけ
です次のパートでは、現在のプロジェクトに基づいてマップエディターを作成するプロセスについて説明します。 エディターは、マップを保存してロードできる必要があります。