【Godot】TileMapのコリジョンからタイル情報を検出する

やりたいこと

GodotのTileMapで横スクロールな2Dステージを作ったとします。

そこでキャラが乗っているタイルの情報が欲しくなりました。

ダメージ床だったらダメージ処理したり、衝突したタイルを破壊したりできます。

タイル情報を検出する方法

TileSetの設定

まずTileSetを設定します。

画像から領域を切って、コリジョンを設定します。

f:id:ueshita:20200902004854p:plain

タイルでステージを作る

TileSetからタイルを選んでTileMapに配置します。

今回は横長タイルにしました。

f:id:ueshita:20200902010521p:plain

キャラの作成

キャラにはKinematicBody2Dを設定します。

今回は2Dユニティちゃんをお借りしました。🙏🙏🙏🙏🙏🙏🙏

f:id:ueshita:20200902120813p:plain

移動処理を書きます。詳細な処理の記載は省いています。

func _physics_process(delta: float):
    # 落下処理
    velocity.y += GRAVITY * delta
    velocity.y = min(velocity.y, MAX_FALLING_SPEED)
    
    # 移動処理
    velocity = move_and_slide_with_snap(velocity, 
        Vector2(0, 4), Vector2.UP, true, 4, deg2rad(48))

コリジョンからタイル情報を検出する

ここからが本題です。

move_and_slide_with_snap で移動している場合、get_slide_collisionKinematicCollision2D が得られます。

KinematicCollision2Dの各プロパティはタイルマップの場合、以下の通りになります

  • collider : CollisionObject2D
  • collider_shape : TileMap
  • collider_shape_index : シェイプ番号 (タイルごとのシェイプ)

collider_shape_index が重要です。乗っているタイルによってここの値が変化することが確認できます。

CollisionObject2Dに登録されているコリジョンシェイプの番号になるので、TIleIDにどうにかして変換する必要があります。

いろいろ調べて、以下の方法で上手くいきました。

func _process(delta: float):
    var collision := get_slide_collision(0)
    if collision:
        # TileMapに衝突しているかどうか
        var tilemap := collision.collider_shape as TileMap
        if tilemap:
            var tileset := tilemap.tile_set
            # collider_shape_indexを使ってメタデータ(タイル位置)を取得
            var tile_pos = Physics2DServer.body_get_shape_metadata(
                collision.collider.get_rid(), 
                collision.collider_shape_index)
            # タイル位置を使ってTIleMapからタイルIDを取得
            var tile_id := tilemap.get_cellv(tile_pos)
            # タイルIDを表示
            print(tileset.tile_get_name(tile_id));

実行画面

f:id:ueshita:20200902005039p:plain

出力ログ

--- Debugging process started ---
Godot Engine v3.2.2.stable.official - https://godotengine.org
OpenGL ES 3.0 Renderer: Intel(R) Iris(R) Plus Graphics
 
基本チップ
ハーフチップ
右上がり斜面
左上がり斜面
右上がり斜面

まとめ

CollisionObject2Dにメタデータという形で記録されたタイル位置情報を collider_shape_index を使ってタイル情報を得る方法の紹介でした。

おまけ(調べた方法)

TileMapのソースコードを読みました。

Godotはエンジンのお気持ちが知りたくなったら、ソースコードを見に行けばいいのでお手軽ですね。

github.com

タイルのコリジョンシェイプをbody(CollisionObject2D)へ登録している箇所があります。

_add_shape(shape_idx, q, shape, shapes[j], xform, Vector2(E->key().x, E->key().y));

シェイプ情報以外にもタイルの位置情報をメタデータとして登録しています。

void TileMap::_add_shape(int &shape_idx, const Quadrant &p_q, const Ref<Shape2D> &p_shape, const TileSet::ShapeData &p_shape_data, const Transform2D &p_xform, const Vector2 &p_metadata) {
    PhysicsServer2D *ps = PhysicsServer2D::get_singleton();

    if (!use_parent) {
        ps->body_add_shape(p_q.body, p_shape->get_rid(), p_xform);
        ps->body_set_shape_metadata(p_q.body, shape_idx, p_metadata);

どうにかしてこのメタデータを取り出せばタイル位置が判明します。

Physics2DServerはGDScriptからも叩けるので、Physics2DServer.body_get_shape_metadataでメタデータを取ることができることが分かりました。

ライセンス表記

f:id:ueshita:20200902122900p:plain

この記事はユニティちゃんライセンス条項の元に提供されています