There are multiple inheritors from the abstract class of Agent - a PlayerAgent and an EnemyAgent. The former accepts input and passes it to the model to act upon (move around, attack, etc) and the latter uses the world setting to pass instructions to the model (move towards the player, attack if the other's model is within a certain distance, etc).
The model accounts for collision, moving around, and other such activities including combat. For the sandbox, that is literally all it does.
The appearance renders animation, changes the frame based on the model's actions and applies other cosmetic details, and so forth.
The projects (sandbox and actual game) are written in C#/XNA, and I was hoping for some feedback as to whether this set-up would be viable for a reasonably sized game meant for XBLIG release with no extraneous features such as multiplayer.
Code below
Delegates.cs
public delegate void Instruction(Avatar avatar);
public delegate bool ValidityCheck(Avatar avatar);
Contract.cs
public sealed class Contract//Observer and intermediary between Agent, Avatar and Appearance
{
#region Static
#endregion
#region Variables
Agent m_agent;
Avatar m_avatar;
Appearance m_appearance;
List<Pair<Instruction, ValidityCheck>> m_instructionQueue;//FIFO
#endregion
public Contract()
{
m_instructionQueue = new List<Pair<Instruction, ValidityCheck>>();
}
#region Methods
public void Link(Agent agent, Avatar avatar, Appearance appearance)
{
m_agent = agent;
m_appearance = appearance;
m_avatar = avatar;
m_instructionQueue.Clear();
}
public void EnqueueInstruction(Instruction instruction, ValidityCheck check)
{
m_instructionQueue.Add(new Pair<Instruction, ValidityCheck>(instruction, check));
}
public void EnqueueInstruction(Pair<Instruction, ValidityCheck> instruction)
{
m_instructionQueue.Add(instruction);
}
public Pair<Instruction, ValidityCheck> DequeueInstruction()
{
if (m_instructionQueue.Count > 0)
{
Pair<Instruction, ValidityCheck> toRet = m_instructionQueue[0];
m_instructionQueue.RemoveAt(0);
return toRet;
}
else
return new Pair<Instruction, ValidityCheck>();
}
public ValidityCheck PeekAtValidityCheck()
{
if (m_instructionQueue.Count > 0)
return m_instructionQueue[0].Value;
else
return null;
}
public void ClearInstructionQueue()
{
m_instructionQueue.Clear();
}
#endregion
#region Properties
public Agent Agent { get { return m_agent; } }
public Appearance Appearance { get { return m_appearance; } }
public Avatar Avatar { get { return m_avatar; } }
public bool IsActive { get { return m_agent != null && m_appearance != null && m_avatar != null; } }
#endregion
}
Appereance.cs
public abstract class Appearance//View aspect of M-V-C
{
#region Static
#endregion
#region Variables
Contract m_contract;
Animation m_anim;
Animation.AnimInstance m_animInst;
Rectangle m_renderRect;
Color m_tint;
#endregion
public Appearance(Contract contract, Animation anim, string defaultAnimName, int renderWidth, int renderHeight)
{
m_contract = contract;
m_anim = anim;
m_animInst = m_anim.CreateAnimationInstance();
m_animInst.ChangeAnim(defaultAnimName);
m_renderRect = new Rectangle(0, 0, renderWidth, renderHeight);
m_tint = Color.White;
}
#region Methods
public void Update(GameTime gameTime)
{
m_anim.Update(gameTime, ref m_animInst);
QueryAvatar(gameTime);
m_renderRect.X = (int)m_contract.Avatar.Position.X;
m_renderRect.Y = (int)m_contract.Avatar.Position.Y;
}
protected abstract void QueryAvatar(GameTime gameTime);
public void Draw(GameTime gameTime)
{
m_anim.Draw(gameTime, ref m_animInst, m_renderRect, m_tint, Microsoft.Xna.Framework.Graphics.SpriteEffects.None, 1.0f);
}
#endregion
#region Properties
#endregion
}
Agent.cs
public abstract class Agent//Controller aspect and the user input aspect in M-V-C
{
#region Static
#endregion
#region Variables
protected readonly Contract m_contract;
#endregion
protected Agent(Contract contract)
{
m_contract = contract;
}
#region Methods
public abstract void Update(GameTime gameTime);
#endregion
#region Properties
#endregion
}
Avatar.cs
public class Avatar//Model aspect of M-V-C
{
#region Static
#endregion
#region Variables
Contract m_contract;
Vector2 m_pos, m_movement;
#endregion
protected Avatar(Contract contract)
{
m_contract = contract;
}
#region Methods
public void Update(GameTime gameTime)
{
//check collision and wotnot and other model-y stuff
m_pos += m_movement;
m_movement = Vector2.Zero;
if (m_contract.PeekAtValidityCheck() != null && m_contract.PeekAtValidityCheck()(this))//if there is something to do, see if it can be done
m_contract.DequeueInstruction().Key(this);//then do it
}
#endregion
#region Properties
public Vector2 Position
{
get { return m_pos; }
set { m_pos = value; }
}
public Vector2 Movement
{
get { return m_movement; }
set { m_movement = value; }
}
#endregion
}
Snippets from InstructionFactory.cs
sealed class InstructionFactory
{
#region Static
static InstructionFactory()
{
//construction here
}
#endregion
#region Variables
#endregion
#region Methods
public Pair<Instruction, ValidityCheck> ConstructMovementInstruction(Vector2 movement)
{
return new Pair<Instruction, ValidityCheck>(
(Avatar avatar) =>
{
avatar.Movement += movement;
},
(Avatar avatar) =>
{
return true;
}
);
//Could be made simpler - the above is for maximum readability since it's so similar to method declaration
/*
return new Pair<Instruction, ValidityCheck>(
avatar => { avatar.Movement += movement; },
avatar => { return true; }
);
*/
}
#endregion
#region Properties
#endregion
}