Developer

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

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

目次

  • 問題パネル
  • 描画処理
  • プロパティ設定関数
  • クリック処理

問題パネル

■描画処理

問題パネルにはサムネイル、難易度、タイトルなどが表示され、押下することでその問題を解くことができる。 QuestPanel はそのためのシーンだ。
ノードツリーは下図のようになっている。

QuestPanel のクラスは ReferenceRect

アプリ実行時のパネル外観は下図のようになっており、これは _draw() をオーバライドすることで実現している。

func _draw():
    # 外枠
    var style_box = StyleBoxFlat.new()
    style_box.set_corner_radius_all(RADIUS)
    style_box.bg_color = Color.darkslategray if !mouse_pushed else Color.gray
    style_box.border_color = Color.green if !mouse_pushed else Color.darkslategray
    style_box.set_border_width_all(2)
    style_box.shadow_offset = Vector2(4, 4) if !mouse_pushed else Vector2(0, 0)
    style_box.shadow_size = 8 # if !mouse_pushed else 4
    draw_style_box(style_box, Rect2(POSITION, SIZE))

StyleBoxFlat クラスを使って、影・枠付きラウンド矩形を描画している。 その方法は簡単で、上記のようにインスタンスを生成し、各種パラメータを設定し、draw_style_box() をコールするだけだ。

問題サムネイルの表示部分のコードを下記に示す。

func _draw():
    .....
    # サムネイル
    var col = Color.lightgray if ans_iamge.empty() else Color("#ffffef") if time >= 0 else Color("#ffffc0") #.lightyellow
    draw_rect(Rect2(THUMBNAIL_X-2, THUMBNAIL_POS-2, THUMBNAIL_WIDTH+4, THUMBNAIL_WIDTH+4), col)
    if !ans_iamge.empty():
        for y in range(IMG_HEIGHT):
            var py = y * TNCELLWD + THUMBNAIL_POS
            var mask = 1 << IMG_WIDTH
            for x in range(IMG_WIDTH):
                mask >>= 1
                if (ans_iamge[y] & mask) != 0:
                    var px = x * TNCELLWD + THUMBNAIL_X
                    draw_rect(Rect2(px, py, TNCELLWD, TNCELLWD), Color.black)

問題がクリア済みかどうか、途中経過かどうかで背景色を決め、draw_rect() でサムネイル領域を塗りつぶしている。
次に、問題がクリア済みの場合は、サムネイル表示を行っている。 解答のイメージは ans_iamge[] にビットマップとして格納されているので、 ビットマスクをシフトしながら当該ビットが1かどうかをチェックして、1であれば draw_rect() でその部分のドットを描画している。

■プロパティ設定関数

問題パネルは、問題番号、難易度などのプロパティを持ち、それを設定する関数を用意している。
それらの実装を下記に示す。

var number :int = 0
func set_number(n : int):
    number = n
    $number.text = "#%d" % n
func set_difficulty(n : int):
    $difficulty.text = "Difficulty: %d" % n
    $jDiffi.text = "難易度 %d" % n

内容は簡単で、引数で渡されたものをメンバ変数に保存し、ラベルテキストを変更するだけだ。

■クリック処理

ボタンクリック処理のコードを下記に示す。

signal pressed(num)

func _input(event):
    if event is InputEventMouseButton:
        if event.is_action_pressed("click"):        # left mouse button
            if get_global_rect().has_point(event.position):     # 
                mouse_pushed = true;
                saved_pos = get_global_rect()
                update()
        elif event.is_action_released("click") && mouse_pushed:
            if get_global_rect() == saved_pos:
                if get_global_rect().has_point(event.position):     # 
                    print("pressed: ", $number.text)
                    emit_signal("pressed", number)      # pressed シグナル発行
            mouse_pushed = false;
            update()
    elif event is InputEventMouseMotion && mouse_pushed:    # mouse Moved
        if get_global_rect() != saved_pos || !get_global_rect().has_point(event.position):  # 
            mouse_pushed = false;
            update()

問題パネル上にマウスカーソルがあるときにマウスボタンが押下されると、_event() がコールされる。 ので、その中で押下アクションかどうかを判定し、押下されたことを記録しておく。
そしてリリースアクションの場合は、マウスカーソルが押下されたパネル上でリリースされた場合は、 クリックされたとみなし、pressed() シグナルを発行している。

押下状態でマウスが移動され、押下されたボタンから外れた場合は mouse_pushed フラグをオフにし、 update() を呼ぶことで再描画を行う。