math - 如何将地图图块无缝地包裹在圆柱形周围?
问题描述
我正在创建一个在地图上进行的游戏,玩家应该能够在地图上滚动。我使用来自 NASA 的真实数据作为 5700 x 2700 像素的图像,分成 4 个较小的图像,每个图像对应一个半球:
我如何分割图像:
玩家将通过摄像头观看世界,目前摄像头的纵横比为 4:3,可以四处移动。它的高度和宽度可以描述为两个变量 x 和 y,目前分别为 480 和 360。
相机型号:
在实践中,相机是“固定的”,而瓷砖移动。相机的中心被描述为两个变量:xcam 和 ycam。目前,4个瓷砖移动和隐藏完美无瑕。当相机经过纬度 180 度的“边缘”时,就会出现问题。应该发生的是,一侧的瓷砖应该显示和移动,好像世界是一个没有任何明显间隙的圆柱体。我通过对它做这个等式来更新 xcam:
xcam = ((xcam + (2700 - x) mod (5400 - x)) - (2700 - x)
并且瓦片的中心根据这些方程式更新(为简单起见,我将只关注瓦片 1 和 2):
tile1_x = xcam - 1350
tile1_y = ycam + 650
tile2_x = xcam + 1350
tile2_y = ycam + 650
使用这个,每当相机移动超过 tile 1 的最左边缘时,它就会“跳过”,而不是 tile 1 仍然可见,而 tile 2 在视图中,它移动得足够多,以至于 tile 2 的最右边缘位于相机的最右边缘。
这是现实中发生的事情:,
这就是我想要发生的事情:
那么,有什么方法可以更新我正在使用的方程式(甚至完全重做所有事情),以便我可以顺利包装?
解决方案
我认为您不必要地对许多图块及其大小进行硬编码,从而将您的代码绑定到这些数据。在我看来,最好将它们存储在一些变量中,以便在数据发生变化时可以在一个地方轻松修改它们。这也让我们可以编写更灵活的代码。
所以,假设我们有变量:
// logical size of the whole Earth's map,
// currently 2 and 2
int ncols, nrows;
// single tile's size, currently 2700 and 1350
int wtile, htile;
// the whole Earth map's size
// always ncols*wtile and nrows*htile
int wmap, hmap;
Tile tiles[nrows][ncols];
// viewport's center in map coordinates
int xcam, ycam;
// viewport's size in map coordinates, currently 480 and 360
int wcam, hcam;
每当我们更新玩家的位置时,我们需要确保该位置在允许的范围内。但是,为了定义允许的范围,我们需要先建立坐标系。例如,如果x
值从0
到wmap-1
,向右增加(向东),并且y
值从0
到hmap-1
,向下增加(向南),则:
// player's displacement
int dx, dy;
xcam = (xcam + dx) mod wmap
ycam = (ycam + dy) mod hmap
确保相机位置始终在地图内。(假设mod
运算符总是返回非负值。如果它像 C 语言%
运算符那样工作,它返回负除数的负结果,需要先添加一个除数以确保第一个参数是非负的:xcam = (xcam + dx + wmap) mod wmap
等)
如果您希望xcam,ycam = 0,0
位于地图的中心(即格林威治子午线和赤道),那么允许的范围将是-wmap/2
through wmap/2-1
forx
和-hmap/2
through hmap/2 - 1
for y
。然后:
xcam = (xcam + dx + wmap/2) mod wmap - wmap/2
ycam = (ycam + dy + hmap/2) mod hmap - hmap/2
更一般地,让x0, y0
表示相机相对于地图左上角的“零”位置。然后我们可以通过将相机位置转换为地图坐标,然后平移和环绕,最后转换回相机坐标来更新相机位置:
xmap = xcam + x0
ymap = ycam + y0
xmap = (xmap + dx) mod wmap
ymap = (ymap + dy) mod hmap
xcam = xmap - x0
ycam = ymap - y0
或者,更简洁:
xcam = (xcam + dx + x0) mod wmap - x0
ycam = (ycam + dy + y0) mod hmap - y0
现在,当我们知道视口(相机)相对于地图的位置时,我们需要用地图瓦片填充它。必须在这里做出新的决定。
当我们从阿拉斯加的安克雷奇(西半球)向北旅行时,我们最终会到达北极,然后我们会发现自己在东半球,向南。如果我们朝同一个方向前进,我们将到达挪威的库萨莫,然后是俄罗斯的圣彼得堡,然后是乌克兰的基辅……但这将是一次南方之旅!我们通常不会将其描述为初始北线的下一部分。因此,我们不会将“越过极点”的部分显示为地图的倒置延伸。因此,地图不应在 row number 上方0
或 row 下方显示图块nrows-1
。
另一方面,当我们沿着纬度圈旅行时,我们会顺利穿过 0 和 180 经线,在东西半球之间切换。因此,如果相机视图覆盖了地图左右边缘两侧的区域,我们需要继续用tiles
数组另一端的瓦片填充视图。如果我们使用按比例缩小的地图,使其小于视口,我们甚至可能需要多次迭代!
相机视图的左边缘对应于 的“经度”,xleft = xcam - wcam/2
右边缘对应于xrght = xcam + wcam/2
。因此,我们可以按 tile 的宽度跨过视口以找出合适的列并显示它们:
x = xleft
repeat
show a column at x
x = x + wtile
until x >= xrght
'在'处显示一列x
'部分需要找到适当的列,然后遍历列以显示相应的图块。让我们找出适合相机视图的图块:
ytop = ycam - hcam/2
ybot = ycam + hcam/2
y=ytop
repeat
show a tile at x,y
y = y + htile
until y >= ybot
要显示瓷砖,我们需要找到合适的瓷砖,然后将其发送到相机视图中的适当位置。但是,我们将列号与行号区别对待:列换行而行不换行:
row = y/htile
if (0 <= row) and (row < nrows) then
col = (x/wtile) mod ncols
xtile = x - (x mod wtile)
ytile = y - (y mod htile)
display tile[row][col] at xtile,ytile
endif
当然xtile
和ytile
是我们的地图比例的经度和纬度,因此“显示图块在”例程必须通过从它们中减去相机位置来将它们转换为相机视图坐标:
xinwiev = xtile - xcam
yinview = ytile - ycam
然后在显示设备(屏幕)上应用相对于相机视图中心的结果值。
如果您想实现放大和缩小视图,则会出现另一个复杂程度,即地图的动态缩放,但我相信您会发现自己需要应用缩放因子以获得正确结果的计算。:)
推荐阅读
- cmake - 字符串中的变量引用不会被“set”评估
- python - Django - 将使用 xhtml2pdf 生成的 pdf 文件保存到磁盘
- docker - 使用 Ansible 根据使用 docker_prune 的标签删除图像
- c++ - 如何检查每个头文件是否包含所需的包含文件?
- java - 如何在 HttpDecompressor 之前添加 GlobalTrafficShapingHandler
- sap-gui - 是否有可能在用户级别关闭日期扩展?
- python-3.x - python with lightgbm,预测类标签(0或1)而不是概率
- javascript - 希望根据某些条件检查将值保持在切换按钮的一种状态
- node.js - 在 Multer 中上传图像后移动和重命名图像
- django - curl: (35) schannel: next InitializeSecurityContext failed: SEC_E_INVALID_TOKEN (0x80090308) - 提供给函数的令牌无效