아이소 메트릭 스프라이트를 올바른 순서로 어떻게 정렬합니까?


19

일반적인 하향식 2D 게임에서는 화면 y 축을 사용하여 이미지를 정렬 할 수 있습니다. 이 예에서 나무는 올바르게 정렬되었지만 등각 벽은 그렇지 않습니다.

이미지 예 : 화면 y별로 정렬

벽 2는 벽 1 아래의 한 픽셀이므로 벽 1 다음에 그려지고 상단으로 끝납니다.

아이소 메트릭 y 축으로 정렬하면 벽이 올바른 순서로 나타나지만 나무는 그렇지 않습니다.

이미지 예 : 아이소 메트릭 y로 정렬

이 작업을 올바르게 수행하려면 어떻게합니까?


3
Painter 's Algorithm에서 일반적인 문제에 부딪힌 것처럼 들립니다. "일반적인"솔루션은 z- 버퍼 =)를 사용하는 것입니다. 일부 해결 방법에는 객체 센터를 코너 대신 정렬 키로 사용하는 것이 포함됩니다.
Jari Komppa

답변:


12

아이소 메트릭 게임은 기능적으로 3D이므로 내부적으로 게임의 각 엔터티에 대한 3D 좌표를 저장해야합니다. 선택하는 실제 좌표는 임의적이지만 X와 Y는 두 개의 지상 축이며 Z는 지상에서 공중으로 올라갑니다.

그런 다음 렌더러는 화면에 물건을 그리려면 2D로 투영해야합니다. "아이소 메트릭"은 그러한 투영법 중 하나입니다. 3D에서 2D로 등각 투영하는 것은 매우 간단합니다. X 축이 두 개의 수평 픽셀 각각에 대해 왼쪽 상단에서 오른쪽 하단으로, 한 픽셀 아래로 이동한다고 가정 해 봅시다. 마찬가지로 Y 축은 오른쪽 상단에서 왼쪽 하단으로 이동합니다. Z 축이 똑바로 올라갑니다. 3D에서 2D로 변환하는 방법은 다음과 같습니다.

function projectIso(x, y, z) {
    return {
        x: x - y,
        y: (x / 2) + (y / 2) - z
    };
}

이제 원래 질문으로 정렬하십시오. 이제 객체를 3D로 직접 작업하므로 정렬이 훨씬 간단 해집니다. 여기서 좌표 공간에서 가장 먼 스프라이트는 x, y 및 z 좌표가 가장 낮습니다 (즉, 세 축 모두 화면에서 가리킴). 그래서 당신은 그것들의 합계로 정렬합니다 :

function nearness(obj) {
    return obj.x + obj.y + obj.z;
}

function closer(a, b) {
    if (nearness(a) > nearness(b)) {
        return "a";
    } else {
        return "b";
    }
}

프레임마다 엔티티를 다시 정렬하지 않으려면 여기 에 자세히 설명 된 비둘기 구멍 정렬을 사용 하십시오 .


1
나는 이것이 오래되고 좋은 시작이라는 것을 알고 있지만 단순히 번역뿐만 아니라 객체의 3D 경계를 통합하는 방법을 알고 있습니다. 겹치는 경우 심도 정렬이 더 복잡해집니다.
Rob

4

스프라이트가 직사각형 타일 세트를 차지한다고 가정하면 (임의의 경우 임의의 세트를 차지하는 경우 일반적으로 올바르게 그릴 수 없습니다), 문제는 요소 사이에 총 순서 관계가 없으므로 정렬 할 수 없다는 것입니다 O (nlogn) 비교를 수행하는 정렬을 사용합니다.

두 객체 A와 B의 경우 A는 B보다 먼저 그려야하고 (A <-B), B는 A보다 먼저 그려야하고 (B <-A) 순서에 관계없이 그릴 수 있습니다. 그것들은 부분적인 순서를 형성합니다. 3 개의 겹치는 객체로 몇 가지 예를 그리면 첫 번째와 세 번째 객체가 겹치지 않아도 직접적인 종속성이없는 경우에도 그리기 순서는 방법에 따라 두 번째 객체에 따라 다릅니다. 당신이 그것을 배치하면 다른 도면 주문을 얻을 수 있습니다. 결론-전통적인 종류는 여기서 작동하지 않습니다.

한 가지 해결책은 Dani가 언급 한 비교를 사용하고 각 개체를 서로 다른 개체와 비교하여 종속성을 결정하고 종속성 그래프 (DAG가 됨)를 형성하는 것입니다. 그런 다음 그래프에서 위상 정렬을 수행하여 도면 순서를 결정하십시오. 객체가 너무 많지 않으면 충분히 빠를 수 있습니다 ( O(n^2)).

또 다른 해결책은 (밸런싱- 의사 용 ) 쿼드 트리를 사용하여 모든 객체의 사각형을 저장하는 것입니다.

그런 다음 모든 객체 X를 반복하고 쿼드 트리를 사용하여 객체 X 위의 스트라이프에 객체 Y가 있는지 확인합니다.이 객체는 가장 왼쪽부터 시작하여 객체 X의 가장 오른쪽 모서리로 끝나는 Y, Y < -X. 이와 같이 여전히 그래프를 작성하고 위상 적으로 정렬해야합니다.

그러나 당신은 그것을 피할 수 있습니다. 객체 목록 Q와 객체 테이블 T를 사용합니다. 모든 가시적 슬롯을 x 축 (1 행)에서 작은 값에서 큰 값으로 반복하여 y 축에서 한 행씩 반복합니다. 해당 슬롯에 객체의 하단 모서리가있는 경우 위 절차를 수행하여 종속성을 확인하십시오. 객체 X가 그 위에있는 다른 객체 Y에 의존하고 (Y <-X), 그러한 모든 Y가 이미 Q에 있으면 X를 Q에 추가합니다. Q에없는 Y가 있으면 X를 추가합니다 T는 Y <-X입니다. Q에 오브젝트를 추가 할 때마다 T에 보류중인 오브젝트의 종속성을 제거합니다. 모든 종속성이 제거되면 T의 오브젝트가 Q로 이동됩니다.

오브젝트 스프라이트가 아래쪽, 왼쪽 또는 오른쪽 (그림의 나무처럼 위쪽에만 있음)의 슬롯에서 벗어나지 않는다고 가정합니다. 이는 많은 객체의 성능을 향상시킵니다. 이 접근 방식은 다시 O(n^2)되지만 이상한 크기의 객체 및 / 또는 이상한 객체 구성을 포함하는 최악의 경우에만 해당됩니다. 대부분의 경우 O(n * logn * sqrt(n))입니다. 스프라이트의 높이를 알면 sqrt(n)위의 전체 스트라이프를 확인할 필요가 없으므로를 제거 할 수 있습니다 . 화면의 객체 수에 따라 쿼드 트리를 어떤 슬롯이 사용되는지 나타내는 배열로 교체 할 수 있습니다 (객체가 많은 경우 의미가 있습니다).

마지막으로이 소스 코드를 확인하여 아이디어를 얻으십시오. https://github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala


2

나는 수학적인 해결책이 없다고 생각합니다. 아이템이 살고있는 2D 세계에 데이터가 충분하지 않을 수 있습니다. 벽이 X에 미러링 된 경우 "정확한"순서입니다. 그런 다음 다시 이미지의 경계 상자로 겹침 테스트를 수행 할 수 있지만 이것은 익숙하지 않은 영역입니다.

타일 ​​당 화면 Y별로 정렬하고 더 복잡한 것은 "디자인 문제"라고 말해야합니다. 예를 들어 콘텐츠를 제작하는 경우 디자이너에게 정렬 알고리즘을 알려주고 문제를 해결하기 위해 wall2 2 픽셀을 밀어 넣으십시오. 이것이 제가 작업 한 아이소 메트릭 게임에서 수정해야하는 방식입니다. 여기에는 "긴"항목을 가져 와서 타일 크기의 덩어리로 나누는 것이 포함될 수 있습니다.

사용자가 컨텐츠를 편집 할 수있게하려면, 타일을 기반으로하고 최대 하나의 타일을 크게 만드는 것이 안전합니다. 그렇게하면 문제를 피할 수 있습니다. 타일보다 큰 것을 만들면 가능하지만 정사각형 일 경우에만 가능합니다. 나는 그걸로 놀지 않았다.


2

완벽한 아이소 메트릭 정렬은 어렵습니다. 그러나 첫 번째 방법으로 항목을 올바르게 정렬하려면 겹치는 각 객체에 대해보다 복잡한 비교 기능을 사용해야합니다. 이 함수는이 조건을 확인해야합니다. 다음과 같은 경우 겹치는 객체 "a"가 "b"뒤에 있습니다.

(a.posX + a.sizeX <= b.posX) 또는 (a.posY + a.sizeY <= b.posY) 또는 (a.posZ + a.sizeZ <= b.posZ)

물론 이것은 순진한 아이소 메트릭 구현을위한 첫 번째 아이디어입니다. 보다 복잡한 시나리오 (보기 회전, z 축의 픽셀 별 위치 등)를 원하면 더 많은 조건을 확인해야합니다.


+1, 너비 / 높이가 다른 타일이있는 경우이 답변의 비교 기능을보십시오.
mucaho

2

OpenGL의 작은 아이소 메트릭 엔진과 잘 작동하는 매우 간단한 공식을 사용합니다.

각각의 개체 (나무, 바닥 타일, 문자 등)는 화면에서 X 및 Y 위치를 갖습니다. DEPTH TESTING을 활성화하고 각 값에 대해 좋은 Z 값을 찾아야합니다. 간단하게 다음을 수행 할 수 있습니다.

z = x + y + objectIndex

나는 바닥과 바닥 위에있을 물건에 대해 다른 색인을 사용합니다 (바닥에는 0, 높이가있는 모든 물건에는 1). 이것은 잘 작동합니다.



1

동일한 x 위치에서 y 값을 비교하면 매번 작동합니다. 따라서 중심과 중심을 비교하지 마십시오. 대신 한 스프라이트의 중심을 다른 스프라이트의 동일한 x 위치와 비교하십시오.

여기에 이미지 설명을 입력하십시오

그러나 각 스프라이트에 대한 지오메트리 데이터가 필요합니다. 스프라이트의 하위 경계를 설명하는 왼쪽에서 오른쪽으로 두 점만큼 쉬울 수 있습니다. 또는 스프라이트 이미지 데이터를 분석하고 투명하지 않은 첫 번째 픽셀을 찾을 수 있습니다.

더 쉬운 방법은 모든 스프라이트를 x 축을 따라 대각선, y 축을 따라 대각선 및 평면의 세 그룹으로 나누는 것입니다. 두 객체가 모두 같은 축을 따라 대각선이면 다른 축을 기준으로 정렬합니다.


0

동일한 유형의 모든 객체에 고유 한 ID를 할당해야합니다. 그런 다음 모든 객체를 위치별로 정렬하고 ID 순서대로 객체 그룹을 그립니다. 따라서 그룹 A의 객체 1은 그룹 A의 객체 2를 절대로 덮어 쓰지 않습니다.


0

이것은 실제로 답변이 아니라, 단지 논평과 공감입니다. 나는이 axel22의 답변을 여기에주고 싶습니다 /gamedev//a/8181/112940

나는 다른 답변을 찬성 투표하거나 언급 할 정도로 충분한 평판을 얻지 못했지만 그의 대답의 두 번째 단락은 아마도 "현대적인"3D에 의존하지 않고 아이소 메트릭 게임에서 개체를 정렬하려고 할 때 알아야 할 가장 중요한 것입니다. Z- 버퍼와 같은 기술.

내 엔진에서 나는 "오래된 학교", 순수한 2D를하고 싶었습니다. 그리고 나는 "정렬"(내 경우에는 c ++ std :: sort)에 대한 호출이 특정 맵 구성에서 올바르게 작동하지 않는 이유를 파악하려고 많은 시간을 보냈다.

이것이 "부분 순서"상황이라는 것을 깨달았을 때만 나는 그것을 해결할 수 있었다.

지금까지 웹에서 찾은 모든 작업 솔루션은 문제를 올바르게 처리하기 위해 일종의 토폴로지 정렬을 사용했습니다. 토폴로지 정렬은 갈 길입니다.

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