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 23

【Rustのまほう2】#23 物理エンジンの初期化

Last updated at Posted at 2024-12-22

適度なネタが思いつかなかったので、一番大きな話題ともいえる物理エンジンについて触れておきます。

大抵のアクションゲームでは、たとえばキャラクターと壁の当たり判定や、ジャンプしたときに放物線にそって動く計算などが必要になるでしょう。簡単な動きだけでいいなら自力でコーディングしてもいいですし、実際私もおととし書いたアドベントカレンダーでは自力で当たり判定を書いています。

でも、自力で実装したアクションというのはかなりショボいです。そしてそうやって自力で実装したアクションゲームだと、キャラクターとリフトの摩擦が考慮されておらずリフトに乗ったらリフトだけが移動して取り残されたキャラクターが落下というバグがよく起こったりします。いろんな挙動をなるべく自然に見せたかったら、結局物理エンジンを導入するのが無難かなと思います。

一方で、物理エンジンは設定項目が多く、設定を間違えると変な動きをするわけに原因がさっぱりわからない、と非常に厄介です。かなり覚悟を決めてかかる必要があります。ぶっちゃけアクションゲームはこの物理エンジン設定バトルが大きいので、慣れていない人はアクションゲームを作るのは避けたほうがいい気がします。私はアクションが一番好きなのでどうしてもアクションゲームにしてしまうのですが……。

物理エンジンの選択

BevyやRust界隈だと、Rapierという物理エンジンが最もメジャーだと思います。RapierはBevyとの統合も比較的こなれています。

それ以外の物理エンジンだと、Avianというのがあります。これは別の物理エンジンを作っていた作者が新たにBevy向けに作り直したという新しいエンジンです。かなり期待が持てそうなのですが、まだ出てきて間もないので今回はパスしました。いつか試してみたいです。

bevy_rapier2dの初期化

rapierではプラグインを追加するときにいくつか設定項目があります。デフォルトでは長さ1が1メートルになっており、たとえば縦横16ピクセルのスプライトに縦横16の剛体を設定すると、高さ16メートルのビルが倒れるときのようなゆっくりとした動きに見えます。これでは奇妙なので、pixels_per_meterで縮尺を調整するといいでしょう。私は0.01を設定して、縦横16ピクセルは縦横16センチメートルにしてあります。

app.add_plugins(
    RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(PIXELS_PER_METER)
        .in_fixed_schedule()
        .with_custom_initialization(RapierContextInitialization::NoAutomaticRapierContext),
)

また、シミュレーションステップ間隔を固定するin_fixed_schedule()や、Rapierのワールドのコンテキストを自力で設定するwith_custom_initializationを設定しています。

ワールドの初期化

最新版のRapier 0.27から物理エンジン世界はエンティティとなり、アプリケーション全体で複数のワールドを持てるようになりました。複数のワールドなんてどういうときに使うのか想像つきませんが……。

with_custom_initializationを呼んで自力で初期化する場合は、以下のようにして RapierContextをspawnします。このRapierContextがRapierの物理世界であり、この中にさまざまな剛体がツリー構造で収められているイメージです。デフォルトのコンテキストを指定するため、DefaultRapierContextも付けておきます。また、この物理世界はRapierConfigurationで重力を設定できます。デフォルトでは垂直方向に-0.98の重力加速度が設定されていますが、今回私が作っているゲームは見下ろし型なので重力加速度はゼロとします。

fn setup_rapier_context(mut commands: Commands) {
    commands.spawn((
        Name::new("default rapier context"),
        DefaultRapierContext,
        RapierContext::default(),
        RapierConfiguration {
            gravity: Vec2::ZERO,
            physics_pipeline_active: true,
            query_pipeline_active: true,
            scaled_shape_subdivision: 10,
            force_update_from_transform_changes: false,
        },
    ));
}

見下ろし型のゲームでもこの重力を設定すれば、キャラクターが風に流されるような効果を出すこともできるでしょう。

物理シミュレーションの停止

ゲームのポーズなど、物理シミュレーションを一時的に止めたいときがあります。その場合はRapierConfigurationphysics_pipeline_activefalseに設定すればOKです。

剛体の設定

剛体の設定については、以前の記事で少しだけ触れました。RigidBody::Dynamicを設定すれば、それだけでその物体は剛体として振舞うようになり、重力に従って落ちたり、ぶつかったら跳ね返ったりするようになります。

剛体の種類は以下の四種類です。

  • Dynamic 普通の剛体。私のゲームでは、動き回るキャラクターはすべてDynamicです。また、宝箱や灯篭などの一見動かなそうなエンティティもDynamicにして、密度を大きくして動きにくくしてあります。なので灯篭を魔法で攻撃するとちょっと動いたりします。なんかいろいろ動いたほうが画面が賑やかで楽しいからです。
  • Fixed 絶対に移動しない物体。壁などはFixedです。
  • KinematicPositionBased プログラムで動的に位置を指定する形で移動することがある物体。私のゲームでは扉がKinematicPositionBasedになっています。
  • KinematicVelocityBased プログラムで速度を指定する形で移動する物体。魔法の弾丸がKinematicVelocityBasedになっています。この剛体は他の物体が当たって移動方向が変わったりしませんので、一定速度で動く弾丸に向いています。速度を細かく制御しやすいので、プレイヤーキャラクターをKinematicVelocityBasedにする手もありそうです。

剛体として設定されたエンティティは、摩擦や密度、外力や衝突グループなど、さまざまな大量の設定項目があります。とても全部は紹介しきれませんが、次回以降に少しずつ紹介していくかもしれません。

参考資料

bevy_rapier2dは Bevy 0.15 に合わせてAPIが破壊的に変更されており、最新版に対応したドキュメントや記事などは、日本語圏はもとより英語圏にも存在しません!頑張ってサンプルコードから学びましょう。

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?