-
I will add some more levels of abstraction to Elysia's design.
Code:
class IGameObject
{
public:
virtual ~IGameObject() { }
virtual void Render() = 0;
virtual void Update(float timeDelta) = 0;
};
class Creature : public IGameObject
{
...
};
class IPhysics
{
public:
virtual ~IPhysics() { }
virtual void Update(float timeDelta) = 0;
virtual bool AddObject(const std::string &objectName,IGameObject *pObject) = 0;
virtual bool RemoveObject(const std::string &objectName) = 0;
virtual IGameObject* GetObject(const std::string &objectName) = 0;
};
class Physics : public IPhysics
{
public:
Physics();
virtual ~IPhysics();
typedef std::map<std::string,IGameObject *> ObjectMap;
typedef ObjectMap::iterator ObjectMapIter;
typedef ObjectMap::const_iterator ObjectMapConstIter;
virtual void Update(float timeDelta);
virtual bool AddObject(const std::string &objectName,IGameObject *pObject);
virtual bool RemoveObject(const std::string &objectName);
virtual IGameObject* GetObject(const std::string &objectName);
private:
ObjectMap m_Objects;
};
- IGameObject - abstract base for all game objects
- Creature - base class for all creature game objects
- IPhysics - abstract base for physics system
- Physics - base class impl of one physics system
- Physics::Update(float timeDelta) - calcuates forces on all objects that have been added to the container. This could be anything from gravity to thrusters to any type of force that is modelled.
- Can also add a GameObjectManager that would call update and render on all objects in its container
EDIT:
Correction: Render() was supposed to be pure virtual in IGameObject.
-
wow... Why is almost everything virtual o.O?
And I don't think I'll be using that, since I find it abit confusing, and don't wanna change my code right now xP might change it later tho ;) but still thanks! I'm really greatful ;)
-
It's virtual to allow for flexibility. IPhysics is an abstract base class. So different types of physics can be derived from it and passed into a function expecting a IPhysics* or IPhysics& and it will work.
Polymorphism gives good flexibility and power.
-
The key in designing a system is to always program to an interface and not an implementation. The interface describes the behavior of the object but doesn't care how the behavior happens. The impl follows the interface and implements the actual behavior while adhering to the interface.
So if we were building a vehicle class I would do this:
Code:
class IVehicle
{
public:
virtual ~IVehicle() { }
virtual void Accelerate() = 0;
virtual void Decelerate() = 0;
virtual void TurnLeft() = 0;
virtual void TurnRight() = 0;
};
This interface defines the behavior of a vehicle. As long as you adhere to the interface you can implement these functions in any way you want. I have not defined how a vehicle accelerates, turns, etc., but I have defined that it does have this behavior.
In addition to this you could add ITransmission and IEngine which would define the behavior of the engine and the transmission.
This would allow us to derive from IVehicle, IEngine, and ITransmission to create our final vehicle. It would allow us to define several types of vehicles, engines, and transmissions that could all basically be dropped in without changing any of the interface code. This means our system is extremely flexible, powerful, and easily extended. I would envision that IEngine and ITransmission would be aggregated into the vehicle impl since a vehicle is not a transmisson or engine but certainly does have both.
So for example:
Code:
class IEngine
{
public:
virtual ~IEngine { }
virtual void StartEngine() = 0;
virtual void StopEngine() = 0;
virtual unsigned int GetNumberOfCylinders() = 0;
virtual unsigned int GetHorsePower() = 0;
...
};
class ITransmission
{
public:
virtual ~ITransmission() { }
virtual void ShiftUp() = 0;
virtual void ShiftDown() = 0;
virtual unsigned int GetNumberOfGears() = 0;
virtual float GetGearRatio(unsigned int gear) = 0;
};
class Car : public IVehicle
{
public:
Car();
virtual ~Car()
virtual void Accelerate();
virtual void Decelerate();
virtual void TurnLeft();
virtual void TurnRight();
void AttachTransmission(ITransmission *pTransmission);
void AttachEngine(IEngine *m_pEngine);
...
...
private:
IEngine *m_pEngine;
ITransmission *m_pTransmission;
};
Now a car is a vehicle with an engine and a transmission. As long as the engine implementes the IEngine interface and the transmission implements the ITransmission interface then they can be attached to this car. I would envision that Car would be used as a base class for all cars. Technically you could derive trucks and motorcyles from Car as well but for clarity's sake I would not.
I left out a possible IDifferential would allow you to attach different differentials (or differential gears) to the car allowing you to change the final drive gear. As well you would need ITire which would describe a tire and allow you to create many different types of tires and attach them to your car. Note that you would not want to aggregate a specific amount of ITire pointers in car because this would limit you to that amount. You probably would want some type of ITireSystem object that would encapsulate how many tires belonged on the car. Given this you very well could design and test a car that had less/more than 4 tires which might be useful for other types of vehicles.
If your physics code took all of this information into account you would essentially have a very robust simulation of a vehicle in which you could swap out tires, engines, trannies, diffs, etc. and test all of them on your virtual track. You would be able to design tractors, trucks, cars, race cars, semis, etc. all with this simple interface. Thus the power of polymorphism.