게임 로직과 애니메이션 및 드로우 루프를 분리하는 몇 가지 방법은 무엇입니까?


9

MovieClips를 사용하여 이전에 플래시 게임 만 만들었고 내 게임 로직에서 애니메이션을 분리했습니다. 이제 Android 용 게임을 만들려고 노력하고 있지만 이러한 것들을 분리하는 것에 대한 게임 프로그래밍 이론은 여전히 ​​혼란 스럽습니다. 게임 웹이 아닌 응용 프로그램을 개발 한 배경에서 왔기 때문에 더 MVC와 같은 패턴에 정통하며 게임 프로그래밍에 접근 할 때 그 사고 방식에 갇혀 있습니다.

예를 들어 각각 속성이 포함 된 타일 클래스의 인스턴스가있는 타일 그리드에 대한 데이터를 포함하는 게임 보드 클래스를 사용하여 게임 추상화와 같은 작업을 수행하려고합니다. 내 드로우 루프에 이것에 대한 액세스 권한을 부여하고 게임 보드의 각 타일 속성에 따라 게임 보드를 그리도록 할 수 있지만 애니메이션이 정확히 어디로 가야하는지 이해할 수 없습니다. 내가 알 수있는 한, 애니메이션 종류는 추상화 된 게임 로직 (모델)과 드로우 루프 (보기) 사이에 있습니다. 내 MVC 사고 방식을 사용하면 애니메이션이 실제로 어디로 가야할지 결정하는 것이 실망 스럽습니다. 모델과 관련하여 상당히 많은 데이터가 있지만 프레임 독립 애니메이션과 같은 것을 가지려면 드로우 루프와 매우 밀접하게 연결되어야합니다.

이 사고 방식에서 벗어나 게임에 더 적합한 패턴에 대해 어떻게 생각할 수 있습니까?

답변:


6

애니메이션은 여전히 ​​논리와 렌더링간에 완벽하게 분리 될 수 있습니다. 애니메이션의 추상 데이터 상태는 그래픽 API가 애니메이션을 렌더링하는 데 필요한 정보입니다.

예를 들어, 2D 게임에서, 그려야 할 스프라이트 시트의 현재 부분을 표시하는 영역을 표시하는 사각형 영역 일 수 있습니다 (시트로 구성된 시트가있는 경우 캐릭터의 다양한 단계를 포함하는 80x80 30 개의 그림을 말할 수 있습니다) 점프, 앉기, 이동 등). 또한 렌더링에 필요하지 않은 모든 종류의 데이터 일 수 있지만 현재 애니메이션 단계가 만료 될 때까지 남은 시간 또는 애니메이션 이름 ( "walking", "standing")과 같은 애니메이션 상태 자체를 관리하기위한 것일 수도 있습니다. 등) 모든 것을 원하는 방식으로 표현할 수 있습니다. 그것이 논리 부분입니다.

렌더링 부분에서는 평소와 같이 모델에서 사각형을 가져오고 렌더러를 사용하여 실제로 그래픽 API를 호출합니다.

코드에서 (여기에서 C ++ 구문 사용) :

class Sprite //Model
{
    private:
       Rectangle subrect;
       Vector2f position;
       //etc.

    public:
       Rectangle GetSubrect() 
       {
           return subrect;
       }
       //etc.
};

class AnimatedSprite : public Sprite, public Updatable //arbitrary interface for classes that need to change their state on a regular basis
{
    AnimationController animation_controller;
    //etc.
    public:
        void Update()
        {
            animation_controller.Update(); //Good OOP design ;) It will take control of changing animations in time etc. for you
            this.SetSubrect(animation_controller.GetCurrentAnimation().GetRect());
        }
        //etc.
};

그것이 데이터입니다. 렌더러가 해당 데이터를 가져 와서 그립니다. 일반 스프라이트와 애니메이션 된 스프라이트 모두 같은 방식으로 그려 지므로 여기에서 다형성을 사용할 수 있습니다!

class Renderer
{
    //etc.
    public:
       void Draw(const Sprite &spr)
       {
           graphics_api_pointer->Draw(spr.GetAllTheDataThatINeed());
       }
};

TMV :

다른 예를 생각해 냈습니다. RPG가 있다고 가정하십시오. 예를 들어, 세계지도를 나타내는 모델은 세계에서 캐릭터의 위치를지도의 타일 좌표로 저장해야 할 수 있습니다. 그러나 캐릭터를 움직이면 한 번에 몇 픽셀 씩 다음 정사각형으로 이동합니다. 이 "타일 사이"위치를 애니메이션 오브젝트에 저장합니까? 캐릭터가 맵의 다음 타일 좌표에서 "도착"했을 때 모델을 어떻게 업데이트합니까?

월드 맵은 플레이어 위치를 직접 알지 못합니다 (Vector2f 또는 플레이어 위치를 직접 저장하는 것과 같은 것은 없지만 대신 플레이어 객체 자체에 대한 직접 참조가 있으며, 이는 AnimatedSprite에서 파생됩니다) 렌더러에 쉽게 전달하고 필요한 모든 데이터를 가져올 수 있습니다.

일반적으로 타일 맵이 모든 것을 할 수는 없습니다. 모든 타일 관리를 담당하는 "TileMap"클래스가 있으며, 아마도 내가 넘겨 준 객체 사이의 충돌 감지를 수행 할 수도 있습니다. 지도의 타일 그런 다음 다른 "RPGMap"클래스를 사용하거나 타일 맵과 플레이어에 대한 참조가 있고 플레이어와 플레이어에 대한 실제 Update () 호출을 모두 호출하고 싶습니다. 타일 ​​맵.

플레이어가 움직일 때 모델을 업데이트하는 방법은 수행하려는 작업에 따라 다릅니다.

플레이어가 타일 사이를 독립적으로 이동할 수 있습니까 (젤다 스타일)? 입력을 처리하고 모든 프레임에 따라 플레이어를 이동하십시오. 아니면 플레이어가 "오른쪽"을 누르기를 원하고 캐릭터가 자동으로 타일 하나를 오른쪽으로 이동 하시겠습니까? RPGMap 클래스가 플레이어가 목적지에 도착할 때까지 플레이어 위치를 보간하고 그 동안 모든 이동 키 입력 처리를 잠그십시오.

어느 쪽이든, 더 쉽게 만들고 싶다면 변수의 값을 변경하는 대신 실제로 자신을 업데이트하는 논리가 필요한 경우 모든 모델에 Update () 메서드가 있습니다-컨트롤러를 포기하지 않습니다 그런 식으로 MVC 패턴에서 코드를 "한 단계 위"(컨트롤러)에서 모델로 이동하면 컨트롤러는 모델의 Update () 메소드를 호출합니다 (이 경우 컨트롤러는 RPGMap). 로직 코드를 쉽게 바꿀 수 있습니다. 클래스의 코드를 직접 변경하거나 완전히 다른 동작이 필요한 경우 모델 클래스에서 파생하여 Update () 메서드 만 재정의 할 수 있습니다.

이 접근법은 메소드 호출과 그와 같은 것들을 줄입니다-순수한 MVC 패턴의 주요 단점 중 하나였습니다 (GetThis () GetThat ()을 매우 자주 호출하게 됨)-코드를 더 길고 읽기가 어렵고 속도가 느려집니다. 컴파일러가 그런 것들을 많이 최적화하면 처리 할 수 ​​있습니다.


게임 로직을 포함하는 클래스, 게임 루프를 포함하는 클래스 또는 둘 다와 별도로 애니메이션 데이터를 유지 하시겠습니까? 또한 애니메이션 데이터를 실제로 화면을 그리는 것으로 변환하는 방법을 이해하는 것은 전적으로 루프 또는 루프를 포함하는 클래스에 달려 있습니다. 스프라이트 시트의 섹션을 나타내는 rect를 가져 와서 스프라이트 시트에서 비트 맵 그리기를 클립하는 데 사용하는 것만 큼 간단하지 않은 경우가 많습니다.
TMV

다른 예를 생각해 냈습니다. RPG가 있다고 가정하십시오. 예를 들어, 세계지도를 나타내는 모델은 세계에서 캐릭터의 위치를지도의 타일 좌표로 저장해야 할 수 있습니다. 그러나 캐릭터를 움직이면 한 번에 몇 픽셀 씩 다음 정사각형으로 이동합니다. 이 "타일 사이"위치를 애니메이션 오브젝트에 저장합니까? 캐릭터가 맵의 다음 타일 좌표에서 "도착"했을 때 모델을 어떻게 업데이트합니까?
TMV

의견에 충분한 문자가 허용되지 않으므로 귀하의 질문에 대한 답변을 편집했습니다.
TravisG

내가 모든 것을 올바르게 이해한다면 :
TMV

View 내에 "Animator"클래스의 인스턴스가있을 수 있으며 뷰에 의해 모든 프레임이 호출되는 공용 "update"메서드가 있습니다. update 메소드는 그 안에 다양한 종류의 개별 애니메이션 객체 인스턴스의 "update"메소드를 호출합니다. 애니메이터와 그 안의 애니메이션은 모델에 대한 참조 (생성자를 통해 전달됨)를 가지므로 애니메이션이 모델 데이터를 변경하는 경우 모델 데이터를 업데이트 할 수 있습니다. 그런 다음 그리기 루프에서 뷰에서 이해하고 그릴 수있는 방식으로 애니메이터 내부 애니메이션에서 데이터를 가져옵니다.
TMV

2

원하는 경우이 문제를 해결할 수 있지만 루프에서 그리라는 중앙 렌더러가 있습니다. 오히려

handle input

for every entity:
    update entity

for every entity:
    draw entity

나는 더 같은 시스템을 가지고

handle input (well, update the state. Mine is event driven so this is null)

for every entity:
    update entity //still got game logic here

renderer.draw();

렌더러 클래스는 객체의 드로어 블 구성 요소에 대한 참조 목록을 보유합니다. 이들은 단순화를 위해 생성자에 할당됩니다.

예를 들어, 타일 수가 많은 GameBoard 클래스가 있습니다. 각 타일은 분명히 위치를 알고 있으며 일종의 애니메이션을 가정합니다. 타일이 소유 한 일종의 애니메이션 클래스에이를 반영하고 자체 참조를 렌더러 클래스에 전달하도록합니다. 거기서 모두 분리되었습니다. Tile을 업데이트하면 애니메이션에서 Update를 호출하거나 자체적으로 업데이트합니다. Renderer.Draw()호출 되면 애니메이션을 그립니다.

프레임 독립형 애니메이션은 드로우 루프와 관련이 없을 것입니다.


0

나는 최근에 패러다임을 스스로 배우고 있었기 때문에이 답변이 불완전하다면 누군가가 그것을 추가 할 것이라고 확신합니다.

게임 디자인에 가장 적합한 방법은 화면 출력에서 ​​로직을 분리하는 것입니다.

대부분의 경우 다중 스레드 접근 방식을 사용하고 싶을 것입니다. 해당 주제에 익숙하지 않은 경우에는 모두 고유 한 문제입니다 ( 여기의 위키 입문서) . 기본적으로 게임 로직을 하나의 스레드에서 실행 하여 데이터 무결성을 보장하기 위해 액세스해야하는 변수를 잠그기 를 원합니다 . 논리 루프가 엄청나게 빠르면 (슈퍼 메가 애니메이션 3d 퐁?) 작은 시간 동안 스레드를 잠자 게하여 루프가 실행하는 주파수를 고정 할 수 있습니다 (이 포럼에서 게임 물리 루프에 대해 120hz가 제안되었습니다). 동시에 다른 스레드는 업데이트 된 변수를 사용하여 화면을 다시 그리기 (60 hz가 다른 주제에서 제 안됨)하고 변수에 액세스하기 전에 변수에 대한 잠금을 다시 요청합니다.

이 경우 애니메이션 또는 전환 등이 드로잉 스레드로 이동하지만 게임 로직 스레드가 아무것도하지 않아야한다는 (또는 다른 작업을 수행해야한다는 일종의 전역 상태 변수) 플래그를 통해 신호를 보내야합니다. 아마 새로운지도 매개 변수).

동시성에 대해 머리를 숙이면 나머지는 상당히 이해할 수 있습니다. 동시성에 대한 경험이 없으면 흐름이 어떻게 발생하는지 이해할 수 있도록 몇 가지 간단한 테스트 프로그램을 작성하는 것이 좋습니다.

도움이 되었기를 바랍니다 :)

[편집] 멀티 스레딩을 지원하지 않는 시스템에서 애니메이션은 여전히 ​​드로우 루프로 들어갈 수 있지만 다른 무언가가 발생하고 있고 처리하지 않는 로직에 신호를 보내도록 상태를 설정하려고합니다. 현재 레벨 /지도 / 등


1
나는 여기에 동의하지 않습니다. 대부분의 경우, 특히 작은 게임 인 경우 멀티 스레딩을 원하지 않습니다.
공산주의 오리

@TheCommunistDuck 멀티 스레딩의 오버 헤드와 복잡성은 충분히 과잉이 될 수 있으며, 게임 크기가 작 으면 빠르게 업데이트 할 수 있어야합니다.
Stephen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.