グリッドベースの移動

ℹ️ 留意事項

この記事は Godot 3から Godot 4 へ内容の書き換え中です。 Godot4では存在しない変数、関数が含まれている場合があります。もしその場合はリポジトリのIssuesまでご報告ください。

課題

グリッド状に移動する2Dキャラクターが必要です。

解決策

グリッド制またはタイルベースの移動方式では、キャラクターの位置が制限されます。特定のタイル上にのみ立つことができ、2枚のタイルの間に立ち続けることはできません。

キャラクター設定

以下がプレイヤーで使用するノードです。

  • Area2D (“プレイヤー”): Area2D を使用することで、オブジェクトのピックアップや敵との衝突判定が可能になります。
    • Sprite2D: ここではスプライトシートを使用できます(アニメーション設定は後述します)。
    • CollisionShape2D: ヒットボックスが大きすぎないように注意してください。プレイヤーがタイルの中心に立つため、オーバーラップ判定も中央から行われます。
    • RayCast2D: 指定された方向への移動が可能かどうかを確認する際に使われます。
    • AnimationPlayer: キャラクターの歩行アニメーションを再生に使います。

インプットマップに操作を追加してください。この例では「上」「下」「左」「右」を使用します。

基本的な動き

まず、アニメーションや補間なしで、タイル単位の移動設定から始めてください。

extends Area2D

var tile_size = 64
var inputs = {"right": Vector2.RIGHT,
            "left": Vector2.LEFT,
            "up": Vector2.UP,
            "down": Vector2.DOWN}

tile_sizeにはタイルのサイズに合わせて適切な値を設定してください。大規模なプロジェクトでは、メインシーンからプレイヤーインスタンスを生成する際にこを設定します。以下の例では 64×64ピクセルのタイルを使用しています。

inputs Dictonaryは入力アクション名と方向ベクトルを対応付けます。ここでの名前とインプットマップでの表記が完全一致していることを確認してくください(大文字小文字の区別に注意!)。

func _ready():
    position = position.snapped(Vector2.ONE * tile_size)
    position += Vector2.ONE * tile_size/2

snapped() 関数は位置をタイルの増分に最も近い値に「丸め」ます。さらに、半タイル量を追加することで、プレイヤーが必ずタイル中心に配置されるようになります。

func _unhandled_input(event):
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

func move(dir):
    position += inputs[dir] * tile_size

以下が実際の移動処理コードです。入力イベントが発生すると、4方向をチェックし、該当する方向を特定した後にmove()関数に渡して位置を変更します。

alt alt

衝突事故

以下の例のように、障害物を追加する方法は複数あります。手動で障害物を追加したい場合はStaticBody2Dオブジェクトを使用するか(グリッドに正確に配置できるようスナップ機能を有効にしてください)、衝突判定が定義されたTileMapを利用することもできます。

移動先タイルへの進入が許可されているかどうかを判定するために、RayCast2D 機能を使用します。

@onready var ray = $RayCast2D

func move(dir):
    ray.target_position = inputs[dir] * tile_size
    ray.force_raycast_update()
    if !ray.is_colliding():
        position += inputs[dir] * tile_size

レイキャストの target_position プロパティを変更する場合、物理エンジンは次の物理フレームまで衝突を再計算しません。force_raycast_update() を使用すると、即座にレイの状態を更新できます。もし衝突が発生していなければ、移動できます。

alt alt

メモ

もう一つの一般的な方法として、各方向に一つずつ計4つのレイキャストを使用する手法があります。

動きのアニメーション化

最後にタイル間の位置補間を行い、滑らかな移動感を実現します。アニメーションにはTweenノードを使用してpositionプロパティを制御します。


var animation_speed = 3
var moving = false

ノード Tween に参照を追加し、移動速度を設定する変数を作成します。

func _unhandled_input(event):
    if moving:
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

Tween が実行されている間は入力を無視し、直接的な 位置 変更を無効とすることで、Tween自体がその処理を行えるようにします。

func move(dir):
    ray.target_position = inputs[dir] * tile_size
    ray.force_raycast_update()
    if !ray.is_colliding():
        #position += inputs[dir] * tile_size
        var tween = create_tween()
        tween.tween_property(self, "position",
            position + inputs[dir] *    tile_size, 1.0/animation_speed).set_trans(Tween.TRANS_SINE)
        moving = true
        await tween.finished
        moving = false

alt alt

異なるトランジション効果を試してみます。

プロジェクトのダウンロード

プロジェクトコードはこちらよりダウンロードできます。https://github.com/godotrecipes/2d_grid_movement/