スクリーンショット_2020-01-21_0

セルをグリッド状に並べる(Dig The Shape!の作り方)

 これはDig The Shape!の制作の中で個人的に取り上げたいトピックを抜き出して紹介する記事です。

- プログラマ向け
- 汎用的なノウハウというよりは練習問題的なやつ
- この記事内のコードのライセンスはCC0(引用や外部リンクを除く)

 プログラミング作業って、小さな問題を解くことの連続で、ひとつひとつの問題はまあそんなに難易度高くないし、場当たり的にでも解けるんです。でもそうやってるとだんだん変更に弱くなったりデバッグしづらくなったりしてきちゃうので、

ひとつひとつの問題を、全体を見通しつつ解く

つまり、

「なぜその方法で解くのか」に自覚的になる

ということがとても大事だと思っています。

 このシリーズでは比較的簡単な問題を扱います。解答も相応に簡単なものになると思います。が、主題は解き方そのものではなく、その裏にある理由や意図の方ですのでそのつもりで読んでいただけるとありがたいです。

 前置きが長くなりました。それではどうぞ。

グリッドレイアウト

 何かをグリッド状に並べたいとき、開発環境によってはレイアウトマネージャみたいなやつがうまく並べてくれたりするのかもしれませんが、自前でやるにしてもどちらにしろ、以下の値について考えることになると思います。

 仮に、並べるマスをCell、並べる先の場のことをFieldと呼ぶことにすると

- Fieldのサイズ
- Cellのサイズ
- Cellの数

 このうち2つが決まれば残りの1つは計算で決まるので、作りたいものに合わせて、どの2つを主導にするかを考えます。

 今回は画面レイアウト的な視点からFieldのサイズを、ゲームデザイン的な視点からCellの数を固定したかったので、そうしました。なお、その後Cellの表示上のサイズも変えたくなったので、それはそれで新たに調整項目として追加しました。

スクリーンショット 2020-01-21 0.06.40

 余談ですが、見る人によってはCell Sizeという変数はCellクラスの方に持たせたくなるかもしれませんが、今回はField側の都合によって最適なCellのサイズが変わる(グリッドをいい感じに充填するサイズ)ことと、調整作業時に隣にあった方が便利だという主に2つの理由でField側に持たせました。GUIがある環境の場合は特に、こういうちょっとdirtyなことをやっても良いかなと個人的には思います。

論理位置と表示位置

 さてめでたくいい感じの位置にいい感じのサイズでCellが配置できましたね。次はCellの座標について考えます。

名称未設定-3

 左の画像で赤いブロックをクリックすると、隣接した赤ブロック3つが消えて、右の画像のように上からブロックが落ちてくるわけですが、右の画像の瞬間にカーソルが乗ってる黄色いブロックをクリックしたとして、横並びに見える3つもしくは4つの黄色ブロックが消えては困るわけです。(そういうゲームにしたいならそれはそれ。今回は違うので困る。)

 ここからわかることは、隣接判定に使うCellの位置と、実際にCellが表示される位置とは別物だと捉えた方がよさそう、ということです。それぞれ論理位置表示位置と呼ぶことにしましょう。論理位置は整数で、表示位置は小数で持つのが良さそうです。

コード

 だいたいこのくらいまで考えてから、FieldとCellのレイアウトまわりのコードを書き始めます。実際はUnity使用なのでC#で書きましたが、なんとなく書き慣れたC++っぽく書いてみます。意図が伝われば良いかなと思って適当に略して書くので、コンパイルしようと思わないでください。

class Cell {
private:
    ivec2 logic_pos;
    fvec2 disp_pos;
    fvec2 disp_size;
};
class Field {
public:
    void initCells() {
        cells.clear();
        for(index in num_cells) {
            Cell cell;
            cell.logic_pos = {x_index, y_index};
            cell.disp_pos = disp_size/num_cells * cell.logic_pos;
            cell.disp_size = cell_disp_size;
            cells.push(cell);
        }
    }
private:
    vector<Cell> cells;
    ivec2 num_cells;
    fvec2 disp_size;
    fvec2 cell_disp_size;
};

おわりに

 いかがだったでしょうか?多分このくらいの問題なら、この記事に書いたようなことは特に何も意識せずに作り始める方がほとんどだと思います。僕もそうです。意識せずに書けるに越したことはありません。

 意識せずに書いたとしても、ちゃんとその背景にある理由や設計を説明できるとつよいんじゃないかなと思った次第です。

サポートしていただけたら、書く勢いになります!