Sign in to follow this  
GGulati

Importing Software Patterns


Recommended Posts

I'm working on a game for which, as always, I need sprites. I've used both conventional OOP and components for sprites, but neither of them are suitable for my current project. To clarify, this is a medium-sized project (in the 10's of K LOC range) which I am working on with 2 friends - one for art & sound and the other coding as well. I was exploring [url="http://en.wikipedia.org/wiki/Model-view-controller"]Model-View-Controller[/url] for the GUI part of the game when I considered using the pattern for the sprites. That is, as a hybrid, almost-component-like system. Each sprite is composed of an Agent (controller), Avatar (model) and Appearance (view). A Contract acts as an intermediary between the three and allows for information sharing between the 3, as well as decoupling the Avatar from the other two, Agent from the Appearance, and Appearance from the Agent (each aspect only needs to be aware of up to 1 other aspect). I tested it in a small sandbox and it seems promising and flexible.

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.

[size="3"]Code below[/size]
Delegates.cs
[code]public delegate void Instruction(Avatar avatar);

public delegate bool ValidityCheck(Avatar avatar);[/code]

Contract.cs
[code]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
}[/code]

Appereance.cs
[code]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
}[/code]

Agent.cs
[code]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
}[/code]

Avatar.cs
[code]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
}[/code]

Snippets from InstructionFactory.cs
[code]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
}[/code]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this