标签:
对于赛车游戏来说,最关键的就是对赛车的实时监测,从而进行实时排名、比赛完成判断、行进方向判断。只有有了实时监测,才能实现游戏的可控性、实现对赛车游戏的管理,才算得上是一款赛车游戏。
今天我主要记录的就是我的赛车游戏制作道路监测点的过程。
道路监测点,就是道路赛车碰撞检测点,监测点会密布在整条赛道上,尽量的均匀散开。用来实时记录赛车当前位置、计算排名、检测赛车行进是否正常等。
足够长:能够检测到道路上所有车辆的过往
足够扁:能够检测到车辆就够了,没必要宽
足够高:太低可能会碰不到车辆的碰撞体
我觉得可能我用的Cube模型都有些复杂
下图中的灰色长板就是一个 道路监测点,在使用时需要将Mesh Renderer钩去掉,达到只能检测不能看见的目的,其中重要的就是Box Collider组件了,用来检测碰撞,同时勾选Is Trigger选项。那么勾选和不勾选有什么区别呢?不勾选这就是一个含有物理碰撞的碰撞体,不勾选这就是一个只能检测出碰撞却不能实际发挥物理作用的检测体。
图中就是道路上的一串检测体,基本均匀分布在道路上,并且所有的碰撞体都是Z轴朝向汽车前进方向,作用就是方便到时对汽车行进方向进行检测及对自动运行车辆进行方向导航
下图中绿色的横线就是游戏所有道路上的所有碰撞体
我一共有两条路线,不过两条路线有重叠的一部分,于是我将所有道路划分成了四段
RoadColliderGroup03(包含一组道路监测点)
与道路监测点对应的就是四段道路
我的路线一采用了(RoadColliderGroup00、RoadColliderGroup01、RoadColliderGroup03)
道路监测点也就是(RoadGroup00、RoadGroup01、RoadGroup03)
一块儿构成了下图中的红色标注的一圈内围城市公路赛道,
我的路线二采用了(RoadColliderGroup00、RoadColliderGroup02)
道路监测点也就是(RoadGroup00、RoadGroup02)
一块儿构成了下图中的蓝色标注的一圈外围景观草皮赛道
每次进入游戏时,读配置文件,得知当前是哪条赛道,然后取出该赛道的若干组监测点,装载到当前赛道监测点数组中,供当前游戏使用
如我使用
//所有路段合集(部分子路段可组合成一个新的赛道,每个路段对象都含有一组道路监测点)
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 = 0;
int curColliderIndex = 0;
//遍历该路线的所有路段名称
for (int i = 0; i < SelectedRoadName.Length; i++) {
for (int j = 0; j < AllRoad.Length; j++) {
//从所有的路段中找到需要的路段
if (AllRoad[j].name == SelectedRoadName[i]) {
//将被选择的路段中的每一个道路监测点逐个添加到本次游戏的道路监测点集合(RoadColliderGrouop)中
for (int k = 0; k < AllRoad[j].transform.childCount; k++) {
RoadColliderGroup.Add (AllRoad[j].transform.GetChild(k).gameObject);
}
curRoadIndex++;
}
}
}
}
经过这些步骤,RoadColliderGroup中就是一条赛道从头到尾的所有道路监测点了。如果赛道没有重叠 ,其实就可以没必要上面那些复杂的过程了,直接将赛道上的所有道路监测点放置到一个游戏对象上面进行管理就好了。
道路监测点的工作就是检测到碰撞,然后汇报到管理类中即可
道路监测点挂的脚本组件是RoadColliderManager
//总的道路管理对象
//检测到碰撞
void OnTriggerEnter(Collider car)
{
//假如道路管理对象不为空
if (GroupManager != null) {
//假如检测到的对象是玩家/敌人
if (car.gameObject.tag == "PlayerCollider")
{
//向道路管理对象汇报碰撞(碰撞者,碰撞检测者)
GroupManager.UpdateTargetState(gameObject, car.transform.parent.parent.gameObject);
}
}
}
道路检测管理对象就是负责汇总所有的道路碰撞信息,然后用来进行排名、检测输赢等具体工作
我的管理对象脚本是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 = 0; 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 = 0; 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;
}
}
}
}
}
每次道路监测点管理对象收到碰撞信息后,流程图如下:
其中更新位置信息的流程如下:
其中更新圈数信息的流程如下:
我另外会再写一张专门是关于给赛车排次的文章,虽然是个简单的知识点,但也有必要记录下来
使用Unity3D 自主实战开发的赛车游戏实例,关键点记录 (四)之道路监测点
标签:
原文地址:http://blog.csdn.net/narutojzm1/article/details/51319378