一.綜述
對于賽車遊戲來說,最關鍵的就是對賽車的實時監測,進而進行實時排名、比賽完成判斷、行進方向判斷。隻有有了實時監測,才能實作遊戲的可控性、實作對賽車遊戲的管理,才算得上是一款賽車遊戲。
今天我主要記錄的就是我的賽車遊戲制作道路監測點的過程。
二.什麼是道路監測點
道路監測點,就是道路賽車碰撞檢測點,監測點會密布在整條賽道上,盡量的均勻散開。用來實時記錄賽車目前位置、計算排名、檢測賽車行進是否正常等。
三.道路監測點的制作
1.制作長方體碰撞對象
1)長方體要足夠長足夠扁足夠高的長方體
足夠長:能夠檢測到道路上所有車輛的過往
足夠扁:能夠檢測到車輛就夠了,沒必要寬
足夠高:太低可能會碰不到車輛的碰撞體
2)模型要夠簡單
我覺得可能我用的Cube模型都有些複雜
3)我的遊戲執行個體
下圖中的灰色長闆就是一個 道路監測點,在使用時需要将Mesh Renderer鈎去掉,達到隻能檢測不能看見的目的,其中重要的就是Box Collider元件了,用來檢測碰撞,同時勾選Is Trigger選項。那麼勾選和不勾選有什麼差別呢?不勾選這就是一個含有實體碰撞的碰撞體,不勾選這就是一個隻能檢測出碰撞卻不能實際發揮實體作用的檢測體。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISNwADMyUzMwATNwUDM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
圖中就是道路上的一串檢測體,基本均勻分布在道路上,并且所有的碰撞體都是Z軸朝向汽車前進方向,作用就是友善到時對汽車行進方向進行檢測及對自動運作車輛進行方向導航
下圖中綠色的橫線就是遊戲所有道路上的所有碰撞體
2.管理道路監測點
1)一般情況下,道路監測點統一放置在一個清單中即可
2)将起點終點的道路監測點使用Tag标記成如:StartTag、EndTag
3)每條路線都需要一個清單管理該路線所有道路監測點
4)我的執行個體
(1)道路監測點的管理
我一共有兩條路線,不過兩條路線有重疊的一部分,于是我将所有道路劃分成了四段
- RoadColliderGroup00(包含一組道路監測點)
- RoadColliderGroup01(包含一組道路監測點)
- RoadColliderGroup02(包含一組道路監測點)
-
RoadColliderGroup03(包含一組道路監測點)
與道路監測點對應的就是四段道路
- RoadGroup00(包含一組道路網格)
- RoadGroup01(包含一組道路網格)
- RoadGroup02(包含一組道路網格)
- RoadGroup03(包含一組道路網格)
我的路線一采用了(RoadColliderGroup00、RoadColliderGroup01、RoadColliderGroup03)
道路監測點也就是(RoadGroup00、RoadGroup01、RoadGroup03)
一塊兒構成了下圖中的紅色标注的一圈内圍城市公路賽道,
使用Unity3D 自主實戰開發的賽車遊戲執行個體,關鍵點記錄 (四)之道路監測點一.綜述二.什麼是道路監測點三.道路監測點的制作 我的路線二采用了(RoadColliderGroup00、RoadColliderGroup02)
道路監測點也就是(RoadGroup00、RoadGroup02)
一塊兒構成了下圖中的藍色标注的一圈外圍景觀草皮賽道
使用Unity3D 自主實戰開發的賽車遊戲執行個體,關鍵點記錄 (四)之道路監測點一.綜述二.什麼是道路監測點三.道路監測點的制作
每次進入遊戲時,讀配置檔案,得知目前是哪條賽道,然後取出該賽道的若幹組監測點,裝載到目前賽道監測點數組中,供目前遊戲使用
如我使用
//所有路段合集(部分子路段可組合成一個新的賽道,每個路段對象都含有一組道路監測點)
public GameObject[] AllRoad = null;
//目前賽道所有道路監測點集合
public List<GameObject> RoadColliderGroup = new List<GameObject> ();
void Start()
{
LoadRoadColliderGroup();
}
//将所有的路點碰撞檢測對象加載到一個隊列裡邊,為本次遊戲服務
void LoadRoadColliderGroup()
{
//加載目前選擇的路線的所有所有路段名稱
string[] SelectedRoadName = ConfigurationManager.Paths [ConfigurationManager.Instance.PathID];
int curRoadIndex = ;
int curColliderIndex = ;
//周遊該路線的所有路段名稱
for (int i = ; i < SelectedRoadName.Length; i++) {
for (int j = ; j < AllRoad.Length; j++) {
//從所有的路段中找到需要的路段
if (AllRoad[j].name == SelectedRoadName[i]) {
//将被選擇的路段中的每一個道路監測點逐個添加到本次遊戲的道路監測點集合(RoadColliderGrouop)中
for (int k = ; k < AllRoad[j].transform.childCount; k++) {
RoadColliderGroup.Add (AllRoad[j].transform.GetChild(k).gameObject);
}
curRoadIndex++;
}
}
}
}
經過這些步驟,RoadColliderGroup中就是一條賽道從頭到尾的所有道路監測點了。如果賽道沒有重疊 ,其實就可以沒必要上面那些複雜的過程了,直接将賽道上的所有道路監測點放置到一個遊戲對象上面進行管理就好了。
(2)道路監測點的工作
道路監測點的工作就是檢測到碰撞,然後彙報到管理類中即可
道路監測點挂的腳本元件是RoadColliderManager
//總的道路管理對象
//檢測到碰撞
void OnTriggerEnter(Collider car)
{
//假如道路管理對象不為空
if (GroupManager != null) {
//假如檢測到的對象是玩家/敵人
if (car.gameObject.tag == "PlayerCollider")
{
//向道路管理對象彙報碰撞(碰撞者,碰撞檢測者)
GroupManager.UpdateTargetState(gameObject, car.transform.parent.parent.gameObject);
}
}
}
(3)道路檢測管理對象的工作
道路檢測管理對象就是負責彙總所有的道路碰撞資訊,然後用來進行排名、檢測輸赢等具體工作
我的管理對象腳本是RoadColliderGroupManager
下面的代碼中分别包括了根據碰撞檢測來更新玩家位置資訊,與圈數資訊的内容
//這個函數由道路檢測點調用,當檢測到玩家/敵人碰撞時調用該函數
public void UpdateTargetState(GameObject collider, GameObject target)
{
//更新碰撞到監測點的遊戲對象的位置資訊
if (!UpdatePosInfo (collider, target))
{
//如果更新位置出現False說明,位置暫時不用更新了,因為玩家沒有按照預期碰撞到下一個碰撞點,而是 碰撞到了别的,沒有按規則行走,是以直接傳回,直到玩家碰撞了預期的下一個碰撞點,再更新位置資訊
//這樣做的目的是防止玩家走捷徑到達終點
return;
}
//更新圈數資訊
UpdateCicleNumInfo (collider, target);
}
//更新遊戲對象位置資訊
private bool UpdatePosInfo(GameObject collider,GameObject target)
{
//判斷碰撞體是不是玩家
if (target == _player) {
if (GetThePlayerNextCollider() != collider) {
//如果新的碰撞點不是玩家本應該碰撞的下一個點,說明玩家在亂跑,不用管它,
return false;
}
//根據碰撞點位置,更新玩家目前位置
_currentPlayerPos = RoadColliderGroup.IndexOf(collider);
//假如目前遊戲模式是競賽模式,則更新排次
if(ConfigurationManager.Instance.CurrentGameModel == ConfigurationManager.GameModel.RacingModel) {
//更新位置資訊到排次表中
if (_allPlayersRank.ContainsKey(target)) {
_allPlayersRank[target].Pos = _currentPlayerPos;
}
}
return true;
}
//判斷碰撞體是不是敵人
for (int i = ; i < _enemyPlayers.Length; i++) {
if (_enemyPlayers[i] == target)
{
//更新敵人位置資訊
_currentEnemyPos[i] = RoadColliderGroup.IndexOf(collider);
if (ConfigurationManager.Instance.CurrentGameModel == ConfigurationManager.GameModel.RacingModel) {
//設定敵人自動尋路控件的下一個目标點
SetEnemyCarDestionation(i);
//更新位置資訊到排次表中
if (_allPlayersRank.ContainsKey(target)) {
_allPlayersRank[target].Pos = _currentEnemyPos[i];
}
}
}
}
return true;
}
/// <summary>
/// 更新圈數資訊
/// </summary>
/// <param name="collider">碰撞檢測點</param>
/// <param name="target">目标</param>
private void UpdateCicleNumInfo(GameObject collider, GameObject target)
{
//假如目前碰撞點不是“StartTag”,則不用更新圈數資訊
//注:我的賽道都是閉環賽道,是以StartTag 也是EndTag
if (collider.tag != "StartTag") {
return;
}
if (target == _player) {
//假如再次觸發起點檢測器發現,圈數已經夠了,則表示Mission Completed!
if (CurrentPlayerCircleCount == _gameTotalCircleNum) {
//任務完成,向外立即通知
//記錄玩家完成任務時的總時間
_playerTimer = PlayTotalTime;
//記錄這個玩家是第幾個完成比賽的
_raceRankCount++;
//向遊戲管理對象通知該玩家比賽完成
if (PlayObjectCompletedEvent != null) {
PlayObjectCompletedArgs args = new PlayObjectCompletedArgs();
args.PlayObject = target;
args.completedTime = _playerTimer;
PlayObjectCompletedEvent.Invoke (this, args);
}
}
else {
//如果沒有完成比賽則給該玩家的完成圈數+1
CurrentPlayerCircleCount++;
}
if (ConfigurationManager.Instance.CurrentGameModel == ConfigurationManager.GameModel.RacingModel) {
//更新圈數資訊到排次表中
if (_allPlayersRank.ContainsKey(target)) {
_allPlayersRank[target].CircleCount = CurrentPlayerCircleCount;
_allPlayersRank[target].Rank = _raceRankCount;
}
}
}
for (int i = ; i < _enemyPlayers.Length; i++) {
if (_enemyPlayers[i] == target) {
if (CurrentEnemyPlayersCircleCount[i] == _gameTotalCircleNum) {
//任務完成,向外立即通知
//記錄敵人完成任務時的總時間
_enemyPlayersTimer[i] = PlayTotalTime;
_raceRankCount++;
EnableEnemyCarMeshAgent(i, false);
if (PlayObjectCompletedEvent != null) {
PlayObjectCompletedArgs args = new PlayObjectCompletedArgs();
args.PlayObject = target;
args.completedTime = _enemyPlayersTimer[i];
PlayObjectCompletedEvent.Invoke (this, args);
}
}
else
{
CurrentEnemyPlayersCircleCount[i]++;
}
if (ConfigurationManager.Instance.CurrentGameModel == ConfigurationManager.GameModel.RacingModel) {
//更新位置資訊到排次表中
if (_allPlayersRank.ContainsKey(target)) {
_allPlayersRank[target].CircleCount = CurrentEnemyPlayersCircleCount[i];
_allPlayersRank[target].Rank = _raceRankCount;
}
}
}
}
}
每次道路監測點管理對象收到碰撞資訊後,流程圖如下:
其中更新位置資訊的流程如下:
其中更新圈數資訊的流程如下:
我另外會再寫一張專門是關于給賽車排次的文章,雖然是個簡單的知識點,但也有必要記錄下來