HaLake Magazine

コワーキングスペースHaLakeの技術情報発信サイト!IoT,AR,VRなど最新技術情報をお届け!

Defoldで物理演算を利用したゲームの作り方

前回Defoldで簡単なカーソル移動でコインを集めるゲームを作成しました。

今回は物理演算も自動で行ってくれる強味を活かしたゲームを作れるようになるために、機能の説明をしていきます。

最後に出来上がったファイルはこちらにサンプルとして載せておきます。

github.com

環境

  • windows 10 home 64bit
  • Macでの動作確認はしていません。

前提事項

前回説明した範囲はなるべく省いてテンポが良いように説明していきますので、前回の記事を読んでからこちらの記事を進めてもらうと分かりやすいです。下にリンクを載せておきます。

magazine.halake.com

前回と同様こちらで使用する画像はライセンスCC0と明記されたものを使用して行います。各自で使用する画像を変える場合は著作権などに違反しないようにしましょう。以下に今回使用する画像をダウンロードしたサイトを示します。

  • 注意:ネットの画像を使用する際はライセンスなどに注意して使用しましょう。本記事を参考にした場合に起こった問題は全て個人の責任になるため、筆者は一切の責任を負いかねます。

pixelfrog-assets.itch.io

目次

  1. 準備
  2. オブジェクトに力を加える
  3. タイルマップの使い方
  4. カメラの位置を変える
  5. まとめ

準備

前回同様、新規プロジェクトを作成してダウンロードした画像フォルダを解凍して、作成したassetsフォルダに入れておきます。

まず最初に重要な編集をしていきます。

game.projectを開いてゲーム内の全体的なパラメータを編集をしました。

ここではScaleパラメータの方が重要なので先に説明します。

リファレンスではScaleは以下のような説明になっています。

Scale
Tells the physics engine how to scale the physics worlds in relation to the game world for numerical precision, 0.01–1.0. If the value is set to 0.02, it means that the physics engine will view 50 units as 1 meter ($1 / 0.02$). The default value is 1.0.

引用:https://defold.com/manuals/project-settings/#scale

Deeplによる訳

物理エンジンに、ゲーム世界に対して物理世界をどのようにスケールさせるかを数値の精度で指示します。0.02に設定すると、物理エンジンは50ユニットを1メートルと見なすことを意味します($1 / 0.02$)。デフォルトは1.0です。

forum.defold.com

上記のフォーラムを合わせて読みと分かりやすく、スケールを0.02に設定すると50(ユニット=ピクセル)が1メートル相当の計算になるようです。

そのため、デフォルトの1.0設定だと数値的には1メートル進んでいますがピクセルでも1ピクセルしか進んでいないため、人間から見るととても遅く見えるようです。

そのため、0.02などの1より少ない値にすることで1メートルあたりのピクセル数を大きくしてあげることによって現実世界の実際の長さとピクセル数を寄せる必要があるようです。もちろん完全に合わせることは無理なので、直感的なスピードになる程度に合わせることが重要です。

これらを含め、Gravity Yで設定した値が-500だったのは、スケールで1メートルを50ピクセル分と設定したため以下の式が成り立ちます。(重力は下方向に働く力のためマイナス)

-500 / 50 = -10 ≒ -9.8 (m/s2)

分かりやすくするためにはスケールを変えたら、重力もしっかりと現実世界にリンクさせる必要があります。

これで設定とその説明は終わりました。

オブジェクトに力を加える

前回までの説明で、ゲームオブジェクトの作成と当たり判定のコリジョン、ゲーム画面への出力は説明済みのため省略します。

一部詳細設定

  • 画像が小さかっため、画像スケールは2倍
  • コリジョンオブジェクトのTypeDynamic
  • コリジョンオブジェクトの名前をangry-pigに変更(重要)

現時点ではこんな感じです。

画面左下に表示しているので実行すると一瞬で下に行って消えます。

なので適当にコリジョンオブジェクトで作成した透明の床を作ります。(TypeStatic

そうすると透明の床で止まってくれます。

次に、オブジェクトを飛ばすためのスクリプトを書いていきます。

スクリプトを作成して以下のプログラムに書き変えたうえでゲームオブジェクトに適用していきます。

angry-pig.script

function init(self)
    msg.post(".", "acquire_input_focus")
end


function on_input(self, action_id, action)
    if action_id == hash("key_right") and action.pressed == true then
        msg.post("#angry-pig", "apply_force", {force = vmath.vector3(10000, 150000, 0), position = go.get_world_position()})
    end
    if action_id == hash("key_left") and action.pressed == true then
        msg.post("#angry-pig", "apply_force", {force = vmath.vector3(-10000, 150000, 0), position = go.get_world_position()})
    end
end

キーボード入力するので以下のようにgame.projectを変更してください。キー割り当てはデフォルトですべて割り当てられているファイルがあるのでそれを使用します。

再度ビルドして実行すると左右の矢印キーでピョンピョン飛び跳ねるようになります。

msg.post("#コリジョンオブジェクトの名前", "apply_force", {force = vmath.vector3(xの数値, y数値, 0), position = go.get_world_position()})

このように、コリジョンオブジェクトにapply_forceメッセージを送ることによって任意のタイミングでオブジェクトに力を加えることができます。上記のangry-pig.scriptのように記述する場合、Idがangry-pigと名のついたコリジョンオブジェクトが存在する必要があります。

defold.com

タイルマップの使い方

最初に示したパブリックドメインの画像集をダウンロードすると以下のような画像が含まれていますので、こちらを使って説明していきます。

Terrain (16x16).png

以下の作業をします。

編集画面でSpaceキーを押すとタイル選択画面になるので、欲しいタイルをダブルクリックして選択します。 選択するとまた編集画面に戻ってきます。

Shiftキーを押しながら編集画面でタイルをダブルクリックすると選択したマスのタイルを選択状態にすることができます。

Shiftキーを押しながら何もないマス目を選ぶと消しゴムと同じ効果を得られます。

また、編集画面でShiftを押したままドラッグすると範囲コピーできるので、べた塗りの時はある程度埋めて範囲コピーして全体に適応していく方が早いと思います。人マスずつ埋めていくのが基本なのかこのマップ作成方法はかなり面倒でしたがほかに良い方法が見つけられなかったです。

これらを駆使して床や壁、背景の見た目を作れます。

編集したマップを床のコリジョンオブジェクトが適応されているGameObject内に配置するとこんな感じになりました。動画撮影の影響で右端に余白がありますが実際のゲーム画面ではすべて収まっています。

今回はキャラクターを2倍のスケールで表示しているのでタイルマップも2倍のスケールに設定しています。

また、初期設定でタイルマップを置きすぎると以下のエラーが出るようなので、Game.Project内の設定を変更すると回避できます。

私の環境では以下のような、横長のフィールドを用意していますが、カメラが固定のため画面から外れると見えなくなってしまいます。そのため次の項ではカメラをキャラクタに追従させる方法を説明します。

カメラの位置を変える

こちらの記事通りに行うカメラで特定の物を追従できますが、特定の物を回転させたときにカメラも一緒に回ってしまうのと、若干記述量が多く難しくなってしまうため単純にカメラの座標を随時更新させる方法で進めて行きます。

defold.com

既に前の項で作成したangry-pig.scriptを以下の内容に書き変えます。

angry-pig.script

function init(self)
    msg.post(".", "acquire_input_focus")
    self.follow = false
end


function on_input(self, action_id, action)
    if action_id == hash("key_right") and action.pressed == true then
        msg.post("#angry-pig", "apply_force", {force = vmath.vector3(500000, 200000, 0), position = go.get_world_position()})
    end
    if action_id == hash("key_left") and action.pressed == true then
        msg.post("#angry-pig", "apply_force", {force = vmath.vector3(-500000, 200000, 0), position = go.get_world_position()})
    end
end

function update(self, dt)
    local p = go.get_position()
    if self.follow then
        go.set_position(vmath.vector3(p.x - 980 / 2, 0, 0), "camera")
    else
        go.set_position(vmath.vector3(0, 0, 0), "camera")
    end
    if p.x > 980 then
        self.follow = true
    else
        self.follow = false
    end
end

camera.scriptを作成して以下に編集します。

camera.script

function init(self)
    msg.post("#camera", "acquire_camera_focus")
end

その次にmain.collection内cameraと名付けたGameObjectを作成します。

そして、新しくCameraコンポーネントと先ほど作成したcamera.scriptcameraゲームオブジェクト内に作成します。

そうするとこのような感じになります。

これで実行すると以下のようになることがわかります。

camera.scriptではカメラを固定でなく動的なものとして設定しています。

defold.com

angry-pig.scriptではupdate関数が追加されて、キャラクターがx座標980を超えたら画面中央の縦のライン内に来るように随時cameraゲームオブジェクトの座標を変更するように記述しています。

まとめ

今回は、他のの記事ではあまり説明されていない内容を重点的に説明していきました。 以前の記事の内容よりも、これらを利用することによって作れるゲームの幅が広まったと思います。