After reworking the classes a bit, this is what I have. The functionality as it pertains to my game isn't implemented yet but the concept of how I'm trying to lay out my classes now are shown. Critiques and suggestions appreciated!
Note: Ignore the blockdodge namespace at the top, It's temporary until I rework everything else
Note 2: I accidently wrote GrowSmallerItem class in the class diagram. Grow bigger and grow smaller are pretty much the same.
Class diagram
Object class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using BlockDodgeWithGSM;
namespace BlockDodgeWithGSMv2
{
public abstract class GameObject
{
#region Properties
/// <summary>
/// By default the object is set to false meaning it does not handle touch input.
/// </summary>
bool isTouchable = false;
bool isHuman = false;
//default size is 32,32
Vector2 size = new Vector2(32, 32);
Vector2 position;
//Have default game sounds in here or in the specific classes?
SoundEffect defaultPlayerDeathSound = null;
protected bool IsTouchable
{
get { return isTouchable; }
protected set { isTouchable = value; }
}
/// <summary>
/// The width and height of the object
/// </summary>
protected Vector2 Size
{
get { return size; }
set { size = value; }
}
protected Vector2 Position
{
get { return position; }
protected set { position = value; }
}
//TODO Consider getting rid of isHuman
protected bool IsHuman
{
get { return isHuman; }
protected set { isHuman = value; }
}
public Rectangle HitBounds
{
get
{
Rectangle r = new Rectangle(
(int)(position.X),
(int)(position.Y),
(int)(size.X),
(int)(size.Y));
return r;
}
}
#region DefaultSounds
#region PlayerSounds
protected SoundEffect DefaultPlayerDeathSound
{
get { return defaultPlayerDeathSound; }
protected set { defaultPlayerDeathSound = value; }
}
#endregion
#endregion
#endregion
protected virtual void HandleInput(InputState input) { }
protected virtual void Update(GameTime gameTime) { }
protected virtual void Draw(SpriteBatch spriteBatch) { }
//TODO this is where effects will go. Objects can ovveride this to provide their own effect
public virtual void Grow() { }
public virtual void Shrink() { }
public static List<GameObject> Objects = new List<GameObject>();
}
}
Player class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Audio;
using BlockDodgeWithGSM;
namespace BlockDodgeWithGSMv2
{
public enum PlayerState
{
Alive,
Dead,
Invincible,
Smaller,
Bigger
}
public class Player : GameObject
{
Texture2D texture;
Texture2D overlay;
Vector2 velocity;
Vector2 minSize = new Vector2(48, 48);
Vector2 maxSize = new Vector2(96, 96);
bool inDragState = false;
PlayerState playerState = PlayerState.Alive;
#region Properties
public Vector2 Velocity
{
get { return velocity; }
protected set { velocity = value; }
}
public Vector2 MaxSize
{
get { return maxSize; }
protected set { maxSize = value; }
}
public Vector2 MinSize
{
get { return minSize; }
protected set { minSize = value; }
}
public PlayerState PlayerState
{
get { return playerState; }
set { playerState = value; }
}
#endregion
public Player(Texture2D texture, Vector2 position, Vector2 size, Vector2 velocity, bool isTouchable, bool isHuman)
{
this.texture = texture;
this.Position = position;
this.Size = size;
this.velocity = velocity;
this.IsTouchable = isTouchable;
this.IsHuman = isHuman;
GameObject.Objects.Add(this);
/* remove ishuman
if (isHuman == false)
BlockManager.obstacles.Add(this);
* */
}
public override void HandleInput(InputState input)
{
foreach (GestureSample gesture in input.Gestures)
{
foreach (TouchLocation touch in input.TouchState) //need this to know if it was a press or move
{
switch (gesture.GestureType)
{
case GestureType.FreeDrag:
if (touch.State == TouchLocationState.Moved)
{
if (this.inDragState) //is the block already being dragged?
{
//TODO get rid of this graphics somehow
int ScreenWidth = BlockDodgeWithGSM.BlockDodgeWithGSM.graphics.PreferredBackBufferWidth;
int ScreenHeight = BlockDodgeWithGSM.BlockDodgeWithGSM.graphics.PreferredBackBufferHeight;
int ScreenTop = 0 + GameplayScreen.adLocation.Height;
//We want the player to be centered on the touch and not the top left corner
this.Position = new Vector2(gesture.Position.X - this.Size.X / 2, gesture.Position.Y - this.Size.Y / 2);
//make sure player is within screen limits
//X
if (this.Position.X + this.Size.X > ScreenWidth)
this.Position = new Vector2(ScreenWidth - this.Size.X, this.Position.Y);
else if (this.Position.X < 0)
this.Position = new Vector2(0, this.Position.Y);
//Y
if (this.Position.Y + this.Size.Y > ScreenHeight)
this.Position = new Vector2(this.Position.X, ScreenHeight - this.Size.Y);
else if (this.Position.Y < ScreenTop)
this.Position = new Vector2(this.Position.X, ScreenTop);
break;
}
else
{
if (this.HitBounds.Contains(new Point((int)gesture.Position.X, (int)gesture.Position.Y))) //since block is not being dragged. check if touch is on block
{
//TODO get rid of this graphics somehow
int ScreenWidth = BlockDodgeWithGSM.BlockDodgeWithGSM.graphics.PreferredBackBufferWidth;
int ScreenHeight = BlockDodgeWithGSM.BlockDodgeWithGSM.graphics.PreferredBackBufferHeight;
int ScreenTop = 0 + GameplayScreen.adLocation.Height;
//We want the player to be centered on the touch and not the top left corner
this.Position = new Vector2(gesture.Position.X - this.Size.X / 2, gesture.Position.Y - this.Size.Y / 2);
//make sure player is within screen limits
//X
if (this.Position.X + this.Size.X > ScreenWidth)
this.Position = new Vector2(ScreenWidth - this.Size.X, this.Position.Y);
else if (this.Position.X < 0)
this.Position = new Vector2(0, this.Position.Y);
//Y
if (this.Position.Y + this.Size.Y > ScreenHeight)
this.Position = new Vector2(this.Position.X, ScreenHeight - this.Size.Y);
else if (this.Position.Y < ScreenTop)
this.Position = new Vector2(this.Position.X, ScreenTop);
this.inDragState = true;
break;
}
break;
}
}
break;
case GestureType.DragComplete: //drag finished so block no longer in drag state
this.inDragState = false;
break;
}
}
}
}
public override void Update(GameTime gameTime)
{
if (this.playerState != PlayerState.Invincible)
{
foreach (GameObject o in GameObject.Objects)
{
if (o is Enemy)
{
if (o.HitBounds.Intersects(this.HitBounds))
{
//Game over human was hit
if (GameplayScreen.isPlayerAlive == true)
{
//TODO: boolean in GameController? class to set sound
if (OptionsMenuScreen.sound)
GameplayScreen.death.Play();
GameplayScreen.isPlayerAlive = false;
this.PlayerState = PlayerState.Dead;
}
}
}
}
}
}
public override void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(this.texture, this.HitBounds, Color.White);
if (this.playerState == PlayerState.Invincible)
spriteBatch.Draw(overlay, this.HitBounds, new Color(255, 255, 255, 0.8f));
}
public override void Grow()
{
if (OptionsMenuScreen.sound)
GameplayScreen.growSound.Play();
if (this.Size.X <= this.MaxSize.X || this.Size.Y <= this.MaxSize.Y)
{
this.Size += new Vector2(16, 16);
//text = "You grew!";
}
else
{
//text = "You're too big to grow!";
}
}
public override void Shrink()
{
if (OptionsMenuScreen.sound)
GameplayScreen.shrinkSound.Play();
if (this.Size.X >= this.MinSize.X || this.Size.Y >= this.MinSize.Y)
{
this.Size -= new Vector2(16, 16);
//text = "You Shrunk!";
}
else
{
//text = "You're too tiny to shrink!";
}
}
}
}
Item class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace BlockDodgeWithGSMv2
{
public abstract class Item : GameObject
{
//TODO instead of Texture2D create your own animated texture type
protected Texture2D texture;
protected TimeSpan timeOnScreen;
protected bool isTimed = false;
protected bool triggered;
protected int price;
protected string name;
protected double weight;
}
}
GrowBiggerItem class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using BlockDodgeWithGSM;
namespace BlockDodgeWithGSMv2
{
public class GrowBiggerItem : Item
{
TimeSpan itemTimer;
TimeSpan displayTimer;
string text;
public GrowBiggerItem(Texture2D texture, Vector2 position)
{
this.texture = texture;
this.Position = position;
this.Size = new Vector2(32, 32);
itemTimer = TimeSpan.Zero;
displayTimer = TimeSpan.Zero;
text = "";
}
public GrowBiggerItem(Texture2D texture, Vector2 position, Vector2 size)
{
this.texture = texture;
this.Position = position;
this.Size = size;
itemTimer = TimeSpan.Zero;
displayTimer = TimeSpan.Zero;
text = "";
}
public override void Update(GameTime gameTime)
{
foreach (GameObject obj in GameObject.Objects)
{
if (this.HitBounds.Intersects(obj.HitBounds))
{
obj.Grow();
triggered = true;
}
}
if (false == triggered)
{
itemTimer += gameTime.ElapsedGameTime;
if (itemTimer > TimeSpan.FromSeconds(3))
{
//remove item
//GameplayScreen.itemManager.RemoveItem(this);
return;
}
}
//otherwise we picked it up at some point so check the timer and see if its time to take away the buff
else
{
displayTimer += gameTime.ElapsedGameTime;
if (displayTimer > TimeSpan.FromSeconds(3))
{
//GameplayScreen.itemManager.RemoveItem(this);
displayTimer = TimeSpan.Zero;
}
}
}
public override void Draw(SpriteBatch spriteBatch)
{
int ScreenTop = 0 + GameplayScreen.adLocation.Height;
Vector2 Center = new Vector2((BlockDodgeWithGSM.BlockDodgeWithGSM.graphics.PreferredBackBufferWidth / 2) - (GameplayScreen.itemFont.MeasureString(text).X / 2), BlockDodgeWithGSM.graphics.PreferredBackBufferHeight / 2);
if (false == triggered)
spriteBatch.Draw(this.texture, new Rectangle((int)this.Position.X, (int)this.Position.Y, (int)this.Size.X, (int)this.Size.Y), Color.White);
else
spriteBatch.DrawString(GameplayScreen.itemFont, text, Center, Color.White);
}
}
}