さくさく理解する Godot 入門(ただし2Dに限る)応用編 シーズン2(数独パズル)【第5回】
目次
- 数字ボタン押下時処理
- 盤面セル押下時処理
- 手がかり数字押下
■数字ボタン押下時処理
数字ボタンが押されたら、どのボタンが押されたかを記憶し、その数字ボタン位置に選択カーソルを移動する。 また、盤面に入っている同じ数字を強調(背景色黄色)する。
Button が押下されると pressed() というシグナルが発行される。 シグナルは任意の処理関数(ハンドラ)に結合(コネクト)することができる。 コネクトはエディタで行うことも出来るし、アプリ実行時にスクリプトにより行うことも可能だ。 要するにシグナルは動的バインディング可能な関数ということだ。 この仕組により、UI クラスを独立な部品として実装することが可能になっている。
数字ボタン押下時シグナルを、それを処理ハンドラにコネクトするには、 ボタンノード選択状態にてインスペクタペインで「ノード」を選び、 「pressed()」(下図参照)をダブルクリックするか、または右ボタンメニューで「接続…」を選ぶ(下図参照)。
そうすると下図のシグナル接続ダイアログが表示されるので、【接続】を押す。
以上の操作で、numButton1 ボタンが押下されると _on_numButton1_pressed() がコールされるようになる。
_on_numButton1_pressed() の実装は以下のようになる。
var cur_numButton = 1 # 選択されている数字ボタン ..... func _on_numButton1_pressed(): # 数字ボタン1が押された場合の処理 var rct = $CenterContainer2/VBoxContainer/HBoxContainer/numButton1.get_global_rect() $numCursor.set_position(rct.position) cur_numButton = 1 update_cell_cursor() # 選択数字のセル強調
内容は、数字ボタンカーソルを押下された数字ボタン位置に移動し、現数字(cur_numButton)を設定し、 選択数字のセル強調処理関数(update_cell_cursor())を呼ぶだけだ。
数字ボタン2~9 についてもシグナルを各処理関数にコネクトし、同様のコードを書く。 ただし、ボタンオブジェクト名、cur_numButton に設定する値は適切なものに置き換える。
update_cell_cursor() の実装は以下のようになる。
func show_cell_cursor(x, y, b : bool): # (x, y) セルを強調表示 $CenterContainer/cursorTileMap.set_cell(x, y, 0 if b else -1) func update_cell_cursor(): # 選択数字ボタンと同じ数字のセルを強調 for y in range(9): for x in range(9): var cn = get_cell_number(x, y) show_cell_cursor(x, y, cur_numButton == cn)
画面を作るときに、セルカーソルを表示する TileMap が正しく設定されていれば、set_cell(x, y, 0) で黄色背景が表示される。
また、下記のように _ready() で update_cell_cursor() をコールするようにする。
func _ready(): set_quest("008010240090320061102805007039452700 670103092 001679380 900706108 780091020 015030600") update_cell_cursor() # 選択数字セル強調
以上で、アプリを実行すると、下図のように押下された数字ボタンの数字が現数字となり、それが強調されるはずだ。
■盤面セル押下時処理
ユーザが画面をクリックしたときは、インプットイベントが発生し、_input(event) ハンドラがコールされる。 そこにクリックに対応する処理を記述する。
インプットイベントにはキーなどいくつかの種類があるので、最初にマウス押下イベントかどうかをチェックする(下記参照)。
func _input(event): if event is InputEventMouseButton and event.pressed: # マウスボタン押下イベントか? var xy = posToXY(event.position) # クリック位置 → 盤面座標に変換 #print(xy) if xy.x >= 0: cell_pressed(xy.x, xy.y)
event.position でマウスクリック位置を取得できるので、posToXY(pos) をコールして、盤面座標に変換する。 盤面座標とは、最も左上のセルが (0, 0) で、セルごとに x, y 座標を1ずつ増やす座標系のことだ。 そのコードは下記の通り。
func posToXY(pos): # 画面座標 → 盤面座標変換 pos -= $CenterContainer/numTileMap.global_position # 盤面相対座標に変換 var xy = $CenterContainer/numTileMap.world_to_map(pos) # セル座標に変換 if xy.x >= 9 || xy.y >= 9 || xy.y < 0: # 範囲チェック return Vector2(-1, -1) else: return xy
盤面には TileMap を配置しているので TileMap.world_to_map(pos) を使って、盤面座標に変換する。 引数の pos は TileMap 位置からの相対座標で、event.position は画面左上が原点のグローバル座標系なので、 変換を行う必要がある。その処理は簡単で、コードのようにグローバル座標から TileMap.global_position を減算するだけだ。
world_to_map() は有効な盤面外をクリックされた場合も値を返すので、範囲チェックを行い、盤面外であれば x 座標を -1 に設定している。
下記に cell_pressed() のとりあえずの実装を示しておく。次節以降から、ここにコードを追加していき、 機能を徐々に実装していく。
func cell_pressed(x, y): # 盤面セルがクリックされた場合 print("(", x, ", ", y, ")") pass
■手がかり数字押下
盤面の手がかり数字がクリックされた場合は、その数字ボタンが押下されたものとみなす。
その実装のために、いくつかの関数を定義する。
最初は、セルに手がかり数字が入っているかどうかを判定する is_clue_cell(x, y) を実装する。 そのコードは下記のとおり。
const NUM_OFFSET = 9*2 ..... func is_clue_cell(x, y): var n = $CenterContainer/numTileMap.get_cell(x, y) return n >= 0 && n < NUM_OFFSET
数字タイルは ‘1’~’9′ が4セットあり、前半2セットが手がかり数字用なので、NUM_OFFSET を 9*2 と定義しておき、 TileMap.get_cell(x, y) で取得したタイルIDが 0 以上 NUM_OFFSET 未満であれば、手がかり数字と判定する。
次に、数字 n の数字ボタンオブジェクトを取得する関数と、そこにカーソルを移動する関数を定義する。
func get_numbutton(n): var hbn = (n-1)/3 + 1 # HBoxContainer 番号 var name = "CenterContainer2/VBoxContainer/HBoxContainer%d/numButton%d" % [hbn, n] return get_node(name) func updateNumButtonCursor(): # 現数字ボタンカーソル位置更新 var btn = get_numbutton(cur_numButton) $numCursor.set_position(btn.get_global_rect().position)
数字ボタンは「CenterContainer2/VBoxContainer/HBoxContainer%d/numButton%d」という形式で、 HBoxContainer の番号は 1-3 が 1, 4-6 が 2, 7-9 が 3 なので、(n-1)/3 + 1 で計算し、numButton 番号は n をそのまま用いる。 こうして作成したノード名を get_node() に引数として渡せば、数字ボタンオブジェクトを取得可能だ。
updateNumButtonCursor() は、現在選択されいる数字ボタンの位置に数字ボタンカーソルを移動する関数だ。 数字ボタンオブジェクトを get_numbutton() で取得し、そのグローバル座標を取得し、それを数字ボタンカーソルの位置とする。
以上で、準備が整ったので、cell_pressed(x, y) の中身を実装しよう。 最初に n に現在セル入っている数字を格納しておき、is_clue_cell(x, y) で手がかり数字がクリックされたかどうかを判定する。 そうであれば、現数字を更新し、updateNumButtonCursor(), update_cell_cursor() をコールして、 その数字ボタンを選択状態にし、現数字と同じ盤面に入っている数字を強調する。
func cell_pressed(x, y): # 盤面セルがクリックされた場合 print("(", x, ", ", y, ")") var n = get_cell_number(x, y) if is_clue_cell(x, y): # 手がかり数字セルがクリックされた場合 cur_numButton = n # 選択数字設定 updateNumButtonCursor() # 数字ボタン選択カーソル位置更新 update_cell_cursor() # 選択数字のセル強調
TechProjin Godot入門 関連連載リンク
Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次
標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次