さくさく理解する Godot 入門(ただし2Dに限る)応用編 お絵かきパズル【第1回】
目次
- はじめに
- メイン画面
- プロジェクト作成・ルートノード・背景
- タイトルバー
- グローバル定数・変数
はじめに
上図の様な、2画面から成る 15×15 サイズ固定のお絵かきパズルを Godot で実装する方法の解説を行う。 本稿で示す方法はあくまでもひとつの実装方法であり、必ずしも最適な実装方法ではないかもしれないが、 この方法で実装可能なのだと理解していただければ幸いだ。 もし本稿よりもよりよい実装方法があれば、筆者にメール等でご教授していただければ助かる。
お絵かきパズルのルールは、「ピクロス」「お絵かきロジック」「イラストロジック」と同一で、 左・上左部の手がかり数字部分で、その行・列に存在するキャンパスエリアの黒の数を示す。 本稿のアプリは、「さくさくロジック15」というアプリ名でフリーゲーム夢現 様で公開しているので、実際に試してみたい場合はそれを参照されたい。
これまで本連載では、画面をすべて作成→スクリプトの実装を解説 という順序だったが、 本シリーズでは機能ごとに画面を作成→スクリプトの実装を解説を行うものとする。
解説順序は、おおむね実際に筆者が機能を実装していった順序とする。 この方が、実装方法を理解しやすいのではないかと考えたからだ。
ソースコード全体は github にて、MIT ライセンスにて公開している。 ただし、問題の著作権は各問題作者にあるので、各問題を別プロジェクトで再利用する場合は、著作権者の承諾が必要なので注意されたい。
※「ピクロス」は任天堂株式会社の、「お絵かきロジック」は 株式会社世界文化ホールディングスの、 「イラストロジック」は 株式会社日本文芸社、株式会社コナミデジタルエンタテインメントの登録商標です。
メイン画面
本稿の最初に示したスクショを見るとわかるように、本アプリは問題一覧画面とパズルを作る・解くメイン画面の2画面からなる。 まずは、作って楽しいメイン画面から実装・解説してくことにする。 また、パズルを解くには問題が必要なので、先に問題を作るための機能を実装していく。
■プロジェクト作成・ルートノード・背景
なにはともあれ、下図のように新規プロジェクトを作成する。プロジェクト名は「GDLogic15」だ。
WebGL での実行を可能にするため、レンダラー:「OpenGLES 2.0」を選択している。
プロジェクトを作成後、設定 > プロジェクト設定 メニューを選び、 下図のように、画面サイズを 480×800 とする。
これは、横・縦比をおおむね 1:1.618 の黄金比にするためと、 セルの幅をキリのいい20ピクセルとし、手がかり数字最大8個なので、20*(15*8) = 460ピクセルで、 左右の空白を10ピクセルにするとちょうどいいと判断したからだ。 今考えると、左右の余白を20ピクセル*左右にし、横幅をキリのいい500ピクセルにしてもよかったかもしれない。 まあ、このへんは気分だ。;^p
次に、ルートノードとして Node2D を作成し、「MainScene」とリネームすると下図のようになる。
背景として ColorRect ノードを追加し、「BG」という名前にする。
インスペクタでサイズ・色を指定する。サイズは画面全体なので 480×800、色は #c0ffc0 とした。
■タイトルバー
次にタイトルバーとして ColorRect ノードを追加し、「TitleBar」という名前にする。
サイズは 480×50、色は #2e4f4f とした(下図参照)。
次に、タイトルバーに影を付けるのだが、タイトルバーの明度が低く、影が見えづらいので、 緑角丸矩形枠を StyleBoxFlat を使って描画している。
「TitleBar」ノードを選択し、右ボタンメニュー の 「スクリプトをアタッチ」、または「+巻物」のようなアイコンをクリックし、 スクリプトをアタッチする。スクリプトの中身は以下のように記述する。
extends ColorRect const RADIUS = 5 const POSITION = Vector2(0, 0) func _draw(): # 描画関数 var style_box = StyleBoxFlat.new() # 影、ボーダなどを描画するための矩形スタイルオブジェクト style_box.bg_color = color # 矩形背景色 style_box.border_color = Color.green style_box.set_border_width_all(2) style_box.set_corner_radius_all(RADIUS) style_box.shadow_offset = Vector2(4, 4) # 影オフセット style_box.shadow_size = 8 # 影(ぼかし)サイズ draw_style_box(style_box, Rect2(POSITION, self.rect_size)) # style_box に設定した矩形を描画
_draw() は自前でノードを描画するための関数だ。
ここでは StyleBoxFlat クラスを使って、角丸矩形枠を描画している。 使い方は簡単で、背景色や角丸矩形半径などのパラメータを設定し、draw_style_box() をコールするだけだ。 詳しくはリファレンスの StyleBoxFlat を参照していただきたい。
以上を実行すると下図のようになる。タイトルバーの影がいい感じに表示されたのではないだろうか?
タイトルバーに任意の画像を表示したい場合は ColorRect ではなく TextureRect を使用する。
筆者にとっては、画像を自分で作ったり、いい画像を探すよりスクリプトで記述した方が楽なのだ。 20×20等、別のサイズに変更したい場合も、画像を修正するよりもスクリプトを変更する方がはるかに楽だ。
次に、タイトルバーに「戻る」ボタンを設置する。
TextureButton を TitleBar の子ノードとしてノードツリーに追加し、「BackButton」にリネームする。 通常・押下時・ディセーブル時画像に、それぞれ白・黒・グレイの「←」画像を割り当てる(下図参照)。 なお、矢印画像は Google様の マテリアルアイコン をありがたく利用させていただいている。
戻るボタンが押されると、問題一覧画面に遷移するのだが、今はまだその画面を作成していないので、 イベントハンドラの設定・記述は問題一覧画面作成後に行うことにする。
同様に、Label を TitleBar の子ノードとして追加し、タイトルラベルをとりあえず設置しておく(下図参照)。
以上で、背景・タイトルバーの実装は終わりだ。ここまでを実行すると下図のようになる。
■グローバル定数・変数
GDScript には何故かグローバル定数・変数がないのだが、シーンをまたいでデータを保持したい場合などに困るので、 スクリプトを自動ロード・シングルトンにしておくことで、実質的にグローバル定数・変数を利用することができる。 その手順は以下のとおりだ。
新規シーンとして「Global」を作成する。クラス名は「Node2D」で「Global」にリネームする。
Global にスクリプトをアタッチし、Global.gd というファイル名で保存しておく。
設定 > プロジェクト設定… メニューで「自動読み込み」タブを選び、パス:部分で Global.gd を選び、 最右の「追加」ボタンを押す。そうすると下図のように Global.gd が各シーンで自動読み込みされるようになる。
「シングルトン」のところが有効になっているので、自動読み込みされる Global.gd はシングルトンとなり、 グローバル定数・変数として機能するというわけだ。
Global.gd の内容は、とりあえず下記のように、画面・盤面サイズなどの定数を定義しておく。 後で必要に応じてグローバル変数なども追加していく。
const SCREEN_WIDTH = 500.0 # 画面横サイズ const SCREEN_HEIGHT = 800.0 # 画面縦サイズ const BOARD_WIDTH = 460.0 # 盤面横サイズ const BOARD_HEIGHT = BOARD_WIDTH # 盤面縦サイズ const LR_SPC = (SCREEN_WIDTH - BOARD_WIDTH) / 2 const N_CLUES_CELL_HORZ = 8 # 手がかり数字 セル数 const N_IMG_CELL_HORZ = 15 # 画像 セル数 const N_TOTAL_CELL_HORZ = N_CLUES_CELL_HORZ + N_IMG_CELL_HORZ const N_CLUES_CELL_VERT = 8 # 手がかり数字 セル数 const N_IMG_CELL_VERT = 15 # 画像 セル数 const N_TOTAL_CELL_VERT = N_CLUES_CELL_VERT + N_IMG_CELL_VERT const CELL_WIDTH = BOARD_WIDTH / N_TOTAL_CELL_HORZ # セル幅 const CLUES_WIDTH = CELL_WIDTH * N_CLUES_CELL_HORZ # 手がかり数字部分幅 const IMG_AREA_WIDTH = CELL_WIDTH * N_IMG_CELL_HORZ # キャンパスエリアサイズ const IMAGE_ORG = Vector2(CELL_WIDTH*(N_CLUES_CELL_HORZ), CELL_WIDTH*(N_CLUES_CELL_VERT)+1)
なお、Global.gd で定義された定数・変数は「Global.識別子」で参照することができる。
が、個人的には、このままではちょっと長いと考えているので、各スクリプトの最初のあたりで「var g = Global」と定義し、 「g.識別子」で定数・変数を参照するようにしている。
TechProjin Godot入門 関連連載リンク
Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次
標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次