【第20回】斜方投射でボールの予想経路を示す・スクリプト実践編

【第20回】斜方投射でボールの予想経路を示す・スクリプト実践編

前回のチュートリアルでは、経路を表示するためのオブジェクトをスクリプトからインスタンス化する方法をお送りしました。

今回はそれを応用し、実際にガイドとしてボールが飛ぶ経路を予想して表示させてみます。やろうとしていることが物理的なシミュレーションなので、どうしても物理の話が出てきてしまいますがご了承ください。

あれっ? 初心者用チュートリアルだったような……? という訳で厳密な計算というよりは、ざっくりと計算していくので楽な気持ちでやってみましょ。

前回のチュートリアルはこちらから。

 

今回の目的

斜方投射で予想される経路を3Dの球を並べて表示します。

今回は実際にスクリプトからガイド用のPrefabをインスタンス化します。なお、表示する範囲は地面にぶつかるまでの経路とします。

バウンドした後についても扱おうかと思いましたが、初心者チュートリアルの範囲を超えていたので割愛。

プロジェクトの準備

前回のチュートリアルで作成したプロジェクトをそのまま使います。

このページに先にたどり着いた方は、チュートリアルの初回から追っていただけるといいかもしれません。

経路の計算イメージ

今回の計算は以下の方針に基づいて行います。

予想経路の計算方針
予想経路の計算方針

 

斜方投射が行われた後、再び地面に衝突する時の時刻tを算出します。

その時刻を分割(ここでは8分割)して、時刻に対応した位置を計算し、各ガイドオブジェクトの位置にセットします。緑色の球がガイドの位置を表しています。

初速度さえ分かれば公式に当てはめていくだけなので、実は教科書通りに計算できます。

細かいズレ等はあるかもしれませんが、そこはゲームなので生暖かい目で見ることにしましょ。多少のズレは……誤差だよ誤差!

というかUnityの物理シミュレーションをさらにシミュレーションするのもなかなかシュールですね。

なお、今回使った公式については綺麗にまとまっていたこちらのページを参考にさせてもらいました。

スクリプトの編集

上記の計算イメージを踏襲して、以下の方針でスクリプトを編集します。

  • 最初のフレームでガイド用オブジェクトをインスタンス化
  • インスタンス化した時に、ガイドのListに格納
  • 『Sphere』オブジェクトの力の大きさから初速度を出し、地面に衝突する時刻を算出
  • その時刻から、分割されたポイントを通過する時刻をList化
  • 時刻Listから位置を計算し、ガイドListのオブジェクトに対して適用

これをうまく盛り込めば、上のグラフのように、緑色の球が配置されるはず……!

と祈りを込めて書いたスクリプトについて解説していきます。

今回はSphereBooster.csと、前回作成したGuideManager.csを編集しています。

 

 

このメソッドはSphereBooster.csに追加します。今回はSphereBoosterの外から、『Sphere』オブジェクトに加わる力を取得する必要があるんです。

力の大きさを表すforceMagnitudeをpublicにして参照する……のでもいいのですが、外から知りたいのは力の大きさと向きの両方をかけた値です。

なので、これをSphereBoosterに計算させてしまって、結果だけ返してもらうようにしています。これならメンバ変数をpublicにする必要もないですし、安全かな。

 

 

次はGuideManagerの方です。ここにはメンバ変数を追加しています。

今回SphereBoosterに追加したGetBoostForce()を呼びたいので、スクリプトへの参照をキャッシュしています。

List<GameObject>は、GameObject型のリストを表しています。C#だと配列の他にListを使うことができます。Listの場合は要素の追加や削除が簡単なので、何かとお世話になる機能。

画面にプロットするガイドの数もここで定義しています。

 

 

Start()ではSphereBoosterへの参照、『Sphere』オブジェクトのRigidbodyへの参照、guideListの初期化を行なっています。実はスクリプトも他のコンポーネントと同じようにGetComponentできるんです。

InstantiateGuidePrefabs()は前回から存在しているメソッドですが、今回は8つのガイドオブジェクトをインスタンス化する処理を行なっています。

SetGuidePositions()はguideListに存在するガイドオブジェクトの位置をセットするメソッドで、詳細は後述します。

 

 

InstantiateGuidePrefabs()の詳細です。インスタンス化する流れは前回と似ていますが、新たにfor文を入れて、プロットするガイドの数だけオブジェクトを生成しています。

生成したオブジェクトはListのAddメソッドを使ってguideLstに追加しています。

 

 

続いてSetGuidePositions()です。最初にGuideParentの位置を『Sphere』オブジェクトの位置に合わせています。

リストの検証では、nullチェックと要素数のチェックをしています。guideListに何も含まれていない場合は、ガイドオブジェクトがインスタンス化されていないので処理を行いません。

物理的なパラメータに関してですが、今回は力の大きさから初速度を出して計算を行なっています。そのため、『Sphere』オブジェクトから力の大きさと向きをかけたものを読み込んでいます。

質量については『Sphere』オブジェクトのRogidbodyから、重力についてはPhysicsManagerからそれぞれ取得しています。

初速度については、力積のmv=FΔtを式変形して導いています。力を与える前は停止しているので、変化後の運動量と力積の値は等しくなります。

上の式を変形すればv=FΔt/mという形で表せますね。SphereBoosterの方で力の加え方をImpulseにしているのがここで生きてきます。

Impulseと言えば力積。AddForceの中でForceMode.Impulseと指定すると、引数に渡すベクトルを力積として扱ってくれるんです。GetBoostForce()で返しているベクトルは、SphereBoosterのAddForceで引数として渡している値と同じもの。したがって力積FΔtを返してくれていることになるため、これを質量で割れば初速度を求められます。

ソースコードを見ることができないので、内部の正確な計算は正直わからないのですが、デバッグ文を仕込んで位置を確認してみると、だいたい合っていそうな動きをしていました。

初速度、重力、プロット数を元に、着地する時刻から導いたプロット毎の時刻をリスト化し、それをGetExpectedPosition()に渡して位置を計算しています。

 

 

GetTimeProtsList()では、初速度、重力、プロット数を元に時刻のリストを計算します。landingTimeは地面に到達する時の時刻で、これをプロット数で割っています。公式がそのまま使えるので便利ですね。

なおマイナスがかかっていますが、gravity.yは負の値なので時刻は正の値になります。

ゼロ除算を避けるため、プロット数が0でないことを確認してから各プロットの時刻を計算しています。

 

 

最後にガイドの位置を計算するGetExpectedPosition()です。

こちらのメソッドは上記の記事を参考にさせてもらいました。

時刻毎に位置を計算しています。各成分について位置を求めたいのですが、Vector3をそのまま使える点が素敵です。

なお、上記の記事では、AddForceのForceModeをデフォルト(ForceMode.Force)にされていたので、このチュートリアルとは速度の求め方が違う点にご注意ください。

 

スクリプトの全文は『GitHub Gist』に。SphereBooster.csGuideManager.csを置いてあります。

動作確認

Unityに戻ったらゲームを実行してみます。

ガイドが表示された
だいたい通ってるっぽい!

 

うまくいっていそうですね。厳密には若干のズレがあって、SphereBooster.csのforceMagnituneを100とかにするとガイドの位置を通らなくなります。angularDragとかも影響しているんでしょうかね?(未検証)

が、そこまで力を大きくしなければ問題なし!

だいたい10とか20といった力の大きさならガイドで表せそうなので、それくらいの範囲で力の大きさを決められるようにしましょ。

実際にゲームにすることを考えると、上下するメーターで力の大きさを決めて飛ばす感じにしたいので、おおよその位置が示せれば問題ないと思います。飛行中はガイドも消す予定ですし、だいたいで大丈夫です。

これでボールに与える力の大きさが決められそうなので、次回からはゲーム画面から力の大きさを決める処理と、それに合わせた落下判定オブジェクトの配置を行なっていきましょ。

 

Prefabのインスタンス化について単独でまとめた記事もあるので、こちらもご参照ください。

 

まとめ

チュートリアルを4回ほど使って、斜方投射を行う際、ボールの予想経路を画面に表示する機能を実装しました。

その過程でMaterial、Prefab、そしてスクリプトからのインスタンス化など、ゲームを作る上で重要な機能も紹介したので、色々試してみてね。

次回は、打ち出す力を決めるメーターを作ってみましょうか。

     

ゲーム開発の攻略チャートを作りました!

CTA-IMAGE

「ゲームを作ってみたいけど、何から手を付けていいか分からない!」


そんなお悩みをお持ちの方向けに、todoがアプリをリリースした経験を中心に、ゲーム作りの手順や考慮すべき点をまとめたe-bookを作成しました。ゲーム作りはそれ自体がゲームのように楽しいプロセスなので、「攻略チャート」と名付けています。


ゲームを作り始めた時にぶつかる壁である「何をしたら良いのか分からない」という悩みを吹き飛ばしましょう!