【第21回】ボールを打ち出す強さを決めるためのメーターを作るUnityチュートリアル
- 2018.04.21
- Unityチュートリアル
- Unity, チュートリアル
前回のチュートリアルでは、ボールの斜方投射を行う際、予想経路を画面に表示する方法をお送りしました。
計4回にわたるがっつりとした山場でしたね。
今回も若干重めかもしれませんが、UIのスライダーをカスタマイズして、ボール発射の強さを決めるメーターを作ります。
この時、メーターはゲームっぽく上下するようにしてみましょ。
前回のチュートリアルはこちらから。
今回の目的
ボールを打ち出す強さを決めるメーターをUIのSliderで実装します。
メーターは自動で上下するようにして、ゲームのプレイヤーが目押しで強さを決められるようにします。
プロジェクトの準備
前回のチュートリアルで作成したプロジェクトをそのまま使います。
このページに先にたどり着いた方は、チュートリアルの初回から追っていただけるといいかもしれません。
UIスライダーの作成
まずは今回使うスライダーを作成してみましょ。『Hierarchy』ウィンドウで『Canvas』オブジェクトを選択し、右クリックまたは二本指タップでコンテキストメニューを開きます。メニューから[Create Empty]を選択します。
名前は[PowerMeterParent]にします。このオブジェクトの子オブジェクトとして、スライダーを追加します。
今度は『PowerMeterParent』オブジェクトを選択してからコンテキストメニューを開き、[UI] -> [Slider]を選択します。
出来上がったスライダーは[PowerMeter]にリネームします。
ここでSliderコンポーネントの設定をいくつかしておきます。
Interactableの設定は[false]に。これはゲームのプレイヤーが画面から値を変えられるかどうかの設定で、今回はメーターを自動で動かすためチェックを外します。
次に、Handle Rectの設定を[None]にします。これは例のごとく右の丸ボタンから選択できます。ゲームのプレイヤーが値を変えないので、ハンドルも必要ありません。
Directionは[Bottom To Top]に変更します。縦に動くメーターにしたいので向きを変えます。
Min Valueには[3]、Max Valueには[15]を入力します。前回のチュートリアルでガイドを表示してみたところ、力の大きさが10とか20くらいだったらちょうど良さそうだったので、間をとって15にしました。メーターの最大値で止めればガイド通りに飛ぶイメージです。
最小値は0にするとあまりに飛ばなすぎるため、多少動きが出るように3としています。
Sliderの向きを変えたので、Transformも変えておきましょ。Widthを[30]に、ScaleをX, Y, Zそれぞれ[3]にします。オブジェクトの名前は[PowerMeter]にしました。
続いて、『PowerMeter』オブジェクトの子オブジェクトである『Handle Slide Area』を削除します。今回はSliderコンポーネントでHandleを外しているので、オブジェクト自体も削除してしまいます。
『Fill Area』オブジェクトでは、Topを[5]、Bottomも[5]にします。スライダーのハンドルが無くなった分、位置を修正しておきます。
『Fill』オブジェクトでは、Topを[-5]、Bottomも[-5]にします。この値は『PowerMeter』オブジェクトのSliderコンポーネントで、Valueを最大値(ここでは15)にしておくと調整しやすいです。Fillエリアがどこまで伸びているか分かりやすいですからね。
『Fill』オブジェクトではImageコンポーネントで色も変えましょ。Colorを[FFA840FF](オレンジ)にしておきます。
設定の仕上げに、『PowerMeterParent』オブジェクトの位置を動かします。アンカーを[alt(option)]キーと[shift]キーを押しながら[middle – right]に設定した後、Pos Xを[-25]に設定。
スライダーを配置した後のイメージはこんな感じ。画面右のオレンジのメーターが今回作ったもの。
実は右下のボタン、太字にしちゃいました。ずっと気になってたんですよね、ここだけNormalだったのが。『Button』オブジェクトの子オブジェクトである『Text』オブジェクトのTextコンポーネントで、Font Styleを[Bold]に変更しました。
ついでに[Add Component] -> [UI] -> [Effects] -> [Outline]からOutlineコンポーネントも追加。Effect DistanceのXを[2]、Yを[-2]に設定します。
配置が終わったので、今度はスクリプトを編集して動きを作っていきます。
スクリプトの編集方針
今回の編集方針は以下の通りです。
- 上下するメーターに応じてボールに加わる力を変える
- メーターを上下させる
- 最大値、最小値の時は少し止めたい
メーターの上下はUpdate()を使って毎フレーム値を変えていけば良さそうです。上下だと、上がる時と下がる時で動きを切り替える必要がありますね。
また、最大値、最小値になった時はメーターがちょっとだけ止まるようにします。最大値を選択できるのが1フレーム(60FPSで0.016秒)だけとか、TASさんじゃないとムリ。
編集したものがこちら
上記方針で編集したスクリプトについて、更新点を中心に解説していきます。全文を確認したい場合は『GitHub Gist』をみてね。
まずはメンバ変数の追加と更新から。
先に既存部分の更新点ですが、forceMagnitudeの[SerializeField]を外しました。今回メーターでボールに加わる力の強さが変わるようにしたので、Inspectorから値を変えてもすぐにメーターの値が格納されることに。余計な設定を残しておくのは混乱の元なので、Inspectorから変えられないようにしました。
今回新規で追加したのは、『PowerMeter』オブジェクトへの参照、Sliderコンポーネントへの参照、メーターの上下するスピード、ディレイとウェイト、メーター増加中フラグです。
参照はいつものアレですが、meterSpeedはメーターが上下するスピードで、Inspectorから調整できるようにしています。毎フレームこのスピードの分だけメーターが変動します。
delayTimeは最大値または最小値で止まる時間で単位は秒。0.08秒だと、60FPSの場合だいたい5フレームから6フレームくらいの入力猶予があります。あまり止めすぎてもゲームとして面白くないし、かといって短くしすぎてもシビアすぎるしで調整が難しいところ。
waitTimeは最大値または最小値でどれだけ待ったかを表しています。この時間がdelayTimeを超えたらメーターが動き出すようにします。
メーターの上下を表現したいので、そのフラグも用意。
Start()の中ではpowerMeterSliderへの参照をセットしています。Sliderコンポーネントは特にアクセスする回数が多いので、GetComponentで参照をキャッシュしておかないと泣く羽目になります。
Update()では、MovePowerMeter()を呼ぶ処理を追加しました。この処理の中身は後述しますが、Update()が呼ばれるためにスライダーの値を変えていきます。時間変化で量が変わる処理はUpdate()を使うことが多いです。
GetBoostForce()は力の大きさと方向をかけてVector3を返すメソッドですが、外部からガイドを表示するために呼ばれています。ガイドは最大値を基準に表示したいので、スライダーの最大値と、角度によって計算される力の方向をかけて値を返すようにします。
この時、外部のスクリプト(GuideManager)にあるStart()の方が早く処理されることがあり、SphereBoosterのStart()より早いと参照がセットされてなくて「NullReferenceException」のエラーが出ます。なので、nullチェックして参照がなかったらここで参照をセットするようにしています。
今回のメインであるMovePowerMeter()です。メーターを上下させるのは飛行中フラグがfalseの時にします。なのでtrueだったらreturnしてメーターを動かさないようにしています。
境界値は、メーターを止める時の値、つまり最大値か最小値を格納するために用意しています。分岐後の処理を共通化したかったので、ローカル変数boundaryValueを作っています。
メーターが増加中にはスライダーの値を増やし、減少中にはスライダーの値を減らしています。スライダーの値を直接変えているのは、最大値や最小値になったらその値でストップしてくれるためです。
分岐の後は、境界値に達したかどうかを確認しています。float型の変数の場合、等価演算子(==)を使った比較は非推奨なので、MathfのApproximately(ほぼ同じ)を使って比較しています。floatには誤差があるため、ぴったり一致しないんですよねぇ。
境界値に達していたら、そこで少し待つ処理を入れています。
最後に、forceMagnitudeにスライダーの値を入れて、力の計算ができるようにしています。
WaitAtBoundaryValue()は境界値で待つ処理です。Time.deltaTimeで前フレームの処理にかかった時間を取得して、フレーム間の処理時間をwaitTimeに足しています。
delayTimeを超えたら、増加フラグを反転させてメーターの動きを逆にします。
今回実装したスクリプトの全文は『GitHub Gist』にあります。
オブジェクトの参照をセット
スクリプトを更新したらオブジェクトをセットします。
今回新しく追加されたのは『PowerMeterObject』の項目です。『Hierarchy』ウィンドウから『PowerMeter』オブジェクトをドラッグ&ドロップするか、右の丸ボタンから選択しましょう。
動作確認
んじゃ確認しましょ。(GIFは2倍速)
メーターが上下して、その値に対応してボールが飛んでいます。力に関してはこれでいいでしょう。GIFが2倍速なのでメーターの動きがめっちゃ早いですが、実際にはこの半分だから目押しも大丈夫そう。
次は角度を変えられるようにしましょうか。角度は0度から90度の範囲でゲームのプレイヤーが任意の値を選択できるようにしましょ。
角度と力をゲーム内で操作して、ゴールを目指す感じのゲームにできそう。うん、カービィボウルっぽい。
まとめ
今回はメーターを自動で上下させて、ボタンを押した時の値を読み取ってボールの強さとする処理を実装しました。
ボールの飛ばし方を決めるのは力の大きさと方向なので、力の大きさの方を実装することができました。ということは次回は、力の方向ですね。
角度についてはプレイヤーが任意の値を選べるようにして、それにしたがってガイドも動かすようにします。
ゲーム開発の攻略チャートを作りました!
-
前の記事
【第20回】斜方投射でボールの予想経路を示す・スクリプト実践編 2018.04.19
-
次の記事
【第22回】アンカー・ピボットを使ったUIのレイアウトのチュートリアル 2018.04.23
こちらのサイトで勉強させていただいています。
SphereBooster.csのスクリプトを反映させていざ動作確認をしようとしたところ、UnassignedReferenceExceptionエラーが起きました。
SphereのPowerMeterObjectにPowerMeterをドラッグ&ドロップすると問題なく動作確認できたので、その作業も説明していると親切だと思います。
個人的にはエラーの対処の訓練にはなりました。
スクリプト編集後のオブジェクト参照のセットについて
記事内で項目を追加しました。
ご指摘いただきありがとうございます!
他のページについても順次確認していきますが、
この後のページでもオブジェクト参照について
手順に入っていない! なんてことがあるかもしれないので、
その部分については今回と同様に
ゲーム実行前に参照のチェックをお願いできればと思います。