9

我如何制作一个处理旋转的无限/重复世界,就像在这个游戏中一样:

http://bloodfromastone.co.uk/retaliation.html

我通过具有这样的层次结构对我的旋转移动世界进行了编码:

场景
  - mainLayer (CCLayer)
     - rotationLayer(CCNode)
        - positionLayer(CCNode)

rotationLayer 和 positionLayer 具有相同的大小(现在为 4000x4000 像素)。

我通过旋转rotationLayer来旋转整个世界,我通过移动positionLayer来移动整个世界,让玩家始终保持在设备屏幕的中心,并且是移动和旋转的世界。

现在我想这样做,如果玩家到达世界的边界(世界被移动以便世界边界与设备屏幕边界接触),那么世界被“包裹”到相反的边界所以世界是无限的。如果世界没有旋转,那将很容易,但现在我不知道如何做到这一点。我是一个数学和数学思维的傻瓜,所以我需要一些帮助。

现在我认为我不需要任何与 cocos2d-iphone 相关的帮助。我需要的是某种方法来计算我的玩家是否在世界范围之外,然后某种方法来计算我必须为世界提供什么新位置来包裹世界。

我想我必须计算一个圆的半径,这将是我在方形世界中的铸造厂,无论方形世界处于什么角度,都将确保可见矩形(屏幕)始终在世界广场。然后我需要一种方法来计算可见矩形边界是否在边界圆之外,如果是这样,我需要一种方法来计算边界圆中新的相对位置以将世界移动到。所以为了说明,我添加了 5 张图片。

在旋转的方形世界内的边界圆内的可见矩形:
旋转方形世界内的边界圆内的可见矩形

可见矩形顶部在旋转的方形世界中击中边界圆:
在此处输入图像描述

旋转的方形世界移动到相反的垂直位置,因此可见矩形的底部现在在旋转的世界内达到边界圆:
在此处输入图像描述

另一个可见矩形顶部的示例在旋转的方形世界中击中边界圆,以说明不同的场景:
在此处输入图像描述

再次旋转的方形世界移动到相反的垂直位置,以便可见矩形的底部现在在旋转的世界内达到边界圆:
再次旋转的方形世界移动到相反的垂直位置,以便可见矩形的底部现在在旋转的世界内达到边界圆

在非旋转的情况下移动 positionLayer 是我计算出来的数学,正如我所说,只要世界不旋转,我就可以计算出这个,但它确实如此。被移动/定位的世界/CCNode (positionLayer) 在被旋转的世界/CCNode (rotationLayer) 内。旋转的rotationLayer 的锚点始终位于屏幕的中心,但由于移动的positionLayer 在旋转的rotationLayer 内,它会围绕rotationLayer 的锚点旋转。然后我迷路了......当我将 positionLayer 向下移动到足以使其顶部边框到达屏幕顶部时,我需要按照 JohnPS 描述的方式包装该 positionLayer,但不是那么简单,我需要将它包装在基于矢量的关于rotationLayer CCNode 的旋转。这个我不知道怎么办。


谢谢索伦_

4

3 回答 3

7

就像约翰说的,最简单的事情就是建立一个圆环世界。想象一下,你的船是甜甜圈表面上的一个点,它只能在表面上移动。假设您位于两个圆圈(图中的红色和紫色)​​相交的点:

圆环上的圆圈.

如果你关注这些圈子,你最终会回到你开始的地方。另外,请注意,无论您在表面上如何移动,都无法到达“边缘”。圆环的表面没有这样的东西,这就是为什么它可以用作无限的 2D 世界。它有用的另一个原因是方程非常简单。您可以通过两个角度指定您在环面上的位置:从紫色圆圈上的“原点”行进以找到红色圆圈的角度,以及您在红色圆圈上行进的角度以找到您感兴趣的点。这两个角度环绕 360 度。让我们称这两个角thetaphi。它们是你的船在世界上的坐标,当你改变速度时你会改变什么等等。你基本上把它们当作你的xy,除非您必须确保在更改它们时始终使用模数(您的世界在每个方向上只会是 360 度,然后会环绕)。

现在假设你的船在坐标(theta_ship,phi_ship)并且有方向gamma_ship。您想绘制一个方形窗口,船在其中心,长度/宽度等于整个世界的某个百分比 n(假设您一次只想看到世界的四分之一,那么您将设置n = sqrt(1/4) = 1/2并拥有窗口的长度和宽度设置为n*2*pi = pi)。为此,您需要一个函数,该函数采用屏幕坐标 (x和) 中表示的点并在世界坐标 (和)y中吐出一个点。例如,如果你问它对应于世界的哪个部分,它应该返回船的坐标。如果船的方向为零(并且将与和对齐thetaphi(0,0)(theta_ship,phi_ship)xythetaphi) 然后某个坐标(x_0,y_0)将对应于(theta_ship+k*x_0, phi_ship+k*y_0),其中k某个比例因子与人们可以在屏幕中看到多少世界以及 和 上的边界x有关y。旋转 bygamma_ship引入了一点三角,在下面的函数中有详细说明。有关数量的确切定义,请参见图片。!蓝色是屏幕坐标系,红色是世界坐标系和配置变量(描述船在世界中位置的东西)。世界坐标中表示的对象是绿色的。

坐标变换函数可能如下所示:

# takes a screen coordinate and returns a world coordinate
function screen2world(x,y)
    # this is the angle between the (x,y) vector and the center of the screen           
    alpha = atan2(x,y); 
    radius = sqrt(x^2 + y^2); # and the distance to the center of the screen

    # this takes into account the rotation of the ship with respect to the torus coords
    beta = alpha - pi/2 + gamma_ship;

    # find the coordinates
    theta = theta_ship + n*radius*cos(beta)/(2*pi);
    phi = phi_ship + n*radius*sin(beta)/(2*pi));

    # return the answer, making sure it is between 0 and 2pi
    return (theta%(2*pi),phi%(2*pi))

我想,差不多就是这样。数学只是一些相对简单的三角,你应该画一点图来说服自己它是正确的。或者,您可以通过使用旋转矩阵及其更大的兄弟刚体变换(特殊的欧几里得群 SE(2))以更自动化的方式获得相同的答案。对于后者,我建议阅读Murray, Li, Sastry的前几章,该书是在线免费的。

如果你想做相反的事情(从世界坐标到屏幕坐标),你必须或多或少做同样的事情,但反过来:

beta = atan2(phi-phi_ship, theta-theta_ship);
radius = 2*pi*(theta-theta_ship)/(n*cos(beta));
alpha = beta + pi/2 - gamma_ship;
x = radius*cos(alpha);
y = radius*sin(alpha);
于 2011-11-18T21:42:27.360 回答
2

您需要定义您想要“相反界限”的含义。有关二维示例,请参阅基本多边形。有 4 种方法可以将正方形的边映射到另一边,得到球体、实射影平面、克莱因瓶或圆环。经典的街机游戏Asteroids实际上有一个圆环面。

这个想法是您需要将每个边界点粘合到其他有意义且一致的边界点。

如果您的世界是真正的 3 维世界(不仅仅是 2-D 表面地图上的 3-D),那么我认为您的任务将变得更加难以确定如何将边缘粘合在一起——您的边缘现在是嵌入的表面在 3-D 世界中。

编辑:

假设您有一张 2-D 地图,并且想要像 Asteroids 中那样环绕。

如果地图是1000x1000单位,x=0是地图的左边界,x=999右边界,并且您正在向右看并看到20前方的单位。然后在x=995你想看最多的地方1015,但这不在地图的右侧,所以1015应该变成15

如果您在x=5并查看左侧的20单位,那么您会看到x=-15您真正想要成为的单位985

当您越过地图边界时,要获得这些数字(始终在 0 和 999 之间),您需要使用模运算符

new_x = x % 1000; // in many programming languages

x为负时,每种编程语言处理x % 1000不同的结果。它甚至可以由实现定义。即它并不总是积极的(在0和之间999),所以使用它会更安全:

new_x = (x + 1000) % 1000; // result 0 to 999, when x >= -1000

因此,每次移动或更改视图时,您都需要重新计算位置坐标和视图中任何内容的坐标。您应用此操作来取回地图上的坐标xy坐标。

于 2011-11-11T19:49:53.233 回答
0

我是 Cocos2d 的新手,但我想我可以尝试帮助您解决几何计算问题,因为正如您所说,这不是框架问题。

我首先将您正在使用的每一层的锚点设置在它们的视觉中心。

然后让我们同意第一个接触边缘的部分总是一个角的假设。

如果您只想检查它是否在圆圈内,只需检查所有四个边是否都在圆圈内。

如果您想知道哪条边与圆的圆周接触,只需检查离点 x=0 y=0 最远的边,因为锚点将位于中心。

如果您有理由不将锚点放在中间,则可以使用相同的逻辑,只要在所有内容上包含每个对象宽度的一半即可。

于 2011-11-18T10:37:53.743 回答