地形地图管理器
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class PlaneManager : MonoBehaviour
{
public Slider slider;//控制地图缩放
public Transform water;//水面
public RectTransform mapBox;//地图的容器
public Image map;//地图块
public Transform player;//玩家
public MyPlane plane;//地形块
public static int wh = 100;//地形宽高
Vector2Int pos = Vector2Int.zero;//人物当前所处地图id
Dictionary<Vector2Int, MyPlane> show = new Dictionary<Vector2Int, MyPlane>();//已经显示出来的数据
public static Dictionary<Vector2Int, Dictionary<Vector3, int>> treeDic = new Dictionary<Vector2Int, Dictionary<Vector3, int>>();//全部地图上的植物数据
Queue<MyPlane> pool = new Queue<MyPlane>();//对象池 存储不使用的物体
Dictionary<Vector2Int, Image> showImage = new Dictionary<Vector2Int, Image>();//已经显示出来的数据
Queue<Image> MapPool = new Queue<Image>();//对象池 存储不使用的物体
public static float mapp = 0;//地形与地图的大小比例
void Start()
{
//计算比例尺(用于地图和地形的坐标转换)
mapp = map.rectTransform.sizeDelta.x / (float)wh;
//计算玩家初始高度
player.transform.position = Vector3.up *( MyPlane.Turbulence(0, 0, wh) * 10);
//初始化地形
OnMove();
}
public void OnSlider()
{
//控制地图缩放
mapBox.localScale = Vector3.one * slider.value;
}
//人物移动后调用
public void OnMove()
{
//确定玩家所属地形块
pos = new Vector2Int((int)(player.position.x - wh / 2) / wh, (int)(player.position.z - wh / 2) / wh);
//获取周围9块地图
Dictionary<Rect, Vector2Int> dic = new Dictionary<Rect, Vector2Int>();
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
Rect rect = new Rect((i + pos.x) * wh, (j + pos.y) * wh, wh, wh);
//这里是创建所以全部添加,如果是加载需要判断有数据再添加
dic.Add(rect, new Vector2Int(i + pos.x, j + pos.y));
}
}
//玩家矩形1.5倍地图块大小,与之重叠的地形块会被创建
Rect playerRect = new Rect(player.position.x - wh / 2, player.position.z - wh / 2, wh * 1.5f, wh * 1.5f);
//循环找到需要显示的数据
List<Vector2Int> datas = new List<Vector2Int>();
foreach (var item in dic)
{
if (IsLap(playerRect, item.Key))
{
datas.Add(item.Value);
}
}
//找到已经显示但不需要显示的存入对象池
List<Vector2Int> dustbin = new List<Vector2Int>();//垃圾箱
foreach (var item in show)
{
if (!datas.Contains(item.Key))
{
//地形
item.Value.gameObject.SetActive(false);
pool.Enqueue(item.Value);//存入对象池
dustbin.Add(item.Key);//存入垃圾箱
//地图
showImage[item.Key].gameObject.SetActive(false);
MapPool.Enqueue(showImage[item.Key]);
}
}
//清理垃圾箱
foreach (var item in dustbin)
{
//地形
show.Remove(item);
//地图
showImage.Remove(item);
}
//创建新数据
foreach (var data in datas)
{
//判断数据是否已经显示
if (!show.ContainsKey(data))
{
if (pool.Count > 0)
{
//地形
show.Add(data, pool.Dequeue());
show[data].gameObject.SetActive(true);
//地图
showImage.Add(data, MapPool.Dequeue());
showImage[data].gameObject.SetActive(true);
}
else
{
//地形
show.Add(data, Instantiate(plane));
//地图
showImage.Add(data, Instantiate(map,mapBox));
}
//地形
show[data].Init(data, showImage[data]);
//地图
showImage[data].GetComponent<MapMove>().pos = data;
}
}
}
private void Update()
{
//这些应该是每次移动后调用(一般使用消息中心,Update消耗太大且浪费)
OnMove();
//计算地图轴心(使用轴心做移动才不会影响缩放后的位置)
float px = (player.position.x + wh / 2) / wh;
float py = (player.position.z + wh / 2) / wh;
mapBox.pivot = new Vector2(px, py);
//水面随人物移动
water.position = new Vector3(player.position.x, 3, player.position.z);
}
//判断两个矩形是否相交
public bool IsLap(Rect rect1, Rect rect2)
{
float rect1MinX = rect1.x - rect1.width / 2;
float rect1MaxX = rect1.x + rect1.width / 2;
float rect1MinY = rect1.y - rect1.height / 2;
float rect1MaxY = rect1.y + rect1.height / 2;
float rect2MinX = rect2.x - rect2.width / 2;
float rect2MaxX = rect2.x + rect2.width / 2;
float rect2MinY = rect2.y - rect2.height / 2;
float rect2MaxY = rect2.y + rect2.height / 2;
return rect1MinX < rect2MaxX && rect2MinX < rect1MaxX && rect1MinY < rect2MaxY && rect2MinY < rect1MaxY;
}
}
地形块地图块统一计算
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MyPlane : MonoBehaviour
{
public GameObject[] treeArr;//植物预制体数组
public MeshFilter filter;//网格过滤器
public MeshRenderer renderer;//网格渲染器
public MeshCollider collider;//网格碰撞框
public Texture2D gao;//地图高处显示纹理
public Texture2D di;//地图低处显示纹理
public Texture2D texture;//地图纹理
public float waterH = 3;//水平面高度
List<Queue<GameObject>> pool = new List<Queue<GameObject>>();//植物对象池
List<List<GameObject>> treeList = new List<List<GameObject>>();//植物列表
private void Awake()
{
//根据植物类型创建列表和对象池
for (int i = 0; i < treeArr.Length; i++)
{
pool.Add(new Queue<GameObject>());
treeList.Add(new List<GameObject>());
}
}
void Start()
{
}
//初始化和更新地形
public void Init(Vector2Int pos,Image image)
{
//把所有遗留植物存入对象池
for (int i = 0; i < treeList.Count; i++)
{
foreach (var item in treeList[i])
{
item.SetActive(false);
pool[i].Enqueue(item);
}
}
//判的是否有植物数据,没有创建新数据
Dictionary<Vector3, int> trees;
if (PlaneManager.treeDic.ContainsKey(pos))
{
trees = PlaneManager.treeDic[pos];
}
else
{
trees = new Dictionary<Vector3, int>();
}
//创建地图纹理
texture = new Texture2D(PlaneManager.wh, PlaneManager.wh);
//创建地形网格
Mesh mesh = new Mesh();
//绘制网格使用顶点助手
VertexHelper vh = new VertexHelper();
//循环宽高
for (int i = 0; i <= PlaneManager.wh; i++)
{
for (int j = 0; j <= PlaneManager.wh; j++)
{
//根据坐标计算高度
float y = Turbulence((pos.x * PlaneManager.wh + i), (pos.y * PlaneManager.wh + j), PlaneManager.wh);
//计算uv坐标
float uvx = (float)i / (float)PlaneManager.wh;
float uvy = (float)j / (float)PlaneManager.wh;
//顶点
Vector3 pp = new Vector3(i, y * 10, j);
//添加顶点
vh.AddVert(pp, Color.white, new Vector2(uvx, uvy));
//添加绘制三角形
if (i != PlaneManager.wh && j != PlaneManager.wh)
{
//每4个顶点绘制2个三角形(所以宽高各少一个)
vh.AddTriangle(i * (PlaneManager.wh + 1) + j, i * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j);
vh.AddTriangle(i * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j + 1, (i + 1) * (PlaneManager.wh + 1) + j);
//绘制地图纹理
if (y * 10 < waterH)//低于水平线地图绘制蓝色
{
texture.SetPixel(i, j, Color.blue);
}
else//高于水平线地图绘制高低颜色差值
{
texture.SetPixel(i, j, Color.Lerp(di.GetPixel(i, j), gao.GetPixel(i, j), y));
}
}
//创建植物
if (PlaneManager.treeDic.ContainsKey(pos))//有数据读取植物数据
{
if (trees.ContainsKey(pp))
{
//获得植物id
int treeid = trees[pp];
GameObject tree;
//对象池有优先对象池取,没有再创建
if (pool[treeid].Count > 0)
{
tree = pool[treeid].Dequeue();
tree.SetActive(true);
}
else
{
tree = Instantiate(treeArr[treeid], transform);
}
//修改植物坐标
tree.transform.localPosition = pp;
//加入植物列表
treeList[treeid].Add(tree);
}
}
else if(y * 10 > waterH && Random.Range(0,100) < 10)//没植物数据随机创建(高于水平面才创建植物)
{
//随机植物类型
int treeid = Random.Range(0, treeArr.Length);
GameObject tree;
if (pool[treeid].Count > 0)
{
tree = pool[treeid].Dequeue();
tree.SetActive(true);
}
else
{
tree = Instantiate(treeArr[treeid], transform);
}
tree.transform.localPosition = pp;
treeList[treeid].Add(tree);
//存入植物数据
trees.Add(pp, treeid);
}
}
}
if (!PlaneManager.treeDic.ContainsKey(pos))
{
//保存随机出来的植物数据
PlaneManager.treeDic.Add(pos, trees);
}
//把顶点助手的信息写入mesh
vh.FillMesh(mesh);
//显示网格
filter.mesh = mesh;
//renderer.material.mainTexture = texture;
collider.sharedMesh = mesh;
//设置地形块位置
transform.position = new Vector3(pos.x * PlaneManager.wh, 0, pos.y * PlaneManager.wh);
//设置地图纹理显示类型
texture.wrapMode = TextureWrapMode.Clamp;
//保存纹理设置
texture.Apply();
//创建材质球(使用默认UI的shader)
Material material = new Material(Shader.Find("UI/Default"));
//为材质球设置纹理
material.mainTexture = texture;
//为地图块设置材质球
image.material = material;
//设置地图块位置
image.rectTransform.anchoredPosition = new Vector2((pos.x + 0.5f) * image.rectTransform.sizeDelta.x, (pos.y + 0.5f) * image.rectTransform.sizeDelta.y);
}
//调整后的噪声算法(x坐标,y坐标,地图的宽高)用于计算地形高度
public static float Turbulence(float x, float z, int wh)
{
x = x * 0.03f;
z = z * 0.03f;
float y = -0.5f;
for (int f = 1; f <= (wh + 1) / 12; f *= 2)
{
y += Mathf.Abs(Mathf.PerlinNoise(z * f, x * f) / f);
}
return y;
}
}
点击地图计算在地形上的位置(可用于导航移动)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class MapMove : MonoBehaviour, IPointerClickHandler
{
public static Vector3 navpos;//点击后的地形位置
public Vector2Int pos;//地图块坐标
//点击
public void OnPointerClick(PointerEventData eventData)
{
//计算点击位置相对于当前地图块的位置
Vector2 mypos = (transform as RectTransform).InverseTransformPoint(eventData.position);
//地图块轴心在中间所以加上一半的宽高
mypos += (transform as RectTransform).sizeDelta / 2;
//计算位于地图上的位置 = 点击位置 / 比例尺 + 地图块坐标 * 地形宽高
Vector2 v2 = mypos / PlaneManager.mapp + pos * PlaneManager.wh;
//计算该位置高度
float y = MyPlane.Turbulence(v2.x, v2.y, PlaneManager.wh) * 10;
//点击的位置坐标(静态可在任意位置获取)
navpos = new Vector3(v2.x, y, v2.y);
Debug.Log(navpos);
}
}