无缝衔接的地图
我现在有四个地图,名字分别为Pattern_1、Pattern_2、Pattern_3、Pattern_4,然后计算地图的长度,我的是160,每次进行无缝衔接的时候,只需将要生成的地图在当前地图的基础上加上160即可进行无缝衔接了,然后每次都是随机生成一个地图进行衔接的。
代码如下:
由于地图是无缝衔接的,要经常生成地图,然后销毁地图的,但这样做,性能不太好,所以下面也用到了对象池技术,包括障碍物的生成与销毁也用到了对象池技术,这也相当于是对象池技术的一个应用了,具体的对象池技术的代码实现,请移步去https://blog.csdn.net/weixin_43839583/article/details/103405831学习。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RoadChange : MonoBehaviour
{
private GameObject roadNow;//当前的路
private GameObject roadNext;//下一条路
private GameObject parent;//游戏第一次运行时,第一条路的游戏物体
// Start is called before the first frame update
void Start()
{
if(parent==null)
{
parent = new GameObject();
parent.transform.position = Vector3.zero;
parent.name = "Road";
}
roadNow = Game.Instance.objectPool.Spawn("Pattern_1", parent.transform);
roadNext = Game.Instance.objectPool.Spawn("Pattern_2", parent.transform);
roadNext.transform.position += new Vector3(0, 0, 160);
AddItem(roadNow);//这是下面的添加障碍物的方法,之后会用到
AddItem(roadNext);
}
private void OnTriggerEnter(Collider other)//当主角碰到了地图上的碰撞体,就开始生成新地图
{
if(other.gameObject.tag==Tag.road)
{
//回收
Game.Instance.objectPool.UnSpawn(other.gameObject);
//创建新的跑道
SpawnNextRoad();
}
}
/// <summary>
/// 创建新的跑道,即生成下一条路的方法
/// </summary>
private void SpawnNextRoad()
{
int i = Random.Range(1, 5);//四条路随机取一条
roadNow = roadNext;//原本的下一条跑道变成当前跑道
//生成新的跑道
roadNext = Game.Instance.objectPool.Spawn("Pattern_" + i.ToString(), parent.transform);
roadNext.transform.position = roadNow.transform.position + new Vector3(0, 0, 160);//新跑道是原跑道的基础上加160
AddItem(roadNext);//在新跑道上添加障碍物
}
/// <summary>
/// 生成障碍物
/// </summary>
/// <param name="obj"></param>
public void AddItem(GameObject obj)
{
var itemChild = obj.transform.Find("Item");
if(itemChild!=null)
{
var patternManager = PatternManager.Instance;
if(patternManager!=null&&patternManager.patterns!=null&&patternManager.patterns.Count>0)
{
//随机取一个方案
var pattern = patternManager.patterns[Random.Range(0, patternManager.patterns.Count)];
if(pattern!=null&&pattern.patternItems!=null&&pattern.patternItems.Count>0)
{
foreach (var itemList in pattern.patternItems)
{
GameObject go = Game.Instance.objectPool.Spawn(itemList.prefabName, itemChild);
go.transform.parent = itemChild;
go.transform.localPosition = itemList.pos;
}
}
}
}
}
}
上面有个 public void AddItem(GameObject obj)的生成障碍物的方法,其中的PatternManager类的代码实现如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public class PatternManager : MonoSingleton<PatternManager>
{
public List<Pattern> patterns = new List<Pattern>();//很多套生成障碍物的方案
}
//一个游戏物体的信息
[Serializable]
public class PatternItem
{
public string prefabName;
public Vector3 pos;
}
//一套方案
[Serializable]
public class Pattern
{
public List<PatternItem> patternItems = new List<PatternItem>();
}
在Unity中先新建一个PatternManager 的空物体,然后挂载这个同名的脚本,然后在这个脚本中手动赋值,添加每个障碍物的名字和位置。
需要注意的是,在地图的Prefab中添加一个名为Item的子物体,然后以后生成的障碍物都放在这个Item下,调用上面的AddItem(GameObject obj)方法就可以添加障碍物了。
编辑器扩展的妙用
在上面的操作中,可以生成无缝衔接的地图了,也能随机取一套障碍物进行生成,但是障碍物的名字和位置必须要我们手动赋值,障碍物比较少的时候还好说,当一套方案中有五六十个障碍物,然后你要摆十几套方案时,一个一个的赋值是非常耗时间的,效率非常低,所以我们使用编辑器扩展功能,非常的方便。首先要新建一个名为Editor的文件夹,然后在Editor中新建一个脚本,我将它取名为SpawnManager。SpawnManager必须继承自Editor.
具体的实现代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
public class SpawnManager : Editor
{
[MenuItem("Tools/Click me!")]
static void PatternSystem()
{
GameObject spawnManager = GameObject.Find("PatternManager");
if(spawnManager!=null)
{
var patternManager = spawnManager.GetComponent<PatternManager>();
if(Selection.gameObjects.Length==1)
{
var item = Selection.gameObjects[0].transform.Find("Item");
if(item!=null)
{
Pattern pattern = new Pattern();
foreach (var child in item)
{
Transform childTrans = child as Transform;
if (childTrans != null)
{
//找到它对应的预制体
var prefab = UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(childTrans.gameObject);
if(prefab!=null)
{
PatternItem patternItem = new PatternItem
{
pos = childTrans.localPosition,
prefabName = prefab.name
};
pattern.patternItems.Add(patternItem);
}
}
}
patternManager.patterns.Add(pattern);
}
}
}
}
}
[MenuItem(“Tools/Click me!”)]表示在编辑器上添加了Tools/Click me!的按钮,点击的时候,执行它下面的静态方法。即执行PatternSystem()方法。
if(Selection.gameObjects.Length==1)表示在编辑器中选中的游戏物体个数是1个的时候执行下面的代码。
UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(childTrans.gameObject)是根据Hierarchy面板上的物体找到它对应的预制体。为什么要找预制体?因为如果Item下有很多同名的物体,比如coin,那个下一个就是coin(1)了,那么名字就对不上了,因为它的名字就只是coin而已,而coin和coin(1)的预制体对应的名字都是coin,所以要取得预制体。然后将名字写入新建的patternItem,而位置的话,取原本的位置,不能取预制体的位置,因为预制体的位置都是一样的,我们不能将所有的障碍物都生成到同一位置吧。最后由patternManager.patterns.Add(pattern)将一套方案添加进去。
需要注意的是,我们要事先在地图中的Item下摆放好每一个障碍物的位置,摆完后,点击这个Item的父物体,即整个地图,然后点击Tools/Click me!就自动把地图上的障碍物的名字和位置信息添加到PatternManager中了,你还可以改变障碍物的位置,继续生成更多套方案,到时候游戏运行的时候就随机取一套方案执行了。
这就是编辑器扩展的妙用了。