posted by REDFORCE 2017. 6. 12. 14:14

이전 글 내용에 이어서 적도록 하겠습니다.


이전 글에서 Terrain에 자유문맥에 해당하는 내용만 담음으로 우리는 지형을 각 종류별로 1번씩만 만들고 Terrain 객체의 포인터를 받아오는 것으로 World를 만들 수 있게됐습니다.



이렇게 지형 종류가 같은 타일들은 모두 같은 Terrain 인스턴스 포인터를 받아오기만 하면 됩니다. 그림으로 표현하면 다음과 같습니다.


3-3.png

(Terrain 객체를 재사용하는 타일들)


Terrain 인스턴스가 여러 곳에서 사용되다보니 동적으로 할당하면 생명주기를 관리하기가 좀 더 어렵습니다. 따라서 World 클래스에 저장합니다.



위와 같이 World를 만듬으로써 이제 다음과 같이 땅을 채울수 있게 됩니다.



이제 지형 속성 값을 World 의 메서드 대신 Terrain 객체에서 바로 얻을 수 있습니다.




posted by REDFORCE 2017. 6. 12. 13:22

이번에는 경량 패턴에 대해서 두 번째 내용을 보도록 하겠습니다.


경량패턴은 앞선 글에서 봤듯이 어떤 객체의 개수가 너무 많아서 좀 더 가볍게 만들고 싶을때 사용합니다.


인스턴스 렌더링에서는 메모리 크기보다는 렌더링할 나무 데이터를 하나씩 GPU버스로 보내는 데 걸리는 시간이 중요하지만, 기본 개념은 경량패턴과 같습니다.


이런 문제를 해결하기 위해 경량 패턴은 객체 데이터를 두 종류로 나눈다는 것이 특징입니다.


먼저 모든객체의 데이터 값이 갚아서 공유할 수 있는 데이터를 모읍니다. 이런 데이터를 GoF에서는 고유상태(intrinsic state) 라고 부르고, 필자가 참고하고 있는 game programming pattern 교재에서는 자유문맥(context-free) 상태 라고 부릅니다. 이전 글에서 다루었던 나무 형태가(geometry)나 텍스쳐가 이에 해당합니다.


그리고 나머지 데이터는 인스턴스별로 값이 다른 외부 상태(extrinsic state)에 해당합니다.

예제로 살펴봤던 Tree Class에서 나무의 위치, 크기, 색 등이 이에 해당합니다. 

이전 글의 예제코드에서 확인한 것과 같이. 경량 패턴은 한 개의 고유 상태를 다른 객체에서 공유하게 만들어 메모리 사용량을 줄 이는 방식입니다.


여기까지만 확인한 걸로는 기초적인 자원 공유 기법이지 패턴이라 부를만한 내용은 충족하지 않는 것 처럼 보입니다. TreeModel 클래스로 깔끔하게 분리가 될 수 있었기에 그렇게 보이는 측면도 있지만, 실제로 공유 객체가 명확하지 않은 경우 경량 패턴은 잘 드러나 보이지 않습니다.


해서 이번엔 위 예제에 이어서 지형 정보(타일같은...)를 이용해서 경량 패턴을 쓰임을 확인해보도록 하겠습니다.


나무를 심을 땅도 게임에서 표현을 해야하는데, 보통 이런 땅들은 흙, 언덕, 호수, 강 같은 다양한 지형을 이어 붙여서 만들게 됩니다. 여기에서 이 땅들을 보통 타일 기반으로 만들게 될텐데요.

이 타일들은 게임플레이에 영향을 주는 여러 속성들이 들어있을 수 있습니다.

  • 플레이어가 얼마나 빠르게 이동할 수 있는지를 결정하는 이동 비용 값
  • 강이나 바다처럼 보트로 건널 수 있는 곳인지 여부
  • 렌더링할 때 사용할 텍스쳐


이런 속성을 지형 타일마다 따로 저장하는 일은 있을 수 없습니다. 이런 지형 종류에 대해서는 아래와 같이 열거형을 사용하는게 일반적이죠.



그리고 이런 지형을 거대한 격자형태인 2차원 배열로 관리합니다.


다음 타일 관련 데이터는 다음과 같이 얻을 수 있습니다.




그러나 위 코드들은 동작은 가능하지만 모든 경우의 수에 대해서 하드코딩을 해야하며

각 지형종류에 대한 데이터가 여러메서드로 나뉘어져 있습니다.


이런 경우에는 하나로 합쳐서 캡슐화를 시키는 게 좋습니다.


해서 아래와 같이 지형 클래스를 따로 만드는 방법을 추천합니다.



하지만 타일마다 매번 Terrain 인스턴스를 생성하는 것은 결과적으로 똑같이 많은 비용을 발생시키게 됩니다. 따라서, Terrain 클래스에는 타일위치와 관련된 내용은 전혀 없는 것을 볼 수 있습니다. 즉. 경량패턴식으로 이야기하자면 모든 지형 상태는 '고유'하다(context) 자유 문맥에 해당한다는 것을 알 수 있습니다.


나머지 내용은 3번째 글에서 이어적도록 하겠습니다...



posted by REDFORCE 2017. 6. 9. 22:20

이번 글에서는 경량 패턴에 대해서 다루도록 하겠습니다.


3D 게임에서 숲을 구현하고자 할 때, 수 천 그루가 넘는 나무마다 각각 수천 폴리곤의 형태로 표현을 해야합니다. 이 때 메모리가 충분하다하여도 숲을 그리기 위해 전체 데이터를 CPU에서 GPU로 버스를 통해 전달해야합니다. 이때 들어가는 비용은 매우 적지않을거라는 것을 쉽게 추측할 수 있습니다.


이때 나무마다 필요한 데이터들은 일반적으로 다음과 같습니다.


 - 줄기, 가지, 잎의 형태를 나타내는 폴리곤 메시

 - 나무 겁질과 잎사귀 텍스쳐

 - 숲에서의 위치와 방향

 - 각각의 나무가 다르게 보이도록 크기와 음영같은 값을 조절할 수 있는 매게변수


위 항목들을 코드로 나열해보면




대충 이런 내용들이 필요할 것이라 볼 수 있습니다.


관련 이미지

(결과적으로 작은 상자에 들어있는 값들은 모든 나무가 동일하다)


위에서 보듯이 나열되는 항목들의 데이터들이 적지 않은데다가, 메시와 텍스쳐는 크기도 큽니다.

해서  이렇게 많은 객체들(나무)이 있는 숲 전체는 1Frame에 GPU로 모두 전달하기에는 양이 너무 많습니다.


이런 문제에 대해서 다행히, 검증된 해결법으로 아래와 같이 시도하면 됩니다.


핵심은 숲에 나무가 수천그루가 있다고 해도 대부분 비슷해보인다는 점 입니다.

따라서, 모든 나무를 같은 메시와 텍스쳐로 표현할 수 있을거라 가정하고 나무 객체에 들어가는 데이터는 인스턴스별로 크게 다르지 않을 거라는 예상이 보입니다.


이런 가정하에 먼저 객체를 반으로 쪼개어 명시적으로 모델링을 데이터를 나눌 수 있습니다.


모든 나무가 같이 사용하는 데이터를 뽑아내 새로운 클래스에 모아봅니다.



이렇게하면 게임내에서 같은 메시와 텍스쳐를 여러번 메모리에 올릴 필요가 없기 때문에 TreeModel 객체는 하나만 존재하게 됩니다. 이제 각 나무 인스턴스는 공유 객체인 TreeModel을 참조하기만 하면 됩니다.


Tree 클래스에는 인스턴스별로 다르게 표기되어야 하는 데이터들만 아래와 같이 남겨둡니다.



위 형태를 그림으로 표현해보면 아래 그림과 같습니다.




주메모리에 객체를 저장하기 위해서라면 이 정도면 충분하지만, 렌더링을 하는 것은 또 별게의 이야기가 됩니다. 예를들어서 수천개의 인스턴스를 렌더링하기 위해 인스턴스의 Render()를 Call하는 것은 많은 오버헤드를 발생시키게 됩니다.


이런 오버헤드를 막기 위해 요즘나오는 그래픽 카드나 API에서는 여러번 렌더링 되어야하는 인스턴스의 공유 데이터를 받아서 50번 Render()를 Call()해야 할 것을 1번에 전체를 렌더링해주는 방법을 지원해주고 있습니다.


이를 인스턴스 렌더링(instanced rendering) 이라 부릅니다.


이 방법을 이용하면 그리기(draw) 함수를 한 번만 호출하여 전체 숲을 다 그릴 수 있습니다.

(자세한 방법은 인스턴스 렌더링에 대해서 알아보시면 많은 자료들이 나옵니다)

posted by REDFORCE 2017. 5. 25. 16:51

이번 글에서 다룰 명령 패턴 내용은 실행취소와 재실행에 대한 내용입니다.


어떤 명령 객체가 실행할 수 있다면, 이를 실행취소 할 수 있게 만드는것도 어렵지 않습니다.

게임개발 툴 같은곳에서는 필수적으로 지원이 되야하는 기능입니다. 만약 이 기능이 지원되지 않는다면....


(기획자가 당신을 죽이려들지도...)



그냥 실행취소 기능을 구현하려면 굉장히 어렵습니다. 그러나 명령패턴을 활용하면 이를 쉽게 만드는게 가능합니다. 예를 들어서 턴제 게임에서 이동 취소 기능을 추가한다고 하겠습니다.


이미 명령 객체를 2편에서 명령 객체를 이용해서 입력 처리를 추상화해둔 덕분에, 플레이어 이동도 명령에 캡슐화되어 있습니다. 이제 어떤 유닛을 옮기는 명령을 생각해보도록 하겠습니다.



MoveUnitCommand 클래스는 이전 에졔와 약간 다르게 생겼습니다.

이전 에졔에서는 명령에서 변경하려는 액터와 명령 사이를 추상화로 격리시켰지만 이번에는 이동하려는 유닛과 위치 값을 생성자에서 받아서 명령과 명시적으로 바인드되어 있습니다.

MoveUnitCommand 명령 인스턴스는 '무엇인가를 움직이는' 보편적인(언제든지 써먹을 수 있는) 작업이 아니라 게임에서의 구체적인 실제 이동을 담고있습니다.


2편에서 다룬 에졔 같은 경우, 어떤 일을 하는지를 정의한 명령 객체 하나가 매번 재사용 됩니다.

입력 핸들러 코드에서는 특정 버튼이 눌릴 때마다 여기에 연결된 명령 객체의 execute() 를 호출 했었습니다.


이번에 만든 명령 클래스는 특정 시점에 발생될 일을 표현한다는 점에서 더 구체적입니다.

이를 테면, 입력 핸들러 코드는 플레이어가 이동을 선택할 때마다 명령 인스턴스를 생성해야합니다.



Command 클래스가 일회용이라는 게 장점이라는 걸 이제 명령을 취소할 수 있도록 순수가상함수 undo() 를 정의하여 보면 알 수 있습니다.



Undo( ) 에서는  execute( ) 에서 변경하는 게임상태를 반대로 바꿔주면 됩니다. 이제 MoveUnitCommand 클래스에 실행취소 기능을 넣어보겠습니다.




잘보면 MoveUnitCommand  클래스에 상태가 몇 개 추가되었습니다. 즉 유닛이 이동한 후에는 이전 위치를 알수 없기 때문에, 이동을 취소할 수 있도록 이전 위치를 xBefore_, yBefore_멤버 변수에 따로 저장하도록 합니다.


이제 플레이어가 이동을 취소 할 수 있게 하려면 Ctrl + z 키를 눌러서 취소 하는 것과 같이 undo() 명령을 수행하면 됩니다. 만약 다시 재실행을 하고 싶다면 redo() 함수를 만들어서 사용하면 가능합니다.


끝으로 여러단계의 실행취소를 지원하는 것도 어렵지 않습니다. 방법은 가장 최근 명령만 기억하는대신, 명령 목록을 유지하고 '현재' 명령이 무엇인지만 알고 있으면 됩니다.

유저가 명령을 실행하면, 새로 생성 된 명령을 목록 맨 뒤에 추가하고, 이를 '현재' 명령으로 기억하면 됩니다.

posted by REDFORCE 2017. 5. 5. 01:42


명령 패턴 두 번째글 입니다. 혹시나 첫 번째 글을 보시지 않은 분들이 계시다면

다음 링크를 눌러서 바로 가실 수 있습니다. (명령 패턴 : 입력 키 변경)


2. 액터에게 지시하기


명령 패턴을 활용한 방법 두 번째 내용은 액터에게 지시하기 입니다.


이전 글에서 정리한 내용은 순전히 Command 클래스로 만든 Jump() 나 FireGun() 과 같은 전역 함수가 플레이어 캐릭터 객체가 사용할 것임을 암시적으로 찾아서 활용해야 한다는 점이 가정에 깔려 있기 때문에, 플레이어만 사용할 수 있다라는 제한이 있습니다.



그러나 꼭 플레이어만 아닌 어떤 몬스터나 캐릭터나 AI가 움직이는 NPC들도 

Jump() 나 FireGun() 과 같은 동작을 한다면??


일일이 모든 캐릭터에게 위와같은 Command 클래스를 넣어줘야 한다면 그건 그것나름대로 동작은 가능하지만 불편할 것 같습니다.


그래서 행동 자체를 실체화 시킨 것에서 행동할 액터를 받아오게끔 만드는 방법이 있습니다.



위에서 GameActor는 게임 월드를 돌아다니는 캐릭터를 대표하는 '게임 객체' 클래스라 보시면 됩니다. 이제 Command를 상속받은 클래스는 execute()가 호출될 때 GameActor 객체를 인수로 받기 때문에 원하는 액터의 메서드를 호출 할 수 있습니다.


위 말은 이제 원하는 액터의 메서드를 호출 하게끔 다음과 같이 Command 클래스가 이용가능해진다는 뜻 입니다.



코드를 보는것처럼 이제 JumpCommand 클래스 하나로 게임에 등장하는 어떤 캐릭터라도 점프를 할 수 있게끔 가능해졌습니다.


이제 입력 핸들러에서 입력을 받아 적당한 객체의 메서드를 호출하는 명령 객체를 연결하는 것을 만들 차례입니다. 먼저 InputHandler()에서 명령 객체를 반환하도록 변경합니다.



다음으로 명령 객체를 받아서 플레이어를 대표하는 GameActor 객체에 적용하는 코드가 필요합니다.



이렇게 하면 액터(actor)가 플레이어 캐릭터라면 유저 입력에 따라 동작하기 때문에 이전 글에서 다룬 내용과 기능상 다를 게 없지만, 명령을 실행할 때 액터만 바꾸면 플레이어가 게임에 있는 어떤 액터라도 제어 할 수 있게 됩니다.


일반적으로 플레이어가 AI를 제어하는 일은 많이 없지만, 꼭 AI를 제어하는 기능이 아니더라도 위와 같이 액터를 제어하기 위한 AI를 만드는 상황에서도 많은 활용이 되도록 설계가 가능합니다.


posted by REDFORCE 2017. 5. 2. 17:44

본 글은 아래의 교재를 참고하여 정리한 내용입니다.


책 이름 : 게임프로그래밍 패턴 (한빛미디어)

저자 : 로버트 나이스트롬

옮김 : 박일


------------------------------------


GoF에 정의 한 명령 패턴을 따르면 명령패턴이란.


요청자체를 캡슐화하는 것 입니다. 이를 통해 요청과 서로 다른 사용자를 매게변수로 만들고, 요청을 대기시키거나 로깅하며, 되돌릴 수 있는 연산을 지원합니다.(GoF의 디자인 패턴. 311쪽)


위 말만 들어서는 잘 이해가 안가는데? 하고 궁금해할 수 있습니다.


그래서 명령 패턴을 한 줄로 다시 요약해보자면, 

명령 패턴은 메서드 호출을 실체화(reify)한 것이다.


라고 할 수 있습니다. 대체 메서드 호출을 실체화 했다라는게 무슨말일까요?




바로 메서드 자체를 어떤 한 객체로 만들었다는 뜻 입니다. 메서드는 우리가 알다시피 어떤 함수형태로 행위를 하는 녀석이지만, 이 행위의 내용을 바꿀수는 없습니다. 그러나 이 행위 자체를 어떤 객체로 만들어서 실체화 시키면 행위의 내용 자체를 다른 것으로 바꿔서 가능하게끔 하는 것이 바로 명령 패턴입니다.


1. 입력키 변경


그럼 명령 패턴을 활용하는 첫 번째 예를 보겠습니다.


보통 모든 게임은 입력(Input)을 통해 어떤 출력(Output)이 나오게 되는데요.

가장 보편적인 입력 방식인 키보드와 마우스를 누르는 코드가 다음과 같이 있다고 치겠습니다.



일반적으로 이런 형태의 함수로 이루어진 입력 핸들러가

게임 루프 과정에서 매 프레임마다 호출되어 입력에 대한 것을 확인하게 됩니다.


그러나 만약 사용자가 입력키에 대한 변경을 하려면 위의 키가 교체 가능하도록 키입력에 대한 어떤 객체가 필요하게 됩니다. 이 때 명령 패턴을 넣어서 한번 키입력을 변경하도록 만들어보겠습니다.


먼저, 게임에서 할 수 있는 행동을 실행할 수 있는 공통 상위 클래스를 먼저 정의합니다.


다음, 각 행동별로 하위 클래스를 만듭니다.



그리고 입력 핸들러코드로 각 버튼별로 Command 클래스 포인터를 저장합니다.



이렇게 하면 입력 처리는 이제 다음 코드로 위임됩니다.



여기까지가 Input 키 명령 패턴을 이용한 입력키 변경을 위한 방법입니다.

보시다시피 어떤 입력키에 대한 메서드 자체를 클래스로 실체화하여 입력키 값이 변경되더라도

어떤 행동을 한다라는 메서드는 동일하게 수행이 되는 구조가 되는 것이지요.


위 방법이 명령패턴의 핵심입니다만, 이것만 봐서 만족할 순 없겠지요!

다음 글에서 명령 패턴을 더 활용하는 글을 확인하도록 하겠습니다.


마무리는 이 글을 읽으셨을 분들께 감사드리며 짤방 투척!