public void VerticalMachine() { verticalMachine = Fsm <VState, VTrigger> .Builder(VState.STANDING) .State(VState.STANDING) .TransitionTo(VState.DUCKING) .On(VTrigger.DOWN_PRESSED) .TransitionTo(VState.JUMPING) .On(VTrigger.UP_PRESSED) .State(VState.DUCKING) .TransitionTo(VState.STANDING) .On(VTrigger.DOWN_RELEASED) .State(VState.JUMPING) .TransitionTo(VState.DIVING) .On(VTrigger.DOWN_PRESSED) .State(VState.DESCENDING) .TransitionTo(VState.DIVING) .On(VTrigger.DOWN_PRESSED) .State(VState.DIVING) .TransitionTo(VState.DESCENDING) .On(VTrigger.DOWN_RELEASED) .GlobalTransitionTo(VState.STANDING) .OnGlobal(VTrigger.SPACE_PRESSED) .Build(); string s = verticalMachine.GetPlantUml(); }
public void StateChangesStandingDuckingJumpingDiving() { var m = Fsm <State, Trigger> .Builder(State.STANDING) .State(State.DUCKING) .TransitionTo(State.STANDING).On(Trigger.UP) .State(State.STANDING) .TransitionTo(State.DUCKING).On(Trigger.DOWN) .TransitionTo(State.JUMPING).On(Trigger.UP) .State(State.JUMPING) .TransitionTo(State.DIVING).On(Trigger.DOWN) .State(State.DIVING) .Build(); Assert.That(m.Current.Identifier, Is.EqualTo(State.STANDING)); m.Trigger(Trigger.DOWN); Assert.That(m.Current.Identifier, Is.EqualTo(State.DUCKING)); m.Trigger(Trigger.DOWN); Assert.That(m.Current.Identifier, Is.EqualTo(State.DUCKING)); m.Trigger(Trigger.UP); Assert.That(m.Current.Identifier, Is.EqualTo(State.STANDING)); m.Trigger(Trigger.UP); Assert.That(m.Current.Identifier, Is.EqualTo(State.JUMPING)); m.Trigger(Trigger.UP); Assert.That(m.Current.Identifier, Is.EqualTo(State.JUMPING)); m.Trigger(Trigger.DOWN); Assert.That(m.Current.Identifier, Is.EqualTo(State.DIVING)); }
public void WhenMultipleAfterConditionsFireOnASingleUpdateOnEnterAndOnExitShoudFire() { var enterCount = 0; var exitCount = 0; var m = Fsm <State, Trigger> .Builder(State.IDLE) .State(State.IDLE) .OnEnter(t => enterCount++) .OnExit(t => exitCount++) .TransitionTo(State.OVER).After(TimeSpan.FromMilliseconds(10)) .State(State.OVER) .OnEnter(t => enterCount++) .OnExit(t => exitCount++) .TransitionTo(State.PRESSED).After(TimeSpan.FromMilliseconds(10)) .State(State.PRESSED) .OnEnter(t => enterCount++) .OnExit(t => exitCount++) .TransitionTo(State.REFRESHING).After(TimeSpan.FromMilliseconds(10)) .State(State.REFRESHING) .OnEnter(t => enterCount++) .OnExit(t => exitCount++) .TransitionTo(State.IDLE).After(TimeSpan.FromMilliseconds(10)) .Build(); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Update(TimeSpan.FromMilliseconds(40)); Assert.That(enterCount, Is.EqualTo(4)); Assert.That(exitCount, Is.EqualTo(4)); }
public void WhenBuiltWithBuilderAndStringsStackMachineShouldWork() { var m = Fsm <string, string> .Builder("OPENED") .EnableStack() .State("OPENED") .TransitionTo("OPENED").On("OPEN") .TransitionTo("CLOSED").On("CLOSE") .State("CLOSED").ClearsStack() .TransitionTo("CLOSED").On("CLOSE") .TransitionTo("OPENED").On("OPEN") .Build(); m.Trigger("OPEN"); Assert.That(m.Current.Identifier, Is.EqualTo("OPENED")); Assert.That(m.Stack.Select(x => x.Identifier).ToArray(), Is.EquivalentTo(new[] { "OPENED", "OPENED" })); m.Trigger("CLOSE"); Assert.That(m.Current.Identifier, Is.EqualTo("CLOSED")); Assert.That(m.Stack.ToArray(), Is.EquivalentTo(new State <State, Trigger>[] { })); m.Trigger("CLOSE"); Assert.That(m.Current.Identifier, Is.EqualTo("CLOSED")); Assert.That(m.Stack.ToArray(), Is.EquivalentTo(new State <State, Trigger>[] { })); m.Trigger("OPEN"); Assert.That(m.Current.Identifier, Is.EqualTo("OPENED")); Assert.That(m.Stack.Select(x => x.Identifier).ToArray(), Is.EquivalentTo(new[] { "OPENED" })); }
public void Main() { var hero = new Hero(); CreateMachineFor(Fsm <State, Trigger> .Builder(State.IDLE), new Button(), hero); CreateMachineFor(Fsm <State, Trigger> .Builder(State.IDLE), new Button(), hero); }
public void WhenBuiltWithBuilderStackMachineShouldWork() { var m = Fsm <State, Trigger> .Builder(State.OPENED) .EnableStack() .State(State.OPENED) .TransitionTo(State.OPENED).On(Trigger.OPEN) .TransitionTo(State.CLOSED).On(Trigger.CLOSE) .State(State.CLOSED).ClearsStack() .TransitionTo(State.CLOSED).On(Trigger.CLOSE) .TransitionTo(State.OPENED).On(Trigger.OPEN) .Build(); AssertSimpleTest(m); }
public void WhenMultipleAfterConditionsFireOnASingleUpdateAdvanceStateCorrectly() { var m = Fsm <State, Trigger> .Builder(State.IDLE) .State(State.IDLE) .TransitionTo(State.OVER).After(TimeSpan.FromMilliseconds(10)) .State(State.OVER) .TransitionTo(State.PRESSED).After(TimeSpan.FromMilliseconds(10)) .State(State.PRESSED) .TransitionTo(State.REFRESHING).After(TimeSpan.FromMilliseconds(10)) .State(State.REFRESHING) .TransitionTo(State.IDLE).After(TimeSpan.FromMilliseconds(10)) .Build(); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Update(TimeSpan.FromMilliseconds(40)); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); }
public void WhenCallingPopOnStackMachineBuiltWithBuilderShouldBehaveCorrectly() { var m = Fsm <State, Trigger> .Builder(State.OPENED) .EnableStack() .State(State.OPENED) .TransitionTo(State.OPENED).On(Trigger.OPEN) .TransitionTo(State.CLOSED).On(Trigger.CLOSE) .TransitionTo(State.POP).On(Trigger.PUSH_POP) .State(State.CLOSED).ClearsStack() .TransitionTo(State.CLOSED).On(Trigger.CLOSE) .TransitionTo(State.OPENED).On(Trigger.OPEN) .State(State.POP) .PopTransition().On(Trigger.PUSH_POP) .Build(); AssertTestWithPop(m); }
public void WhenAnAfterConditionDoesNotFiresAndATransitionHappensTheTimerIsResetOnReentry() { var m = Fsm <State, Trigger> .Builder(State.IDLE) .State(State.IDLE) .TransitionTo(State.OVER).After(TimeSpan.FromMilliseconds(10)) .TransitionTo(State.OVER).On(Trigger.MOUSE_CLICKED) .State(State.OVER) .TransitionTo(State.IDLE).On(Trigger.MOUSE_CLICKED) .Build(); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Update(TimeSpan.FromMilliseconds(5)); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.OVER)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Update(TimeSpan.FromMilliseconds(5)); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); }
private Fsm <State, Trigger> CreateFiniteStateMachine() { return(Fsm <State, Trigger> .Builder(State.RESOLUTION_800_X600) .State(State.RESOLUTION_800_X600) .OnEnter(args => { SetResolution(800, 600, false); }) .TransitionTo(State.RESOLUTION_800_X600_FULLSCREEN).On(Trigger.SPACE) .State(State.RESOLUTION_800_X600_FULLSCREEN) .OnEnter(args => { SetResolution(800, 600, true); }) .TransitionTo(State.RESOLUTION_1024_X768).On(Trigger.SPACE).State(State.RESOLUTION_1024_X768).OnEnter( args => { SetResolution(1024, 768, false); }) .TransitionTo(State.RESOLUTION_1024_X768_FULLSCREEN).On(Trigger.SPACE) .State(State.RESOLUTION_1024_X768_FULLSCREEN) .OnEnter(args => { SetResolution(1024, 768, true); }) .TransitionTo(State.RESOLUTION_1280_X768).On(Trigger.SPACE).State(State.RESOLUTION_1280_X768).OnEnter( args => { SetResolution(1280, 768, false); }) .TransitionTo(State.RESOLUTION_1280_X768_FULLSCREEN).On(Trigger.SPACE) .State(State.RESOLUTION_1280_X768_FULLSCREEN).OnEnter(args => { SetResolution(1280, 768, true); }) .TransitionTo(State.RESOLUTION_1920_X1080).On(Trigger.SPACE).State(State.RESOLUTION_1920_X1080).OnEnter( args => { SetResolution(1920, 1080, false); }) .TransitionTo(State.RESOLUTION_1920_X1080_FULLSCREEN).On(Trigger.SPACE) .State(State.RESOLUTION_1920_X1080_FULLSCREEN).OnEnter(args => { SetResolution(1920, 1080, true); }) .Build()); }
public void HorizontalMachine() { horizontalMachine = Fsm <HState, HTrigger> .Builder(HState.STANDING) .State(HState.STANDING) .TransitionTo(HState.WALKING_LEFT) .On(HTrigger.LEFT_PRESSED) .TransitionTo(HState.WALKING_RIGHT) .On(HTrigger.RIGHT_PRESSED) .State(HState.WALKING_LEFT) .TransitionTo(HState.WALKING_DELAY_LEFT) .On(HTrigger.LEFT_RELEASED) .State(HState.WALKING_RIGHT) .TransitionTo(HState.WALKING_DELAY_RIGHT) .On(HTrigger.RIGHT_RELEASED) .State(HState.WALKING_DELAY_LEFT) .TransitionTo(HState.WALKING_RIGHT) .On(HTrigger.RIGHT_PRESSED) .TransitionTo(HState.RUNNING_LEFT) .On(HTrigger.LEFT_PRESSED) .State(HState.WALKING_DELAY_RIGHT) .TransitionTo(HState.WALKING_LEFT) .On(HTrigger.LEFT_PRESSED) .TransitionTo(HState.RUNNING_RIGHT) .On(HTrigger.RIGHT_PRESSED) .State(HState.RUNNING_LEFT) .TransitionTo(HState.STANDING) .On(HTrigger.LEFT_RELEASED) .State(HState.RUNNING_RIGHT) .TransitionTo(HState.STANDING) .On(HTrigger.RIGHT_RELEASED) .GlobalTransitionTo(HState.STANDING) .OnGlobal(HTrigger.SPACE_PRESSED) .Build(); string s = horizontalMachine.GetPlantUml(); }
public void StateChangesGunRotation() { // Now for the weapons-machine with basic forward- and backward-rotation. var m = Fsm <WState, WTrigger> .Builder(WState.EMPTY_HANDED) .State(WState.EMPTY_HANDED) .TransitionTo(WState.GUN).On(WTrigger.TAB) .TransitionTo(WState.LASER_RIFLE).On(WTrigger.SHIFT_TAB) .State(WState.GUN) .TransitionTo(WState.SHOTGUN).On(WTrigger.TAB) .TransitionTo(WState.EMPTY_HANDED).On(WTrigger.SHIFT_TAB) .State(WState.SHOTGUN) .TransitionTo(WState.LASER_RIFLE).On(WTrigger.TAB) .TransitionTo(WState.GUN).On(WTrigger.SHIFT_TAB) .State(WState.LASER_RIFLE) .TransitionTo(WState.EMPTY_HANDED).On(WTrigger.TAB) .TransitionTo(WState.SHOTGUN).On(WTrigger.SHIFT_TAB) .Build(); Assert.That(m.Current.Identifier, Is.EqualTo(WState.EMPTY_HANDED)); m.Trigger(WTrigger.TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.GUN)); m.Trigger(WTrigger.TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.SHOTGUN)); m.Trigger(WTrigger.TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.LASER_RIFLE)); m.Trigger(WTrigger.TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.EMPTY_HANDED)); m.Trigger(WTrigger.SHIFT_TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.LASER_RIFLE)); m.Trigger(WTrigger.SHIFT_TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.SHOTGUN)); m.Trigger(WTrigger.SHIFT_TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.GUN)); m.Trigger(WTrigger.SHIFT_TAB); Assert.That(m.Current.Identifier, Is.EqualTo(WState.EMPTY_HANDED)); }
public void WhenStateChangesOnEnterAndOnExitHooksShouldTrigger() { var button = new Button(); var m = Fsm <State, Trigger> .Builder(State.IDLE) .State(State.IDLE) .TransitionTo(State.OVER).On(Trigger.MOUSE_OVER) .OnEnter(t => button.BtnState = State.IDLE) .OnExit(t => button.OldState = button.BtnState) .State(State.OVER) .TransitionTo(State.IDLE).On(Trigger.MOUSE_LEAVE) .TransitionTo(State.PRESSED).On(Trigger.MOUSE_CLICKED) .OnEnter(t => button.BtnState = State.OVER) .OnExit(t => button.OldState = button.BtnState) .Update(a => button.UpdateCounter = button.UpdateCounter + 1) .State(State.PRESSED) .TransitionTo(State.IDLE).On(Trigger.MOUSE_LEAVE) .TransitionTo(State.REFRESHING).On(Trigger.MOUSE_RELEASED).If(a => button.IsActivated) .OnEnter(t => button.BtnState = State.PRESSED) .OnExit(t => button.OldState = button.BtnState) .State(State.REFRESHING) .OnEnter(t => button.BtnState = State.REFRESHING) .OnExit(t => button.OldState = button.BtnState) .Update(a => { button.RefreshTimer -= (float)a.ElapsedTimeSpan.TotalMilliseconds; if (button.RefreshTimer <= 0F) { button.RefreshTimer = 0F; a.Machine.JumpTo(State.OVER); // or m.JumpTo(State.IDLE); } button.UpdateCounter = button.UpdateCounter + 1; }) .Build(); m.Update(TimeSpan.FromMilliseconds(2)); // Should do nothing. Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_LEAVE); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_RELEASED); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_OVER); Assert.That(m.Current.Identifier, Is.EqualTo(State.OVER)); Assert.That(button.BtnState, Is.EqualTo(State.OVER)); Assert.That(button.OldState, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.PRESSED)); Assert.That(button.BtnState, Is.EqualTo(State.PRESSED)); Assert.That(button.OldState, Is.EqualTo(State.OVER)); m.Trigger(Trigger.MOUSE_RELEASED); // Button is deactivated. Assert.That(m.Current.Identifier, Is.EqualTo(State.PRESSED)); Assert.That(button.BtnState, Is.EqualTo(State.PRESSED)); Assert.That(button.OldState, Is.EqualTo(State.OVER)); button.IsActivated = true; m.Trigger(Trigger.MOUSE_RELEASED); // Now it's activated. Assert.That(m.Current.Identifier, Is.EqualTo(State.REFRESHING)); Assert.That(button.BtnState, Is.EqualTo(State.REFRESHING)); Assert.That(button.OldState, Is.EqualTo(State.PRESSED)); m.Update(TimeSpan.FromMilliseconds(1)); // No transition yet... Assert.That(m.Current.Identifier, Is.EqualTo(State.REFRESHING)); Assert.That(button.BtnState, Is.EqualTo(State.REFRESHING)); Assert.That(button.OldState, Is.EqualTo(State.PRESSED)); m.Update(TimeSpan.FromMilliseconds(1)); // But now. Assert.That(m.Current.Identifier, Is.EqualTo(State.OVER)); Assert.That(button.BtnState, Is.EqualTo(State.OVER)); Assert.That(button.OldState, Is.EqualTo(State.REFRESHING)); // Update was triggered twice over all states. Assert.That(button.UpdateCounter, Is.EqualTo(2)); }
public void WhenAfterConditionFiresOnEnterAndOnExitHooksShouldTrigger() { var button = new Button(); var m = Fsm <State, Trigger> .Builder(State.IDLE) .State(State.IDLE) .TransitionTo(State.OVER).On(Trigger.MOUSE_OVER) .OnEnter(t => button.BtnState = State.IDLE) .OnExit(t => button.OldState = button.BtnState) .State(State.OVER) .TransitionTo(State.IDLE).On(Trigger.MOUSE_LEAVE) .TransitionTo(State.PRESSED).On(Trigger.MOUSE_CLICKED) .OnEnter(t => button.BtnState = State.OVER) .OnExit(t => button.OldState = button.BtnState) .Update(a => button.UpdateCounter = button.UpdateCounter + 1) .State(State.PRESSED) .TransitionTo(State.IDLE).On(Trigger.MOUSE_LEAVE) .TransitionTo(State.REFRESHING).On(Trigger.MOUSE_RELEASED).If(a => button.IsActivated) .OnEnter(t => button.BtnState = State.PRESSED) .OnExit(t => button.OldState = button.BtnState) .State(State.REFRESHING) .OnEnter(t => button.BtnState = State.REFRESHING) .OnExit(t => button.OldState = button.BtnState) .TransitionTo(State.OVER).After(TimeSpan.FromSeconds(1)) .GlobalTransitionTo(State.IDLE).AfterGlobal(TimeSpan.FromSeconds(10)) .Build(); m.Update(TimeSpan.FromMilliseconds(16)); // Should do nothing. Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_LEAVE); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_RELEASED); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_OVER); m.Update(TimeSpan.FromMilliseconds(16)); m.Update(TimeSpan.FromMilliseconds(16)); Assert.That(m.Current.Identifier, Is.EqualTo(State.OVER)); Assert.That(button.BtnState, Is.EqualTo(State.OVER)); Assert.That(button.OldState, Is.EqualTo(State.IDLE)); m.Trigger(Trigger.MOUSE_CLICKED); Assert.That(m.Current.Identifier, Is.EqualTo(State.PRESSED)); Assert.That(button.BtnState, Is.EqualTo(State.PRESSED)); Assert.That(button.OldState, Is.EqualTo(State.OVER)); m.Trigger(Trigger.MOUSE_RELEASED); // Button is deactivated. Assert.That(m.Current.Identifier, Is.EqualTo(State.PRESSED)); Assert.That(button.BtnState, Is.EqualTo(State.PRESSED)); Assert.That(button.OldState, Is.EqualTo(State.OVER)); button.IsActivated = true; m.Trigger(Trigger.MOUSE_RELEASED); // Now it's activated. Assert.That(m.Current.Identifier, Is.EqualTo(State.REFRESHING)); Assert.That(button.BtnState, Is.EqualTo(State.REFRESHING)); Assert.That(button.OldState, Is.EqualTo(State.PRESSED)); m.Update(TimeSpan.FromMilliseconds(500)); // No transition yet... Assert.That(m.Current.Identifier, Is.EqualTo(State.REFRESHING)); Assert.That(button.BtnState, Is.EqualTo(State.REFRESHING)); Assert.That(button.OldState, Is.EqualTo(State.PRESSED)); m.Update(TimeSpan.FromMilliseconds(600)); // But now. Assert.That(m.Current.Identifier, Is.EqualTo(State.OVER)); Assert.That(button.BtnState, Is.EqualTo(State.OVER)); Assert.That(button.OldState, Is.EqualTo(State.REFRESHING)); m.Update(TimeSpan.FromMilliseconds(10000)); Assert.That(m.Current.Identifier, Is.EqualTo(State.IDLE)); Assert.That(button.BtnState, Is.EqualTo(State.IDLE)); Assert.That(button.OldState, Is.EqualTo(State.OVER)); // Update was triggered twice over all states. Assert.That(button.UpdateCounter, Is.EqualTo(3F)); }
/// <summary> /// LoadContent will be called once per game and is the place to load /// all of your content. /// </summary> protected override void LoadContent() { // Create a new SpriteBatch, which can be used to draw textures. spriteBatch = new SpriteBatch(GraphicsDevice); var texture = Content.Load <Texture2D>("spaceman"); atlas = TextureAtlas.Create("hero-atlas", texture, 50, 50, 24, 1, 1); var factory = new SpriteSheetAnimationFactory(atlas); factory.Add("idle_left", new SpriteSheetAnimationData(new[] { 1 })); factory.Add("idle_right", new SpriteSheetAnimationData(new[] { 0 })); factory.Add("walk_left", new SpriteSheetAnimationData(new[] { 16, 17, 18, 19, 20, 21, 22, 23 })); factory.Add("walk_right", new SpriteSheetAnimationData(new[] { 8, 9, 10, 11, 12, 13, 14, 15 })); factory.Add("jump_left", new SpriteSheetAnimationData(new[] { 5, 7 }, isLooping: false)); factory.Add("jump_right", new SpriteSheetAnimationData(new[] { 4, 6 }, isLooping: false)); factory.Add("duck_left", new SpriteSheetAnimationData(new[] { 3 })); factory.Add("duck_right", new SpriteSheetAnimationData(new[] { 2 })); hero = new AnimatedSprite(factory); heroStateMachine = Fsm <string, Keys> .Builder("idle_left") .State("idle_left") .Update(args => hero.Play(args.State.Identifier)) .TransitionTo("walk_left").On(Keys.Left) .TransitionTo("duck_left").On(Keys.Down) .TransitionTo("jump_left").On(Keys.Up) .TransitionTo("idle_right").On(Keys.Right) .State("idle_right") .Update(args => hero.Play(args.State.Identifier)) .TransitionTo("walk_right").On(Keys.Right) .TransitionTo("duck_right").On(Keys.Down) .TransitionTo("jump_right").On(Keys.Up) .TransitionTo("idle_left").On(Keys.Left) .State("walk_left") .Update(args => { if (Keyboard.GetState().IsKeyDown(Keys.Left)) { hero.Play(args.State.Identifier); } else { args.Machine.JumpTo("idle_left"); } }) .TransitionTo("duck_left").On(Keys.Down) .TransitionTo("jump_left").On(Keys.Up) .TransitionTo("idle_right").On(Keys.Right) .State("walk_right") .Update(args => { if (Keyboard.GetState().IsKeyDown(Keys.Right)) { hero.Play(args.State.Identifier); } else { args.Machine.JumpTo("idle_right"); } }) .TransitionTo("duck_right").On(Keys.Down) .TransitionTo("jump_right").On(Keys.Up) .TransitionTo("idle_left").On(Keys.Left) .State("jump_left") .OnEnter(args => hero.Play(args.To.Identifier).OnCompleted = () => args.Fsm.JumpTo("idle_left")) .State("jump_right") .OnEnter(args => hero.Play(args.To.Identifier).OnCompleted = () => args.Fsm.JumpTo("idle_right")) .State("duck_left") .Update(args => { if (Keyboard.GetState().IsKeyDown(Keys.Down)) { hero.Play(args.State.Identifier); } else { args.Machine.JumpTo("idle_left"); } }) .TransitionTo("walk_right").On(Keys.Right) .TransitionTo("walk_left").On(Keys.Left) .TransitionTo("jump_left").On(Keys.Up) .State("duck_right") .Update(args => { if (Keyboard.GetState().IsKeyDown(Keys.Down)) { hero.Play(args.State.Identifier); } else { args.Machine.JumpTo("idle_right"); } }) .TransitionTo("walk_right").On(Keys.Right) .TransitionTo("walk_left").On(Keys.Left) .TransitionTo("jump_right").On(Keys.Up) .Build(); }