3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

🦀ひとりRustとBevyでゲーム開発🕊️Advent Calendar 2024

Day 24

【Rustのまほう2】#24 物理エンジン 衝突グループ

Last updated at Posted at 2024-12-23

物理エンジンの設定項目で、密度や摩擦、外力などはまあイメージしやすいと思います。比較的わかりにくい項目に衝突グループの設定があります。現実世界の再現という意味ではあまり使う必要がないのですが、ゲームではこの衝突グループの設定は非常に重要なので解説しておきます。

衝突グループとは

衝突グループ とは、どの物体がどの物体と衝突し干渉するかを制御するものです。え?どの物体どうしでも衝突者するでしょ?と思うかもしれませんが、それは現実世界の話であって、ゲーム世界では少し事情が異なります。

たとえば、敵キャラクターが発射した弾丸が別の敵キャラクターに衝突してしまうと、あっさり同士討ちが始まってしまってゲームになりません。またプレイヤーキャラクターが自分の撃った弾丸に自分が当たって自傷ダメージを受けたりしかねません。さらに、弾丸が地面に散らばっている金塊にまで衝突したら、敵に当たる前に地面の金塊に当たって弾丸が消滅してしまいます。

このように、ゲームでは物理法則を無視してある種の物体同士が衝突せずにすり抜ける設定ができたほうがいいです。この設定が衝突グループです。

衝突グループの設定

Rapierでは衝突グループはあらかじめ32個用意されていて、これらを各物体に割り当てていきます。Group::GROUP_1などの数字ではわかりづらいので、先にエイリアスを用意しておきます。

pub const ENTITY_GROUP: Group = Group::GROUP_1;

pub const WALL_GROUP: Group = Group::GROUP_3;

pub const WITCH_GROUP: Group = Group::GROUP_5;

pub const WITCH_BULLET_GROUP: Group = Group::GROUP_6;

pub const ENEMY_GROUP: Group = Group::GROUP_6;

pub const ENEMY_BULLET_GROUP: Group = Group::GROUP_7;

pub const MAGIC_CIRCLE_GROUP: Group = Group::GROUP_8;

pub const SENSOR_GROUP: Group = Group::GROUP_9;

pub const DOOR_GROUP: Group = Group::GROUP_10;

pub const RABBIT_GROUP: Group = Group::GROUP_11;

それから、各剛体にCollisionGroupsコンポーネントとして割り当てます。最初の引数membershipsは自分が所属する衝突グループ、2つ目の引数filtersが自分がどの衝突グループと衝突するかです。衝突グループどうしはビット演算のOR | で複数のグループを指定できます。

CollisionGroups::new(
    match actor_group {
        ActorGroup::Enemy => ENEMY_GROUP,
        ActorGroup::Player => WITCH_GROUP,
    },
    match actor_group {
        ActorGroup::Enemy => WITCH_BULLET_GROUP,
        ActorGroup::Player => ENEMY_BULLET_GROUP,
    } | ENTITY_GROUP
        | WALL_GROUP
        | WITCH_GROUP
        | ENEMY_GROUP
        | RABBIT_GROUP,
),

私が作っているゲームでは、各キャラクターはプレイヤーと敵側に分類されます。味方のプレイヤー側として召喚されることもあるので、スライムなどのモンスターも味方の衝突グループWITCH_GROUPと条件分岐で切り替わるようになっています。また、そのキャラクターが味方側の場合は味方の弾丸の衝突グループWITCH_BULLET_GROUPには衝突しないし、その逆もまた然り、と条件分岐で切り替えています。どのキャラクター同士も身体はぶつかりますし、壁にもぶつかるので、ENTITY_GROUP | WALL_GROUP | WITCH_GROUP | ENEMY_GROUP | RABBIT_GROUPとそのほかの衝突グループを列挙しています。

同様に弾丸にも衝突グループを設定します。弾丸を発射したキャラクターの種別に応じて、次のようにmembershipsfiltersを設定しわけています。

memberships: match actor.actor_group {
    ActorGroup::Player => WITCH_BULLET_GROUP,
    ActorGroup::Enemy => ENEMY_BULLET_GROUP,
},
filters: match actor.actor_group {
    ActorGroup::Player => ENEMY_GROUP,
    ActorGroup::Enemy => WITCH_GROUP,
} | ENTITY_GROUP
    | WALL_GROUP
    | RABBIT_GROUP,

さらに設定を工夫するなら、たとえばfiltersからWALL_GROUPを取り除けば、壁をすりぬけて移動できる幽霊型モンスターも作れるでしょう。

またショップの店員は、ショップの外に押し出そうとしても見えない壁に阻まれて外に行かないようになっています。というのも、プレイヤーが未精算の商品を持っているとショップの扉が開かなくなり万引きができないようになっているのですが、その際に店員が扉の外にいると会計ができなくなって詰んでしまうからです。こういう制御も衝突グループで行っています。

参考資料

Rapierの公式ドキュメントを隅々まで目を通しましょう。見落としがあるとハマって死にます。

少し古いですが、以下の資料も参考になります。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?