Is my Design "Too much inheritance?"

Started by
4 comments, last by ravinDavin 12 years, 6 months ago
Below is my design, Can anyone suggest if there is a better way to do things?



Entity

Character Bullet Item

human AI Powerup Weapon


/*I then have separate classes:
Animation -> needs a spritesheet
SpriteSheet ->needs texture,number of frames etc
PlayerInventory -> Only the human has one
EntityManager -> draws all entities



Basically I'm not sure whether I should create separate classes for Weapon types or powerup types.
And also, as it stands everything has to be an entity if it is drawn, should I make a separate Sprite class instead?*/




I can post actual classes if you like
Advertisement
Too much? No. Looks like a reasonable and logical heirarchy, except the fact your ascii art didn't hold up :)

The trick and your snags are going to be in the design of your managers and animation logic. Your EntityManager should be clean enough, but its in the inventory, and animation/sprite side where your inter-dependencies are going to get the trickiest. For example, who will own the animation? The entity class? The derived class?


As to your question, I would keep everything at the entity level, so your EntityManager deals only with Entity classes. Make the base as streamlined as possible, nothing superfluous. Load, Destroy, Get, Update and Draw should be about all you need. Either that or make your EntityManager a ( or have access to a ) factory class which is in charge of creating all entities. If you give entity an Update() virtual method, animated classes derived from it can transparently handle animation that way.

Hopefully that made sense.

Too much? No. Looks like a reasonable and logical heirarchy, except the fact your ascii art didn't hold up :)

The trick and your snags are going to be in the design of your managers and animation logic. Your EntityManager should be clean enough, but its in the inventory, and animation/sprite side where your inter-dependencies are going to get the trickiest. For example, who will own the animation? The entity class? The derived class?


As to your question, I would keep everything at the entity level, so your EntityManager deals only with Entity classes. Make the base as streamlined as possible, nothing superfluous. Load, Destroy, Get, Update and Draw should be about all you need. Either that or make your EntityManager a ( or have access to a ) factory class which is in charge of creating all entities. If you give entity an Update() virtual method, animated classes derived from it can transparently handle animation that way.

Hopefully that made sense.


Thanks for the reply. Currently, this is my Entity base class:

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using CGPLibrary.Managers;
using CGPLibrary;
using System.Collections.Generic;
using System;

namespace CGPLibrary.Entities
{
public class Entity
{
#region NEEDED HANDLES
protected EntityManager entManager;
Animation anim_control;
public HumanPlayer thePlayer;
#endregion

public string name;
public Texture2D texture;
protected Vector2 position;
protected int entHeight;
protected int entWidth;

protected float rotation;
protected Vector2 origin;
protected Vector2 gravity;
protected float scale;
protected Color color;


#region PROPERTIES
protected float zDepth { get; set; }
protected bool bAnimate { get; set; }
public Rectangle srcRect { get; set; }
public Rectangle destRect { get; set; }


#region SCALE PROPERTY
//resizes the destination rectangle when a scale is applied
public float Scale
{
get { return scale; }
set
{
scale = value;
entHeight = (int)(entHeight * scale);
entWidth = (int)(entWidth * scale);
destRect = new Rectangle((int)position.X, (int)position.Y, (int)(entWidth), (int)(entHeight));

}

}
#endregion
#endregion

#region CONSTRUCTOR ENTITY
//Will construct an entity with an animation if the SpriteSheet is non-null
//otherwise it will just use the texture
public Entity(EntityManager entManager, string name, Texture2D texture, Vector2 position, SpriteSheet spriteSheet, float zDepth)
{

scale = 1.0f;
#region IF ENTITY IS ANIMATED
if (spriteSheet != null)
{
anim_control = new Animation(spriteSheet);
this.texture = spriteSheet.texture;
this.srcRect = spriteSheet.srcRect;
this.entWidth = (int)(spriteSheet.frameWidth*scale);
this.entHeight = (int)(spriteSheet.frameHeight*scale);
this.destRect = new Rectangle((int)position.X, (int)position.Y, (int)(entWidth), (int)(entHeight));
bAnimate = true;
}
#endregion
#region IF NO ANIMATION
else
{
this.texture = texture;
this.srcRect = new Rectangle(0,0,texture.Width,texture.Height);
this.entHeight = (int)(texture.Height * scale);
this.entWidth = (int)(texture.Width * scale);
this.destRect = new Rectangle((int)position.X, (int)position.Y, (int)(entWidth),(int) (entHeight));
bAnimate = false;
}
#endregion


#region DEFAULT VALUES
this.zDepth = zDepth;
this.entManager = entManager;
this.gravity = new Vector2(0,5);
this.rotation = 0;
this.origin = Vector2.Zero;
this.color = Color.White;
this.name = name;
this.position = position;
#endregion


entManager.entityList.Add(this);
// entManager.positionList.Add(position, this);
}
#endregion



public void animate(float elapsed)
{
anim_control.animate(this,elapsed);
}


#region MISC METHODS
public bool onScreen()
{
if (position.X < 0 || position.X > GLOBALS.SCREENWIDTH)
return false;
if (position.Y < 0 || position.Y > GLOBALS.SCREENHEIGHT)
return false;

return true;

}

public int distance(Rectangle pos1, Rectangle pos2)
{
Point centre1 = pos1.Center;
Point centre2 = pos2.Center;
double x1 = centre1.X;
double y1 = centre1.Y;
double x2 = centre2.X;
double y2 = centre2.Y;

double xDiff = x2 - x1;
double yDiff = y2 - y1;

double xDiffSq = xDiff * xDiff;
double yDiffSq = yDiff * yDiff;

double sum = xDiffSq + yDiffSq;

double distance = Math.Sqrt(sum);

return (int)distance;


}
#endregion
public virtual void Draw(SpriteBatch batch)
{

batch.Draw(texture, position
,this.srcRect,
Color.White,
rotation,
origin,
Scale,
SpriteEffects.None,
zDepth);


}
public virtual void Update(GameTime gameTime)
{

this.origin = new Vector2(entWidth / 2, entHeight / 2);

//updates dest rectangle
destRect = new Rectangle((int)position.X, (int)position.Y, (int)(entWidth), (int)(entHeight));
float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;


if(bAnimate)
animate(elapsed);

}



#region ANIMATION CONTROL
public void setAnimFrame(int frame)
{
int width = anim_control.width;
int height = anim_control.height;
if (anim_control.horizontal)
srcRect = new Rectangle(frame*width, srcRect.Y, width, height);
else
srcRect = new Rectangle(srcRect.X, frame * height, width, height);
}
//sets the col/row depending on if the animation is read horiz/vertical
//therebey drawing a new set of frames / new animation
public void setAnimation(int animation)
{

anim_control.setAnimation(animation);

}
#endregion

}
}




As you can see, when an entity is created, it will determine if it needs to be Animated (if SpriteSheet was null test) and add the entity to the list which contains all entities. Currently, it is structured like you said, my update method will animate all entities with an animation. But I am currently trying to implement a HUD.

My idea was, to create a HUD class which contained all textures needed(life bars/armour bars) etc. Problems arose though when I realised I would need too access some attributes of the player,
namely CurrentHealth, CurrentArmour, and I would also need the HUD to show which Weapon was currently equipped. This is when my brain started melting.

EDIT: Also, is it normal for every subclass to have the same constructor as the base class?(minus the extra variables)
Your HUD problem is acutally fairly easily resolved. You are passing your entityManager into each entity, therefore an entity will be able to find other entities if you provide the funcitonality.

I assume your EntityManager is essentially a wrapper around some kind of data structure, either a vector or a map, errrmmm, dictionary. I would recommend you use a dictionary then you can store objects via name/id.

So then you can do stuff like:

HudObject hudObject;
hudObject.Name = "HUD";

EntityManager.Add(hudObject);



Then elsewhere within another class:

class Bullet : Entity
{
void Update()
{
((HudObject)this.entityManager.Find("HUD")).Score += 1;
}
}



Of course, this is all pseudoCode and presents the problem of requiring names to be unique ( unless of course you maintained two datastructures in EntityManager for anonymous and named entities ), but it will allow decouple objects to deal with other entities if needed. Also, you are creating a bit of a dependency between objects ( like the Bullet class needing to know the type of HudObject for the cast ), but this can easily me fixed by implementing a messaging system between entities, that is, if it bothers you.


I would also consider making Entity an interface and moving the graphical code to a different base class, this will give you much more flexibility down the road, especially if you decide to add non-graphical entities or you wish to graphically implement things different for different entities, such as a particle system.
Just going to pick out one of the questions in your OP. "Should I make separate classes for weapon / power-up types?"

I would make separate classes for objects which require different behaviour. If the only difference between then is different properties, then no seperate classes are required.

I wouldn't make separate classes for weapons - what is the difference between weapons? Fire rate, max ammo, bullet type? These should be fields in the weapon class, and the values of these should be different for each weapon. The class can have the methods your would expect from a weapon class eg. Fire() - Check if enough ammo, create a bullet of the type specified in the bullet_type field at the players position, reduce ammo by 1.

Power-ups - I would go for separate classes for these - A base powerup class, with a virtual OnPickup() method. A derived class for each powerup could implement the OnPickup() method where the effects of the powerup are implemented.

Bullets - I think the same as powerups, a separate class for each type. Base bullet class, with virtual methods such as Update(), OnHit(). Maybe one type of bullet follows a straight line when fired, another follows a sine wave type movement. These can be implemented in the update function of each specific bullet class. The OnHit() method could be different for each type of bullet. Maybe one type of bullet makes a small explosion when it hits on enemy. Another type splits into several smaller bullets when it hits an enemy.


Only my initial ideas - as always, there is more than one way to approach the problem.
Thanks for the replies guys, helped me to think straight again!

This topic is closed to new replies.

Advertisement