0%

简单的无限地图实现

突然想写一个类吸血鬼幸存者的 demo, 想了一下无限地图挺重要的,于是就设计了一个简单的无限地图实现,感觉性能一般般,过两天找一下类似的代码比对一下性能,这个功能实现还是很简单的,今天记录一下.

数学原理

呢讲到无限地图算法,首先就要考虑他的数学原理.我实现的这个方法也是目前应用比较广,实现起来也是最简单的那种了.

Alt text

在这张图上,我们给出了原点OO,橙色框所框中的是以玩家出生点为原点渲染出来的第一块地图,我们可以记为M(0,0)M(0,0),图中还给出了框的长和宽XXYY,这里的红框和绿框的大小是相同的,他的的焦点,也是相交的几条边的中点.

玩家的视野范围,即摄像机所捕获的范围是不大于橙色框的
我们通过矩形的四个点就可以确定所需要渲染出的地图区块,比如说当玩家在所示绿框中活动的时候,我们就需要渲染出M(0,0),M(1,0),M(1,1),M(0,1)M(0,0),M(1,0),M(1,1),M(0,1)这四块地图

整张地图所需要记录的只是以XX为X轴上的单位长度和以YY为Y轴上的单位长度组成的点阵图,当玩家处于任意四个点组成的举行范围内时,我们只需要把以这四个点为中点,以XXYY为长和宽的标准地图就可以了.

代码实现

首先我们定义一个结构体来储存我们的地图信息

1
2
3
4
5
6
7
8
9
/// <summary>
/// 地图数据
/// </summary>
public struct MapData
{
public MapUnit map;
public int X;
public int Y;
}

这个结构体中有三个成员,map是一个MapUnit类型的变量,XY分别是以XY为单位长度的坐标.

下面放一下MapUnit的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class MapUnit : MonoBehaviour
{
private MapData _myData;
private MapUnit _mapUnit;
void Start()
{
MapManager.Instance.mapList.Add(_myData);
}

void Update()
{
if (MapManager.Instance.checkPlayerPosition(transform))
{
Destroy(gameObject);
}
}

public MapData GetMyData(int _X, int _Y)
{
_myData = new MapData()
{
map = _mapUnit,
X = _X,
Y = _Y
};
return _myData;
}
private void OnDestroy()
{
MapManager.Instance.mapList.Remove(_myData);
}
}

也很简单,这个类有两个成员字段,分别存贮自己的地图数据和自己的脚本,他还有一个方法,用来获取自己的地图数据.
Start方法中,我们把自己的地图数据添加到MapManagermapList中,在Update方法中,地图会自行检测自己是否在玩家的视野范围内,如果是,就销毁自己,在OnDestroy方法中,他会把自己的地图数据从MapManagermapList中移除.

最后来看一下地图管理类的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using Unity.Mathematics;
public class MapManager : MonoBehaviour
{
public static MapManager Instance;
private GameObject _player;
private bool _playerLive;
private int _mapX;
private int _mapY;
public GameObject mapPrefabs;
public Transform grid;

public List<MapData> mapList = new List<MapData>();

private void Awake()
{
Instance = this;
_mapX = 18;
_mapY = 10;
}

void Update()
{
if (_playerLive)
{
ConrtolAutoMap();
}
}
/// <summary>
/// 获取玩家对象
/// </summary>
/// <param name="player"></param>
public void GetPlayer(GameObject player)
{
_player = player;
_playerLive = true;
}

/// <summary>
/// 自动检查玩家身边的地图,并自动加载未加载地图
/// </summary>
public void ConrtolAutoMap()
{
var x = _player.transform.position.x;
var y = _player.transform.position.y;
int X = (int)x / _mapX;
int Y = (int)y / _mapY;
int XX = x > 0 ? X + 1 : X - 1;
int YY = y > 0 ? Y + 1 : Y - 1;
if (!CheckMapLive(X, Y))
{
var newMap = Instantiate(mapPrefabs, new Vector3(X * _mapX, Y * _mapY), quaternion.identity,grid);
newMap.GetComponent<MapUnit>().GetMyData(X, Y);
}

if (!CheckMapLive(XX, Y))
{
var newMap = Instantiate(mapPrefabs, new Vector3((XX) * _mapX, Y * _mapY), quaternion.identity,grid);
newMap.GetComponent<MapUnit>().GetMyData(XX, Y);
}

if (!CheckMapLive(X, YY))
{
var newMap = Instantiate(mapPrefabs, new Vector3(X * _mapX, (YY) * _mapY), quaternion.identity,grid);
newMap.GetComponent<MapUnit>().GetMyData(X, YY);

}

if (!CheckMapLive(XX, YY))
{
var newMap = Instantiate(mapPrefabs, new Vector3((XX) * _mapX, (YY) * _mapY), quaternion.identity,grid);
newMap.GetComponent<MapUnit>().GetMyData(XX, YY);

}
}

/// <summary>
/// 检查目标地点是否存在地图单元
/// </summary>
/// <param name="x_target">坐标x</param>
/// <param name="y_target">坐标y</param>
/// <returns>存在返回true</returns>
bool CheckMapLive(int x_target, int y_target)
{
var target =
from m in mapList
where m.X == x_target && m.Y == y_target
select m.map;
return target.Any();
}
/// <summary>
/// 检查地图块是否已经超出了玩家的区块范围
/// </summary>
/// <param name="t">地图的Transform</param>
/// <returns>如果地图快已经超出了玩家的区块范围,则返回true</returns>
public bool checkPlayerPosition(Transform t)
{
return math.abs(t.position.x - _player.transform.position.x) > _mapX ||
math.abs(t.position.y - _player.transform.position.y) > _mapY
? true
: false;
}

private void OnDestroy()
{
mapList.Clear();
}
}

显然,写的有点蠢,不过好歹能用,生成不同地图的方法也很简单,多做几个预制体,生成的时候写一个随机方法就可以了.

过两天把这个优化一下.


欢迎关注我的其它发布渠道