概要
レースゲームに必要な順位をつけ,画面に表示するシステムを構築しました。 とはいっても,リアルタイムに,すぐに順位を切り替えるのは難しいため,一周ごとに順位を切り替えることにした。 プレイヤーとCPUの車は,UnityStandardAssetsのカーオブジェクトを使用しました。Assets/Standard Assets/Vehicles/Car/Prefabs/Car.prefab
CPUは,
Assets/Standard Assets/Vehicles/Car/Prefabs/CarWaypointBased.prefab
モデルの部分は,Blenderで作成したものを使用しました。
流れ
スタートライン/ゴールラインにBoxColliderを置き,isTriggerのチェックをONにする。車側にOnTriggerStayの関数を用意し,スタートライン/ゴールラインのBoxColliderをすり抜けた時,周回数を更新できるようにしました。 そして,プレイヤーの車がスタートライン/ゴールラインにBoxColliderをすり抜けた場合は,プレイヤーの周回数と同じか,それ以上に周回している車が何台いるか数え,順位の表示を更新する形にしました。 また,今回は,コースが壁に囲まれてるため不可能ですが,ショートカット防止のため,BoxColliderの中継地点をつくり,そこを通らない場合は,スタートライン/ゴールラインを通過しても周回が加算されず,順位の表示の更新もされない仕様にしました。 また,図のようにプレイヤーが使用する車は,BoxColliderを通過する時,他のCPUと区別するため,ColliderBottomをMyColliderBottomという名前に変更しておきます。ポイントの設置
checkPointというゲームオブジェクトを用意し,その子要素としてスタートライン・ゴールラインにPoint,コースを半周程した場所にPoint2を用意する。 下の画像のように,Point,Point2ともに,Add ComponentでBoxColliderを加え,isTriggerのチェックをONにする。プログラム(車側)
プレイヤーの車の周回数取得するプログラムをmyPos.csという名前で,Carオブジェクト内に組み込みました。mylapという変数を使用し,スタート時は,0とします。最初は,スタートラインより,手前から,スタートしているため,0周からスタートし,Pointオブジェクト(スタートライン・ゴールライン)のBoxColliderを越えて,1周目に入ることにしました。 この時点で,半周後のPoint2のチェックポイントに向かうため,mylapの変数を5とします。半周のため0.5としたかったのですが,intの型の変数の方が扱いが容易なので5としました。Point2オブジェクトのBoxColliderを通り抜けるとき,mylapの値が5だった場合,値を10とし,PointオブジェクトのBoxColliderに向かうようにします。 そして,PointオブジェクトのBoxColliderを通り抜けるとき,mylapの値が10だった場合,値を15とし,Point2オブジェクトのBoxColliderに向かうようにします。このように,
Pointオブジェクト(スタートライン・ゴールライン)でmylapの値が0だった場合は,5に
↓
Point2オブジェクト(中間地点)でmylapの値が5だった場合は,10に
↓
Pointオブジェクトでmylapの値が10だった場合は,15に
↓
Point2オブジェクトでmylapの値が15だった場合は,20に
と半周ごとに値を大きくしていきます。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class myPos : MonoBehaviour
{
public int mylap = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnTriggerStay(Collider col)
{
if (col.gameObject.name == "Point")
{
if (mylap == 0) //0 = スタート
{
mylap = 5; //Point2を目指す
}
else if (mylap == 10) //1周目
{
mylap = 15; //Point2を目指す
}
else if (mylap == 20) //2周目
{
mylap = 25; //Point2を目指す
}
else if (mylap == 30) //3周目
{
mylap = 35; //Point2を目指す
}
else if (mylap == 40) //4周目
{
mylap = 45; //Point2を目指す
}
else if (mylap == 50) //5周目
{
mylap = 55; //フィニッシュ
}
}
else if (col.gameObject.name == "Point2")
{
if (mylap == 5) //5 = 半周
{
mylap = 10; //Pointを目指す
}
else if (mylap == 15) //1周半
{
mylap = 20; //Pointを目指す
}
else if (mylap == 25) //2周半
{
mylap = 30; //Pointを目指す
}
else if (mylap == 35) //3周半
{
mylap = 40; //Pointを目指す
}
else if (mylap == 45) //4周半
{
mylap = 50; //Point(ゴール)を目指す
}
}
}
}
CPUのスクリプトも,myPos.csと内容は変わりませんが,mylapという半周ごとの周回を取得する変数をrivalLapという変数名に変更しています。
また,CPUの1台目のプログラム名はrivalPosだが,2台目はrivalPos1,3台目はrivalPos2とし,CPUの車のオブジェクトに組み込むことにしました。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class rivalPos : MonoBehaviour
{
public int rivalLap = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
public void OnTriggerStay(Collider col)
{
if (col.gameObject.name == "Point")
{
if (rivalLap == 0) //0 = スタート
{
rivalLap = 5; //Point2を目指す
}
else if (rivalLap == 10) //1周目
{
rivalLap = 15; //Point2を目指す
}
else if (rivalLap == 20) //2周目
{
rivalLap = 25; //Point2を目指す
}
else if (rivalLap == 30) //3周目
{
rivalLap = 35; //Point2を目指す
}
else if (rivalLap == 40) //4周目
{
rivalLap = 45; //Point2を目指す
}
else if (rivalLap == 50) //5周目
{
rivalLap = 55; //フィニッシュ
}
}
else if (col.gameObject.name == "Point2")
{
if (rivalLap == 5) //5 = 半周
{
rivalLap = 10; //Pointを目指す
}
else if (rivalLap == 15) //1周半
{
rivalLap = 20; //Pointを目指す
}
else if (rivalLap == 25) //2周半
{
rivalLap = 30; //Pointを目指す
}
else if (rivalLap == 35) //3周半
{
rivalLap = 40; //Pointを目指す
}
else if (rivalLap == 45) //4周半
{
rivalLap = 50; //Point(ゴール)を目指す
}
}
}
}
プログラム(Pointオブジェクト側)
順位の表示や周回数の表示は,Pointオブジェクト内のプログラム(LapControl.cs)で制御することにしました。Updateメソッド内で,GetComponentを使用し,プレイヤーの車のオブジェクトやCPUの車のオブジェクトから,mylapの変数やrivalLapの変数の値を取得します。 Pointオブジェクト内のプログラムでは,mylap変数の値は,LapMyという変数に代入し,rivalLap変数は,各CPUの車のオブジェクトによって,変わってくるため,RivalPosというint型の配列の要素として,それぞれの値を代入することにしました。 プレイヤーの車のMyColliderBottomがBoxColliderと接触した時,countPosというメソッドを使用し,順位の更新が始まります。この時,あまりする必要はなかったかもしれませんが,countPosメソッドが実行されている最中に,RivalPosの各要素の値が変更するしても問題がない状態にするため,RivalPosNowというint型の配列を新規で作成し,RivalPosNowにRivalPosの配列の値を代入します。そして,RivalPosNowを引数に入れてからcountPosメソッドを実行します。 countPosメソッド内では,count変数を使用し,LapMyの値よりも大きい値のRivalPosNowの要素がある場合,count変数の値を+1し,PosNowという順位の表示に使用する変数に代入します。 例として,0周目(スタートしたばかり)の時にPointオブジェクトのBoxColliderに接触し,countPosメソッドが実行された場合,mylapの値は更新されていない(OnTriggerEnterで接触を検知している)ため,LapMyの値は0のままで,LapMyが0より大きいRivalPosNowの要素の値がある場合,count変数の値に+1される形になります。順位と周回数の表示はOnGUIメソッドでおこないます。コースを5周するレースのため,LapMyの値が45か50(5周目)の時は,順位の表示を"FINAL LAP"としました。また,LapMyの値が55(50以上の場合)は,レースが終了しているため,順位の表示を"FINISH"としました。また,順位の表示については,先ほど,countPosメソッド実行時に,count変数の値を代入したPosNowの値を表示します。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LapControl : MonoBehaviour
{
int PosNow = 12; //11台のCPUがいるため(初期の順位)
int LapMy;
int count;
bool finish = false;
int[] RivalPos = new int[11];
public GUIStyle style;
// Start is called before the first frame update
void Start()
{
style = new GUIStyle();
}
// Update is called once per frame
void Update()
{
//オブジェクトの読み込み
GameObject obj = GameObject.Find("CarWaypointBased(0)"); //CPU(ライバルカー)の1台目
GameObject obj1 = GameObject.Find("CarWaypointBased (1)"); //CPUの2台目
GameObject obj2 = GameObject.Find("CarWaypointBased (2)"); //CPUの3台目
GameObject obj3 = GameObject.Find("CarWaypointBased (3)"); //CPUの4台目
GameObject obj4 = GameObject.Find("CarWaypointBased (4)"); //CPUの5台目
GameObject obj5 = GameObject.Find("CarWaypointBased (5)"); //CPUの6台目
GameObject obj6 = GameObject.Find("CarWaypointBased (6)"); //CPUの7台目
GameObject obj7 = GameObject.Find("CarWaypointBased (7)"); //CPUの8台目
GameObject obj8 = GameObject.Find("CarWaypointBased (8)"); //CPUの9台目
GameObject obj9 = GameObject.Find("CarWaypointBased (9)"); //CPUの10台目
GameObject obj10 = GameObject.Find("CarWaypointBased (10)"); //CPUの11台目
GameObject myobj = GameObject.Find("Car"); //プレイヤーの車
//mylap,rivalLap変数の読み込み
rivalPos myRival;
myRival = obj.GetComponent<rivalPos>();
RivalPos[0] = myRival.rivalLap;
rivalPos1 myRival1;
myRival1 = obj1.GetComponent<rivalPos1>();
RivalPos[1] = myRival1.rivalLap;
rivalPos2 myRival2;
myRival2 = obj2.GetComponent<rivalPos2>();
RivalPos[2] = myRival2.rivalLap;
rivalPos3 myRival3;
myRival3 = obj3.GetComponent<rivalPos3>();
RivalPos[3] = myRival3.rivalLap;
rivalPos4 myRival4;
myRival4 = obj4.GetComponent<rivalPos4>();
RivalPos[4] = myRival4.rivalLap;
rivalPos5 myRival5;
myRival5 = obj5.GetComponent<rivalPos5>();
RivalPos[5] = myRival5.rivalLap;
rivalPos6 myRival6;
myRival6 = obj6.GetComponent<rivalPos6>();
RivalPos[6] = myRival6.rivalLap;
rivalPos7 myRival7;
myRival7 = obj7.GetComponent<rivalPos7>();
RivalPos[7] = myRival7.rivalLap;
rivalPos8 myRival8;
myRival8 = obj8.GetComponent<rivalPos8>();
RivalPos[8] = myRival8.rivalLap;
rivalPos9 myRival9;
myRival9 = obj9.GetComponent<rivalPos9>();
RivalPos[9] = myRival9.rivalLap;
rivalPos10 myRival10;
myRival10 = obj10.GetComponent<rivalPos10>();
RivalPos[10] = myRival10.rivalLap;
myPos my;
my = myobj.GetComponent<myPos>();
LapMy = my.mylap;
}
//順位,周回数の表示
void OnGUI()
{
style.fontSize = 30;
if (finish == false)
{
if (LapMy == 0)
{
GUI.Box(new Rect(800, 220, 80, 80), "Lap: 0/5", style);
}
else if (LapMy == 5 || LapMy == 10)
{
GUI.Box(new Rect(800, 220, 80, 80), "Lap: 1/5", style);
}
else if (LapMy == 15 || LapMy == 20)
{
GUI.Box(new Rect(800, 220, 80, 80), "Lap: 2/5", style);
}
else if (LapMy == 25 || LapMy == 30)
{
GUI.Box(new Rect(800, 220, 80, 80), "Lap: 3/5", style);
}
else if (LapMy == 35 || LapMy == 40)
{
GUI.Box(new Rect(800, 220, 80, 80), "Lap: 4/5", style);
}
else if (LapMy >= 45 || LapMy == 50)
{
GUI.Box(new Rect(800, 220, 80, 80), "FINAL LAP", style);
}
}
else //LapMy(mylap)の値が50以上(ゴールしている)の場合
{
GUI.Box(new Rect(800, 220, 80, 80), "FINISH", style);
}
GUI.Box(new Rect(400, 220, 80, 80), "Position: " + PosNow.ToString(), style);
}
public void OnTriggerEnter(Collider col)
{
//プレイヤーの車がBoxColliderを通過した場合
if (col.gameObject.name == "MyColliderBottom")
{
int[] RivalPosNow = new int[11];
RivalPosNow = RivalPos; //RivalPosの値を代入
countPos(RivalPosNow); //順位を数えるメソッドを実行
}
}
//順位を数えるメソッド
void countPos(int[] RivalPosNow)
{
if (LapMy == 0)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
//プレイヤーより前をはしっているCPUをカウント
if (0 < RivalPosNow[i]) //LapMyより大きいRivalPosNowの要素をカウント
{
count += 1;
}
}
PosNow = count; //カウントした値を代入
}
//その後も同じ作業
else if (LapMy == 10)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
if (10 < RivalPosNow[i])
{
count += 1;
}
}
PosNow = count;
}
else if (LapMy == 20)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
if (20 < RivalPosNow[i])
{
count += 1;
}
}
PosNow = count;
}
else if (LapMy == 30)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
if (30 < RivalPosNow[i])
{
count += 1;
}
}
PosNow = count;
}
else if (LapMy == 40)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
if (40 < RivalPosNow[i])
{
count += 1;
}
}
PosNow = count;
}
else if (LapMy == 50)
{
count = 1;
for (int i = 0; i < RivalPosNow.Length; i++)
{
if (50 < RivalPosNow[i])
{
count += 1;
}
}
PosNow = count;
finish = true;
}
Debug.Log("LapMy: " + LapMy);
Debug.Log("RivalPosNow: " + string.Join(", ", RivalPosNow));
Debug.Log("PosNow: " + PosNow);
}
}
テスト動画
テストをUnity上でおこないました。最後尾からスタートして,CPUを追い越した分,順位が上がっていき,スタートライン・ゴールラインで順位と周回数がどちらも切り替わっていたため,問題なく動作していると考えられます。また,今回は,ゴールした後も走れるようになっているが,レース終了後,周回を重ねても,ゴール後から表示が変わることはありませんでした。また,下の画像は,スタートライン・ゴールラインを越えて2周目から3周目に入ったところのもので,緑の枠の中に,この時のLapMy変数,RivalPosNowの要素,PosNowのそれぞれの値が出力されています。LapMyの値は20となっていて,それよりも大きい値のRivalPosNowの要素が4つあります。したがって,PosNowの値は,5となり,ゲーム画面のPosition(順位)の値も同じものが出力されているため,このことからも,構築したシステムが問題なく動いていることがわかります。