답변:
업데이트 : 맵 렌더링 알고리즘 수정, 더 많은 일러스트레이션 추가, 형식 변경
타일을 화면에 매핑하는 "지그재그"기술의 이점은 타일 x
과 y
좌표가 세로 및 가로 축에 있다고 할 수 있습니다 .
"다이아몬드로 그리기"접근 방식 :
"다이아몬드로 그리기"를 사용하여 등각지도를 그리면 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
모든 타일에서 일관되게 작동하는 상당히 직진 논리를 가진 단순한 중첩 루프 라는 것입니다 .
불리:
그 방법에 대한 한 단점은이다 x
및 y
지도 타일의 좌표가 더 어려워 시각적 배열로서 표시되는지도 화면의 위치를 매핑 할 수있는 대각선으로 증가한다 :
그러나 위의 예제 코드를 구현하는 데는 함정이있을 것입니다. 렌더링 순서는 특정 타일 뒤에있는 타일이 타일의 맨 앞에 그려지게합니다.
이 문제를 수정하려면, 내부 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_width
와 tile_height
상기 코드 예에서 사용 된 타일을 나타내는 이미지의 바닥 타일의 폭과 높이를 참조 :
이미지 크기와 타일 크기가 일치하는 한 이미지의 크기를 사용하면 효과가 있습니다. 그렇지 않으면, 타일 맵은 타일 사이에 간격을두고 렌더링 될 수 있습니다.
j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;
.
어느 쪽이든 작업이 완료됩니다. 지그재그로 다음과 같은 것을 의미한다고 가정합니다. (숫자는 렌더링 순서입니다)
.. .. 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의 위치를 찾기 위해 약간의 크 런칭이 필요합니다. 결국, 두 방법 모두 특정 수준의 효율성에 필요한 수학 측면에서 거의 동일합니다.
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;
}