老规矩先上效果:
一:创建空节点,为了生成地形和动态烘焙地形
<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);
}
}
}
}
这里面的预制体prefab是一块plane,注意下面的组件添加好
这里的脚本<Terrrain1>如下:
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);
}
}
注意这里的Track:
就是上面的预制体,下面的size可以尽可能的大,大过于你的地形就好
扫描二维码关注公众号,回复:
16619860 查看本文章
二:制作一张图片Image,用来做选框
注意这里的图片类型为Sliced
在图片上挂<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);
}
}
}
}
}
三:创建空节点生成几个角色
挂载脚本<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>();
}
}