Tdd in unity

2,537 views

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,537
On SlideShare
0
From Embeds
0
Number of Embeds
33
Actions
Shares
0
Downloads
24
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide
  • So can we do it? Can we do TDD in game development? Is professional software engineering so different from game development?
  • I look at this code and I see 6 parameters in the function, pointers to pointers, optional parameters, a clear command query violation, and NULL params that have to be checked against in the implementation. I say UGH! in SE. The const rule, and no input/output parameters is probably the single most important thing, in my eyes, that separate good code from beautiful code. It makes the whole system easier to understand and easier to edit or refactor . This code was called “Exceptionally Beautiful” in kotaku magazine, because they look for different things: - const - no in/out params
  • What does a Software Craftsman care about, and what do Game Developers care about? What about Fail Fast? Crashing is NOT an option as a game dev!
  • Can I really test drive this? Isn’t this just UI? Are the rules of the game, if you’ll pardon the pun, fundamentally different.?
  • This is a technical talk. Please ask questions. I cannot possibly guess anybody’s familiarity with the tools I’m discussing. XNA / MonoGame cameo. Maybe a live coding session if we have time.
  • I am a man with two backgrounds. I am a software craftsman at 8th Light with 15 years experience in traditional software development. I’m a TDD zealot. I value SOLID principles and Clean code. I even mentor others on this. This is who I am professionally. But I am also a wanna-be Indie Game Developer. I have a master’s degree in Video Game Development. I’m participating in the One Game a Month competition, I’m beginnng an 8th Light game design book club on Friday afternoons, and I’m somebody who has wanted to make games for a very very long time. So why don’t I? Well I have two choices. 1) Quit my job and go work for a big game company. Find out that they don’t care about the code the way I do, take a pay cut, and be unhappy. Full disclosure I did that out of college. Or 2) I can go the indie game development route. I can make games in my free time. I chose the second, or did I.
  • Who knows this game? About two years ago I sat down and tried to write a Kaboom! port for the iPhone. Instead of using the
  • So I wrote OCDSpec. Crap. And somebody else made a kaboom port. Crap 2.
  • I wrote this in a day.
  • Eskimo is a framework for doing Javascript/HTML5 game development at the command line, with TDD in mind. It’s built with best practices in mind.
  • I wrote this in a year. The game is actually slower, less fun, and about 5x the code as I got lost in abstraction after abstraction. Is Eskimo crap? No probably not, but what I should have done was tweaked Boberoids until I had something fun to keep. Then made a second game. THEN took out a bunch of libraries. Describe the plot of FTQ.
  • Meta Fail is a movie about making movies. It’s an Agile Consulting company making a tool for Agile Development. It’s developer who tries to make a game, but keeps making development tools.
  • I also missed the #1 test for a game - the is it fun! I still don’t know if Feed The Quinn is fun! So I really started to doubt my approach. Maybe TDD and what I call good design don’t matter. Maybe I’m full of shit! As I was trying to go through the One Game A Month Challenge. I needed something that would solve the question of “is it fun” FAST!
  • Cubicle Wars is a turn based strategy game that represents the real battle we all face - the one in your companies. Hackers/Salesmen and Office “Drones” face off in a new plan to eliminate the redundancies in your organization, to the death. Is it fun? I don’t know.
  • http://variancetheory.com/wp-content/uploads/2009/10/Detonator102shot.jpg So I decided to start with the Unity3D game development environment, and immediately realized that one of my TDD problems was solved - I don’t have to write the view. I create a scene, I drag and drop, I import models, etc. Rather than, as I did with HTML5, test driving an audio controller only to find out it doesn’t actually work when I hear the sound, I just drag and drop the file. But how do we add behavior in Unity.
  • Unity has scenes. Objects are bound to scripts in scenes in Unity. If you’re familiar with Entity Component Frameworks you’ll know this is a “component” and your objects are entities. Where do we start? With testing, can I open this file?
  • If you double click those C# files you get a project/solution - but you can’t add to it. It is autogenerated every time.
  • Your C# files do this? These scripts look like this and they inherit from MonoBehavior, a Unity concept. This is true if you use C#, Boo or Javascript and here we get our first real problem. You cannot instantiate these objects directly. It’s against the rules. Old me would have found a way to do it, there probably is one, but that wasn’t gonna get me to - “is it fun?” So to review - I can’t add NUnit to the project and I can’t instantiate the objects in isolation anyway. How to fix.
  • So instead I setup a library. I assumed Unity would have data - initial values, unit names, the geometry of the scene. I also decided it would have a layer of scripts or components. Then I would have my game logic. My actual game is defined in the Game Logic, and all talk is done through interfaces. I have an assembly that has no Unity dependencies. Win! (Yes I realize I could have used NUnit for that)
  • Even better I can put the assembly right in the project, and the project then builds it. This means I can use any assemblies I want - provided they are built with mono on .NET 3.5. So this does mean I’ll need to build my own support libraries and can’t just grab stuff. Show a quick demo of Xamarin running your tests.
  • There you can get started. Now if this was all I did, I’d be okay. I could create new objects, and then I could ask the objects for their data and I’d be in an improved TDD State.
  • Notice this test - THERE IS NO SIGN OF UNITY!
  • My first pass on the view did the “simplest thing” that could work. I had unity objects implement an interface, and pass themselves to their corresponding cubicle wars unit.
  • A lot of stuff like this. The game logic would callback into unity - through the interface so it’s in the circle. This worked okay until you got code like this: two units need to interact, but all that Unity had access to is the Unity component.
  • So you ended up with something like this. The unity object implemented the same interface as the cubicle wars unit, as well as it’s “view” interface. It got clunky fast. What I really wanted was an observer.
  • This is in the Cubicle Wars Library - that delegate is in the State Machine interface.
  • Seems reasonable enough
  • And I was happy! Developer happiness! Bind the Unity Object to the Domain Object, retrieve the events. Unity becomes responsible for data and events. No coupling! I won!
  • I had a new problem. That health indicator requires a GUIText game object, and it’s a DIFFERENT object than the one attached to the stapler. How to get the health to update when the stapler has been attacked? I would need to bind more than one Unity Object to the same Cubicle Wars Unit. I considered: - Firing events when units are created - but I’d need unique ids. - Having a lookup table - same problem. I needed a way to setup the game objects, and a way to bind unity objects. And remember I don’t create the objects (although I could instantiate a pre-fab). I really needed a way to “poll” the objects.
  • I really needed to start handling responsibilities. Somebody creates the domain object. Somebody else sends events to the game object. Some other objects respond to domain events and/or updates the view during the update loop (polling).
  • Go through the Unit Setup. - Highlight where you bind to the Unit Object. - Point Out Update - Router. - Controller. - Flashing View. - Health View.
  • Weren’t we going to talk about how to Unit Test in Unity? Well yes. The reason we’re doing this is because Unit Testing is supposed to be FAST! To run, and to write. As such we need to write our unity code in such a way that we can unit test all the codez!
  • We attach this to a ‘monostate’ game object - static data - because there can be only one. All controllers may need to have access to it.
  • Point out the “UnityObject” for the game data.
  • Remove the object when it is killed.
  • Demo your command line thing.
  • So could I do it? Did I resolve the conflict? Did I find out if “it’s fun?” Did I do worthwhile TDD? Is this code good enough?
  • Remember there is no Assert_Pretty Port to MonoGame? Fuck the Game Ended!
  • Wednesday I recreated this game. The hard part is getting models in.
  • Tdd in unity

    1. 1. ?
    2. 2. int idSurface::Split(const idPlane &plane,const float epsilon,idSurface **front,idSurface **back,int *frontOnPlaneEdges = NULL,int *backOnPlaneEdges = NULL) const;http://kotaku.com/5975610/the-exceptional-beauty-of-doom-3s-source-code
    3. 3. Software Craftsman Game DeveloperTestsClaritySOLIDSpeedNo CrashMemory
    4. 4. Test Driven UnityA Sane Approach
    5. 5. Software Craftsman Game DeveloperTestsClaritySOLIDSpeedNo CrashMemory
    6. 6. QuickTime™ and aH.264 decompressorare needed to see this picture.
    7. 7. Eskimo!
    8. 8. QuickTime™ and aH.264 decompressorare needed to see this picture.
    9. 9. Meta-Fail
    10. 10. Is It Fun?
    11. 11. Cubicle Wars!
    12. 12. UnityMakes your life easier when it’s not making it harder
    13. 13. Scripts
    14. 14. public class NewScript : MonoBehaviour {// Use this for initializationvoid Start () {}// Update is called once per framevoid Update () {}}
    15. 15. void Awake() {stateMachine = newCubicleWarsStateMachine(new HumanPlayer("Player1"),new HumanPlayer("Player2"));
    16. 16. [Test]public void ItAllowsAddingAUnitToAPlayer(){var unit = Substitute.For<Unit>();stateMachine.AddUnitToPlayer("PlayerOne", unit);stateMachine.AddUnitToPlayer("PlayerTwo", unit);playerOne.Received().AddUnit(unit);playerTwo.Received().AddUnit(unit);}
    17. 17. Updating theView
    18. 18. Update theViewpublic void Attack(Unit unit){if (CurrentState == State.Attacking){unit.AttackWith(CurrentPlayer.Weapon());
    19. 19. Shared Interfaces
    20. 20. C# Eventspublic delegate void GameOverEvent(String winner);...public event GameOverEvent GameOver = delegate { };...private void AnnouncePlayerWins(){GameOver(CurrentPlayer.Name);}
    21. 21. Wiring the Eventsmachine.GameOver += delegate(string winner){winMessage.SendMessage("ShowWinner",String.Format("{0} wins!", winner));};
    22. 22. Health
    23. 23. “MVC”
    24. 24. Demo
    25. 25. What is in the Library• Game Models (Units, Characters)• State Machines• Effects• Anything that doesn’t depend onUnityEngine
    26. 26. State MachineGame State MachineStateState EventEvent ActionAction DestinationDestinationWaitingForSelection ClickWeapon TryToSelectUnit SelectingSelecting AssignWeapon SwapWaitingUnit AttackingAttacking ClickWeapon ResolveAttack ResolvingAttackResolvingAttack PlayerDead PlayerWinsResolvingAttack NextTurn SwitchPlayers SwitchingPlayersSwitchingPlayers SwitchedPlayers WaitingForSelection
    27. 27. Effects{protected float Amplitude { get; set; }protected float Frequency { get; set; }protected float Offset { get; set; }public SineWave(float amplitude, float frequency, float offset){Amplitude = amplitude;Frequency = frequency;Offset = offset;}public float at(float time){
    28. 28. Effects[Test]public void ItReturnsANormalSinWaveOnTime(){var sineWave = new SineWave(1, 1, 0);Assert.AreEqual(0, sineWave.at(0));Assert.AreEqual(1, sineWave.at((float) Math.PI / 2.0f));}
    29. 29. Game Unitspublic StandardUnit(ConflictResolver resolver, UnityObject unity){Resolver = resolver;Health = unity.InitialHealth;UnitName = unity.Name;}public void AttackWith (Unit enemy){Health -= enemy.AttackStrengthAgainst (this);Attacked();}
    30. 30. Live Code!
    31. 31. Software Craftsman Game DeveloperTestsClaritySOLIDSpeedNo CrashMemory
    32. 32. The Ultimate Test
    33. 33. ControllersComponents.Add (new UnitController(this,GameData.GlobalData.PlayerOneName,(UnitData) GameData.DataFor(GameData.GlobalData.PlayerOneName).Sales));
    34. 34. Controllerspublic class UnitController : DrawableGameComponent, UnityObject{protected String player;protected Model model;protected UnitData initialData;protected Unit unit;public int InitialHealth {get {return initialData.Health;}}public string Name {get {return initialData.Name;}}public UnitController (Game game, String player, UnitData initialData) : base(game){var warGame = game as Startup;this.player = player;this.initialData = initialData;unit = new StandardUnit(GameData.Resolver, this);warGame.MouseClick += (s, args) => CheckMouseClick(s, args as ClickEventArgs);}
    35. 35. Viewpublic UnitView (Game game, String player, Model model, UnitData initialData, Unit unit) : base(game){this.model = model;this.unit = unit;this.initialData = initialData;this.player = player;wave = new SineWave(AMPLITUDE, FREQUENCY, OFFSET);waiting = false;unit.Waiting += () => waiting = true;unit.DoneWaiting += () => waiting = false;}
    36. 36. The Ultimate Test #2
    37. 37. Remember• There’s no AssertPretty, and no AssertFun• TDD needs to be fast to be effective• Separate your concerns• Slides with bullets suck
    38. 38. Thanks!@paytonruleswww.paytonrules.comwww.8thlight.comwww.github.com/paytonrules

    ×