と本講座ではUnityで3Dレースゲームの作り方について説明しています。
今回は第8回目となります。前回はUnityのCinemachineを利用して対戦相手となるCPUの車を作成し、自動運転の機能を実装しました。
前回の記事 :
それぞれの車にはコライダーを追加しているため、今のままだと衝突した際にCPUの車がおかしな挙動を取ってしまいます(車体をぶつけた後コースアウトしていったまま戻ってこないなど)。
そこで、車のコライダーや重心の調整や速度制限などをまず行います。
また、周回数を計測しているのでプレイヤーと敵合わせてレースゲーム終了後の順位を決められるようにしていきます。
では始めていきましょう!
CPUの調整 速度制限・体当たりされた時に元のコースに戻す処理
ここではCPUが自動で運転しながらレースを行う際に妨げとなるバグや不都合な部分を取り除いていきます。
まずは速度が出すぎた時に判定処理が成功しないと困るため、速度制限の実装からいきましょう。
対戦相手の車の移動速度の制限を実装しよう
前回少し触れました、周回数の計測にコライダーを使用しているため速度が速い時にうまく計測されない可能性があります。(Playerが追いつけないほど速度が速いとこうした計測バグが起こり得ます)
これを防ぐためにはコライダーの大きさを大きくしたり、周回数の計測方法を変更したりでもいいのですが、今回は簡単に速度を制限します。
前回作成しました、「CPU」の移動処理は、「CPUController.cs」の「lapTime」で1周にかかる時間を設定しており、その時間が短いほど早くなります。なのでこの値を制限すればOKです。
では、「CPUController.cs」を開いて修正します。
1 2 3 4 5 6 7 |
public class CPUController : MonoBehaviour { ・・・ // ラップタイム(1周にかかる時間). [SerializeField, Range( 40f, 200f )] float lapTime = 40f; ・・・ } |
これだけです。
変更したのは、変数の「lapTime」の属性部分です。元々あった「SerializeField」の横に「,」をつけて「Range」という属性をつけます。
数値は各々変更しても構いませんが、上記のように記載すると「lapTime」を「40f〜200f」に制限することができます。また「SerializeField」属性でUnityのInspectorで値が変更できますが、その際の値が制限されバーのような形になります。
車体同士が衝突した時にコースアウトさせないようにする処理を実装
前回周回数を計測するためにコライダーとリジッドボディを付与しました。これによりPlayerとの衝突が可能になっています。
このコライダーとリジッドボディでの衝突は、「付与されているオブジェクト」が衝突し物理的な反発などの作用がおきます。
当然と言えば当然ですが、今回コンポーネントを付与しているのは「Free_Racing_Car_Red」ですのでこのゲームオブジェクトが衝突によって動いたりします。まずはこのことを頭に置いておいてください。
次に、今回CPUの移動に使用している「Cinemachine」の機能ですが、パスの設定をし「Dolly Cart1」を動かしているというのは前回お話ししました。この「Dolly Cart1」が移動しているので、ちょうどカートのように、親に乗っかってその子にある「Free_Racing_Car_Red」も移動します。
そして、ここに衝突した場合にその影響で動くのはコライダー、リジッドボディを付与している車なので子の部分のみ動くことになります。
例えば、上方に移動しているときに左方向から衝突された場合、下記のように「Dolly Cart1」から「Free_Racing_Car_Red」のみが移動し親子間で位置の差が出ます。
しかし「Dolly Cart1」の移動が続いていれば、ずれたままで移動し続けることになります。
実際はここまで極端には移動しませんが、これを修正するためには親子間の位置の差がでたらその差を戻すようにしてあげればOKです。
なので処理としては、「Free_Racing_Car_Red」の位置を親である「Dolly Cart1」に合わせ続けるようにしていきます。
そして「Free_Racing_Car_Red」は「Dolly Cart1」の子なので、「Free_Racing_Car_Red」の「LocalPosition」「LoacalRotation」を全て「0」にすることが、合わせることになります。
では、処理を書いていきます、「CPUController.cs」を開きましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public class CPUController : MonoBehaviour { // 車のトランスフォーム. [SerializeField] Transform carTransform = null; ・・・ void Update() { if( CurrentState == GameController.PlayState.Play ) { var deltaDistance = velocity * Time.deltaTime; dollyCart.m_Position += deltaDistance; if( carTransform.localPosition != Vector3.zero || carTransform.localRotation != Quaternion.identity ) { FixedCarPosition(); } } else if( CurrentState == GameController.PlayState.Ready ) { dollyCart.m_Position = 0; } } // ------------------------------------------------------------ /// <summary> /// 車の位置をカートに徐々に合わせる. /// </summary> // ------------------------------------------------------------ void FixedCarPosition() { carTransform.localPosition = Vector3.Lerp( carTransform.localPosition, Vector3.zero, Time.deltaTime * 1f ); carTransform.localRotation = Quaternion.Lerp( carTransform.localRotation, Quaternion.identity, Time.deltaTime * 1f ); } } |
まず変数に「SerializeField」で「carTransform」というTransform型の変数を作成します。これは後でInspectorで「Free_Racing_Car_Red」をドラック&ドロップしましょう。
そして、関数「FixedCarPosition()」を追加します。この関数は「Update()」内で「carTransform」つまり車の位置が「0(Vector3.zero)」もしくは角度が「0(Quaternion.identity)」でない時に実行されます。
中の処理は、「carTransform」のローカル位置と角度を0にする処理ですが、ずっと0を入れ続けると動かない状態になってしまいますので、位置では「Vector3.Lerp」、角度は「Quaternion.Lerp」を使い徐々に位置を調整しています。
引数にある「Time.deltaTime * 1f」の「1f」ですが、係数を入れるときはこれを変更しましょう。このままでも大丈夫ですが、戻る速度が変わります。
この「Lerp」はカメラがプレイヤーを追跡する処理の時にも使用しましたが、ざっくり書き方を覚えておけばOKです。
Rigidbody回転の固定
衝突できることにより、車同士などの衝突で車が変な方向に傾いてしまったりします。なので今回は簡単にCPUの車はY軸方向(左右回転)以外はできないようにしましょう。
Hierarchyの「CPU」の子の車「Free_Racing_Car_Red」を選択しInspectorで「Rigidbody」コンポーネントの「Constraints」の「FreezeRotation」でXとZにチェックを入れてください。
Playerの調整
1 2 3 4 5 6 7 8 9 10 11 12 |
public class PlayerCarController : MonoBehaviour { ・・・ void Start() { if( rigid == null ) rigid = GetComponent<Rigidbody>(); rigid.centerOfMass = new Vector3( 0, -0.2f, 0 ); ・・・ } } |
とても簡単で、「Start」関数内で、「rigid.centerOfMass」の値を「new Vector3( 0, -0.2f, 0 )」に設定します。
「centerOfMass」というのは文字通りリジッドボディの質量の中心、つまり重心です。これを「Vector3」でYの値を「ー0.2」つまり、中心より少し下に置いて転倒を防止します。
順位を決める
ここからはレースらしく順位を決めていきます。
今はPlayerとCPUの2台しかいないので、寂しい方はCPUを同じように追加してみましょう。コピーして「DollyTrack1」のルートをすこし変えるだけです。
車の色を変えたい場合は「Free_Racing_Car_Red」を別のものに差し替えますが、この時「Rigidbody」と「Collider」のコピーと、「CPUController.cs」のInspectorへの車のドラック&ドロップ、また「GameController.cs」の「cpuList」へのドラック&ドロップ、変更した車のGameObjectのTagを「CPU」に変更する、この4つを忘れないようにしましょう。
ここでは、ひとつ増やして「CPU2」という車を増やしました。
UIの微調整
一旦UIを調整します。現在ゴールした時のリトライUIが「Goal」の文字より前に来て邪魔になっているのでこれを調整します。そしてここに順位を表示します。
まず、Hierarchyで「Canvas」の子を見てください。ここに「CountdownText」というのがあると思いますが、これをドラック&ドロップして「Retry」の下に移動して順番を変えましょう。
これだけで「Retry」の前に「CountdownText」が見えるようになったかと思います。Unityでは同じCanvas内にある場合、Hierarchy上でより下にあるものが手前に表示されます。
そして「CountdownText」を適当に上に移動しておきましょう。これでカウントダウン時も、ゴール時も見える位置になりました。
またこの時「CountdownText」の「Text」コンポーネントで「RaycastTarget」のチェックを外しておきましょう。この「RaycastTarget」というのは文字や画像にあるもので、「Raycast」のターゲットにするかのフラグです。ボタンを押す時などUIをクリックやタップで操作する時には必ず「Raycast(レーザーのようなもの)」をクリック位置に放って、それにヒットしたUIを「クリックされた」とUnity内部で処理しています。
ボタンを押したりする必要のないUIでは、「RaycastTarget」をオフにしておくと押したいUIの前に別のUIが被って押せない、ということを防ぐことができるので意識しておくといいでしょう。
順位決定処理の追加
では、処理を追加していきましょう。まずは「CPUController.cs」を開いてください。
今CPUはゴールした時に止まってしまい、他の車の移動の邪魔になってしまうのでゴール後も動き続けるように修正も同時にやっていきます。
次回に向けて
長くなりましたがこれで完了です。
Unityに戻ってゲームを開始してみましょう。
スタート→指定周回数を走る→ゴール&順位表示ができるようになっているかと思います。
これで一通りのゲームの完成です。今回はここまでにします。
次回は簡易的ではありますが、スマホで操作できるようにしていきます。
次回の記事 :
コメント