Developer

さくさく理解する Godot 入門(ただし2Dに限る)応用編 お絵かきパズル【第6回】
2021.12.20
Lv1

さくさく理解する 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++ライブラリ 連載目次