地图网格系统
顶点
生成顶点,确定顶点的索引和坐标
顶点和网格的其实位置不同,边界上是没有顶点的(顶点四周要求必须都有网格),所以第一个网格-第一个顶点-第二个网格-第二个顶点的顺序。顶点的索引由x,y的索引(与CellSize无关)生成(实际是一个Int类型的Vector2变量),因此一个顶点既可以由2维索引也可以由2个1维索引确定,顶点索引的范围在[1,map.width]和[1,map.height],而格子在[0,map.width]和[0,map.height]。
public class MapGrid
{
// 顶点数据
public Dictionary<Vector2Int, MapVertex> vertexDic = new Dictionary<Vector2Int, MapVertex>();
public MapGrid(int mapHeight, int mapWidth, float cellSize)
{
MapHeight = mapHeight;
MapWidth = mapWidth;
CellSize = cellSize;
//生成顶点数据
for (int x = 1; x < mapWidth; x++)
{
for (int z = 1; z < mapHeight; z++)
{
AddVertex(x, z);
//TODO: 还要生成网格
}
}
}
public int MapHeight { get; private set; }
public int MapWidth { get; private set; }
public float CellSize { get; private set; }
public void AddVertex(int x,int y)
{
vertexDic.Add(new Vector2Int(x, y)
, new MapVertex()
{
Position = new Vector3(x * CellSize, 0, y * CellSize)
}) ;
}
}
/// <summary>
/// 地图顶点
/// </summary>
public class MapVertex
{
public Vector3 Position;
}
按照坐标顺序生成顶点索引,根据格子大小具体生成坐标(在MapGenerator中也要对height和width进行对应缩放)
foreach (var item in vertexDic.Values)
{
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Sphere);
temp.transform.position = item.Position;
temp.transform.localScale = Vector3.one * 0.25f;
}
生成一个4*4的地图网格,顶点处用Sphere代替。
public MapVertex GetVertex(Vector2Int index)
{
return vertexDic[index];
}
public MapVertex GetVertex(int x,int y)
{
return GetVertex(new Vector2Int(x, y));
}
/// <summary>
/// 通过世界坐标获取顶点
/// </summary>
/// <param name="position"></param>
/// <returns></returns>
public MapVertex GetVertexByWorldPosition(Vector3 position)
{
// 处理边界
int x = Mathf.Clamp(Mathf.RoundToInt(position.x / CellSize),1,MapWidth);
int y = Mathf.Clamp(Mathf.RoundToInt(position.z / CellSize), 1, MapHeight);
return GetVertex(x, y);
}
补充三个获取顶点的重载方法(顶点二维索引,顶点一维索引(并不是实际坐标,有cellSize缩放),世界坐标),在世界坐标里通过和cellSize相除取四舍五入来得到顶点横纵坐标编号,需要注意通过Clamp处理一下顶点的边界在[1,MapWith]和[1,MapHeight]之间。
public void TestVertex()
{
print(grid.GetVertexByWorldPosition(testObj.transform.position).Position);
}
测试Capsule离哪个点最近,结果正确。
格子
生成格子
public Dictionary<Vector2Int, MapCell> cellDic = new Dictionary<Vector2Int, MapCell>();
public void AddCell(int x, int y)
{
float offset = CellSize / 2;
cellDic.Add(new Vector2Int(x, y),
new MapCell()
{
Position = new Vector3(x * CellSize - offset, 0, y * CellSize - offset)
});
}
定义好格子数据,需要注意的是格子的坐标中心在两个顶点中间,因此顶点数量也比格子数量少一,生成是需要注意for循环的次数。
for (int x = 1; x < mapWidth; x++)
{
for (int z = 1; z < mapHeight; z++)
{
AddVertex(x, z);
AddCell(x, z);
}
}
foreach (var item in cellDic.Values)
{
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
temp.transform.position = item.Position - new Vector3(0,0.49f,0);
temp.transform.localScale = new Vector3(CellSize,1,CellSize);
}
可以发现绘制出来的网格少一行一列,就是因为格子夹着顶点的原因,二者数量并不统一。
// 增加一行一列
for (int x = 1; x <= mapWidth; x++)
{
AddCell(x, mapHeight);
}
for (int z = 1; z < mapHeight; z++)
{
AddCell(mapWidth, z);
}
补一行一列,注意一行一列有一个交叉点,第二次for循环时左闭右开。
基于顶点找格子
public MapCell GetCell(Vector2Int index)
{
return cellDic[index];
}
public MapCell GetCell(int x,int y)
{
return GetCell(new Vector2Int(x, y));
}
底层提供方法可以根据Cell二维索引和一维索引直接从dic中找到对象。
/// <summary>
/// 获取左下角格子
/// </summary>
public MapCell GetLeftBottomMapCell(Vector2Int vertexIndex)
{
return cellDic[vertexIndex];
}
/// <summary>
/// 获取右下角格子
/// </summary>
public MapCell GetRightBottomMapCell(Vector2Int vertexIndex)
{
return cellDic[new Vector2Int(vertexIndex.x + 1,vertexIndex.y)];
}
/// <summary>
/// 获取左上角格子
/// </summary>
public MapCell GetLeftTopMapCell(Vector2Int vertexIndex)
{
return cellDic[new Vector2Int(vertexIndex.x, vertexIndex.y + 1)];
}
/// <summary>
/// 获取右上角格子
/// </summary>
public MapCell GetRightTopMapCell(Vector2Int vertexIndex)
{
return cellDic[new Vector2Int(vertexIndex.x + 1, vertexIndex.y + 1)];
}
一个顶点周围有四个格子,每个格子和顶点的一维索引存在左减右加,上加下减的关系。
foreach (var item in cellDic.Values)
{
GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
temp.transform.position = item.Position - new Vector3(0,0.49f,0);
temp.transform.localScale = new Vector3(CellSize,1,CellSize);
}
测试(1,3)点左下角的格子坐标为(0.5,2.5),注意实际获得的是格子的索引,相当于获取到了格子的对象,不只是坐标。