아이소 메트릭 게임 세계 그리기


178

2D 게임에서 등각 타일을 그리는 올바른 방법은 무엇입니까?

I는 (예컨대, 판독 레퍼런스했습니다 이것 타일을하는 방식으로 표현 될 제안) 맵의 2D 배열 표현의 각 컬럼은 지그 재그. 나는 그것들이 다이아몬드 방식으로 더 그려 져야한다고 상상합니다. 화면에 그려지는 것이 2D 배열의 모습과 더 밀접하게 관련되어 있으며 약간 회전했습니다.

두 가지 방법 모두에 장단점이 있습니까?

답변:


505

업데이트 : 맵 렌더링 알고리즘 수정, 더 많은 일러스트레이션 추가, 형식 변경

타일을 화면에 매핑하는 "지그재그"기술의 이점은 타일 xy좌표가 세로 및 가로 축에 있다고 할 수 있습니다 .

"다이아몬드로 그리기"접근 방식 :

"다이아몬드로 그리기"를 사용하여 등각지도를 그리면 for이 예제와 같이 2 차원 배열에 중첩 된 루프를 사용하여지도를 렌더링하는 것입니다 .

tile_map[][] = [[...],...]

for (cellY = 0; cellY < tile_map.size; cellY++):
    for (cellX = 0; cellX < tile_map[cellY].size cellX++):
        draw(
            tile_map[cellX][cellY],
            screenX = (cellX * tile_width  / 2) + (cellY * tile_width  / 2)
            screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
        )

이점:

이 접근법의 장점은 for모든 타일에서 일관되게 작동하는 상당히 직진 논리를 가진 단순한 중첩 루프 라는 것입니다 .

불리:

그 방법에 대한 한 단점은이다 xy지도 타일의 좌표가 더 어려워 시각적 배열로서 표시되는지도 화면의 위치를 매핑 할 수있는 대각선으로 증가한다 :

타일 ​​맵 이미지

그러나 위의 예제 코드를 구현하는 데는 함정이있을 것입니다. 렌더링 순서는 특정 타일 뒤에있는 타일이 타일의 맨 앞에 그려지게합니다.

렌더링 순서가 잘못된 결과 이미지

이 문제를 수정하려면, 내부 for루프의 순서는 가장 높은 값에서 시작하여 더 낮은 값으로 렌더링되어야합니다.

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    for (j = tile_map[i].size; j >= 0; j--):  // Changed loop condition here.
        draw(
            tile_map[i][j],
            x = (j * tile_width / 2) + (i * tile_width / 2)
            y = (i * tile_height / 2) - (j * tile_height / 2)
        )

위 수정으로 맵 렌더링을 수정해야합니다.

올바른 렌더링 순서의 결과 이미지

"지그재그"접근 방식 :

이점:

"지그재그"접근 방식의 장점은 렌더링 된 맵이 "다이아몬드"접근 방식보다 약간 더 수직적으로 작게 보일 수 있다는 것입니다.

렌더링에 대한 지그재그 접근 방식이 간결 해 보입니다

불리:

지그재그 기술을 구현하려는 시도의 단점은 렌더링 코드를 작성하는 것이 조금 어렵다는 것입니다. 렌더링 코드 for는 배열의 각 요소에 대해 중첩 루프 처럼 간단하게 작성할 수 없기 때문입니다 .

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    if i is odd:
        offset_x = tile_width / 2
    else:
        offset_x = 0

    for (j = 0; j < tile_map[i].size; j++):
        draw(
            tile_map[i][j],
            x = (j * tile_width) + offset_x,
            y = i * tile_height / 2
        )

또한 렌더링 순서의 지그재그 특성으로 인해 타일의 좌표를 알아내는 것이 약간 어려울 수 있습니다.

지그재그 순서 렌더링에 대한 좌표

참고 :이 답변에 포함 된 그림은 다음과 같은 int배열을 맵으로 사용하여 제시된 타일 렌더링 코드의 Java 구현으로 작성되었습니다 .

tileMap = new int[][] {
    {0, 1, 2, 3},
    {3, 2, 1, 0},
    {0, 0, 1, 1},
    {2, 2, 3, 3}
};

타일 ​​이미지는 다음과 같습니다.

  • tileImage[0] -> 안에 상자가있는 상자.
  • tileImage[1] -> 블랙 박스.
  • tileImage[2] -> 흰색 상자.
  • tileImage[3] -> 키가 큰 회색 물체가 들어있는 상자입니다.

타일 ​​너비 및 높이에 대한 참고 사항

변수 tile_widthtile_height상기 코드 예에서 사용 된 타일을 나타내는 이미지의 바닥 타일의 폭과 높이를 참조 :

타일 ​​너비와 높이를 보여주는 이미지

이미지 크기와 타일 크기가 일치하는 한 이미지의 크기를 사용하면 효과가 있습니다. 그렇지 않으면, 타일 맵은 타일 사이에 간격을두고 렌더링 될 수 있습니다.


136
당신은 그림을 그렸습니다. 노력입니다.
zaratustra

건배 쿠퍼. 나는 현재 개발중인 게임에 다이아몬드 접근법을 사용하고 있으며 치료 중입니다. 다시 감사합니다.
Benny Hallett

3
여러 높이 레이어는 어떻습니까? 가장 낮은 레벨에서 시작하여 가장 높은 레벨에 도달 할 때까지 계속 전투를 할 수 있습니까?
NagyI

2
@DomenicDatti : 당신의 친절한 말에 감사드립니다 :)
coobird

2
대단해. 방금 다이아몬드 접근 방식을 사용했으며 누군가 화면 위치에서 그리드 좌표를 가져와야하는 경우 다음과 같이했습니다 j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;.
Kyr Dunenkoff

10

어느 쪽이든 작업이 완료됩니다. 지그재그로 다음과 같은 것을 의미한다고 가정합니다. (숫자는 렌더링 순서입니다)

..  ..  01  ..  ..
  ..  06  02  ..
..  11  07  03  ..
  16  12  08  04
21  17  13  09  05
  22  18  14  10
..  23  19  15  ..
  ..  24  20  ..
..  ..  25  ..  ..

그리고 다이아몬드는 다음을 의미합니다.

..  ..  ..  ..  ..
  01  02  03  04
..  05  06  07  ..
  08  09  10  11
..  12  13  14  ..
  15  16  17  18
..  19  20  21  ..
  22  23  24  25
..  ..  ..  ..  ..

첫 번째 방법은 전체 화면이 그려 지도록 더 많은 타일을 렌더링해야하지만 경계를 쉽게 확인하고 화면에서 완전히 벗어난 타일을 건너 뛸 수 있습니다. 두 방법 모두 타일 01의 위치를 ​​찾기 위해 약간의 크 런칭이 필요합니다. 결국, 두 방법 모두 특정 수준의 효율성에 필요한 수학 측면에서 거의 동일합니다.


14
나는 실제로 다른 길을 의미했습니다. 다이아몬드 모양 (지도의 가장자리를 매끄럽게 유지)과 지그재그 방식으로 가장자리를 뾰족하게 유지
Benny Hallett

1

다이아몬드의 범위를 초과하는 타일이있는 경우 깊이 순서로 그리는 것이 좋습니다.

...1...
..234..
.56789.
..abc..
...d...

1

Coobird의 답변은 정확하고 완전한 답변입니다. 그러나 그의 힌트를 다른 사이트의 힌트와 결합하여 내 앱 (iOS / Objective-C)에서 작동하는 코드를 만들었습니다.이 코드를 찾고있는 사람과 공유하고 싶었습니다. 이 답변이 마음에 들거나 투표권이있는 경우 원본도 동일하게하십시오. 내가 한 것은 "거인의 어깨에 서서"였다.

정렬 순서와 관련하여 내 기술은 수정 된 화가의 알고리즘입니다. 각 객체에는 (a) 기준 고도 ( "레벨"이라고 함) 및 (b) "기준"또는 "발"에 대한 X / Y가 있습니다. 이미지 (예 : 아바타의 기지는 발에 있고 나무의 기지는 뿌리에 있습니다. 비행기의 기지는 중앙 이미지 등입니다) 그런 다음 나는 가장 낮은 수준에서 가장 높은 수준으로 정렬 한 다음 가장 낮은 (화면에서 가장 높은) 화면에서 가장 높은 기본으로 정렬합니다. Y, 가장 낮은 (가장 왼쪽)에서 가장 높은 base-X. 그러면 타일이 예상대로 렌더링됩니다.

화면 (포인트)을 타일 (셀)로 변환하는 코드 :

typedef struct ASIntCell {  // like CGPoint, but with int-s vice float-s
    int x;
    int y;
} ASIntCell;

// Cell-math helper here:
//      http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
    const float halfHeight = rfcRowHeight / 2.;

    ASIntCell cell;
    cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
    cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));

    return cell;
}


// Cell-math helper here:
//      http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
    CGPoint result;

    result.x = (cell.x * rfcColWidth  / 2) + (cell.y * rfcColWidth  / 2);
    result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);

    return result;
}

1

시청자가 가장 높고 가장 가까운 지점에서 유클리드 거리를 사용할 수 있습니다. 구형 정렬 순서가 발생합니다. 더 멀리서 살펴보면 바로 잡을 수 있습니다. 더 멀리 곡률이 평평 해집니다. x, y 및 z 구성 요소에 1000이라고 말하면 x ', y'및 z '가됩니다. x '* x'+ y '* y'+ z '* z'에 대한 정렬입니다.


0

실제 문제는 두 개 이상의 다른 타일을 교차 / 확장하는 타일 / 스프라이트를 그릴 때입니다.

2 개월 동안 (개인적으로) 문제가 발생한 개인 분석은 마침내 새로운 cocos2d-js 게임을위한 "올바른 렌더 드로잉"을 찾아 구현했습니다. 해결책은 스프라이트가 "정면, 후면, 상단 및 후면"인 각 타일에 대한 매핑으로 구성됩니다. 그렇게하면 "재귀 논리"에 따라 그릴 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.