nhn next gpg study

58
GPG 1.1 객객 객객객 객객객객객객 객객객객 NHN NEXT 객객객

Upload: -

Post on 13-Aug-2015

58 views

Category:

Software


5 download

TRANSCRIPT

Page 1: NHN NEXT GPG Study

GPG 1.1 객체 지향적 프로그래밍과 설계기법NHN NEXT 김승현

Page 2: NHN NEXT GPG Study

목차• C++ 의 게임 프로그래밍에서의 장점

• 코딩 스타일

• 클래스 설계

클래스 계통 구조의 설계

설계 패턴 - 싱글톤 , 퍼사드 , 스테이트 , 팩토리

Page 3: NHN NEXT GPG Study

C++ 의 게임 프로그래밍에서의 장점- C 로부터 전해진 높은 이식성과 효율성을 가지고 있다 .

- 큰 규모의 프로젝트일수록 코드의 재사용성이 요구되기 때문에 ,

재사용 가능한 라이브러리와 게임 엔진의 작성이 불가결하다 .

이를 위해 기본적인 OOP 원칙들과 개발 기법들을 제대로 이해하고 사용해야 한다 .

이 장에서 주로 다룰 내용이 이에 대한 설계 기법들이다 .

Page 4: NHN NEXT GPG Study

코딩 스타일

Page 5: NHN NEXT GPG Study

코딩 스타일

- 헝가리식 표기법

Page 6: NHN NEXT GPG Study

코딩 스타일

- 클래스 이름 접두어

클래스의 용도에 따라 C 로 시작하는 클래스는 구체 (Concrete) 클래스 ,

I 는 인터페이스 클래스 . 즉 , 설계 템플릿으로 쓰일 클래스들을 뜻하게 한다 .

기능에 따라 UI 등의 접두어를 붙일 수도 있다 .

Page 7: NHN NEXT GPG Study

클래스 설계

Page 8: NHN NEXT GPG Study

클래스 설계

- 클래스에서 메서드 이름에 관한 명시적 규칙이 있는 것은

생성자와 소멸자 뿐이다 .

이에 대한 적절한 규약을 만들어두면 여러모로 도움된다 .

- 우측의 경우 , 일단 생성자가 매우 직관적이다 .

그리고 객체 생성 시점 이외의 시점에서도 클래스 변수를

초기화할 수 있게 되었다 .

이런식으로 객체의 초기화를 생성자로부터 분리하면 ,

Create(), Destroy() 를 여러번 호출하는 것으로

메모리 할당 , 해제의 반복을 줄일 수 있다 .

또한 기본생성자와 달리 반환값을 가질 수 있다는 이점도 있다 .

Page 9: NHN NEXT GPG Study

클래스 계통 구조의 설계

Page 10: NHN NEXT GPG Study

클래스 계통 구조의 설계

- 객체지향에서 코드 재사용성을 높이는 데 가장 중요한 것은 상속 개념이다 .

이를 위해 클래스를 연결하는 기본적인 방법은 상속과 레이어링이 있다 .

( 레이어링은 합성 , 포함 , 내포 등을 의미 )

상속은 하나의 클래스에서 다른 클래스를 파생하는 것이며 ,

레이어링은 하나의 객체가 다른 객체를 자신의 멤버로 가지는 것을 의미한다 .

- 간단한 규칙으로 , is-a 관계라면 public 상속을 이용하며 ,

has-a 관계는 레이어링을 사용하라고 한다 .

이 책에선 이에 대한 구현 방식을 자세히 설명하지 않고 , 개념적인 차이만 설명해주고 있음 .

자세히 알고 싶다면 effective c++ 책으로 공부할 것을 추천함 .

Page 11: NHN NEXT GPG Study

설계 패턴

Page 12: NHN NEXT GPG Study

설계 패턴 - 싱글톤 패턴

용도

- 여러 클래스 / 모듈 들이 , 전역적으로 접근할 수 있는 , 단일 객체가 필요할 때 쓰인다 .

아래는 기본적인 구현방법의 하나이다 .

Page 13: NHN NEXT GPG Study

설계 패턴 - 싱글톤 패턴

- 아래는 기본적인 싱글톤 개념에 , 확장 가능하도록 상속 개념을 추가한 구현이다 .

Page 14: NHN NEXT GPG Study

설계 패턴 - 퍼사드 패턴

용도

- 클래스들 사이의 상호의존성 ( 커플링 ) 을 최소화하기 위해서 사용한다 .

이를 위해 각각의 서브시스템들을 대표하는 관리자 클래스를 만든다 .

Page 15: NHN NEXT GPG Study

설계 패턴 - 퍼사드 패턴

커플링을 줄여야 하는 이유

- 한 서브 시스템에서 큰 변화가 생겼을 때 ,

서브 시스템의 클래스들과 커플링 된 외부의 클래스들도 수정해야함 .

당연히 커플링이 적을수록 작업량이 적다 .

Page 16: NHN NEXT GPG Study

설계 패턴 - 상태 패턴

이른바 state 패턴 .

용도

- 게임의 규모가 클 때 , 다양한 곳에서 다루는 상태를 관리하기 위해 사용한다 .

혹은 상태의 구조가 복잡할 때도 사용하면 좋다 .

- 우측과 같이 상태를 바꾸는 로직은 관리자역의 클래스에서

하도록 하는 것이 관리하기가 쉽다 .

Page 17: NHN NEXT GPG Study

설계 패턴 - 팩토리 패턴

- 하나의 클래스가 다양한 종류의 객체의 생성을 책임지게 하는 방식으로 ,

메모리 할당을 모아서 하거나 , 객체들을 자원 관리자에 넣는 등

공통적인 작업을 한번에 수행하기 쉬워진다 .

- 한 객체 모델에서 파생된 , 서로 다른 수많은 객체들을 생성해야 된다면 ,

팩토리 패턴을 사용하는 것이 이롭다 .

AI, 텍스처 , 사운드 등의 자원은 물론 추상적인 객체도 적용 대상이 될 것이다 .

Page 18: NHN NEXT GPG Study

설계 패턴 - 팩토리 패턴

- 상속 개념을 통해 확장성과 유연성에 이득을 볼수도 있다 .

클래스 ID 를 받아 객체를 생성하고 , 기반 클래스에 대한 포인터를 돌려주는 방식으로

팩토리 패턴으로 다룰 수 있는 객체의 종류를 추가 / 제거하는 작업을 쉽게 할 수 있다 .

Page 19: NHN NEXT GPG Study

설계 패턴 - 팩토리 패턴

- 상속을 활용한 구현 예

Page 20: NHN NEXT GPG Study

Page 21: NHN NEXT GPG Study

GPG 1.12 assert 의 비법들NHN NEXT 김승현

Page 22: NHN NEXT GPG Study

assert 의 기본- 코드상 임의의 가정이 부합하는지 감시하는 용도 .

( 넘어온 인자가 nullptr 이 아니라고 가정할 때 등 )

- 다만 위 예제의 경우 , 릴리즈 모드에서 강제종료 될 수 있음에 주의 .

if 문으로 처리할 지 assert 를 쓸지 상황에 따라 생각해봐야 한다 .

Page 23: NHN NEXT GPG Study

assert 의 동작- 실행 중 가정이 어긋나면 (false 이면 ) 프로그램을 일시 정지시킨다 .

이 때 사용자는 프로그램을 계속 돌릴지 ,

종료하고 문제가 생긴 코드로 이동할 지 결정할 수 있다 .

( 근데 실제로 해보거나 구글해보면 , 무조건 abort() 를 호출하도록 되어있다 . vs 버전에 따라 다

르거나 assert 표준이 바뀐듯 . )

- 릴리즈 모드에서 assert 구문이 들어간 코드는 컴파일되지 않으며 , 작동하지 않는다 .

때문에 assert 안에서 프로그램의 상태를 변화시킬 경우 ,

릴리즈 모드와 디버깅 모드의 동작이 달라지는 문제가 발생할 수 있다 .

Page 24: NHN NEXT GPG Study

assert 의 동작- assert 안의 코드를 콘솔이나 메세지 박스에 출력하며 ,

해당 소스가 있는 파일과 line 도 알려준다 .

- 혹은 다시 시도 버튼을 누른 후 중단시켜

중단된 시점의 call 스택과 변수의 상태를 확인하여

에러의 원인을 쉽게 찾을 수 있다 .

Page 25: NHN NEXT GPG Study

assert 의 용도- 코드상 임의의 가정이 부합하는지 감시하는 용도 .

- 제대로 동작했다면 논리적으로 올 수 없는 flow 에 도달했는지 검사하는 용도 .

- 매우 빠르게 수행되어야 하는 저레벨의 함수를 작성할 때 ,

어떤 문제가 발생했는지 점검하는 용도 .

( 릴리즈에서 동작하지 않기 때문에 약간의 가속효과를 얻을 수 있다 . )

- 하지만 assert 를 사용하는 것은 일종의 디버깅 테크닉일 뿐으로 ,

예외처리와 구분하여 사용해야 한다 . 예외처리를 해야하는 경우엔 if 문을 쓰자 .

Page 26: NHN NEXT GPG Study

assert 의 용도- 예외처리를 해야되는 경우와 assert 를 사용해도 되는 경우의 구분

- 예외처리

> 외부 입력 ( 소켓 , 키보드 , 파일 등 ) 으로부터 저장된 변수의 경우

- assert 사용

> 설계상 반드시 조건을 갖춰야 하는 변수의 경우 ( NULL 이 아니라던지 , 0 보다 커야 하던지 )

> enum 값을 매개변수나 리턴 값으로 사용하는 경우

> enum 을 사용한 switch 문의 default 에서 , 필요하다면 assert(0)

> if else chain 에서 맨 마지막 else 에서 , 필요하다면 assert(0)

> 리소스를 해제할 때 메모리 릭이 발생하는지 확인할 때

Page 27: NHN NEXT GPG Study

비법 #1 : 더 많은 정보를 넣는 법- assert(src != 0) 이 실패하면 , “src != 0” 이라는 메시지가 표시된 대화상자가 나타난다 .

( 근데 설정이나 버전에 따라 나타나는 방식에 차이가 있는듯 . 제껀 콘솔창에 표시됨 .. )

다음과 같이 어떤 문제인지 구체적으로 적는다면 , 디버그 시간을 단축할수도 있다 .

혹은 assert(0) 을 사용하는 대신에 ,

이런 코드를 사용할수도 있다 .

Page 28: NHN NEXT GPG Study

비법 #2 : 좀더 편하게- 매크로를 사용하여 비법 1 을 사용하기 편하게 할 수 있다 .

Page 29: NHN NEXT GPG Study

비법 #3 : 커스텀 assert 만들기- 표준 C assert 에는 귀찮은 문제가 하나 있다 .

디버깅 중 조건이 실패할 때 , 소스 파일에 작성한 assert 문이 아니라 ,

assert.c 파일 안의 assert 문으로 유도된다는 점이다 .

( 근데 내가 쓸땐 잘만 되는데 , 이역시 vs 버전에 따라 다른 것 같음 . )

- 오른쪽과 같은 커스텀 assert 를

만들어서 중단점이 소스 파일의

assert 로 유도되도록 할 수 있다 .

Page 30: NHN NEXT GPG Study

비법 #4 : 이것도 중요 !

- 커스텀 assert 를 만들 때 , assert 대화상자에 “다음부터 항상 무시” 옵션을 추가하는 것이 좋다 .

assert 가 프레임마다 호출되는 등 귀찮은 경우가 있기 때문 .

우측은 구현 예의 하나 .

Page 31: NHN NEXT GPG Study

비법 #5 : 초고수만 볼 것- assert 의 또다른 문제로 ,

1. assert 가 함수의 내부에 있고

2. 함수가 받은 매개변수가 assert 의 발생 원인이 될 수 있는 경우

디버거를 같이 쓰지 않는 한 , 왜 assert 가 발생했는지 쉽게 추적할 수 없다는 것이다 .

때문에 거의 디버거를 붙여서 실행하지 않는 , 테스터를 통한 도움을 받기 힘들다 .

이에 대한 간단한 해결책은 , assert 대화상자 안에 스택 상태를 표시하는 것이다 .

아니면 assert 가 발생하면 스택의 상태를 클립보드에 복사되게 하거나 .

Page 32: NHN NEXT GPG Study

비법 #5 : 초고수만 볼 것

Page 33: NHN NEXT GPG Study

끗 !

Page 34: NHN NEXT GPG Study

GPG 3.3 A* 길찾기 알고리즘의 기초NHN NEXT 김승현

Page 35: NHN NEXT GPG Study

A*( 에이 스타 ) 알고리즘- 정확하면서 효율적인 길찾기 알고리즘의 하나 .

- 대부분의 상황에서 최적의 수행시간을 제공하며 ,

몇가지 조건만 갖춘다면 다른 어떤 알고리즘보다도 빠르게 찾을 수 있다 .

Page 36: NHN NEXT GPG Study

동작 방식- 각 지점 (node) 에 목표지점까지의 직선거리 (h) 를 저장한다 .

두 가지 경로 목록을 관리한다 .( Open list ), ( Closed list )

시작 지점을 Open list 에 넣으며 시작하며 , 아래의 작업을 반복수행하는 것으로

최적의 경로를 구한다 .

> 시작지점부터 현재 탐색중인 지점까지 지나온 경로의 weight 를 합한 값 (g) 을 구한다 .

시작 지점은 이 값이 0.

> Open list 에 있는 지점 중 비용 (g+h) 이 가장 작은 값을 선택한다 .

> 선택한 지점을 Closed list 에 추가하며 , 인접한 지점을 Open list 에 추가한다 .

> 목표지점에 도착했다면 지나온 경로를 리턴하며 빠져나온다 .

Page 37: NHN NEXT GPG Study
Page 38: NHN NEXT GPG Study

비용- 바퀴가 달린 전차는 다른 길보다 도로에서 더 빠를것이다 .

수륙 양용의 차라면 바다도 건널 수 있을 것 .

시작지점이 평지고 도착지점이 언덕인 경우와 그 반대인 경우는

경로의 비용에 차이가 있을것이다 .

이처럼 유닛의 종류나 이동의 양 끝 위치를 인자로 받아야 하는 경우도 있다 .

- 보드게임처럼 바닥이 격자로 된 경우 , 직선거리 (h) 는 최소로 목표지점까지 이동하는 칸 수가 될 것이다 .

Page 39: NHN NEXT GPG Study

비용- weighted 그래프의 경우 추정 직선거리 (h) 가 실제 최단거리보다 커질 수 있다 .

이 경우 최단 경로가 정확하지 않을 수 있다 .

보다 정확한 최단 경로를 구하기 위해서는 h 를 설정할 때는 직선거리의 값을

가장 weigh 가 작은 한 경로와 같은 비율이 되게 해야한다 .

- 다만 값을 너무 작게 할 경우 올바른 방향으로 가기보다

‘ 안가봤던 방향’으로 향하는 경우가 더 많아져 비효율적이 될 수 있다 .

Page 40: NHN NEXT GPG Study

비용

Page 41: NHN NEXT GPG Study

효율적이고 자연스러운 길찾기를 위한 그래프- 시작위치와 도착지점에 맞아떨어지는 지점 (node) 은 존재하지 않을 수 있다 .

때문에 그에 대한 지점은 검색이 일어날 때 추가해야 한다 .

혹은 각각 가장 인접한 지점을 시작 , 도착 지점으로 하던지 .

- 도달하지 못하는 장소가 없도록 충분히 설정하되 , 너무 많아선 안된다 .

유닛이 좀더 지능적으로 이동하는 것처럼 보이려면 , 되도록 지점을 지그재그로 배치하지 말자 .

Page 42: NHN NEXT GPG Study

그래프를 만드는 다양한 방법

Page 43: NHN NEXT GPG Study

A* 의 약점- 시작지점에서 목표까지의 경로가 존재하지 않을 경우

모든 지점을 조사하고나서야 그것을 알 수 있다 .

이에 대한 타개책으로 , 독립된 구역에 대해 ( 섬 이라든지 ) 같은 구역 내에서만 길찾기를 허용하는 것도 하나의 방법이다 .

Page 44: NHN NEXT GPG Study

끗 !

Page 45: NHN NEXT GPG Study

GPG 4.6 다해상도 맵을 이용한 충돌 판정NHN NEXT 김승현

Page 46: NHN NEXT GPG Study

다룰 내용- 다양한 크기의 많은 객체들이 돌아다니는 게임에서

충돌 판정의 횟수를 줄이기 위한 방법 .

- 이런 로직을 설계하지 않았다면 O(n^2) 지옥을 볼수있다 .

Page 47: NHN NEXT GPG Study

쉬운 문제부터 접근해보자 .

- 다양한 크기라는 조건을 뺀다고 가정한다 .

동일한 크기의 적당히 큰 격자맵을 둔다 .

각 격자는 그 칸에 중점을 두고 있는 객체들을 담은 링크드 리스트를 관리한다 .

어떤 객체에 대해 충돌을 검사하는 경우 , 그 객체가 속한 칸과 주변 칸의 리스트

안에 담긴 객체들 하고만 검사하면 된다 .

Page 48: NHN NEXT GPG Study
Page 49: NHN NEXT GPG Study

쉬운 문제부터 접근해보자 .

- 모든 객체들에 서로의 충돌여부를 검사하는 경우 .

좌상단의 첫번째 칸부터 진행 .

각 칸의 리스트 내 객체를 순회하며 순서대로 모든 객체를 한번씩 선택함 .

선택한 객체의 칸과 동쪽 , 남동쪽 , 남쪽의 세 칸에 대해서만 검사해주면 된다 .

( 책에서는 이렇게 말했지만 남서쪽도 확인해줘야 할 듯 . )

그 외의 칸들은 이전에 확인했던 객체를 통해 검사가 끝났기 때문 .

이 방법으로 모든 객체들에 대한 충돌검사를 빠르게 처리할 수 있다 .

Page 50: NHN NEXT GPG Study

이제 본 문제 .

- 객체의 크기가 다양한 경우 이 방법을 그대로 적용하긴 어렵다 .

( 객체가 격자 한칸보다 크다거나 . )

물론 격자의 크기를 가장 큰 객체의 크기만큼 늘리면 충돌은 제대로 검사하지만 ,

그만큼 효율이 떨어진다 .

- 이에 대한 쉬운 해결책 중 하나는 , 객체의 중점을 포함하는 칸 뿐만 아니라 ,

실제로 객체가 닿는 모든 격자의 리스트에 객체를 추가하는 것이다 .

구현은 간단하지만 그리 깔끔한 방법은 아니다 .( 계산량이 오히려 많아질수도 있음 )

Page 51: NHN NEXT GPG Study

다해상도 맵- 저자가 제시하는 해결책은 다음과 같다 .

격자를 하나만 사용하지 않고 , 칸의 크기에 차이가 있는 여러개의 격자를 사용 .

칸의 크기는 2 의 제곱수로 제한 ( 최적화를 위해 )

각 객체는 크기에 따라 적절한 격자맵을 선택하고 ,

중점이 속한 칸의 링크드 리스트에 추가한다 .

여기서 적절한 크기란 , 객체가 한 격자의 안에 들어갈 수 있는 격자 중 최소인 크기 .

Page 52: NHN NEXT GPG Study
Page 53: NHN NEXT GPG Study

다해상도 맵- 충돌을 검출하는 방식은 앞서 설명한 방식과 유사하다 .

먼저 자신과 주변 칸을 검사한다 .

그리고 서로 다른 격자맵을 가진 객체간의 충돌도 검출해야 한다 .

이를 위해 객체가 속한 칸이 더 큰 격자맵에서 어떤 격자에 속하는지 구하고 ,

주변의 더 큰 객체와의 충돌을 검출한다 .

( 자신보다 작은 객체가 자신과의 충돌을 검출하기 때문에 )

더 작은 객체에 대해서는 확인할 필요가 없다 .

Page 54: NHN NEXT GPG Study

다해상도 맵- 그럼 맵을 몇개나 만들어야 할까 ?

일단 가장 작은 격자의 크기는 가장 작은 객체를 포함할 수 있는 2 의 제곱수여야 한다 .

그로부터 한 단계씩 줄여 해상도 1 의 맵 전체를 포함하는 격자맵을 만든다면 ,

어떤 크기의 객체가 추가되어도 ( 화면 전체를 뒤덮는 폭발 효과 라던지 ) 대처할 수 있다 .

격자맵이 많을수록 부하가 심할 것처럼 느낄 수 있겠지만 ,

한 단계마다 격자의 수는 ¼ 로 줄어들기 때문에 그리 큰 부담이 아니다 .

물론 가장 큰 객체가 확실히 정해졌다면 해상도의 하한을 결정할 수 있다 .

가장 효율적인 방법은 개발 도중에는 하한을 두지 말고 , 완료 직전에 결정하는 것이다 .

Page 55: NHN NEXT GPG Study

코드

Page 56: NHN NEXT GPG Study

코드

Page 57: NHN NEXT GPG Study

코드

Page 58: NHN NEXT GPG Study

끗 !