게임을 위한 테스트 주도 개발 : 무엇을 ,  왜 그리고 어떻게 ? Noel Llopis Senior Architect High Moon Studios 번역  :  박일  http:// ParkPD.egloos.com ...
<ul><li>1.  테스트 주도 개발 ( TDD) 이란 ? </li></ul><ul><li>2.  우리는  TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><...
<ul><li>1.  테스트 주도 개발 (TDD) 이란 ?   </li></ul><ul><ul><li>( 그리고 어쩌다 이것을 도입하게 되었는가에 대하여 ) </li></ul></ul><ul><li>2.  우리는  TD...
define G(n) int n(int t, int q, int d) #define X(p,t,s) (p>=t&&p<(t+s)&&(p-(t)&1023)<(s&1023)) #define U(m) *((signed char...
 
TDD 는 앞서 언급된 문제들을 해결해주었다
<ul><li>불과 몇 분밖에 걸리지 않는다 . </li></ul>TDD 의 순환 과정 체크 인 체크 인 TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shie...
장점 :  단순함과 모듈화
장점 :  안전망
장점 :  즉각적인 피드백 <ul><li>마일스톤 : ~2 개월 </li></ul><ul><li>주기 : 2~4 주 </li></ul><ul><li>야간 구축 : 1 일 </li></ul><ul><li>자동 구축 : ~...
 
장점 :  문서화
TDD !=  단위 검사 TDD !=  테스팅 전략 TDD ==  개발 기법
<ul><li>1.  테스트 주도 개발 (TDD) 이란 ? </li></ul><ul><li>2.  우리는  TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><l...
캐릭터   +  방패 Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(flo...
3 가지 검사 방법 <ul><li>반환되는 값을 검사하기 </li></ul><ul><li>상태를 검사하기 </li></ul><ul><li>객체간의 상호작용을 검사하기 </li></ul>
반환되는 값 검사 TEST (ShieldCanBeDamagedIfFull) { } 방패 방패 검사하기 bool Damage() 데미지 ? Shield shield; CHECK (shield.Damage()); “ Shi...
상태 검사 TEST (LevelCannotBeDamagedBelowZero) { } 방패 방패 검사하기 Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_...
어디에 검사를 삽입하는 게 좋을까 ? <ul><li>TestGame.exe (Game.lib 와 연결됨 ) </li></ul><ul><li>#ifdef UNIT_TESTS </li></ul><ul><li>GameTest...
검사를 작성하기 <ul><li>새로운 검사를 추가하기 손쉽도록 ,  단위 검사 프레임워크를 사용하라 </li></ul><ul><li>UnitTest++ 는 게임에 꽤 적합하다 </li></ul>
매 빌드마다 검사하기
상호작용 검사 ( 초기에 문제가 될 수 있다 . ) 캐릭터 검사하기 캐릭터 Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character...
class IShield { public: virtual float Damage(float amount) = 0; } class FancyShield : public IShield { public: float Damag...
Mock 을 사용해서 검사하기 캐릭터 검사하기 TEST(CharacterUsesShieldToAbsorbDamage) { } 가짜 방패   MockShield mockShield = new MockShield; mock...
최상의 관행 :  근처의 코드만 검사하기 검사 검사중인 코드 검사 검사중인 코드 하위 시스템   A 하위 시스템  B 하위 시스템  C 고양이가 끄집어낸 어떤 것 부엌의 싱크대 누가 알겠어
최상의 관행 :  간결한 검사 TEST (ShieldStartsAtInitialLevel) { ShieldComponent shield(100); CHECK_EQUAL (100, shield.GetLevel()); } ...
최상의 관행 :  신속한 검사 Slow Test(24 > 20 ms): CheckSpotOverlapIsHandledCorrectly1Test Slow Test(25 > 20 ms): CheckSpotOverlapIsH...
최상의 관행 :  신속한 검사 Debug  상태에서  TestDebugServer 의 단위 검사 실행중 .. 검사  116 번 실행 실패한 검사 없음 .   소요 시간 : 0.016 초 . Debug  상태에서  Tes...
최상의 관행 :  비의존적인 검사 g_CollisionWorldSingleton
<ul><li>1.  테스트 주도 개발 (TDD) 이란 ? </li></ul><ul><li>2.  우리는  TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><l...
콘솔의 경우 , PC 보다는 덜 자주 검사했다 .
 
API  전체를 감싸기 (wrap)
API  상태를 직접 검사하기
API  함수 호출을 제외한 모든 코드를 검사하기
미들웨어까지 포함해서 검사하기 Havok RenderWare Unreal Novodex OpenGL DirectX
기존의 엔진에서  TDD 하기
TDD 를 해보고는 싶지만 ...
<ul><li>1. TDD  란 ? </li></ul><ul><li>2. TDD  사용법 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4.  우리가 얻은 교훈들 </li></ul...
교훈   #1:  고수준의 게임 코드에도  TDD 를 적용할 수 있다 .
예시 :  공격형 인공지능 function TestEnemyChoosesLightAttack() { FightingComp = new(self) class'FightingComponent'; FightingComp.Ad...
예시 :  캐릭터의 행동 TEST_F( CharacterFixture,   SupportedWhenLeapAnimationEndsTransitionsRunning ) { LandingState state(Characte...
구조의 선택
교훈   #2: TDD 와 코드 설계
교훈   #3:  검사 횟수는 프로젝트 진행의 척도가 될 수 있다
교훈   #4: TDD 는 빌드의 안정성을 높여준다
교훈   #5: TDD 는 더 많은 코드를 만들어 낸다
교훈   #6:  개발 속도
교훈   #7: TDD 를   도입하기
교훈   #7: TDD  도입하기 <ul><li>위험할수록 ,  대가가 크다 </li></ul><ul><li>(High risk – High reward) </li></ul>
1.  테스트 주도 개발 (TDD) 이란 ? 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
결론
질문 ? <ul><li>자료 </li></ul><ul><li>Games from Within  http:// www.gamesfromwithin.com   </li></ul><ul><li>위  Site 에는 이 강연의 ...
Upcoming SlideShare
Loading in …5
×

Agile Test Driven Development For Games What, Why, And How

3,106 views

Published on

0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,106
On SlideShare
0
From Embeds
0
Number of Embeds
330
Actions
Shares
0
Downloads
63
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide
  • Agile Test Driven Development For Games What, Why, And How

    1. 1. 게임을 위한 테스트 주도 개발 : 무엇을 , 왜 그리고 어떻게 ? Noel Llopis Senior Architect High Moon Studios 번역 : 박일 http:// ParkPD.egloos.com 도움 : 김기웅 http://betterways.tistory.com/
    2. 2. <ul><li>1. 테스트 주도 개발 ( TDD) 이란 ? </li></ul><ul><li>2. 우리는 TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4. 우리가 얻은 교훈들 </li></ul><ul><li>5. 결론 </li></ul>
    3. 3. <ul><li>1. 테스트 주도 개발 (TDD) 이란 ? </li></ul><ul><ul><li>( 그리고 어쩌다 이것을 도입하게 되었는가에 대하여 ) </li></ul></ul><ul><li>2. 우리는 TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4. 우리가 얻은 교훈들 </li></ul><ul><li>5. 결론 </li></ul>
    4. 4. define G(n) int n(int t, int q, int d) #define X(p,t,s) (p>=t&&p<(t+s)&&(p-(t)&1023)<(s&1023)) #define U(m) *((signed char *)(m)) #define F if(!--q){ #define I(s) (int)main-(int)s #define P(s,c,k) for(h=0; h>>14==0; h+=129)Y(16*c+h/1024+Y(V+36))&128>>(h&7)?U(s+(h&15367))=k:k G (B) { Z; F D = E (Y (V), C = E (Y (V), Y (t + 4) + 3, 4, 0), 2, 0); Y (t + 12) = Y (t + 20) = i; Y (t + 24) = 1; Y (t + 28) = t; Y (t + 16) = 442890; Y (t + 28) = d = E (Y (V), s = D * 8 + 1664, 1, 0); for (p = 0; j < s; j++, p++) U (d + j) = i == D | j < p ? p--, 0 : (n = U (C + 512 + i++)) < ' ' ? p |= n * 56 - 497, 0 : n; } n = Y (Y (t + 4)) & 1; F U (Y (t + 28) + 1536) |= 62 & -n; M U (d + D) = X (D, Y (t + 12) + 26628, 412162) ? X (D, Y (t + 12) + 27653, 410112) ? 31 : 0 : U (d + D); for (; j < 12800; j += 8) P (d + 27653 + Y (t + 12) + ' ' * (j & ~511) + j % 512, U (Y (t + 28) + j / 8 + 64 * Y (t + 20)), 0); } F if (n) { D = Y (t + 28); if (d - 10) U (++Y (t + 24) + D + 1535) = d; else { for (i = D; i < D + 1600; i++) U (i) = U (i + 64); Y (t + 24) = 1; E (Y (V), i - 127, 3, 0); } } else Y (t + 20) += ((d >> 4) ^ (d >> 5)) - 3; } }
    5. 6. TDD 는 앞서 언급된 문제들을 해결해주었다
    6. 7. <ul><li>불과 몇 분밖에 걸리지 않는다 . </li></ul>TDD 의 순환 과정 체크 인 체크 인 TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel()); } Shield::Shield() : m_level (Shield::kMaxLevel) { } 테스트 작성 코드 작성 리팩토링 테스트 실패 테스트 통과 테스트 통과
    7. 8. 장점 : 단순함과 모듈화
    8. 9. 장점 : 안전망
    9. 10. 장점 : 즉각적인 피드백 <ul><li>마일스톤 : ~2 개월 </li></ul><ul><li>주기 : 2~4 주 </li></ul><ul><li>야간 구축 : 1 일 </li></ul><ul><li>자동 구축 : ~1 시간 </li></ul><ul><li>TDD : 30 회 / 3~4 분 </li></ul>
    10. 12. 장점 : 문서화
    11. 13. TDD != 단위 검사 TDD != 테스팅 전략 TDD == 개발 기법
    12. 14. <ul><li>1. 테스트 주도 개발 (TDD) 이란 ? </li></ul><ul><li>2. 우리는 TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4. 우리가 얻은 교훈들 </li></ul><ul><li>5. 결론 </li></ul>
    13. 15. 캐릭터 + 방패 Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(float amount); float GetHealth() const; };
    14. 16. 3 가지 검사 방법 <ul><li>반환되는 값을 검사하기 </li></ul><ul><li>상태를 검사하기 </li></ul><ul><li>객체간의 상호작용을 검사하기 </li></ul>
    15. 17. 반환되는 값 검사 TEST (ShieldCanBeDamagedIfFull) { } 방패 방패 검사하기 bool Damage() 데미지 ? Shield shield; CHECK (shield.Damage()); “ ShieldLevelStartsFull 에서 오류 발생 : 100 을 예상했지만 , 0 이 나옴 . ”
    16. 18. 상태 검사 TEST (LevelCannotBeDamagedBelowZero) { } 방패 방패 검사하기 Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
    17. 19. 어디에 검사를 삽입하는 게 좋을까 ? <ul><li>TestGame.exe (Game.lib 와 연결됨 ) </li></ul><ul><li>#ifdef UNIT_TESTS </li></ul><ul><li>GameTests.DLL </li></ul><ul><li>GameTests.upk </li></ul>
    18. 20. 검사를 작성하기 <ul><li>새로운 검사를 추가하기 손쉽도록 , 단위 검사 프레임워크를 사용하라 </li></ul><ul><li>UnitTest++ 는 게임에 꽤 적합하다 </li></ul>
    19. 21. 매 빌드마다 검사하기
    20. 22. 상호작용 검사 ( 초기에 문제가 될 수 있다 . ) 캐릭터 검사하기 캐릭터 Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? 방패 GetHealth() 화려한 방패
    21. 23. class IShield { public: virtual float Damage(float amount) = 0; } class FancyShield : public IShield { public: float Damage(float amount) { … }; } class MockShield : public IShield { public: float damagePassedIn; float damageToReturn; float Damage(float amount) { damagePassedIn = amount; return damageToReturn; } } 가짜 객체가 , 검사 대상인 해당 단위 (unit) 의 외부에 위치한 객체를 대신한다 .
    22. 24. Mock 을 사용해서 검사하기 캐릭터 검사하기 TEST(CharacterUsesShieldToAbsorbDamage) { } 가짜 방패 MockShield mockShield = new MockShield; mockShield->damageToReturn = 10; Character character(400, mockShield); character.Damage(200); CHECK_EQUAL(200, mockShield->damagePassedIn); CHECK_EQUAL(390, character.GetHealth()); 캐릭터 Damage() *m_shield 매개 변수들은 정확한가 ? GetHealth() 반환된 데미지가 정확하게 사용되는가 ?
    23. 25. 최상의 관행 : 근처의 코드만 검사하기 검사 검사중인 코드 검사 검사중인 코드 하위 시스템 A 하위 시스템 B 하위 시스템 C 고양이가 끄집어낸 어떤 것 부엌의 싱크대 누가 알겠어
    24. 26. 최상의 관행 : 간결한 검사 TEST (ShieldStartsAtInitialLevel) { ShieldComponent shield(100); CHECK_EQUAL (100, shield.GetLevel()); } TEST (ShieldTakesDamage) { ShieldComponent shield(100); shield.Damage(30); CHECK_EQUAL (70, shield.GetLevel()); } TEST (LevelCannotDropBelowZero) { ShieldComponent shield(100); shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); } TEST(ActorDoesntMoveIfPelvisBodyIsInSamePositionAsPelvisAnim) { component = ConstructObject<UAmpPhysicallyDrivableSkeletalComponent>(); component->physicalPelvisHandle = NULL; component->SetOwner(owner); component->SkeletalMesh = skelMesh; component->Animations = CreateReadable2BoneAnimSequenceForAmpRagdollGetup(component, skelMesh, 10.0f, 0.0f); component->PhysicsAsset = physicsAsset; component->SpaceBases.AddZeroed(2); component->InitComponentRBPhys(false); component->LocalToWorld = FMatrix::Identity; const FVector actorPos(100,200,300); const FVector pelvisBodyPositionWS(100,200,380); const FTranslationMatrix actorToWorld(actorPos); owner->Location = actorPos; component->ConditionalUpdateTransform(actorToWorld); INT pelvisIndex = physicsAsset->CreateNewBody(TEXT(&quot;Bone1&quot;)); URB_BodySetup* pelvisSetup = physicsAsset->BodySetup(pelvisIndex); FPhysAssetCreateParams params = GetGenericCreateParamsForAmpRagdollGetup(); physicsAsset->CreateCollisionFromBone( pelvisSetup, skelMesh, 1, params, boneThings); URB_BodyInstance* pelvisBody = component->PhysicsAssetInstance->Bodies(0); NxActor* pelvisNxActor = pelvisBody->GetNxActor(); SetRigidBodyPositionWSForAmpRagdollGetup(*pelvisNxActor, pelvisBodyPositionWS); component->UpdateSkelPose(0.016f); component->RetransformActorToMatchCurrrentRoot(TransformManipulator()); const float kTolerance(0.002f); FMatrix expectedActorMatrix; expectedActorMatrix.SetIdentity(); expectedActorMatrix.M[3][0] = actorPos.X; expectedActorMatrix.M[3][1] = actorPos.Y; expectedActorMatrix.M[3][2] = actorPos.Z; const FMatrix actorMatrix = owner->LocalToWorld(); CHECK_ARRAY2D_CLOSE(expectedActorMatrix.M, actorMatrix.M, 4, 4, kTolerance); }
    25. 27. 최상의 관행 : 신속한 검사 Slow Test(24 > 20 ms): CheckSpotOverlapIsHandledCorrectly1Test Slow Test(25 > 20 ms): CheckSpotOverlapIsHandledCorrectly2Test Slow Test(24 > 20 ms): DeleteWaveEventFailsIfEventDoesntExistInCueTest Slow Test(22 > 20 ms): CanGetObjectsInBrowserListPackageTest Slow Test(48 > 20 ms): HmAddActorCallsCreateActorTest Slow Test(74 > 20 ms): HmReplaceActorDoesNothingIfEmptySelectionTest Slow Test(57 > 20 ms): HmReplaceActorWorksIfTwoActorsSelectedTest Slow Test(26 > 20 ms): ThrowExceptionWhenTrackIndexOutOfRangeTest 1923 회 검사의 총소요 시간 : 4.83 초 . 26 회의 느린 검사에 소요된 시간 : 2.54 초 .
    26. 28. 최상의 관행 : 신속한 검사 Debug 상태에서 TestDebugServer 의 단위 검사 실행중 .. 검사 116 번 실행 실패한 검사 없음 . 소요 시간 : 0.016 초 . Debug 상태에서 TestStreams 의 단위 검사 실행중 ... 검사 138 번 실행 실패한 검사 없음 . 소요 시간 : 0.015 초 . Debug 상태에서 TestMath 의 단위 검사 실행중 ... 검사 245 번 실행 실패한 검사 없음 . 소요 시간 : 0.001 초 . 단위 검사 실행중 ... 검사 184 번 실행 실패한 검사 없음 . 소요 시간 : 0.359 초 .
    27. 29. 최상의 관행 : 비의존적인 검사 g_CollisionWorldSingleton
    28. 30. <ul><li>1. 테스트 주도 개발 (TDD) 이란 ? </li></ul><ul><li>2. 우리는 TDD 를 어떻게 사용했는가 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4. 우리가 얻은 교훈들 </li></ul><ul><li>5. 결론 </li></ul>
    29. 31. 콘솔의 경우 , PC 보다는 덜 자주 검사했다 .
    30. 33. API 전체를 감싸기 (wrap)
    31. 34. API 상태를 직접 검사하기
    32. 35. API 함수 호출을 제외한 모든 코드를 검사하기
    33. 36. 미들웨어까지 포함해서 검사하기 Havok RenderWare Unreal Novodex OpenGL DirectX
    34. 37. 기존의 엔진에서 TDD 하기
    35. 38. TDD 를 해보고는 싶지만 ...
    36. 39. <ul><li>1. TDD 란 ? </li></ul><ul><li>2. TDD 사용법 </li></ul><ul><li>3. TDD 와 게임 </li></ul><ul><li>4. 우리가 얻은 교훈들 </li></ul><ul><li>5. 결론 </li></ul>
    37. 40. 교훈 #1: 고수준의 게임 코드에도 TDD 를 적용할 수 있다 .
    38. 41. 예시 : 공격형 인공지능 function TestEnemyChoosesLightAttack() { FightingComp = new(self) class'FightingComponent'; FightingComp.AddAttack(LightAttack); FightingComp.AddAttack(HeavyAttack); enemy.AttachComponent(FightingComp); enemy.FightingComponent = FightingComp; enemy.FindPlayerPawn = MockFindPlayerPawn; enemy.ShouldMeleeAttack = MockShouldAttack; ShouldMeleeAttackReturn = true; enemy.Tick(0.666); CheckObjectsEqual(LightAttack, FightingComp.GetCurrentAttack()); }
    39. 42. 예시 : 캐릭터의 행동 TEST_F( CharacterFixture, SupportedWhenLeapAnimationEndsTransitionsRunning ) { LandingState state(CharacterStateParameters(&character), AnimationIndex::LeapLanding); state.Enter(input); input.deltaTime = character.GetAnimationDuration( AnimationIndex::LeapLanding ) + kEpsilon; character.supported = true; CharacterStateOutput output = state.Update( input ); CHECK_EQUAL(std::string(&quot;TransitionState&quot;), output.nextState->GetClassInfo().GetName()); const TransitionState& transition = *output.nextState; CHECK_EQUAL(std::string(&quot;RunningState&quot;), transition.endState->GetClassInfo().GetName()); }
    40. 43. 구조의 선택
    41. 44. 교훈 #2: TDD 와 코드 설계
    42. 45. 교훈 #3: 검사 횟수는 프로젝트 진행의 척도가 될 수 있다
    43. 46. 교훈 #4: TDD 는 빌드의 안정성을 높여준다
    44. 47. 교훈 #5: TDD 는 더 많은 코드를 만들어 낸다
    45. 48. 교훈 #6: 개발 속도
    46. 49. 교훈 #7: TDD 를 도입하기
    47. 50. 교훈 #7: TDD 도입하기 <ul><li>위험할수록 , 대가가 크다 </li></ul><ul><li>(High risk – High reward) </li></ul>
    48. 51. 1. 테스트 주도 개발 (TDD) 이란 ? 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
    49. 52. 결론
    50. 53. 질문 ? <ul><li>자료 </li></ul><ul><li>Games from Within http:// www.gamesfromwithin.com </li></ul><ul><li>위 Site 에는 이 강연의 보다 자세한 발표 자료와 TDD 및 UnitTest++ 프레임워크에 대한 자료가 있다 . </li></ul><ul><li>[ 역자주 ] </li></ul><ul><li>http://andstudy.com/andwiki/wiki.php/BackwardsIsForward </li></ul><ul><li>이 내용에 대한 Note 가 번역된 페이지입니다 . 참고하세요 . </li></ul>Noel Llopis - [email_address]

    ×