2D 아이소 메트릭 : 화면에서 타일 좌표로


9

아이소 메트릭 2D 게임을 작성 중이며 커서가 어느 타일인지 정확하게 파악하기가 어렵습니다. 그림은 다음과 같습니다.

여기서 xs와 ys는 화면 좌표 (픽셀)이고 xt와 yt는 타일 좌표이고 W와 H는 각각 타일 너비와 타일 높이 (픽셀)입니다. 좌표에 대한 내 표기법은 (y, x)이며 혼란 스러울 수 있습니다. 죄송합니다.

내가 지금까지 알아낼 수있는 최선의 방법은 다음과 같습니다.

int xtemp = xs / (W / 2);
int ytemp = ys / (H / 2);
int xt = (xs - ys) / 2;
int yt = ytemp + xt;

이것은 거의 정확 해 보이지만 매우 부정확 한 결과를 제공하여 특정 타일을 선택하기 어렵거나 때로는 클릭하려는 타일 옆에 타일을 선택하기도합니다. 나는 왜 그런지 이해하지 못하고 누군가가 이것의 논리를 이해하도록 도울 수 있는지 알고 싶습니다.

감사!

답변:


2

정확한 측정을 위해 다음을 고려할 수 있습니다.

먼저 i 및 j 벡터 (isometricMap [i, j]에서와 같이 결정) 또는 화면의 yt 및 xt로 화면의 x와 y로 결정된 등각 공간에서 좌표를 변환하는 방법을 고려해 보겠습니다. 간단하게하기 위해 화면 공간이 등각 공간과 원점을 이루고 있다고 가정 해 봅시다.

변환을 수행하는 한 가지 방법은 먼저 회전을 수행 한 다음 y 또는 x 축의 크기를 조정하는 것입니다. yt 및 xt와 일치하는 데 필요한 값을 얻으려면 여기서 그 자리에 올 수 없습니다. 이 작업을 수행하거나 수행하지 않을 매트릭스를 만든 다음 역행렬을 사용할 수 있지만 역동 작은 기본적으로 원하는 것입니다.

값을 반대로 스케일 한 다음 뒤로 회전하여 값을 얻고 아래로 반올림합니다.

이것에 다른 방법이 있다고 생각하지만 지금 당장 나에게 가장 적합한 것 같습니다.


아아 나는이 게시물을 여러 번 수정 해 왔으며 어쨌든 원하는만큼 깔끔하게 내 요점을 알 수 없다고 생각합니다. 잠이 필요합니다.
Toni

1
감사합니다. 행렬은 확실히 최고의 솔루션입니다. 나는 지금 거의 효과가있다!
Asik

4

내가 쓰는 게임에 대해서도 같은 문제가있었습니다. 이 문제는 아이소 메트릭 시스템을 정확히 구현 한 방법에 따라 달라 지지만 문제를 해결하는 방법을 설명하겠습니다.

먼저 tile_to_screen 함수로 시작했습니다. (저는 타일을 처음에 올바른 위치에 배치하는 방법이라고 가정합니다.)이 함수에는 screen_x 및 screen_y를 계산하는 방정식이 있습니다. 광산은 다음과 같이 보였습니다 (파이썬).

def map_to_screen(self, point):
    x = (SCREEN_WIDTH + (point.y - point.x) * TILE_WIDTH) / 2
    y = (SCREEN_HEIGHT + (point.y + point.x) * TILE_HEIGHT) / 2
    return (x, y)

저는이 두 방정식을 선형 방정식 시스템으로 만들었습니다. 선택한 모든 방법으로이 방정식 시스템을 풉니 다. (rref 방법을 사용했습니다. 또한 일부 그래프 계산기에서이 문제를 해결할 수 있습니다.)

최종 방정식은 다음과 같습니다.

# constants for quick calculating (only process once)
DOUBLED_TILE_AREA = 2 * TILE_HEIGHT * TILE_WIDTH
S2M_CONST_X = -SCREEN_HEIGHT * TILE_WIDTH + SCREEN_WIDTH * TILE_HEIGHT
S2M_CONST_Y = -SCREEN_HEIGHT * TILE_WIDTH - SCREEN_WIDTH * TILE_HEIGHT

def screen_to_map(self, point):
    # the "+ TILE_HEIGHT/2" adjusts for the render offset since I
    # anchor my sprites from the center of the tile
    point = (point.x * TILE_HEIGHT, (point.y + TILE_HEIGHT/2) * TILE_WIDTH)
    x = (2 * (point.y - point.x) + self.S2M_CONST_X) / self.DOUBLED_TILE_AREA
    y = (2 * (point.x + point.y) + self.S2M_CONST_Y) / self.DOUBLED_TILE_AREA
    return (x, y)

보시다시피, 초기 방정식처럼 간단하지 않습니다. 그러나 그것은 내가 만든 게임에서 잘 작동합니다. 선형 대수에 감사합니다!

최신 정보

다양한 연산자로 간단한 Point 클래스를 작성한 후이 답변을 다음과 같이 단순화했습니다.

# constants for quickly calculating screen_to_iso
TILE_AREA = TILE_HEIGHT * TILE_WIDTH
S2I_CONST_X = -SCREEN_CENTER.y * TILE_WIDTH + SCREEN_CENTER.x * TILE_HEIGHT
S2I_CONST_Y = -SCREEN_CENTER.y * TILE_WIDTH - SCREEN_CENTER.x * TILE_HEIGHT

def screen_to_iso(p):
    ''' Converts a screen point (px) into a level point (tile) '''
    # the "y + TILE_HEIGHT/2" is because we anchor tiles by center, not bottom
    p = Point(p.x * TILE_HEIGHT, (p.y + TILE_HEIGHT/2) * TILE_WIDTH)
    return Point(int((p.y - p.x + S2I_CONST_X) / TILE_AREA),
                 int((p.y + p.x + S2I_CONST_Y) / TILE_AREA))

def iso_to_screen(p):
    ''' Converts a level point (tile) into a screen point (px) '''
    return SCREEN_CENTER + Point((p.y - p.x) * TILE_WIDTH / 2,
                                 (p.y + p.x) * TILE_HEIGHT / 2)

예, 두 개의 선형 방정식 시스템도 작동해야합니다. 평행하지 않은 두 개의 벡터가 있다고 가정하면 yt 및 xt의 단위 벡터를 사용하여 평면의 어느 지점이든 얻을 수 있습니다. 비록 당신의 구현이 약간 수 축적으로 보인다고 생각하지만 그것을 귀찮게하지 않을 것입니다.
Toni

2

좋은 좌표계를 사용하고 있습니다. 엇갈린 열을 사용하면 상황이 훨씬 까다로워집니다.

이 문제를 생각하는 한 가지 방법은 (xt, yt)를 (xs, ys)로 바꾸는 기능이 있다는 것입니다. Thane의 답변을 따르고 호출합니다 map_to_screen.

이 기능 의 을 원합니다 . 우리는 그것을 부를 수 screen_to_map있습니다. 함수 역에는 다음과 같은 속성이 있습니다.

map_to_screen(screen_to_map(xs, ys)) == (xs, ys)
screen_to_map(map_to_screen(xt, yt)) == (xt, yt)

이 두 가지 기능을 모두 작성한 후에는 단위 테스트를 수행하는 것이 좋습니다. 역수를 어떻게 쓰나요? 모든 함수에 역수가있는 것은 아니지만이 경우에는

  1. 회전 후 번역으로 쓴 경우 역은 역 변환 (음의 dx, dy)과 역 회전 (음의 각도)입니다.
  2. 행렬 곱셈으로 쓴 경우에는 역행렬이 곱해집니다.
  3. (xt, yt)의 관점에서 (xs, ys)를 정의하는 대수 방정식으로 쓴다면, 주어진 (xs, ys)에 대한 (xt, yt)에 대한 방정식을 풀면 역수가 구됩니다.

역 + 원래 함수가 시작한 답을 돌려 주는지 테스트하십시오. + TILE_HEIGHT/2렌더 오프셋 을 제거하면 Thane가 두 가지 테스트를 모두 통과합니다 . 대수를 풀었을 때 나는 다음을 생각해 냈습니다.

x = (2*xs - SCREEN_WIDTH) / TILE_WIDTH
y = (2*ys - SCREEN_HEIGHT) / TILE_HEIGHT
yt =  (y + x) / 2
xt =  (y - x) / 2

나는 Thane와 동일하다고 생각합니다 screen_to_map.

이 기능은 마우스 좌표를 플로트로 바꿉니다. floor정수 타일 좌표로 변환하는 데 사용 합니다.


1
감사! 변환 행렬을 사용하여 역을 작성하는 것이 간단합니다. 즉, 단지 Matrix.Invert ()입니다. 또한 더 선언적인 코딩 스타일로 이어집니다 (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate ()). 어쩌면 조금 느리지 만 문제가되지는 않습니다.
Asik
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.