Tdd in unity

Preview:

Citation preview

?

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

Software Craftsman Game Developer

Tests

Clarity

SOLID

Speed

No Crash

Memory

Test Driven UnityA Sane Approach

Software Craftsman Game Developer

Tests

Clarity

SOLID

Speed

No Crash

Memory

QuickTime™ and aH.264 decompressor

are needed to see this picture.

Eskimo!

QuickTime™ and aH.264 decompressor

are needed to see this picture.

Meta-Fail

Is It Fun?

Cubicle Wars!

UnityMakes your life easier when it’s not making it

harder

Scripts

public class NewScript : MonoBehaviour {

// Use this for initializationvoid Start () {

}

// Update is called once per framevoid Update () {

}}

void Awake() {stateMachine = new CubicleWarsStateMachine(

new HumanPlayer("Player1"),new HumanPlayer("Player2"));

[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);

}

Updating the View

Update the View

public void Attack(Unit unit){ if (CurrentState == State.Attacking){ unit.AttackWith(CurrentPlayer.Weapon());

Shared Interfaces

C# Eventspublic delegate void GameOverEvent(String winner);

...

public event GameOverEvent GameOver = delegate { };

...

private void AnnouncePlayerWins(){GameOver(CurrentPlayer.Name);}

Wiring the Events

machine.GameOver += delegate(string winner) {winMessage.SendMessage("ShowWinner", String.Format("{0} wins!", winner));};

Health

“MVC”

Demo

What is in the Library

•Game Models (Units, Characters)

•State Machines

•Effects

•Anything that doesn’t depend on UnityEngine

State Machine

Game State Machine

StateState EventEvent ActionAction DestinatioDestinationn

WaitingForSelection ClickWeapon TryToSelectUnit Selecting

Selecting AssignWeapon SwapWaitingUnit Attacking

Attacking ClickWeapon ResolveAttack ResolvingAttack

ResolvingAttack PlayerDead PlayerWins

ResolvingAttack NextTurn SwitchPlayers SwitchingPlayers

SwitchingPlayers SwitchedPlayers WaitingForSelection

Effectspublic class SineWave

{

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)

{

return (Amplitude * (float)Math.Sin(Frequency * time))

+ Offset;

}

}

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));}

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();

}

Live Code!

Software Craftsman Game Developer

Tests

Clarity

SOLID

Speed

No Crash

Memory

The Ultimate Test

ControllersComponents.Add (new UnitController(this, GameData.GlobalData.PlayerOneName, (UnitData) GameData.DataFor(GameData.GlobalData.PlayerOneName).Sales));

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);}

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;

}

The Ultimate Test #2

Remember

•There’s no AssertPretty, and no AssertFun

•TDD needs to be fast to be effective

•Separate your concerns

•Slides with bullets suck

Thanks!

@paytonruleswww.paytonrules.com

www.8thlight.comwww.github.com/paytonrules

Recommended