动态创建地图及随机资源位置_003
发表于2018-08-11
最近结合之前的一个项目,做了一个简单的小Demo,主要功能是动态加载场景资源,
以Demo为例,要在地面随机生成一些树木,要求:
1. 游戏运行以后,动态加载;
2. 随机树木位置,要生成在地面,不能悬空或低于地面,也不能重叠,不然就尴尬了
最终实现效果如下(发现王者荣耀游戏开始的防御塔,也是类似效果,其原理就不得而知了)
主要说明还是写在注释里,大概思路这样:
1. 首先加载地形
2. 在地形上面,依次加载指定数量的树木
2.1 将需要加载的树木,以队列的形式,排队加载
2.2 随机位置,随机数获取X,Z,然后判断与当前场景已存在的树木的距离,保证随机结果分散
2.3 在随机结果X,Z值得基础上,往上偏移Y值,以该点向下射线检测,检测到地面后,获取Y值,作为随机位置的Y值,此时的随机位置,刚好在地形表面,在此点实例化树木。
2.4 利用协程,树木对象实例化完成后,再实例化下一个,避免在一帧里实例化大量资源
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MapManager : MonoBehaviour { //Resources资源名字 [SerializeField] string mapPlaneName; [SerializeField] string treePrefabName; //地图长宽 [SerializeField] Vector2 mapSize; //要生成的树木数量 [SerializeField] int treeCount; //树之间的最小距离 [SerializeField] float treesMinDistance; //场景物体最大高度,射线检测时,射线发射点需要高于该高度 float mapMaxHight = 10; //随机位置时,最多随机次数 int randomMaxCount = 20; //地面Tag string planeTag = "Plane"; Transform planeObj; Transform allTrees; Transform treePrefab; //记录加载到场景中的树 List<Transform> treeList = new List<Transform> (); void Start () { LoadPlaneAndTree(); } void LoadPlaneAndTree() { if (planeObj == null) LoadPlane(); if (allTrees == null) LoadTree(); } //加载地形,仅限于简单地形 //地形较简单时,可采用此方法直接加载,稍大地形,可以采用协程,加载完地形之后,在加载其他资源 //若地形较为复杂,可将地形切割,依次加载,暂不考虑此情况 void LoadPlane() { if(!string.IsNullOrEmpty(mapPlaneName)) { GameObject newObj = Resources.Load<GameObject>(mapPlaneName); if (newObj != null) { planeObj = Instantiate(newObj, transform).transform; Debug.Log("<color=green> Map Load Success </color>"); } else Debug.Log("<color=red> Map Resources Fail </color>"); } else Debug.Log("<color=red> MapPlaneName is Empty </color>"); } //加载场景资源 void LoadTree() { //先要有地形 if (planeObj == null) { Debug.Log("<color=red> Plane is Null </color>"); return; } //设置父节点 if(allTrees == null) { allTrees = new GameObject("allTrees").transform; allTrees.parent = transform; } //预加载prefab if (treePrefab == null) { if(!string.IsNullOrEmpty(treePrefabName)) { GameObject newObj = Resources.Load<GameObject>(treePrefabName); if (newObj != null) { treePrefab = newObj.transform; Debug.Log("<color=green> TreePrefab Load Success </color>"); } else { Debug.Log("<color=red> TreePrefab Resources Fail </color>"); return; } } else Debug.Log("<color=red> TreePrefabName is Empty </color>"); } // StartCoroutine(LoadAllTree()); } IEnumerator LoadAllTree() { yield return new WaitForEndOfFrame(); //利用协程依次生成 while(treeList.Count < treeCount) { yield return StartCoroutine(LoadOneTree()); //可在此设置间隔时间,调整加载间隔时间,调节整体效果 //yield return new WaitForSeconds(0.5f); } } IEnumerator LoadOneTree() { //实例化到场景 Transform newTree = Instantiate(treePrefab, allTrees); //随机位置 newTree.localPosition = GetRandomPosition(); //添加到list方便后管理 treeList.Add(newTree); yield return new WaitForEndOfFrame(); } Vector3 GetRandomPosition() { Vector3 randomPos = Vector3.zero; bool isOk = false; int count = 0; while(!isOk) { //随机X,Z randomPos.x = Random.Range(0, mapSize.x); randomPos.z = Random.Range(0, mapSize.y); count++; isOk = true; //计算随机位置与已有位置的距离 for (int i = 0; i < treeList.Count; i++) { if (Vector3.Distance(randomPos, new Vector3(treeList[i].position.x, 0, treeList[i].position.z)) < treesMinDistance) { isOk = false; break; } } //若随机次数超过指定次数,随机到的位置都小于指定距离,采用此次位置,避免死循环 if (!isOk && count > randomMaxCount) { isOk = true; Debug.Log("<color=blue> While Random Count > RandomMaxCount </color>"); } //射线检测,确定生成位置的高度 if (isOk) { //射线发射点高于地形最高点,向下发射射线 randomPos.y = mapMaxHight + 10; Ray ray = new Ray(randomPos, Vector3.down); RaycastHit hit; if (Physics.Raycast(ray, out hit)) { //射线检测点是否是地面,若是地面,获取该点Y值 if(hit.transform.CompareTag(planeTag)) randomPos.y = hit.point.y; else { isOk = false; Debug.Log("<color=blue> Ray Position is not Plane </color>"); } } else { isOk = false; Debug.Log("<color=blue> Ray is Error </color>"); } } } return randomPos; } }
Demo下载:链接:https://pan.baidu.com/s/1Ve5tRiB7HpmlXEwwJI34EQ 密码:0z4c