Developer

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

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

目次

  • 盤面ドラッグ処理
  • セルカーソル

■盤面ドラッグ処理

マウスボタンドラッグした場合は、連続して黒または☓を入力可能にする。

そのために、下記コードのように mouse_pushed フラグを導入し、マウス押下で true に、リリースで false に設定し、 mouse_pushed が true の状態で InputEventMouseMotion イベントが発生した場合は、矩形カーソルを表示することにした。

var mouse_pushed = false
var pushed_xy = Vector2()
var cell_val = 0
func _input(event):
    if event is InputEventMouseButton:
        if( event.is_action_pressed("click") ||     # left mouse button
                event.is_action_pressed("rt-click") ):      # right mouse button
            print("mouse pushed")
            mouse_pushed = true
            .....
        elif event.is_action_released("click") || event.is_action_released("rt-click"):
            print("mouse released")
            mouse_pushed = false
            .....
    elif event is InputEventMouseMotion:
        var xy = posToXY(event.position)
        if mouse_pushed:    # マウスドラッグ中
            print("mouse moved")
            .....

ドラッグ中に矩形カーソルは Grid.gd の draw() 関数で表示する。下記がそのために Grid.gd に追加したコードだ。

var pos1 = Vector2(-1, -1)      # 矩形起点、-1 for 矩形無し
var pos2 = Vector2(0, 0)        # 矩形終点
var font
func clearRect():
    pos1 = Vector2(-1, -1)
    update()
func setRect(p1, p2):
    pos1 = p1
    pos2 = p2
    update()
func _draw():
    .....
    if pos1.x >= 0:     # ドラッグ中の場合
        #print("pos1 = ", pos1, ", pos2 = ", pos2)
        var left = min(pos1.x, pos2.x)
        var right = max(pos1.x, pos2.x) + 1
        var wd = right - left
        var upper = min(pos1.y, pos2.y)
        var bottom = max(pos1.y, pos2.y) + 1
        var ht = bottom - upper
        #print(left, ", ", upper, ", ", wd, ", ", ht)
        var rct = Rect2(left*g.CELL_WIDTH + g.IMAGE_ORG.x, upper*g.CELL_WIDTH + g.IMAGE_ORG.y,
                            wd*g.CELL_WIDTH, ht*g.CELL_WIDTH)
        draw_rect(rct, Color(0.5, 0.5, 1.0, 0.5))
        #var pos = pos2*CELL_WIDTH + IMAGE_ORG - Vector2(CELL_WIDTH, 2)
        var txt = "%d x %d" % [wd, ht]
        var sz = font.get_string_size (txt)
        var pos = pos2*g.CELL_WIDTH + g.IMAGE_ORG - sz - Vector2(2, 2)
        draw_rect(Rect2(pos, sz+Vector2(2, 2)), Color.white)
        draw_string(font, pos+Vector2(0, sz.y), txt, Color.black)

以上で準備ができたので、MainScene.gd のイベントハンドラ部分を以下のように修正する。

func _input(event):
    .....
        if( event.is_action_pressed("click") ||     # left mouse button
                event.is_action_pressed("rt-click") ):      # right mouse button
            pushed_xy = xy
            last_xy = xy
        elif event.is_action_released("click") || event.is_action_released("rt-click"):
            print("mouse released")
            mouse_pushed = false
            $BoardBG/Grid.clearRect()
            if pushed_xy != last_xy:        # ドラッグされていた場合
                set_cell_rect(pushed_xy, last_xy, cell_val)
                update_miniTileMap()
    elif event is InputEventMouseMotion:
        var xy = posToXY(event.position)
        if mouse_pushed:    # マウスドラッグ中
            print("mouse moved")
            var xy = posToXY(event.position)
            last_xy = xy
            $BoardBG/Grid.setRect(pushed_xy, xy)
func set_cell_rect(pos1, pos2, v):
    var x0 = min(pos1.x, pos2.x)
    var y0 = min(pos1.y, pos2.y)
    var wd = max(pos1.x, pos2.x) - x0 + 1
    var ht = max(pos1.y, pos2.y) - y0 + 1
    for y in range(ht):
        for x in range(wd):
            #var v0 = $BoardBG/TileMap.get_cell(x0+x, y0+y)
            #set_cell_basic(x0+x, y0+y, v)
            $BoardBG/TileMap.set_cell(x0+x, y0+y, v)
            update_clues(x0+x, y0+y)

ドラッグ中は Grid.setRect(pushed_xy, xy) で矩形カーソルを表示し、ドラッグ後にマウスボタンがリリースされると set_cell_rect(pushed_xy, last_xy, cell_val) がコールされて、黒またはバツがまとめて入力される。

以上を実行すると下図のように、ドラッグ中に矩形カーソルが表示される。

ドラッグ機能により、下図のような絵も楽ちんで描けるようになった。

■セルカーソル

盤面サイズが大きくなると、マウスカーソル位置のセルに対応する手がかり数字がどれなのかが、 瞬時にわかりずらくなる。視認性を高めるために、カーソル位置に対応する手がかり数字部分を強調表示する。

まずは、Grid.gd に以下のコードを追加し、指定セル・手がかり数字部分を強調可能にする。

Grid.gd:

var curX = -1
var curY = -1
func set_cursor(cx, cy):
    curX = cx
    curY = cy
    update()
func _draw():
    .....
    if pos1.x >= 0:     # ドラッグ中の場合
        .....
    elif curX >= 0 && curY >= 0:
        var col = Color(0.5, 1, 0.5, 0.25)
        var pos = Vector2(curX, curY) * g.CELL_WIDTH + g.IMAGE_ORG
        draw_rect(Rect2(pos, Vector2(g.CELL_WIDTH, g.CELL_WIDTH)), col)
        #
        var style_box = StyleBoxFlat.new()
        style_box.bg_color = Color(0, 0, 0, 0)   # 矩形背景色
        style_box.border_color = Color.darkgreen
        style_box.set_border_width_all(2)
        pos = Vector2(-g.N_CLUES_CELL_HORZ, curY) * g.CELL_WIDTH + g.IMAGE_ORG
        draw_style_box(style_box, Rect2(pos, Vector2(g.CELL_WIDTH*g.N_CLUES_CELL_HORZ, g.CELL_WIDTH)))
        pos = Vector2(curX, -g.N_CLUES_CELL_VERT) * g.CELL_WIDTH + g.IMAGE_ORG
        draw_style_box(style_box, Rect2(pos, Vector2(g.CELL_WIDTH, g.CELL_WIDTH*g.N_CLUES_CELL_VERT)))

カーソル位置を保持するメンバ変数 curX, curY を導入し、set_cursor(cx, cy) で値を設定可能にする。 設定後に update() をコールし、強制的に画面描画を行わせている。

セルの強調は draw_rect(Rect2, Color) で描画し、手がかり数字部分は draw_style_box(StyleBoxFlat, Rect2) で外枠を描画している。最初はセルと同様に draw_rect() で色を付けていたのだが、それだと見づらかったので、 外枠を描画するように修正した。

以上で準備ができたので、インプットイベントにおいて、マウス移動イベントの場合に Grid.set_cursor(x, y) をコールしている。 ただし、盤面キャンパス以外の場合は引数に (-1, -1) を指定し、セル・手がかり数字強調表示を行わないようにしている。

func _input(event):
    .....
    elif event is InputEventMouseMotion:
        var xy = posToXY(event.position)
        if mouse_pushed:    # マウスドラッグ中
            .....
        else:
            if xy.x >= 0:
                $BoardBG/Grid.set_cursor(xy.x, xy.y)
            else:
                $BoardBG/Grid.set_cursor(-1, -1)

以上を実行すると、下図のような画面となる。これで、カーソル位置に対応する手がかり数字の視認性がとってもよくなったと思う。

TechProjin Godot入門 関連連載リンク

Godotで学ぶゲーム制作
さくさく理解するGodot入門 連載目次

標準C++ライブラリの活用でコーディング力UP!
「競技プログラミング風」標準C++ライブラリ 連載目次