首页 > 解决方案 > 如何将地图图块无缝地包裹在圆柱形周围?

问题描述

我正在创建一个在地图上进行的游戏,玩家应该能够在地图上滚动。我使用来自 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 的最右边缘位于相机的最右边缘。

这是现实中发生的事情:图像

这就是我想要发生的事情图像

那么,有什么方法可以更新我正在使用的方程式(甚至完全重做所有事情),以便我可以顺利包装?

标签: math

解决方案


我认为您不必要地对许多图块及其大小进行硬编码,从而将您的代码绑定到这些数据。在我看来,最好将它们存储在一些变量中,以便在数据发生变化时可以在一个地方轻松修改它们。这也让我们可以编写更灵活的代码。

所以,假设我们有变量:

// 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值从0wmap-1,向右增加(向东),并且y值从0hmap-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/2through wmap/2-1forx-hmap/2through hmap/2 - 1for 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

当然xtileytile是我们的地图比例的经度和纬度,因此“显示图块在”例程必须通过从它们中减去相机位置来将它们转换为相机视图坐标:

xinwiev = xtile - xcam
yinview = ytile - ycam

然后在显示设备(屏幕)上应用相对于相机视图中心的结果值。

如果您想实现放大和缩小视图,则会出现另一个复杂程度,即地图的动态缩放,但我相信您会发现自己需要应用缩放因子以获得正确结果的计算。:)


推荐阅读