さくさく理解する Godot 入門(ただし2Dに限る)応用編 お絵かきパズル【第6回】
目次
- 全消去
- 上下左右移動
■全消去
何の絵を問題にするかなって考えてるときは、これいいじゃね?と思い、描き始めるのだが、 思った以上にドット絵にするのが難しく、途中で諦めてしまうことは少なくない。 そんなときは全消去を行い、新たな気持で別の絵に挑戦だ。
というわけで、全消去機能を説明する。
Undo/Redo 同様に、TextureButton を設置し、pressed シグナルを _on_ClearButton_pressed() に接続する。
_on_ClearButton_pressed() の実装は、下記の通りで、clear_all() をコールするだけだ。
func _on_ClearButton_pressed(): clear_all()
下記に clear_all() のコードを示す。
func clear_all(): var item = [CLEAR_ALL] for y in range(N_IMG_CELL_VERT): item.push_back(get_h_data(y)) push_to_undo_stack(item) clear_all_basic() func clear_all_basic(): for y in range(N_TOTAL_CELL_VERT): # キャンパス部分消去 for x in range(N_TOTAL_CELL_HORZ): if $BoardBG/TileMap.get_cell(x, y) == TILE_BLACK: setup_fallingBlack(xyToPos(x, y)) $BoardBG/TileMap.set_cell(x, y, TILE_NONE) # 盤面状態・手がかり数字消去 $BoardBG/MiniTileMap.set_cell(x, y, TILE_NONE) # 盤面背景消去 if mode == MODE_EDIT_PICT: for y in range(N_TOTAL_CELL_VERT): # 左側手がかり数字消去 for x in range(N_CLUES_CELL_HORZ): $BoardBG/TileMap.set_cell(-x-1, y, TILE_NONE) for x in range(N_TOTAL_CELL_HORZ): # 上側手がかり数字消去 for y in range(N_CLUES_CELL_VERT): $BoardBG/TileMap.set_cell(x, -y-1, TILE_NONE) for y in range(N_IMG_CELL_VERT): # 盤面背景消去 h_clues[y] = [0] for x in range(N_CLUES_CELL_HORZ): $BoardBG/TileMapBG.set_cell(-x-1, y, TILE_NONE) for x in range(N_IMG_CELL_HORZ): # 盤面背景消去 v_clues[x] = [0] for y in range(N_CLUES_CELL_VERT): $BoardBG/TileMapBG.set_cell(x, -y-1, TILE_NONE) else: for y in range(N_IMG_CELL_VERT): # 盤面背景消去 for x in range(N_CLUES_CELL_HORZ): $BoardBG/TileMapBG.set_cell(-x-1, y, TILE_NONE) for x in range(N_IMG_CELL_HORZ): # 盤面背景消去 for y in range(N_CLUES_CELL_VERT): $BoardBG/TileMapBG.set_cell(x, -y-1, TILE_NONE)
Undo を可能にするため、キャンパスのその時の状態を get_h_data(y) で行ごとに数値化し、 それらを配列にして Undo スタックに積んでおく。
キャンパスの状態を実際に全消去するのは clear_all_basic() だ。これは Redo 処理の場合にもコールされる関数だ。
_on_UndoButton_pressed() での Undo 処理コードは下記のようになる。
func _on_UndoButton_pressed(): ..... elif item[0] == CLEAR_ALL: for y in range(N_IMG_CELL_VERT): var d = item[y+1] var mask = 1 << (N_IMG_CELL_HORZ - 1) for x in range(N_IMG_CELL_HORZ): set_cell_basic(x, y, (TILE_BLACK if (d&mask) != 0 else TILE_NONE)) mask >>= 1 ..... update_undo_redo()
全消去前の状態は、行ごとに Undo スタックに積まれている配列に入っているので、 それらを順に取り出して、ビットごとに set_cell_basic(x, y, v) をコールして状態を設定していく。
■上下左右移動
問題絵を描いていると、全体を上下左右に移動したいと思うことがままある。 ので、全体を上下左右に移動する機能を実装する。
実装手順は、例によって上下左右移動アイコンを画面下部に設置し、コマンドハンドラと接続する、という順番だ。。
左移動のコマンドハンドラは _on_LeftButton_pressed() で、コードは下記のようになる。
func _on_LeftButton_pressed(): push_to_undo_stack([ROT_LEFT]) rotate_left_basic()
Undo/Redo のために [ROT_LEFT] を Undo スタックに積み、rotate_left_basic() をコールする。
rotate_left_basic() の実装は下記のとおりだ。
rotate_left_basic() は Undo/Redo にも呼ばれる関数で、実際に画像データを左にローテイトする。
func rotate_left_basic(): var ar = [] # 退避用配列 for y in range(N_IMG_CELL_VERT): # 一番左の状態を配列に退避 ar.push_back($BoardBG/TileMap.get_cell(0, y)) # may be -1 or +1 for x in range(N_IMG_CELL_HORZ-1): # 状態をひとつ左にシフト for y in range(N_IMG_CELL_VERT): $BoardBG/TileMap.set_cell(x, y, $BoardBG/TileMap.get_cell(x+1, y)) for y in range(N_IMG_CELL_VERT): # 退避した状態配列を元に、一番右の状態を設定 $BoardBG/TileMap.set_cell(N_IMG_CELL_HORZ-1, y, ar[y]) update_all_clues() # 手がかり数字再計算 update_miniTileMap() # ミニマップ更新
最初に一番左の列の状態を ar に退避し。ついで、全体を1ドット左に移動する。 最後に退避した状態配列を使って、一番右の状態を設定する。
update_all_clues() の実装は下記の通り。
for 文で回しながら、以前(第3回)に実装した update_h_clues(), update_v_clues() をコールしているだけだ。
func update_all_clues(): for y in range(N_IMG_CELL_VERT): update_h_clues(y) for x in range(N_IMG_CELL_HORZ): update_v_clues(x)
update_miniTileMap() の実装は下記の通り。
for 文で回しながら、セルの状態を参照し、それをミニマップに反映させている。
func update_miniTileMap(): for y in range(N_IMG_CELL_VERT): for x in range(N_IMG_CELL_HORZ): var img = 0 if $BoardBG/TileMap.get_cell(x, y) == TILE_BLACK else TILE_NONE $BoardBG/MiniTileMap.set_cell(x, y, img)
TechProjin Godot入門 関連連載リンク
Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次
標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次