O slideshow foi denunciado.
Seu SlideShare está sendo baixado. ×

NHN NEXT GPG Study

Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Anúncio
Carregando em…3
×

Confira estes a seguir

1 de 58 Anúncio

Mais Conteúdo rRelacionado

Diapositivos para si (14)

Quem viu também gostou (20)

Anúncio

Semelhante a NHN NEXT GPG Study (20)

Anúncio

Mais recentes (20)

NHN NEXT GPG Study

  1. 1. GPG 1.1 객체 지향적 프로그래밍과 설계기법 NHN NEXT 김승현
  2. 2. 목차 • C++의 게임 프로그래밍에서의 장점 • 코딩 스타일 • 클래스 설계 클래스 계통 구조의 설계 설계 패턴 - 싱글톤, 퍼사드, 스테이트, 팩토리
  3. 3. C++의 게임 프로그래밍에서의 장점 - C 로부터 전해진 높은 이식성과 효율성을 가지고 있다. - 큰 규모의 프로젝트일수록 코드의 재사용성이 요구되기 때문에, 재사용 가능한 라이브러리와 게임 엔진의 작성이 불가결하다. 이를 위해 기본적인 OOP 원칙들과 개발 기법들을 제대로 이해하고 사용해야 한다. 이 장에서 주로 다룰 내용이 이에 대한 설계 기법들이다.
  4. 4. 코딩 스타일
  5. 5. 코딩 스타일 - 헝가리식 표기법
  6. 6. 코딩 스타일 - 클래스 이름 접두어 클래스의 용도에 따라 C 로 시작하는 클래스는 구체(Concrete) 클래스, I 는 인터페이스 클래스. 즉, 설계 템플릿으로 쓰일 클래스들을 뜻하게 한다. 기능에 따라 UI 등의 접두어를 붙일 수도 있다.
  7. 7. 클래스 설계
  8. 8. 클래스 설계 - 클래스에서 메서드 이름에 관한 명시적 규칙이 있는 것은 생성자와 소멸자 뿐이다. 이에 대한 적절한 규약을 만들어두면 여러모로 도움된다. - 우측의 경우, 일단 생성자가 매우 직관적이다. 그리고 객체 생성 시점 이외의 시점에서도 클래스 변수를 초기화할 수 있게 되었다. 이런식으로 객체의 초기화를 생성자로부터 분리하면, Create(), Destroy()를 여러번 호출하는 것으로 메모리 할당, 해제의 반복을 줄일 수 있다. 또한 기본생성자와 달리 반환값을 가질 수 있다는 이점도 있다.
  9. 9. 클래스 계통 구조의 설계
  10. 10. 클래스 계통 구조의 설계 - 객체지향에서 코드 재사용성을 높이는 데 가장 중요한 것은 상속 개념이다. 이를 위해 클래스를 연결하는 기본적인 방법은 상속과 레이어링이 있다. ( 레이어링은 합성, 포함, 내포 등을 의미 ) 상속은 하나의 클래스에서 다른 클래스를 파생하는 것이며, 레이어링은 하나의 객체가 다른 객체를 자신의 멤버로 가지는 것을 의미한다. - 간단한 규칙으로, is-a 관계라면 public 상속을 이용하며, has-a 관계는 레이어링을 사용하라고 한다. 이 책에선 이에 대한 구현 방식을 자세히 설명하지 않고, 개념적인 차이만 설명해주고 있음. 자세히 알고 싶다면 effective c++ 책으로 공부할 것을 추천함.
  11. 11. 설계 패턴
  12. 12. 설계 패턴 - 싱글톤 패턴 용도 - 여러 클래스/모듈 들이, 전역적으로 접근할 수 있는, 단일 객체가 필요할 때 쓰인다. 아래는 기본적인 구현방법의 하나이다.
  13. 13. 설계 패턴 - 싱글톤 패턴 - 아래는 기본적인 싱글톤 개념에, 확장 가능하도록 상속 개념을 추가한 구현이다.
  14. 14. 설계 패턴 - 퍼사드 패턴 용도 - 클래스들 사이의 상호의존성(커플링)을 최소화하기 위해서 사용한다. 이를 위해 각각의 서브시스템들을 대표하는 관리자 클래스를 만든다.
  15. 15. 설계 패턴 - 퍼사드 패턴 커플링을 줄여야 하는 이유 - 한 서브 시스템에서 큰 변화가 생겼을 때, 서브 시스템의 클래스들과 커플링 된 외부의 클래스들도 수정해야함. 당연히 커플링이 적을수록 작업량이 적다.
  16. 16. 설계 패턴 - 상태 패턴 이른바 state 패턴. 용도 - 게임의 규모가 클 때, 다양한 곳에서 다루는 상태를 관리하기 위해 사용한다. 혹은 상태의 구조가 복잡할 때도 사용하면 좋다. - 우측과 같이 상태를 바꾸는 로직은 관리자역의 클래스에서 하도록 하는 것이 관리하기가 쉽다.
  17. 17. 설계 패턴 - 팩토리 패턴 - 하나의 클래스가 다양한 종류의 객체의 생성을 책임지게 하는 방식으로, 메모리 할당을 모아서 하거나, 객체들을 자원 관리자에 넣는 등 공통적인 작업을 한번에 수행하기 쉬워진다. - 한 객체 모델에서 파생된, 서로 다른 수많은 객체들을 생성해야 된다면, 팩토리 패턴을 사용하는 것이 이롭다. AI, 텍스처, 사운드 등의 자원은 물론 추상적인 객체도 적용 대상이 될 것이다.
  18. 18. 설계 패턴 - 팩토리 패턴 - 상속 개념을 통해 확장성과 유연성에 이득을 볼수도 있다. 클래스 ID를 받아 객체를 생성하고, 기반 클래스에 대한 포인터를 돌려주는 방식으로 팩토리 패턴으로 다룰 수 있는 객체의 종류를 추가/제거하는 작업을 쉽게 할 수 있다.
  19. 19. 설계 패턴 - 팩토리 패턴 - 상속을 활용한 구현 예
  20. 20.
  21. 21. GPG 1.12 assert 의 비법들 NHN NEXT 김승현
  22. 22. assert 의 기본 - 코드상 임의의 가정이 부합하는지 감시하는 용도. ( 넘어온 인자가 nullptr 이 아니라고 가정할 때 등 ) - 다만 위 예제의 경우, 릴리즈 모드에서 강제종료 될 수 있음에 주의. if 문으로 처리할 지 assert 를 쓸지 상황에 따라 생각해봐야 한다.
  23. 23. assert 의 동작 - 실행 중 가정이 어긋나면(false 이면) 프로그램을 일시 정지시킨다. 이 때 사용자는 프로그램을 계속 돌릴지, 종료하고 문제가 생긴 코드로 이동할 지 결정할 수 있다. ( 근데 실제로 해보거나 구글해보면, 무조건 abort() 를 호출하도록 되어있다. vs 버전에 따라 다 르거나 assert 표준이 바뀐듯. ) - 릴리즈 모드에서 assert 구문이 들어간 코드는 컴파일되지 않으며, 작동하지 않는다. 때문에 assert 안에서 프로그램의 상태를 변화시킬 경우, 릴리즈 모드와 디버깅 모드의 동작이 달라지는 문제가 발생할 수 있다.
  24. 24. assert 의 동작 - assert 안의 코드를 콘솔이나 메세지 박스에 출력하며, 해당 소스가 있는 파일과 line 도 알려준다. - 혹은 다시 시도 버튼을 누른 후 중단시켜 중단된 시점의 call 스택과 변수의 상태를 확인하여 에러의 원인을 쉽게 찾을 수 있다.
  25. 25. assert 의 용도 - 코드상 임의의 가정이 부합하는지 감시하는 용도. - 제대로 동작했다면 논리적으로 올 수 없는 flow 에 도달했는지 검사하는 용도. - 매우 빠르게 수행되어야 하는 저레벨의 함수를 작성할 때, 어떤 문제가 발생했는지 점검하는 용도. ( 릴리즈에서 동작하지 않기 때문에 약간의 가속효과를 얻을 수 있다. ) - 하지만 assert 를 사용하는 것은 일종의 디버깅 테크닉일 뿐으로, 예외처리와 구분하여 사용해야 한다. 예외처리를 해야하는 경우엔 if 문을 쓰자.
  26. 26. assert 의 용도 - 예외처리를 해야되는 경우와 assert 를 사용해도 되는 경우의 구분 - 예외처리 > 외부 입력(소켓, 키보드, 파일 등)으로부터 저장된 변수의 경우 - assert 사용 > 설계상 반드시 조건을 갖춰야 하는 변수의 경우( NULL 이 아니라던지, 0 보다 커야 하던지) > enum 값을 매개변수나 리턴 값으로 사용하는 경우 > enum 을 사용한 switch 문의 default 에서, 필요하다면 assert(0) > if else chain 에서 맨 마지막 else 에서, 필요하다면 assert(0) > 리소스를 해제할 때 메모리 릭이 발생하는지 확인할 때
  27. 27. 비법 #1 : 더 많은 정보를 넣는 법 - assert(src != 0) 이 실패하면, “src != 0” 이라는 메시지가 표시된 대화상자가 나타난다. ( 근데 설정이나 버전에 따라 나타나는 방식에 차이가 있는듯. 제껀 콘솔창에 표시됨.. ) 다음과 같이 어떤 문제인지 구체적으로 적는다면, 디버그 시간을 단축할수도 있다. 혹은 assert(0) 을 사용하는 대신에, 이런 코드를 사용할수도 있다.
  28. 28. 비법 #2 : 좀더 편하게 - 매크로를 사용하여 비법1을 사용하기 편하게 할 수 있다.
  29. 29. 비법 #3 : 커스텀 assert 만들기 - 표준 C assert 에는 귀찮은 문제가 하나 있다. 디버깅 중 조건이 실패할 때, 소스 파일에 작성한 assert 문이 아니라, assert.c 파일 안의 assert 문으로 유도된다는 점이다. ( 근데 내가 쓸땐 잘만 되는데, 이역시 vs 버전에 따라 다른 것 같음. ) - 오른쪽과 같은 커스텀 assert 를 만들어서 중단점이 소스 파일의 assert 로 유도되도록 할 수 있다.
  30. 30. 비법 #4 : 이것도 중요! - 커스텀 assert 를 만들 때, assert 대화상자에 “다음부터 항상 무시” 옵션을 추가하는 것이 좋다. assert 가 프레임마다 호출되는 등 귀찮은 경우가 있기 때문. 우측은 구현 예의 하나.
  31. 31. 비법 #5 : 초고수만 볼 것 - assert 의 또다른 문제로, 1. assert 가 함수의 내부에 있고 2. 함수가 받은 매개변수가 assert 의 발생 원인이 될 수 있는 경우 디버거를 같이 쓰지 않는 한, 왜 assert 가 발생했는지 쉽게 추적할 수 없다는 것이다. 때문에 거의 디버거를 붙여서 실행하지 않는, 테스터를 통한 도움을 받기 힘들다. 이에 대한 간단한 해결책은, assert 대화상자 안에 스택 상태를 표시하는 것이다. 아니면 assert 가 발생하면 스택의 상태를 클립보드에 복사되게 하거나.
  32. 32. 비법 #5 : 초고수만 볼 것
  33. 33. 끗!
  34. 34. GPG 3.3 A* 길찾기 알고리즘의 기초 NHN NEXT 김승현
  35. 35. A*(에이 스타) 알고리즘 - 정확하면서 효율적인 길찾기 알고리즘의 하나. - 대부분의 상황에서 최적의 수행시간을 제공하며, 몇가지 조건만 갖춘다면 다른 어떤 알고리즘보다도 빠르게 찾을 수 있다.
  36. 36. 동작 방식 - 각 지점(node)에 목표지점까지의 직선거리(h)를 저장한다. 두 가지 경로 목록을 관리한다.( Open list ), ( Closed list ) 시작 지점을 Open list 에 넣으며 시작하며, 아래의 작업을 반복수행하는 것으로 최적의 경로를 구한다. > 시작지점부터 현재 탐색중인 지점까지 지나온 경로의 weight 를 합한 값(g)을 구한다. 시작 지점은 이 값이 0. > Open list 에 있는 지점 중 비용(g+h)이 가장 작은 값을 선택한다. > 선택한 지점을 Closed list 에 추가하며, 인접한 지점을 Open list 에 추가한다. > 목표지점에 도착했다면 지나온 경로를 리턴하며 빠져나온다.
  37. 37. 비용 - 바퀴가 달린 전차는 다른 길보다 도로에서 더 빠를것이다. 수륙 양용의 차라면 바다도 건널 수 있을 것. 시작지점이 평지고 도착지점이 언덕인 경우와 그 반대인 경우는 경로의 비용에 차이가 있을것이다. 이처럼 유닛의 종류나 이동의 양 끝 위치를 인자로 받아야 하는 경우도 있다. - 보드게임처럼 바닥이 격자로 된 경우, 직선거리(h)는 최소로 목표지점까지 이동하는 칸 수가 될 것이다.
  38. 38. 비용 - weighted 그래프의 경우 추정 직선거리(h)가 실제 최단거리보다 커질 수 있다. 이 경우 최단 경로가 정확하지 않을 수 있다. 보다 정확한 최단 경로를 구하기 위해서는 h 를 설정할 때는 직선거리의 값을 가장 weigh가 작은 한 경로와 같은 비율이 되게 해야한다. - 다만 값을 너무 작게 할 경우 올바른 방향으로 가기보다 ‘안가봤던 방향’으로 향하는 경우가 더 많아져 비효율적이 될 수 있다.
  39. 39. 비용
  40. 40. 효율적이고 자연스러운 길찾기를 위한 그래프 - 시작위치와 도착지점에 맞아떨어지는 지점(node)은 존재하지 않을 수 있다. 때문에 그에 대한 지점은 검색이 일어날 때 추가해야 한다. 혹은 각각 가장 인접한 지점을 시작, 도착 지점으로 하던지. - 도달하지 못하는 장소가 없도록 충분히 설정하되, 너무 많아선 안된다. 유닛이 좀더 지능적으로 이동하는 것처럼 보이려면, 되도록 지점을 지그재그로 배치하지 말자.
  41. 41. 그래프를 만드는 다양한 방법
  42. 42. A* 의 약점 - 시작지점에서 목표까지의 경로가 존재하지 않을 경우 모든 지점을 조사하고나서야 그것을 알 수 있다. 이에 대한 타개책으로, 독립된 구역에 대해( 섬 이라든지 ) 같은 구역 내에서만 길찾기를 허용 하는 것도 하나의 방법이다.
  43. 43. 끗!
  44. 44. GPG 4.6 다해상도 맵을 이용한 충돌 판정 NHN NEXT 김승현
  45. 45. 다룰 내용 - 다양한 크기의 많은 객체들이 돌아다니는 게임에서 충돌 판정의 횟수를 줄이기 위한 방법. - 이런 로직을 설계하지 않았다면 O(n^2) 지옥을 볼수있다.
  46. 46. 쉬운 문제부터 접근해보자. - 다양한 크기라는 조건을 뺀다고 가정한다. 동일한 크기의 적당히 큰 격자맵을 둔다. 각 격자는 그 칸에 중점을 두고 있는 객체들을 담은 링크드 리스트를 관리한다. 어떤 객체에 대해 충돌을 검사하는 경우, 그 객체가 속한 칸과 주변 칸의 리스트 안에 담긴 객체들 하고만 검사하면 된다.
  47. 47. 쉬운 문제부터 접근해보자. - 모든 객체들에 서로의 충돌여부를 검사하는 경우. 좌상단의 첫번째 칸부터 진행. 각 칸의 리스트 내 객체를 순회하며 순서대로 모든 객체를 한번씩 선택함. 선택한 객체의 칸과 동쪽, 남동쪽, 남쪽의 세 칸에 대해서만 검사해주면 된다. ( 책에서는 이렇게 말했지만 남서쪽도 확인해줘야 할 듯. ) 그 외의 칸들은 이전에 확인했던 객체를 통해 검사가 끝났기 때문. 이 방법으로 모든 객체들에 대한 충돌검사를 빠르게 처리할 수 있다.
  48. 48. 이제 본 문제. - 객체의 크기가 다양한 경우 이 방법을 그대로 적용하긴 어렵다. ( 객체가 격자 한칸보다 크다거나. ) 물론 격자의 크기를 가장 큰 객체의 크기만큼 늘리면 충돌은 제대로 검사하지만, 그만큼 효율이 떨어진다. - 이에 대한 쉬운 해결책 중 하나는, 객체의 중점을 포함하는 칸 뿐만 아니라, 실제로 객체가 닿는 모든 격자의 리스트에 객체를 추가하는 것이다. 구현은 간단하지만 그리 깔끔한 방법은 아니다.( 계산량이 오히려 많아질수도 있음 )
  49. 49. 다해상도 맵 - 저자가 제시하는 해결책은 다음과 같다. 격자를 하나만 사용하지 않고, 칸의 크기에 차이가 있는 여러개의 격자를 사용. 칸의 크기는 2의 제곱수로 제한( 최적화를 위해 ) 각 객체는 크기에 따라 적절한 격자맵을 선택하고, 중점이 속한 칸의 링크드 리스트에 추가한다. 여기서 적절한 크기란, 객체가 한 격자의 안에 들어갈 수 있는 격자 중 최소인 크기.
  50. 50. 다해상도 맵 - 충돌을 검출하는 방식은 앞서 설명한 방식과 유사하다. 먼저 자신과 주변 칸을 검사한다. 그리고 서로 다른 격자맵을 가진 객체간의 충돌도 검출해야 한다. 이를 위해 객체가 속한 칸이 더 큰 격자맵에서 어떤 격자에 속하는지 구하고, 주변의 더 큰 객체와의 충돌을 검출한다. ( 자신보다 작은 객체가 자신과의 충돌을 검출하기 때문에 ) 더 작은 객체에 대해서는 확인할 필요가 없다.
  51. 51. 다해상도 맵 - 그럼 맵을 몇개나 만들어야 할까? 일단 가장 작은 격자의 크기는 가장 작은 객체를 포함할 수 있는 2의 제곱수여야 한다. 그로부터 한 단계씩 줄여 해상도 1의 맵 전체를 포함하는 격자맵을 만든다면, 어떤 크기의 객체가 추가되어도( 화면 전체를 뒤덮는 폭발 효과 라던지 ) 대처할 수 있다. 격자맵이 많을수록 부하가 심할 것처럼 느낄 수 있겠지만, 한 단계마다 격자의 수는 ¼ 로 줄어들기 때문에 그리 큰 부담이 아니다. 물론 가장 큰 객체가 확실히 정해졌다면 해상도의 하한을 결정할 수 있다. 가장 효율적인 방법은 개발 도중에는 하한을 두지 말고, 완료 직전에 결정하는 것이다.
  52. 52. 코드
  53. 53. 코드
  54. 54. 코드
  55. 55. 끗!

×