Compare commits

...

2 Commits

Author SHA1 Message Date
53083818c0 Add a TODO and fix a smol cleanup. 2020-03-05 17:39:30 -05:00
3c4e63ada0 Add basic FSM & use it from NPC. 2020-03-05 17:39:17 -05:00
4 changed files with 91 additions and 17 deletions

40
Shared/FSM.cs Normal file
View File

@ -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<string, IState> states;
IState state;
public FSM(Dictionary<string, IState> 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();
}
}
}

View File

@ -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 NPC(Point position) {
this.position = position;
public void Enter() {
timeInState = 0;
}
public int Facing { get; private set; } = 1;
public string? Update(NPC npc, float modelTime, AABB[] collisionTargets) {
timeInState += modelTime;
if (timeInState > 1.0f) {
npc.Facing *= -1;
return "run";
}
return null;
}
}
public void Update(float modelTime, AABB[] collisionTargets) {
class RunState : IState {
public void Enter() {}
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<string, IState> {
{ "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);
}
}

View File

@ -11,6 +11,7 @@
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)Camera.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ExtensionMethods.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FSM.cs" />
<Compile Include="$(MSBuildThisFileDirectory)NPC.cs" />
<Compile Include="$(MSBuildThisFileDirectory)SoundEffects.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Sprites.cs" />

View File

@ -64,6 +64,7 @@ namespace SemiColinGames {
child.SelectToken("frame.y").Value<int>(),
child.SelectToken("frame.w").Value<int>(),
child.SelectToken("frame.h").Value<int>());
// TODO: convert all durations to floats.
int durationMs = child.SelectToken("duration").Value<int>();
frames.Add(new Frame(source, durationMs));
}
@ -75,7 +76,7 @@ namespace SemiColinGames {
int end = child.SelectToken("to").Value<int>();
string directionString = child.SelectToken("direction").Value<string>();
AnimationDirection direction = directionString == "pingpong" ?
AnimationDirection.PingPong: AnimationDirection.Forward;
AnimationDirection.PingPong : AnimationDirection.Forward;
int durationMs = 0;
for (int i = start; i <= end; i++) {
durationMs += frames[i].DurationMs;