SlideShare a Scribd company logo
게임을 위한 테스트 주도 개발 : 무엇을 ,  왜 그리고 어떻게 ? Noel Llopis Senior Architect High Moon Studios 번역  :  박일  http:// ParkPD.egloos.com 도움  :  김기웅  http://betterways.tistory.com/
1.  테스트 주도 개발 ( TDD) 이란 ? 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
1.  테스트 주도 개발 (TDD) 이란 ?   ( 그리고 어쩌다 이것을 도입하게 되었는가에 대하여 ) 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
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; } }
 
TDD 는 앞서 언급된 문제들을 해결해주었다
불과 몇 분밖에 걸리지 않는다 . TDD 의 순환 과정 체크 인 체크 인 TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel()); } Shield::Shield() : m_level (Shield::kMaxLevel) { } 테스트 작성 코드 작성 리팩토링 테스트 실패 테스트 통과 테스트 통과
장점 :  단순함과 모듈화
장점 :  안전망
장점 :  즉각적인 피드백 마일스톤 : ~2 개월 주기 : 2~4 주 야간 구축 : 1 일 자동 구축 : ~1 시간 TDD : 30 회  /  3~4 분
 
장점 :  문서화
TDD !=  단위 검사 TDD !=  테스팅 전략 TDD ==  개발 기법
1.  테스트 주도 개발 (TDD) 이란 ? 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
캐릭터   +  방패 Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(float amount); float GetHealth() const; };
3 가지 검사 방법 반환되는 값을 검사하기 상태를 검사하기 객체간의 상호작용을 검사하기
반환되는 값 검사 TEST (ShieldCanBeDamagedIfFull) { } 방패 방패 검사하기 bool Damage() 데미지 ? Shield shield; CHECK (shield.Damage()); “ ShieldLevelStartsFull 에서 오류 발생 : 100 을 예상했지만 , 0 이 나옴 . ”
상태 검사 TEST (LevelCannotBeDamagedBelowZero) { } 방패 방패 검사하기 Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
어디에 검사를 삽입하는 게 좋을까 ? TestGame.exe (Game.lib 와 연결됨 ) #ifdef UNIT_TESTS GameTests.DLL GameTests.upk
검사를 작성하기 새로운 검사를 추가하기 손쉽도록 ,  단위 검사 프레임워크를 사용하라 UnitTest++ 는 게임에 꽤 적합하다
매 빌드마다 검사하기
상호작용 검사 ( 초기에 문제가 될 수 있다 . ) 캐릭터 검사하기 캐릭터 Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? 방패 GetHealth() 화려한 방패
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) 의 외부에 위치한 객체를 대신한다 .
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() 반환된 데미지가 정확하게 사용되는가 ?
최상의 관행 :  근처의 코드만 검사하기 검사 검사중인 코드 검사 검사중인 코드 하위 시스템   A 하위 시스템  B 하위 시스템  C 고양이가 끄집어낸 어떤 것 부엌의 싱크대 누가 알겠어
최상의 관행 :  간결한 검사 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); }
최상의 관행 :  신속한 검사 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 초 .
최상의 관행 :  신속한 검사 Debug  상태에서  TestDebugServer 의 단위 검사 실행중 .. 검사  116 번 실행 실패한 검사 없음 .   소요 시간 : 0.016 초 . Debug  상태에서  TestStreams 의 단위 검사 실행중 ... 검사  138 번 실행 실패한 검사 없음 .   소요 시간 : 0.015 초 . Debug  상태에서  TestMath 의 단위 검사 실행중 ... 검사  245 번 실행 실패한 검사 없음 .   소요 시간 : 0.001 초 . 단위 검사 실행중 ... 검사  184 번 실행 실패한 검사 없음 .   소요 시간 : 0.359 초 .
최상의 관행 :  비의존적인 검사 g_CollisionWorldSingleton
1.  테스트 주도 개발 (TDD) 이란 ? 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
콘솔의 경우 , PC 보다는 덜 자주 검사했다 .
 
API  전체를 감싸기 (wrap)
API  상태를 직접 검사하기
API  함수 호출을 제외한 모든 코드를 검사하기
미들웨어까지 포함해서 검사하기 Havok RenderWare Unreal Novodex OpenGL DirectX
기존의 엔진에서  TDD 하기
TDD 를 해보고는 싶지만 ...
1. TDD  란 ? 2. TDD  사용법 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
교훈   #1:  고수준의 게임 코드에도  TDD 를 적용할 수 있다 .
예시 :  공격형 인공지능 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()); }
예시 :  캐릭터의 행동 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()); }
구조의 선택
교훈   #2: TDD 와 코드 설계
교훈   #3:  검사 횟수는 프로젝트 진행의 척도가 될 수 있다
교훈   #4: TDD 는 빌드의 안정성을 높여준다
교훈   #5: TDD 는 더 많은 코드를 만들어 낸다
교훈   #6:  개발 속도
교훈   #7: TDD 를   도입하기
교훈   #7: TDD  도입하기 위험할수록 ,  대가가 크다 (High risk – High reward)
1.  테스트 주도 개발 (TDD) 이란 ? 2.  우리는  TDD 를 어떻게 사용했는가 3. TDD 와 게임 4.  우리가 얻은 교훈들 5.  결론
결론
질문 ? 자료 Games from Within  http:// www.gamesfromwithin.com   위  Site 에는 이 강연의 보다 자세한 발표 자료와  TDD  및  UnitTest++  프레임워크에 대한 자료가 있다 . [ 역자주 ] http://andstudy.com/andwiki/wiki.php/BackwardsIsForward 이 내용에 대한  Note  가 번역된 페이지입니다 .  참고하세요 . Noel Llopis -  [email_address]

More Related Content

PPT
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
PDF
구글테스트
PDF
KGC2010 - 낡은 코드에 단위테스트 넣기
PDF
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
PDF
C++ Concurrency in Action 9-2 Interrupting threads
PPTX
Tdd 4장
PPTX
10장 결과 검증
PDF
C++ 프로젝트에 단위 테스트 도입하기
Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006...
구글테스트
KGC2010 - 낡은 코드에 단위테스트 넣기
테스트 가능한 소프트웨어 설계와 TDD작성 패턴 (Testable design and TDD)
C++ Concurrency in Action 9-2 Interrupting threads
Tdd 4장
10장 결과 검증
C++ 프로젝트에 단위 테스트 도입하기

What's hot (13)

PDF
xUnitTestPattern/chapter7
PDF
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
PDF
시작하자 단위테스트
PPTX
C++ 프로젝트에 단위 테스트 도입하기
PDF
Effective unit testing ch3. 테스트더블
PDF
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
PDF
Spring statemachine
PPT
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
PPTX
[2011 04 11]mock_object 소개
PPTX
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
PDF
인수테스트 주도 개발
PPTX
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
PPTX
스프링프레임워크 & 마이바티스 무.료 강의자료 제공 (Spring IoC & DI)_ 구로자바학원/구로오라클학원/구로IT학원
xUnitTestPattern/chapter7
[WELC] 22. I Need to Change a Monster Method and I Can’t Write Tests for It
시작하자 단위테스트
C++ 프로젝트에 단위 테스트 도입하기
Effective unit testing ch3. 테스트더블
사례를 통해 살펴보는 프로파일링과 최적화 NDC2013
Spring statemachine
GCGC- CGCII 서버 엔진에 적용된 기술 (3) - Exception
[2011 04 11]mock_object 소개
[C++ Korea 2nd Seminar] Ranges for The Cpp Standard Library
인수테스트 주도 개발
온라인 게임에서 사례로 살펴보는 디버깅 in NDC2010
스프링프레임워크 & 마이바티스 무.료 강의자료 제공 (Spring IoC & DI)_ 구로자바학원/구로오라클학원/구로IT학원
Ad

Similar to Agile Test Driven Development For Games What, Why, And How (20)

PDF
TDD&Refactoring Day 03: TDD
PPTX
Droid knights android test @Droid Knights 2018
PDF
NDC11_슈퍼클래스
PDF
한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha
PPTX
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
PDF
어플 개발자의 서버개발 삽질기
PDF
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
PDF
Legacy code refactoring video rental system
PDF
Spring Boot 2
PPTX
Clean code
PDF
TDD.JUnit.조금더.알기
PPTX
120908 레거시코드활용전략 4장5장
PDF
자바 테스트 자동화
PDF
[NDC2016] TERA 서버의 Modern C++ 활용기
PPTX
공유 Jdk 7-2-project coin
PPT
NDC11_김성익_슈퍼클래스
PPTX
불어오는 변화의 바람, From c++98 to c++11, 14
PDF
이산수학07
PPT
프로그램은 왜 실패 하는가
PDF
이산수학 C1 프로젝트 7
TDD&Refactoring Day 03: TDD
Droid knights android test @Droid Knights 2018
NDC11_슈퍼클래스
한국 커뮤니티 데이 트랙2, 세션2 JavaScript 성능향상과 Sencha
온라인 게임에서 사례로 살펴보는 디버깅 in NDC10
어플 개발자의 서버개발 삽질기
[D2 오픈세미나]5.robolectric 안드로이드 테스팅
Legacy code refactoring video rental system
Spring Boot 2
Clean code
TDD.JUnit.조금더.알기
120908 레거시코드활용전략 4장5장
자바 테스트 자동화
[NDC2016] TERA 서버의 Modern C++ 활용기
공유 Jdk 7-2-project coin
NDC11_김성익_슈퍼클래스
불어오는 변화의 바람, From c++98 to c++11, 14
이산수학07
프로그램은 왜 실패 하는가
이산수학 C1 프로젝트 7
Ad

More from Ryan Park (20)

PPTX
위대한 게임개발팀의 공통점
PPTX
Domain Driven Design Ch7
PPTX
Taocp1 2 4
PPTX
즉흥연기와프로그래밍
PDF
Oop design principle SOLID
PPTX
OOP 설계 원칙 S.O.L.I.D.
PPTX
Unicode 이해하기
PDF
Unicode100
PPTX
Unicode
PPTX
Unicode
PPTX
Unicode
PDF
Oop design principle
PPTX
나도기술서번역한번해볼까 in NDC10
PPTX
나도(기술서)번역한번해볼까
PPTX
Programming Game AI by Example. Ch7. Raven
PPTX
AIbyExample - Ch7 raven. version 0.8
PPT
카사 공개세미나1회 W.E.L.C.
PPTX
온라인 게임에서 사례로 살펴보는 디버깅
PPTX
프로그램은 왜 실패하는가 1장
PPT
Working Effectively With Legacy Code - xp2005
위대한 게임개발팀의 공통점
Domain Driven Design Ch7
Taocp1 2 4
즉흥연기와프로그래밍
Oop design principle SOLID
OOP 설계 원칙 S.O.L.I.D.
Unicode 이해하기
Unicode100
Unicode
Unicode
Unicode
Oop design principle
나도기술서번역한번해볼까 in NDC10
나도(기술서)번역한번해볼까
Programming Game AI by Example. Ch7. Raven
AIbyExample - Ch7 raven. version 0.8
카사 공개세미나1회 W.E.L.C.
온라인 게임에서 사례로 살펴보는 디버깅
프로그램은 왜 실패하는가 1장
Working Effectively With Legacy Code - xp2005

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

  • 1. 게임을 위한 테스트 주도 개발 : 무엇을 , 왜 그리고 어떻게 ? Noel Llopis Senior Architect High Moon Studios 번역 : 박일 http:// ParkPD.egloos.com 도움 : 김기웅 http://betterways.tistory.com/
  • 2. 1. 테스트 주도 개발 ( TDD) 이란 ? 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 3. 1. 테스트 주도 개발 (TDD) 이란 ? ( 그리고 어쩌다 이것을 도입하게 되었는가에 대하여 ) 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 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 는 앞서 언급된 문제들을 해결해주었다
  • 7. 불과 몇 분밖에 걸리지 않는다 . TDD 의 순환 과정 체크 인 체크 인 TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel()); } Shield::Shield() : m_level (Shield::kMaxLevel) { } 테스트 작성 코드 작성 리팩토링 테스트 실패 테스트 통과 테스트 통과
  • 8. 장점 : 단순함과 모듈화
  • 9. 장점 : 안전망
  • 10. 장점 : 즉각적인 피드백 마일스톤 : ~2 개월 주기 : 2~4 주 야간 구축 : 1 일 자동 구축 : ~1 시간 TDD : 30 회 / 3~4 분
  • 11.  
  • 12. 장점 : 문서화
  • 13. TDD != 단위 검사 TDD != 테스팅 전략 TDD == 개발 기법
  • 14. 1. 테스트 주도 개발 (TDD) 이란 ? 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 15. 캐릭터 + 방패 Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(float amount); float GetHealth() const; };
  • 16. 3 가지 검사 방법 반환되는 값을 검사하기 상태를 검사하기 객체간의 상호작용을 검사하기
  • 17. 반환되는 값 검사 TEST (ShieldCanBeDamagedIfFull) { } 방패 방패 검사하기 bool Damage() 데미지 ? Shield shield; CHECK (shield.Damage()); “ ShieldLevelStartsFull 에서 오류 발생 : 100 을 예상했지만 , 0 이 나옴 . ”
  • 18. 상태 검사 TEST (LevelCannotBeDamagedBelowZero) { } 방패 방패 검사하기 Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
  • 19. 어디에 검사를 삽입하는 게 좋을까 ? TestGame.exe (Game.lib 와 연결됨 ) #ifdef UNIT_TESTS GameTests.DLL GameTests.upk
  • 20. 검사를 작성하기 새로운 검사를 추가하기 손쉽도록 , 단위 검사 프레임워크를 사용하라 UnitTest++ 는 게임에 꽤 적합하다
  • 22. 상호작용 검사 ( 초기에 문제가 될 수 있다 . ) 캐릭터 검사하기 캐릭터 Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? 방패 GetHealth() 화려한 방패
  • 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) 의 외부에 위치한 객체를 대신한다 .
  • 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() 반환된 데미지가 정확하게 사용되는가 ?
  • 25. 최상의 관행 : 근처의 코드만 검사하기 검사 검사중인 코드 검사 검사중인 코드 하위 시스템 A 하위 시스템 B 하위 시스템 C 고양이가 끄집어낸 어떤 것 부엌의 싱크대 누가 알겠어
  • 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); }
  • 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 초 .
  • 28. 최상의 관행 : 신속한 검사 Debug 상태에서 TestDebugServer 의 단위 검사 실행중 .. 검사 116 번 실행 실패한 검사 없음 . 소요 시간 : 0.016 초 . Debug 상태에서 TestStreams 의 단위 검사 실행중 ... 검사 138 번 실행 실패한 검사 없음 . 소요 시간 : 0.015 초 . Debug 상태에서 TestMath 의 단위 검사 실행중 ... 검사 245 번 실행 실패한 검사 없음 . 소요 시간 : 0.001 초 . 단위 검사 실행중 ... 검사 184 번 실행 실패한 검사 없음 . 소요 시간 : 0.359 초 .
  • 29. 최상의 관행 : 비의존적인 검사 g_CollisionWorldSingleton
  • 30. 1. 테스트 주도 개발 (TDD) 이란 ? 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 31. 콘솔의 경우 , PC 보다는 덜 자주 검사했다 .
  • 32.  
  • 33. API 전체를 감싸기 (wrap)
  • 34. API 상태를 직접 검사하기
  • 35. API 함수 호출을 제외한 모든 코드를 검사하기
  • 36. 미들웨어까지 포함해서 검사하기 Havok RenderWare Unreal Novodex OpenGL DirectX
  • 38. TDD 를 해보고는 싶지만 ...
  • 39. 1. TDD 란 ? 2. TDD 사용법 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 40. 교훈 #1: 고수준의 게임 코드에도 TDD 를 적용할 수 있다 .
  • 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()); }
  • 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()); }
  • 44. 교훈 #2: TDD 와 코드 설계
  • 45. 교훈 #3: 검사 횟수는 프로젝트 진행의 척도가 될 수 있다
  • 46. 교훈 #4: TDD 는 빌드의 안정성을 높여준다
  • 47. 교훈 #5: TDD 는 더 많은 코드를 만들어 낸다
  • 48. 교훈 #6: 개발 속도
  • 49. 교훈 #7: TDD 를 도입하기
  • 50. 교훈 #7: TDD 도입하기 위험할수록 , 대가가 크다 (High risk – High reward)
  • 51. 1. 테스트 주도 개발 (TDD) 이란 ? 2. 우리는 TDD 를 어떻게 사용했는가 3. TDD 와 게임 4. 우리가 얻은 교훈들 5. 결론
  • 53. 질문 ? 자료 Games from Within http:// www.gamesfromwithin.com 위 Site 에는 이 강연의 보다 자세한 발표 자료와 TDD 및 UnitTest++ 프레임워크에 대한 자료가 있다 . [ 역자주 ] http://andstudy.com/andwiki/wiki.php/BackwardsIsForward 이 내용에 대한 Note 가 번역된 페이지입니다 . 참고하세요 . Noel Llopis - [email_address]