Data Driven Development The Technology Behind Live-Ops About Me Daniel Menard Co-founder and CEO First Spin Out Products and Services Products Services • Power Based • Stamina Based • Short Burst + Long Tail • Survival + Build Up • Qualitative • Quantitative • Reviews • React to Players Commit Graphs Game Launch Commit Graphs Game Launch Effects on Tech • Code Quality • Analytics • Ongoing Production Code Quality • Now a major concern • Must be able to add and remove features easily • Maintenance dwarfs production Analytics • Key metrics and balancing • Log everything • Free solutions available • Prioritize analysis capabilities Analytics • Real-Time Data • Time Lagged Data • No Data • Wrong Data Ongoing Production • Only require designers to add content • Good tools • Configuration data • Changes informed by analytics • Regular Patches • Regular QA • Submit to App Stores Tightening the Loop Adjust Deliver 7 day review 1-30 days Analyse Gather Data Driven Development • Decouple data from logic code • Create a data model for your game • Game becomes it’s own “runtime” for your data • Configuration files Why should you care • Data can be made accessible to all • Better code • Flat class hierarchy • Objects become containers for data Class Separation Designer Data Levels Player Data Runtime Data Balance Settings Progression Item Definitions Inventory Health Achievements Score High Scores Combo Counter Anti-Pattern: Configuration Classes • Classes which extend and only override defaults • Configuration should be moved to data class and injected • Create new classes only when consuming different data Data Model 1 Trigger - Key * Shop Group - Key - Name - Items 1 Shop Item - Key - Name * - Price - Icon - Requirements - Costs - Trigger - Effects * Requirement - Text - LUA Function * * Cost - Text - LUA Function - Mutator * * Effect - Text - LUA Function Spectrum Entirely Hardcoded Configuration Files Domain Specific Language Scripted Bindings Virtual Machine Over-Engineering • Be pragmatic • Balance customizability with schedule • Don’t reinvent the wheel, use scripting languages Scripting Language • Pick one and stick with it • LUA, Javascript, Python, many available • Make sure you can interop easily • Use it as a configuration language too • JSON, Lua Pickle LUA Binding • Tolua++ • Code generation from headers • Create regular C++ class • Make accessible to LUA Example enemiesData = CCDictionary:create() -- BOOMER data = BoomerData:create("Boomer1") data:setWeaknessDamageType(DamageType_All) data:setEnemyType(EnemyType_Boomer) data:setImpactType(ImpactType_Explosion) data:setCanBeThrown(true) data:setIndependent(true) data:setSoundPrefix("Boomer") data:addAnimationFile("boomer_animations") data:addAnimationFile("boomer_explode") enemiesData:setObject(data, data:getKey():getCString()) Example function parseNeighbourhood(neighbourhoodDefinition) local neighbourhood = NeighbourhoodData:create(neighbourhoodDefinition.key); neighbourhood:setCollectionMapPosition(neighbourhoodDefinition.collectionMapPosition); neighbourhood:setCollectionTimeMs(neighbourhoodDefinition.collectionTimeMs); neighbourhood:setImagePath(neighbourhoodDefinition.imagePath); neighbourhood:setMapPosition(neighbourhoodDefinition.mapPosition); neighbourhood:setSoundKey(neighbourhoodDefinition.soundKey); neighbourhood:setZOrder(neighbourhoodDefinition.zOrder); if neighbourhoodDefinition.coinReward ~= nil then neighbourhood:setCoinReward(neighbourhoodDefinition.coinReward); end if neighbourhoodDefinition.toothReward ~= nil then neighbourhood:setToothReward(neighbourhoodDefinition.toothReward); end end return neighbourhood; local levels = CCArray:create(); local neighbourhoodDefinitions = loadFileSafe("Scripts/Data/neighbourhoods.lua", {ccc3=ccc3, ccp=ccp}); for key, neighbourhoodDefinition in pairs(neighbourhoodDefinitions) do levels:addObject(parseNeighbourhood(neighbourhoodDefinition)); end Example return { { key="Neighbourhood1", collectionMapPosition=ccp(741, 1120), collectionTimeMs=(60 * 60 * 1000), imagePath="Images/UI/Map/map_redlight01.png", mapPosition=ccp(191, 795), soundKey="Env_RedLight", toothReward=1, zOrder=4 }, … } Player Data • Player data depends on designer data • Designer data is expected to change • Isolate them from each other • Database-style keys Downloader • Necessary because Apple delivery is slow • 1-3 weeks spent in review • Android is much better • Necessary to keep binary small • 50-100MB over-the-air limits • Universal apps Considerations • Restrictions on downloading executable code • No DLLs • Scripts a gray zone • Bandwidth costs • No longer covered by the store Requirements • Ability to segment content by • Platform • Device capabilities • Test group • Security • Data Consistency Our implementation • Simple HTTP Client • JSON Manifest File • Package files • Cryptographic Signatures Manifest File { "manifest_version": 1, "name": "Big Action Mega Fight", "version": "2", "url": "http://update.dblstallion.com/bamf/manifest.php?{...}" "files": [ { "name": "patch1.dz", "url": " http://update.dblstallion.com/bamf/patch1.dz", "signature": "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvb…" } ] } Signatures • SHA-1 Hash, signed with private key • Public key in game memory • Ensures data consistency • Ensures data is yours A/B Testing 50% 2A 1 50% 2B 3 A/B Testing • Always maintain a control group • Can test multiple options (not just 2) • Need a lot of time to determine winner • Some groups will never merge back Save File Transformation • Have a way to update someone’s save file • Format may change • Designer data may change • Do not risk data loss • Unit Testing • QA References • http://www.slideshare.net/KostasAnagnostou/datadriven-game-development • http://www.igda.org/montreal/vid_liveops Thank You! www.doublestalliongames.com dan@dblstallion.com @dblstallion