【Unity】非常に詳細なフレーム選択とダイナミックなナビゲーション、初心者必読

最初に古いルールが適用されます。

 

1: 地形を生成し、地形を動的にベイク処理するために空のノードを作成します。

 

<TerrainManager>スクリプトは次のとおりです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TerrianManager : MonoBehaviour
{
    public int wh = 128;
    public int w = 1;
    public int h = 1;
    public GameObject prefab;
    public Transform mapBox; //父节点 下面是map预制体

    void Start()
    {
        //生成terrain
        for (int i = 0; i < w; i++)
        {
            for (int j = 0; j < h; j++)
            {
                GameObject obj = Instantiate(prefab, transform);
                obj.GetComponent<Terrain1>().wid = i;
                obj.GetComponent<Terrain1>().hid = j;
                obj.GetComponent<Terrain1>().mapbox = mapBox;
                obj.transform.localPosition = new Vector3(i * wh, 0, j * wh);
            }
        }
        
    }

}

ここでのプレハブは平面なので、以下のコンポーネントを追加することに注意してください。

 ここでのスクリプト<Terrain1>は次のとおりです。

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;

    public class Terrain1 : MonoBehaviour
    {
        public Texture2D texture;
        public int wh = 129;   //+1  补缝  像素
        public int wid;  //横着的第几块
        public int hid; //竖着的第几块
        public float p = 0.01f;
        public Color high = Color.cyan;
        public Color low = Color.magenta;

        public Transform mapbox;

        void Start()
        {
            VertexHelper vh = new VertexHelper();
            //材质
            texture = new Texture2D(wh, wh);
            //铺地板  每一点小像素
            for (int x = 0; x < wh; x++)
            {
                for (int z= 0;  z< wh; z++)
                {
                    //柏林噪声算法
                    //float noise=Mathf.PerlinNoise((x+wid*wh)*p, (z+hid*wh)*p);
                    float noise=Turbulance((x+wid*wh)*p, (z+hid*wh)*p);
                    float y = noise * 10;
                    //生成颜色
                    Color color = Color.Lerp(low, high, noise);
                    //给图片添加颜色
                    texture.SetPixel(x, z, color);
                    //生成uv坐标
                    float uvx = (float)x / (float)wh;
                    float uvy = (float)z / (float)wh;
                    //生成顶点
                    vh.AddVert(new Vector3(x, y, z), color, new Vector2(uvx, uvy));
                    if(x<wh-1&&z<wh-1)
                    {
                        vh.AddTriangle(x * wh + z, x * wh + z + 1, (x + 1) * wh + z + 1);
                        vh.AddTriangle(x * wh + z, (x+1 )* wh + z + 1, (x + 1) * wh + z);
                    }

                }
            }
            //应用
            texture.Apply();
            Mesh mesh = new Mesh();
            vh.FillMesh(mesh);
            GetComponent<MeshFilter>().mesh = mesh;
            GetComponent<MeshCollider>().sharedMesh = mesh;
            GetComponent<MeshRenderer>().material.mainTexture = texture;

            //实例化小map
            GameObject map = Instantiate(Resources.Load<GameObject>("map"), mapbox);
            //创建材质球
            Material material = new Material(Shader.Find("UI/Default"));
            material.mainTexture = texture;
            map.GetComponent<Image>().material = material;
            map.transform.localPosition = new Vector3(wid * map.GetComponent<RectTransform>().rect.width, hid * map.GetComponent<RectTransform>().rect.height, 0);
           
            //添加动态导航
            gameObject.AddComponent<BoxCollider>();
            gameObject.AddComponent<NavMeshSourceTag>();
    }
        public float Turbulance(float x, float y)
        {
            float t = -0.5f;
            for (int f = 1; f <= wh / 12; f *= 2)
            {
                t += Mathf.Abs(Mathf.PerlinNoise(x * f, y * f) / f);

            }
            print(t);
            return t;
        }

    }

地形を動的にベイク処理するためのスクリプト<LocalNavMeshBuilder>は次のとおりです。

using UnityEngine;
using UnityEngine.AI;
using System.Collections;
using System.Collections.Generic;
using NavMeshBuilder = UnityEngine.AI.NavMeshBuilder;

// Build and update a localized navmesh from the sources marked by NavMeshSourceTag
[DefaultExecutionOrder(-102)]
public class LocalNavMeshBuilder : MonoBehaviour
{
    // The center of the build
    public Transform m_Tracked;

    // The size of the build bounds
    public Vector3 m_Size = new Vector3(80.0f, 20.0f, 80.0f);

    NavMeshData m_NavMesh;
    AsyncOperation m_Operation;
    NavMeshDataInstance m_Instance;
    List<NavMeshBuildSource> m_Sources = new List<NavMeshBuildSource>();

    IEnumerator Start()
    {
        while (true)
        {
            UpdateNavMesh(true);
            yield return m_Operation;
        }
    }

    void OnEnable()
    {
        // Construct and add navmesh
        m_NavMesh = new NavMeshData();
        m_Instance = NavMesh.AddNavMeshData(m_NavMesh);
        if (m_Tracked == null)
            m_Tracked = transform;
        UpdateNavMesh(false);
    }

    void OnDisable()
    {
        // Unload navmesh and clear handle
        m_Instance.Remove();
    }

    void UpdateNavMesh(bool asyncUpdate = false)
    {
        NavMeshSourceTag.Collect(ref m_Sources);
        var defaultBuildSettings = NavMesh.GetSettingsByID(0);
        var bounds = QuantizedBounds();

        if (asyncUpdate)
            m_Operation = NavMeshBuilder.UpdateNavMeshDataAsync(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
        else
            NavMeshBuilder.UpdateNavMeshData(m_NavMesh, defaultBuildSettings, m_Sources, bounds);
    }

    static Vector3 Quantize(Vector3 v, Vector3 quant)
    {
        float x = quant.x * Mathf.Floor(v.x / quant.x);
        float y = quant.y * Mathf.Floor(v.y / quant.y);
        float z = quant.z * Mathf.Floor(v.z / quant.z);
        return new Vector3(x, y, z);
    }

    Bounds QuantizedBounds()
    {
        // Quantize the bounds to update only when theres a 10% change in size
        var center = m_Tracked ? m_Tracked.position : transform.position;
        return new Bounds(Quantize(center, 0.1f * m_Size), m_Size);
    }

    void OnDrawGizmosSelected()
    {
        if (m_NavMesh)
        {
            Gizmos.color = Color.green;
            Gizmos.DrawWireCube(m_NavMesh.sourceBounds.center, m_NavMesh.sourceBounds.size);
        }

        Gizmos.color = Color.yellow;
        var bounds = QuantizedBounds();
        Gizmos.DrawWireCube(bounds.center, bounds.size);

        Gizmos.color = Color.green;
        var center = m_Tracked ? m_Tracked.position : transform.position;
        Gizmos.DrawWireCube(center, m_Size);
    }
}

 ここのトラックに注目してください。

上はプレハブの本体ですが、下のサイズは地形より大きい限り、できるだけ大きくすることができます。

 2: マーキーとして使用する画像を作成します

ここでの画像タイプはスライスであることに注意してください。

画像に添付されている<DrawBox>スクリプトは次のとおりです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class DrawBox : MonoBehaviour
{
    Camera cam;
    void Start()
    {
        cam = Camera.main;
    }

    Vector3 startPos;
    Vector3 endPos;
    Rect rect;
    bool isDrag = false;
    List<GameObject> players = new List<GameObject>();

    void Update()
    {
        //按下鼠标左键,后续开始画框
        if(Input.GetMouseButtonDown(0))
        {
            startPos = Input.mousePosition;
            isDrag = true;
        }
        if(isDrag)
        {
            float w = Mathf.Abs(startPos.x - Input.mousePosition.x);
            float h = Mathf.Abs(startPos.y - Input.mousePosition.y);
            GetComponent<RectTransform>().sizeDelta = new Vector2(w, h);
            float x = startPos.x < Input.mousePosition.x ? startPos.x + w / 2 : Input.mousePosition.x + w / 2;
            float y = startPos.y < Input.mousePosition.y ? startPos.y + h / 2 : Input.mousePosition.y + h / 2;
            GetComponent<RectTransform>().anchoredPosition = new Vector2(x-Screen.width/2, y-Screen.height/2);
            //rect
            rect = new Rect(x-w/2, y-h/2, w, h);
        }

        if (Input.GetMouseButtonUp(0))
        {
            //把原来的清除
            foreach (var item in players)
            {
                item.transform.Find("Circle(Clone)").gameObject.SetActive(false);
            }
            players.Clear();
            //开始画框
            foreach (var item in PlayerMgr.list)
            {
           
                //把所有在场的人位置转屏幕
                Vector3 pos = cam.WorldToScreenPoint(item.transform.position);
                //如果在框里
                if(rect.Contains(pos))
                {
                    players.Add(item);
                    //显示框
                    item.transform.Find("Circle(Clone)").gameObject.SetActive(true);
                }
            }
            
            GetComponent<RectTransform>().sizeDelta = Vector2.zero;
            //endPos = Vector3.zero;
            isDrag = false;
        }

        //按下右键,导航自动寻路
        if (Input.GetMouseButtonDown(1))
        {
            Ray ray = cam.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit))
            {
                foreach (var item in players)
                {
                    item.GetComponent<NavMeshAgent>().SetDestination(hit.point);
                }
               
            }
        }
    }
}

3: 空のノードを作成して複数のキャラクターを生成する

マウント スクリプト<PlayerMgr>

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

public class PlayerMgr : MonoBehaviour
{
    public static List<GameObject> list = new List<GameObject>();
    void Start()
    {
        for (int i = 0; i < 5; i++)
        {
            list.Add(Instantiate(Resources.Load<GameObject>("Player/player_" + i), transform));
            float x = Random.Range(10, 30);
            float z = Random.Range(10, 30);
            float y = TerrainMgr.Turbulance(x, z);
            list[i].transform.position = new Vector3(x, y+0.7f, z);
            list[i].AddComponent<NavMeshAgent>().radius = 0.3f;
            list[i].GetComponent<NavMeshAgent>().height= 1f;
            //默认不显示圆圈

            Instantiate(Resources.Load<GameObject>("Circle"), list[i].transform).SetActive(false);

        }

        
    }

}

 注: ここでの Circle プレハブは、スクリプト<DrawCircle> がアタッチされた空のノードです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class DrawCircle : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        VertexHelper vh = new VertexHelper();
        float r = 0.5f;
        float r1 = 0.3f;
        float ang = (Mathf.PI * 2) / 20;
        for (int i = 0; i < 20; i++)
        {

            float x1 = Mathf.Sin(i * ang) * r1;
            float y1 = Mathf.Cos(i * ang) * r1;
            vh.AddVert(new Vector3(x1, 0, y1), Color.white, new Vector2((float)i / 20f, 0));
            
            float x = Mathf.Sin(i * ang) * r;
            float y = Mathf.Cos(i * ang) * r;
            vh.AddVert(new Vector3(x, 0, y), Color.white, new Vector2((float)i / 20f, 1));

            if(i==19)
            {
                float x2 = Mathf.Sin(0 * ang) * r1;
                float y2 = Mathf.Cos(0 * ang) * r1;
                vh.AddVert(new Vector3(x2, 0, y2), Color.white, new Vector2(1, 0));

                float x3 = Mathf.Sin(0 * ang) * r;
                float y3 = Mathf.Cos(0 * ang) * r;
                vh.AddVert(new Vector3(x3, 0, y3), Color.white, new Vector2(1, 1));
            }
            vh.AddTriangle(i * 2, i * 2 + 1, (i + 1) * 2 + 1);
            vh.AddTriangle(i * 2, (i+1) * 2 + 1, (i + 1) * 2);

        }

        Mesh mesh = new Mesh();
        vh.FillMesh(mesh);
        gameObject.AddComponent<MeshFilter>().mesh = mesh;
        gameObject.AddComponent<MeshRenderer>();

    }


}

おすすめ

転載: blog.csdn.net/m0_74022070/article/details/130513883