49

动态创建地图及随机资源位置_003-腾讯游戏学院

 5 years ago
source link: http://gad.qq.com/article/detail/286110
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
最近结合之前的一个项目,做了一个简单的小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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK