객체와 렌더링을 분리해야하는 이유는 무엇입니까?


11

Disclamer : 엔터티 시스템 패턴이 무엇인지 알고 있으며 사용 하지 않습니다 .

객체 분리와 렌더링에 대해 많이 읽었습니다. 게임 로직이 기본 렌더링 엔진과 독립적이어야한다는 사실에 대해. 그것은 모두 훌륭하고 멋지고 완벽하게 이해가되지만 다른 많은 고통을 유발합니다.

  • 논리 객체와 렌더링 객체 (애니메이션, 스프라이트 등의 상태를 유지하는 객체) 간의 동기화 필요
  • 렌더링 객체가 로직 객체의 실제 상태를 읽기 위해 로직 객체를 공개적으로 열어야 함

이것은 나에게 좋은 해결책처럼 들리지 않습니다. 반면에 객체를 3d (또는 2d) 표현으로 상상하는 것은 매우 직관적이며 유지 관리가 매우 쉽습니다 (또한 훨씬 더 캡슐화되어 있음).

그래픽 표시와 게임 로직을 함께 유지하면서 (동기화 문제 방지) 렌더링 엔진을 추상화하는 방법이 있습니까? 또는 위의 단점을 유발하지 않는 게임 로직과 렌더링을 분리하는 방법이 있습니까?

(예를 들어, 나는 추상적 대화를 잘 이해하지 못합니다)


1
엔터티 시스템 패턴을 사용하지 않는다고 말했을 때의 의미와 렌더링 문제를 엔터티 / 문제와 분리해야하는지 여부와 관련하여 어떻게 생각하는지에 대한 예를 제공하면 도움이됩니다. 게임 로직.
michael.bartnett

@ michael.bartnett, 나는 대부분의 패턴 구현 방식과 같이 시스템이 처리하는 재사용 가능한 작은 구성 요소로 객체를 분리하지 않습니다. 내 코드는 대신 MVC 패턴을 시도합니다. 그러나 질문이 어떤 코드 (언어조차도)에 의존하지 않기 때문에 실제로 중요하지 않습니다. 나는 일부 사람들이 ECS를 사용하도록 설득하려고 노력했을 것이라는 것을 알고 있었기 때문에 암을 치료했다. 보시다시피, 어쨌든 일어났습니다.
구두

답변:


13

월드 , 플레이어보스 로 구성된 장면 이 있다고 가정합니다 .아, 이것은 3 인칭 게임이므로 카메라 도 있습니다 .

장면은 다음과 같습니다.

class Scene {
    World* world
    Player* player
    Enemy* boss
    Camera* camera
}

(적어도 기본은 데이터 입니다. 데이터 를 포함하는 방법은 전적으로 사용자의 몫입니다.)

게임을 할 때, 일시 정지 할 때가 아니라 메인 메뉴에서만 장면을 업데이트하고 렌더링하려고합니다. 따라서 게임 상태에 연결합니다!

State* gameState = new State();
gameState->addScene(scene);

이제 게임 상태에 장면이 있습니다. 다음으로 장면에서 로직을 실행하고 장면을 렌더링하려고합니다. 논리의 경우 업데이트 기능을 실행하기 만하면됩니다.

State::update(double delta) {
    scene->update(delta);
}

그렇게하면 모든 게임 로직을 Scene 수업 . 그리고 참조를 위해 엔티티 구성 요소 시스템은 다음과 같이 대신 할 수 있습니다.

State::update(double delta) {
    physicsSystem->applyPhysics(scene);
}

어쨌든 이제는 장면을 업데이트했습니다. 이제 표시하고 싶습니다! 이를 위해 위와 비슷한 작업을 수행합니다.

State::render() {
    renderSystem->render(scene);
}

당신은 간다. renderSystem은 장면에서 정보를 읽고 적절한 이미지를 표시합니다. 단순화 된 장면 렌더링 방법은 다음과 같습니다.

RenderSystem::renderScene(Scene* scene) {
    Camera* camera = scene->camera;
    lookAt(camera); // Set up the appropriate viewing matrices based on 
                    // the camera location and direction

    renderHeightmap(scene->getWorld()->getHeightMap()); // Just as an example, you might
                                                        // use a height map as your world
                                                        // representation.
    renderModel(scene->getPlayer()->getType()); // getType() will return, for example "orc"
                                                // or "human"

    renderModel(scene->getBoss()->getType());
}

예를 들어, 플레이어가있는 위치와보고있는 위치에 따라 회전 및 평행 이동을 적용해야합니다. (제 예는 3D 게임입니다. 2D로 가면 공원에서 산책 할 것입니다).

이것이 당신이 찾고있는 것이기를 바랍니다. 위의 내용에서 바라본대로 렌더링 시스템 은 게임의 논리에 신경 쓰지 않습니다 . 장면의 현재 상태 만 사용하여 렌더링합니다. 즉 렌더링하기 위해 필요한 정보를 가져옵니다. 그리고 게임 로직? 렌더러의 기능은 중요하지 않습니다. 도대체 전혀 표시되지 않습니다!

렌더링 정보를 장면에 첨부 할 필요도 없습니다. 렌더러가 오크를 렌더링해야한다는 것을 알고 있으면 충분합니다. 렌더러가 표시 할 것으로 알고있는 오크 모델을 이미로드했습니다.

요구 사항을 충족해야합니다. 그래픽 표현과 논리 모두 동일한 데이터를 사용하기 때문에 결합 됩니다. 그러나 그들은 별개입니다 둘 다 서로 의존하지 않기 때문에 !

편집 : 그리고 왜 이런 식 으로 대답 할까요? 가장 쉬운 이유는 가장 쉬운 이유입니다. "그런데 그런 일이 생겼으니 이제 그래픽을 업데이트해야합니다." 대신, 당신은 일을하고, 게임의 각 프레임은 현재 일어나고있는 것을보고 어떤 방식으로 해석하여 화면상의 결과를 제공합니다.


7

제목이 본문 내용과 다른 질문을합니다. 제목에서 논리와 렌더링을 분리해야하는 이유를 묻지 만 본문에서는 논리 / 그래픽 / 렌더링 시스템의 구현을 요청합니다.

두 번째 질문은 이전에 해결 되었으므로 첫 번째 질문에 중점을 둘 것입니다.

논리와 렌더링을 분리하는 이유 :

  1. 물체가 한 가지 일을해야한다는 널리 알려진 개념
  2. 2D에서 3D로 가고 싶다면 어떻게해야합니까? 프로젝트 중간에 한 렌더링 시스템에서 다른 렌더링 시스템으로 변경하기로 결정하면 어떻게됩니까? 모든 코드를 크롤링하고 게임 로직 중간에 큰 변화를주고 싶지는 않습니다.
  3. 일반적으로 나쁜 생각으로 간주 되는 코드 섹션을 반복해야 할 이유가있을 것입니다.
  4. 작은 조각과 개별적으로 통신하지 않고도 잠재적으로 방대한 양의 렌더링 또는 논리를 제어하는 ​​시스템을 구축 할 수 있습니다.
  5. 보석을 플레이어에게 할당하고 싶지만 보석에 몇 개의 패싯이있어 시스템 속도가 느려지면 어떻게합니까? 렌더링 시스템을 충분히 추상화했다면 값 비싼 렌더링 작업을 설명하기 위해 다른 속도로 업데이트 할 수 있습니다.
  6. 그것은 당신이하고있는 일에 정말로 중요한 것들에 대해 생각할 수있게합니다. 매트릭스 변환 및 스프라이트 오프셋 및 화면 좌표 주위에 두뇌를 감아 왜 이중 점프 방식을 구현하거나 카드를 그리거나 검을 장착하는 것입니까? 장착 된 검 렌더링을 나타내는 스프라이트를 오른쪽에서 왼쪽으로 옮기고 싶기 때문에 밝은 분홍색으로 표시하고 싶지 않습니다.

OOP 설정에서 새 객체를 인스턴스화하는 데는 비용이 들지만 경험상 시스템 리소스에 대한 비용은 내가 수행해야 할 특정 사항을 생각하고 구현할 수있는 비용을 지불하는 작은 가격입니다.


6

이 답변은 실용적인 예제를 직접 제안하기보다는 렌더링과 논리를 분리하는 것이 중요한 이유에 대한 직관을 구축하기위한 것입니다.

우리가 큰 코끼리를 가지고 있다고 가정 해 봅시다. . 방에 아무도 코끼리 전체를 볼 수 없습니다. 어쩌면 모든 사람들이 실제로 그것이 무엇인지에 동의하지 않을 수도 있습니다. 모두가 코끼리의 다른 부분을보고 그 부분 만 다룰 수 있기 때문입니다. 그러나 결국 이것이 큰 코끼리라는 사실을 바꾸지는 않습니다.

코끼리는 모든 세부 사항이있는 게임 개체를 나타냅니다. 그러나 실제로 그 기능을 수행하기 위해 코끼리 (게임 오브젝트)에 관한 모든 것을 알 필요는 없습니다.

게임 로직과 렌더링을 결합하는 것은 실제로 모든 사람이 코끼리 전체를 보게하는 것과 같습니다. 무언가가 바뀌면 모든 사람이 그것에 대해 알아야합니다. 대부분의 경우, 그들은 단지 관심이있는 부분 만 볼 필요가 있습니다. 그것에 대해 알고있는 사람이 무언가를 바꾸었을 때, 그 변화의 결과에 대해 다른 사람에게만 알려 주면됩니다. (메시지 또는 인터페이스를 통한 통신으로 생각하십시오).

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

언급 한 포인트는 단점이 아니며 엔진에 있어야하는 것보다 더 많은 종속성이있는 경우에만 단점입니다. 즉, 시스템은 코끼리의 일부를 필요한 것보다 더 많이 봅니다. 그리고 이것은 엔진이 "정확하게"설계되지 않았 음을 의미합니다.

로직과 렌더링을 두 개의 다른 스레드에 넣는 멀티 스레드 엔진을 사용하고 시스템간에 많은 동기화가 필요한 엔진조차 특별히 설계되지 않은 경우에만 공식 정의와 동기화하면됩니다.

그렇지 않으면 그러한 경우를 처리하는 자연스러운 방법은 시스템을 입력 / 출력으로 설계하는 것입니다. 업데이트는 논리를 수행하고 결과를 출력합니다. 렌더링은 업데이트 결과로만 제공됩니다. 실제로 모든 것을 노출시킬 필요는 없습니다. 두 단계 사이에서 통신하는 인터페이스 만 노출합니다. 엔진의 다른 부분들 간의 통신은 추상화 (인터페이스) 및 / 또는 메시지를 통해 이루어져야합니다. 내부 논리 나 상태가 노출되어서는 안됩니다.

아이디어를 설명하기 위해 간단한 장면 그래프 예제를 보자.

업데이트는 일반적으로 게임 루프 (또는 개별 스레드에서 각각 실행되는 여러 게임 루프)라는 단일 루프를 통해 수행됩니다. 루프가 게임 오브젝트를 업데이트하면 메시징 또는 인터페이스를 통해 객체 1 및 2가 업데이트 된 위치를 최종 변환으로 전달하면됩니다.

렌더링 시스템 은 최종 변환 수행하며 객체에 대해 실제로 변경된 내용 (예 : 특정 충돌이 발생하는 등)을 모릅니다. 이제 해당 객체를 렌더링하려면 해당 객체의 ID와 최종 변환 만 필요합니다. 그 후 렌더러는 다른 것을 알지 않고도 렌더링 API에 메시와 최종 변환을 제공합니다.

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