Upcoming SlideShare
×

# Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006)[200611] Korean

2,041 views

Published on

0 Likes
Statistics
Notes
• Full Name
Comment goes here.

Are you sure you want to Yes No
• Be the first to comment

• Be the first to like this

Views
Total views
2,041
On SlideShare
0
From Embeds
0
Number of Embeds
42
Actions
Shares
0
76
0
Likes
0
Embeds 0
No embeds

No notes for slide
• ### Agd Test Driven Development For Games What, Why, And How)(Game Connect 2006)[200611] Korean

1. 1. 테스트 주도 개발 : 무엇을 왜 , 어떻게 ? Noel Llopis Senior Architect High Moon Studios 번역 : 박일 http://ParkPD.egloos.com [email_address]
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 주기 체크인 Check in TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel()); } Shield::Shield() : m_level (Shield::kMaxLevel) { } 테스트 작성 코드 작성 리펙토링 테스트 실패 테스트 통과 테스트 통과
7. 8. 좋은 점 : 단순함 , 모듈화
8. 9. 좋은 점 : 안전망
9. 10. 좋은 점 : Instant feedback <ul><li>마일스톤 : ~2 개월 </li></ul><ul><li>주기 : 2-4 주 </li></ul><ul><li>Nightly 빌드 : 1 일 </li></ul><ul><li>자동 빌드 : ~1 시간 </li></ul><ul><li>TDD: 3-4 분간에 30 여번 </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) { } Shield Test Shield bool Damage() Damage? Shield shield; CHECK (shield.Damage()); “ Failure in ShieldLevelStartsFull: Expected 100 but got 0”
16. 18. 상태 검사 TEST (LevelCannotBeDamagedBelowZero) { } Shield Test Shield Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
17. 19. 어디에 테스트를 둘까 ? <ul><li>TestGame.exe (links with 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. 테스트 상호작용 ( 문제가 될 수 있는 부분 ) Test Character Character Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? Shield GetHealth() Fancy Shield
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; } } A mock object stands in for an object outside the unit you're testing
22. 24. 테스트에 Mock 쓰기 Test Character TEST(CharacterUsesShieldToAbsorbDamage) { } MockShield MockShield mockShield = new MockShield; mockShield->damageToReturn = 10; Character character(400, mockShield); character.Damage(200); CHECK_EQUAL(200, mockShield->damagePassedIn); CHECK_EQUAL(390, character.GetHealth()); Character Damage() *m_shield Parameters correct? GetHealth() Returned damage correctly used?
23. 25. 실천법 : 가까이 있는 코드만 테스트하기 Test Code under test Test Code under test Subsystem A Subsystem B Subsystem C Something the cat dragged in The kitchen sink Who knows
24. 26. Best Practices: Keep Tests Simple 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 Total time spent in 1923 tests: 4.83 seconds. Time spent in 26 slow tests: 2.54 seconds.
26. 28. 실천법 : 테스트를 빠르게 유지하기 Running unit tests TestDebugServer in Debug... 116 tests run There were no test failures. Test time: 0.016 seconds. Running unit tests for TestStreams in Debug... 138 tests run There were no test failures. Test time: 0.015 seconds. Running unit tests TestMath in Debug... 245 tests run There were no test failures. Test time: 0.001 seconds. Running unit tests... 184 tests run There were no test failures. Test time: 0.359 seconds.
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. 콘솔에서는 테스트를 덜 자주 돌렸습니다 .
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>High risk – High reward </li></ul>
48. 51. 1. TDD 란 ? 2. TDD 쓰는 법 3. TDD 와 게임 4. 배운 점 5. 결론
49. 52. 결론
50. 53. 질문 ? <ul><li>Resources </li></ul><ul><li>Games from Within http://www.gamesfromwithin.com </li></ul><ul><li>Includes paper for this presentation with more details and links to other TDD resources and the UnitTest++ framework. </li></ul>Noel Llopis - [email_address]