SlideShare une entreprise Scribd logo
1  sur  53
Test Driven Development For Games: What, Why, And How Noel Llopis Senior Architect High Moon Studios
[object Object],[object Object],[object Object],[object Object],[object Object]
[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]
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 addresses those problems
[object Object],TDD cycle Check in Check in TEST (ShieldLevelStartsFull) { Shield shield; CHECK_EQUAL (Shield::kMaxLevel, shield.GetLevel()); } Shield::Shield() : m_level (Shield::kMaxLevel) { } Write test Write code Refactor Failing tests Passing tests Passing tests
Benefits: Simplicity, modularity
Benefits: Safety net
Benefits: Instant feedback ,[object Object],[object Object],[object Object],[object Object],[object Object]
 
Benefits:  Documentation
TDD != Unit tests TDD != Testing strategy TDD == Development technique
[object Object],[object Object],[object Object],[object Object],[object Object]
Character + Shield Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(float amount); float GetHealth() const; };
Three Ways to Test ,[object Object],[object Object],[object Object]
Testing Return Values TEST (ShieldCanBeDamagedIfFull) { } Shield Test Shield bool Damage() Damage? Shield shield; CHECK (shield.Damage()); “ Failure in ShieldLevelStartsFull: Expected 100 but got 0”
Testing State TEST (LevelCannotBeDamagedBelowZero) { } Shield Test Shield Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
Where do we put tests? ,[object Object],[object Object],[object Object],[object Object]
Writing Tests ,[object Object],[object Object]
Run Tests With Every Build
Testing Interaction (Can Be A Problem Initially) Test Character Character Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? Shield GetHealth() Fancy Shield
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
Using a Mock in Your Test 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?
Best Practices: Test only the code at hand Test Code under test Test Code under test Subsystem A Subsystem B Subsystem C Something the cat dragged in The kitchen sink Who knows
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); }
Best Practices: Keep Tests Fast 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.
Best Practices: Keep Tests Fast 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.
Best Practices: Keep Tests Independent g_CollisionWorldSingleton
[object Object],[object Object],[object Object],[object Object],[object Object]
Run the tests on consoles... less often
 
Wrap full API
Test API state directly
Test all code except for API calls
Testing With Middleware Havok RenderWare Unreal Novodex OpenGL DirectX
TDD With An Existing Engine
I'd like to use TDD but...
[object Object],[object Object],[object Object],[object Object],[object Object]
Lesson #1: TDD can be used for high-level game code
Fighting AI example 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()); }
Character behavior example 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()); }
Choice of architecture
Lesson #2: TDD and code design
Lesson #3: Number of tests as a measure of progress
Lesson #4: TDD improves build stability
Lesson #5: TDD creates more code
Lesson #6: Development speed
Lesson #7: Adopting TDD
Lesson #7: Adopting TDD ,[object Object]
1. What is TDD? 2. How We Use TDD 3. TDD and Games 4. Lessons Learned 5. Wrap Up
Conclusion
Questions? ,[object Object],[object Object],[object Object],Noel Llopis -  [email_address]

Contenu connexe

Plus de Kay Kim

아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]
아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]
아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]Kay Kim
 
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob PardoKay Kim
 
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...Kay Kim
 
Nutch Homepage Search Engine
Nutch Homepage Search EngineNutch Homepage Search Engine
Nutch Homepage Search EngineKay Kim
 
Hadoop Overview 1
Hadoop Overview 1Hadoop Overview 1
Hadoop Overview 1Kay Kim
 
Google App Engine - Overview #2
Google App Engine - Overview #2Google App Engine - Overview #2
Google App Engine - Overview #2Kay Kim
 
Hadoop Overview 2
Hadoop Overview 2Hadoop Overview 2
Hadoop Overview 2Kay Kim
 
Google App Engine - Overview #1
Google App Engine - Overview #1Google App Engine - Overview #1
Google App Engine - Overview #1Kay Kim
 
Google App Engine - Overview #3
Google App Engine - Overview #3Google App Engine - Overview #3
Google App Engine - Overview #3Kay Kim
 
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)Kay Kim
 
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]Outsourcing: Best Practices at Pandemic Studios [GDC 2008]
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]Kay Kim
 
애자일 게임 개발이란?
애자일 게임 개발이란?애자일 게임 개발이란?
애자일 게임 개발이란?Kay Kim
 
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)애자일 게임 개발: 최전선의 이야기(Gamefest 2006)
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)Kay Kim
 
Agile의 의미와 Agile 계획 수립(Gdc2007)
Agile의 의미와 Agile 계획 수립(Gdc2007)Agile의 의미와 Agile 계획 수립(Gdc2007)
Agile의 의미와 Agile 계획 수립(Gdc2007)Kay Kim
 
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]Kay Kim
 
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...Kay Kim
 
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)Kay Kim
 
애자일 게임 개발(Agile Game Development) - GDC2007
애자일 게임 개발(Agile Game Development) - GDC2007애자일 게임 개발(Agile Game Development) - GDC2007
애자일 게임 개발(Agile Game Development) - GDC2007Kay Kim
 

Plus de Kay Kim (18)

아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]
아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]
아티스트, 기획자 및 관리자들을 위한 '외주: 최상의 실천법들' [GDC2008]
 
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo
교전 수칙: 멀티플레이어 게임 기획에 대한 Blizzard의 접근법 [GDC2008] by Rob Pardo
 
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...
GDC Austin 2009-Final Fantasy XI-Problems And Solutions In A Global Community...
 
Nutch Homepage Search Engine
Nutch Homepage Search EngineNutch Homepage Search Engine
Nutch Homepage Search Engine
 
Hadoop Overview 1
Hadoop Overview 1Hadoop Overview 1
Hadoop Overview 1
 
Google App Engine - Overview #2
Google App Engine - Overview #2Google App Engine - Overview #2
Google App Engine - Overview #2
 
Hadoop Overview 2
Hadoop Overview 2Hadoop Overview 2
Hadoop Overview 2
 
Google App Engine - Overview #1
Google App Engine - Overview #1Google App Engine - Overview #1
Google App Engine - Overview #1
 
Google App Engine - Overview #3
Google App Engine - Overview #3Google App Engine - Overview #3
Google App Engine - Overview #3
 
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)
찰리를 만나봅시다 - 엔터프라이즈 2.0이란 무엇인가 ( Meet Charlie - What is Enterprise 2.0 - Korean)
 
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]Outsourcing: Best Practices at Pandemic Studios [GDC 2008]
Outsourcing: Best Practices at Pandemic Studios [GDC 2008]
 
애자일 게임 개발이란?
애자일 게임 개발이란?애자일 게임 개발이란?
애자일 게임 개발이란?
 
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)애자일 게임 개발: 최전선의 이야기(Gamefest 2006)
애자일 게임 개발: 최전선의 이야기(Gamefest 2006)
 
Agile의 의미와 Agile 계획 수립(Gdc2007)
Agile의 의미와 Agile 계획 수립(Gdc2007)Agile의 의미와 Agile 계획 수립(Gdc2007)
Agile의 의미와 Agile 계획 수립(Gdc2007)
 
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]
애자일 개발을 이용한 게임 기획 (Game Design In Agile Development) [GDC 2007]
 
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
애자일 게임 개발: 현실 세계의 혼돈을 다루는 법 (Agile Game Development: Dealing With Chaos In Th...
 
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)
실전 애자일 게임 개발 (Agile Game Agile Game Development From The Trenches)
 
애자일 게임 개발(Agile Game Development) - GDC2007
애자일 게임 개발(Agile Game Development) - GDC2007애자일 게임 개발(Agile Game Development) - GDC2007
애자일 게임 개발(Agile Game Development) - GDC2007
 

Dernier

Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 

Dernier (20)

Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 

Test Driven Development For Games: What, Why, And How (Game Connect 2006)

  • 1. Test Driven Development For Games: What, Why, And How Noel Llopis Senior Architect High Moon Studios
  • 2.
  • 3.
  • 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.  
  • 7.
  • 10.
  • 11.  
  • 13. TDD != Unit tests TDD != Testing strategy TDD == Development technique
  • 14.
  • 15. Character + Shield Character Damage(x) Shield Damage(x) class Character { IShield* m_shield; public: Character(); void Damage(float amount); float GetHealth() const; };
  • 16.
  • 17. Testing Return Values TEST (ShieldCanBeDamagedIfFull) { } Shield Test Shield bool Damage() Damage? Shield shield; CHECK (shield.Damage()); “ Failure in ShieldLevelStartsFull: Expected 100 but got 0”
  • 18. Testing State TEST (LevelCannotBeDamagedBelowZero) { } Shield Test Shield Damage(200) GetLevel() Shield shield; shield.Damage(200); CHECK_EQUAL (0, shield.GetLevel()); 0?
  • 19.
  • 20.
  • 21. Run Tests With Every Build
  • 22. Testing Interaction (Can Be A Problem Initially) Test Character Character Damage() *m_shield TEST(CharacterUsesShieldToAbsorbDamage) { Character character(400); character.Damage(100); CHECK_EQUAL(390, character.GetHealth()); } 390? Shield GetHealth() Fancy Shield
  • 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
  • 24. Using a Mock in Your Test 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?
  • 25. Best Practices: Test only the code at hand Test Code under test Test Code under test Subsystem A Subsystem B Subsystem C Something the cat dragged in The kitchen sink Who knows
  • 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); }
  • 27. Best Practices: Keep Tests Fast 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.
  • 28. Best Practices: Keep Tests Fast 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.
  • 29. Best Practices: Keep Tests Independent g_CollisionWorldSingleton
  • 30.
  • 31. Run the tests on consoles... less often
  • 32.  
  • 34. Test API state directly
  • 35. Test all code except for API calls
  • 36. Testing With Middleware Havok RenderWare Unreal Novodex OpenGL DirectX
  • 37. TDD With An Existing Engine
  • 38. I'd like to use TDD but...
  • 39.
  • 40. Lesson #1: TDD can be used for high-level game code
  • 41. Fighting AI example 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. Character behavior example 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. Lesson #2: TDD and code design
  • 45. Lesson #3: Number of tests as a measure of progress
  • 46. Lesson #4: TDD improves build stability
  • 47. Lesson #5: TDD creates more code
  • 50.
  • 51. 1. What is TDD? 2. How We Use TDD 3. TDD and Games 4. Lessons Learned 5. Wrap Up
  • 53.