From 3c4e63ada09da00dd8a3f51d0436399ccbb6ef49 Mon Sep 17 00:00:00 2001 From: Colin McMillen Date: Thu, 5 Mar 2020 17:39:17 -0500 Subject: [PATCH] Add basic FSM & use it from NPC. --- Shared/FSM.cs | 40 ++++++++++++++++++++++++++ Shared/NPC.cs | 62 +++++++++++++++++++++++++++++++---------- Shared/Shared.projitems | 1 + 3 files changed, 88 insertions(+), 15 deletions(-) create mode 100644 Shared/FSM.cs diff --git a/Shared/FSM.cs b/Shared/FSM.cs new file mode 100644 index 0000000..3fc1b01 --- /dev/null +++ b/Shared/FSM.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; + +namespace SemiColinGames { + public interface IState { + public void Enter(); + public string? Update(NPC npc, float modelTime, AABB[] collisionTargets); + } + + public class FSM { + float timeInState = 0f; + Dictionary states; + IState state; + + public FSM(Dictionary states, string initial) { + this.states = states; + StateName = initial; + Transition(StateName); + } + + public string StateName { get; private set; } + + public void Update(NPC npc, float modelTime, AABB[] collisionTargets) { + timeInState += modelTime; + string? newState = state.Update(npc, modelTime, collisionTargets); + if (newState != null) { + Transition(newState); + } + } + + void Transition(string state) { + Debug.WriteLine("{0} -> {1} @ {2}", StateName, state, timeInState); + timeInState = 0f; + StateName = state; + IState newState = states[state]; + this.state = newState; + this.state.Enter(); + } + } +} diff --git a/Shared/NPC.cs b/Shared/NPC.cs index cbca207..e4fa341 100644 --- a/Shared/NPC.cs +++ b/Shared/NPC.cs @@ -1,25 +1,33 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using System.Collections.Generic; namespace SemiColinGames { - class NPC { - private Point position; + class IdleState : IState { + float timeInState = 0; - private const int spriteWidth = 96; - private const int spriteHeight = 81; - private const int spriteCenterYOffset = 3; + public void Enter() { + timeInState = 0; + } - public NPC(Point position) { - this.position = position; + public string? Update(NPC npc, float modelTime, AABB[] collisionTargets) { + timeInState += modelTime; + if (timeInState > 1.0f) { + npc.Facing *= -1; + return "run"; + } + return null; } + } - public int Facing { get; private set; } = 1; + class RunState : IState { + public void Enter() {} - public void Update(float modelTime, AABB[] collisionTargets) { + public string? Update(NPC npc, float modelTime, AABB[] collisionTargets) { int moveSpeed = 120; - int desiredX = position.X + (int) (moveSpeed * Facing * modelTime); + int desiredX = npc.Position.X + (int) (moveSpeed * npc.Facing * modelTime); // TODO: define the box modularly & correctly. - AABB npcBox = new AABB(new Vector2(desiredX, position.Y), new Vector2(11, 33)); + AABB npcBox = new AABB(new Vector2(desiredX, npc.Position.Y), new Vector2(1, 33)); Debug.AddRect(npcBox, Color.Cyan); bool foundBox = false; foreach (AABB box in collisionTargets) { @@ -30,20 +38,44 @@ namespace SemiColinGames { } if (!foundBox) { - Facing *= -1; + return "idle"; } - position.X = desiredX; + npc.Position.X = desiredX; + return null; + } + } + + public class NPC { + private const int spriteWidth = 96; + private const int spriteHeight = 81; + private const int spriteCenterYOffset = 2; + + private FSM fsm; + + public NPC(Point position) { + Position = position; + fsm = new FSM(new Dictionary { + { "idle", new IdleState() }, + { "run", new RunState() } + }, "idle"); + } + + public int Facing = 1; + public Point Position; + + public void Update(float modelTime, AABB[] collisionTargets) { + fsm.Update(this, modelTime, collisionTargets); } public void Draw(SpriteBatch spriteBatch) { Rectangle textureSource = Sprites.Executioner.GetTextureSource( - "run", (int) Clock.ModelTime.TotalMilliseconds); + fsm.StateName, (int) Clock.ModelTime.TotalMilliseconds); // TODO: move this into Sprite metadata. Vector2 spriteCenter = new Vector2(spriteWidth / 2, spriteHeight / 2 + spriteCenterYOffset); SpriteEffects effect = Facing == 1 ? SpriteEffects.None : SpriteEffects.FlipHorizontally; Color color = Color.White; - spriteBatch.Draw(Textures.Executioner.Get, position.ToVector2(), textureSource, color, 0f, + spriteBatch.Draw(Textures.Executioner.Get, Position.ToVector2(), textureSource, color, 0f, spriteCenter, Vector2.One, effect, 0f); } } diff --git a/Shared/Shared.projitems b/Shared/Shared.projitems index fae96a1..d5de0ab 100644 --- a/Shared/Shared.projitems +++ b/Shared/Shared.projitems @@ -11,6 +11,7 @@ +