The Gfx and collider components

Now that we have seen the interface, let's look at two very important components. These are important because they allow game objects to interact with two of the other core pieces of our engine, graphics and physics.

The first one we will look at is the GfxComponent class. This class allows the game object to have a visual representation in the game. It contains the two minimum pieces of information to draw an object in the game:

//GfxComponent.h 
enum DrawSpace
{
DS_WORLD,
DS_HUD
};

class GfxComponent : public M5Component
{
public:
GfxComponent(void);
~GfxComponent(void);
void Draw(void) const;
virtual void Update(float dt);
virtual GfxComponent* Clone(void) const;
virtual void FromFile(M5IniFile& iniFile);
void SetTextureID(int id);
void SetDrawSpace(DrawSpace drawSpace);
private:
int m_textureID; //!< Texture id loaded from graphics.
DrawSpace m_drawSpace; //!The space to draw in
};

The two pieces of information we need to draw an object are which texture to draw and which space to draw in. Of course, we need a texture to draw, but the draw space may be a bit more confusing. It is just an enum letting us know which type of graphics projection should be used with the object. For now, it is enough to know that the HUD draw space is always on top and isn't affected by camera movement or camera zoom. Of course, there could be more data, such as texture color and texture coordinates. These could be added in a derived class if we wanted. Here we are just showing the basics.

There are a few functions used to set these values, as well as a FromFile function that we talked about a little before. The Update function doesn't do anything for this component because there is nothing to update. The Draw function will be called by the graphics engine, making each M5Component responsible for drawing itself. However, the most important function for this chapter is the Clone:

GfxComponent* GfxComponent::Clone(void) const 
{
//Allocates new object and copies data
GfxComponent* pNew = new GfxComponent;
pNew->m_pObj = m_pObj;
pNew->m_textureID = m_textureID;
pNew->m_drawSpace = m_drawSpace;

if (m_drawSpace == DrawSpace::DS_WORLD)
M5Gfx::RegisterWorldComponent(pNew);
else
M5Gfx::RegisterHudComponent(pNew);

return pNew;
}

In this function, we simply create a new GfxComponent and copy the relevant data from this object to the newly created one. What you don't see is that in the GfxComponent constructor, the component type is set by calling the M5Component component constructor which, of course, also gives this component a unique ID. The last thing we do is register this component with the graphics engine depending on the draw space. This class automatically unregisters itself when it is destroyed:

GfxComponent::GfxComponent(void): 
M5Component(CT_GfxComponent),
m_textureID(0),
m_drawSpace(DrawSpace::DS_WORLD)
{
}
GfxComponent::~GfxComponent(void)
{
M5Gfx::UnregisterComponent(this);
}

Now that we have seen the GfxComponent, let's look at the most basic of all physics colliders. The Mach5 Engine ColliderComponent is as simple as possible for a 2D game. For now, it is only concerned with circle versus circle collisions. It could easily be extended to test for rectangle collision as well:

//ColliderComponent.h 
class ColliderComponent : public M5Component
{
public:
ColliderComponent(void);
~ColliderComponent(void);
virtual void Update(float dt);
virtual void FromFile(M5IniFile& iniFile);
virtual ColliderComponent* Clone(void) const;
void TestCollision(const ColliderComponent* pOther);
private:
float m_radius;
};

This class is a lot like the previous one because it is connected to one of the core pieces of the game engine. Just like all components, FromFile must be overloaded to read component data from an .ini file. Update must also be overloaded but, just like with the GfxComponent, this doesn't do anything in this simple version. If the class used oriented bounding boxes, the Update function could be used to update the corner points of the oriented box. The TestCollision function is also important. It is called by the Physics Engine to test if this object is colliding with another object. If it is, the two objects are added to a list of colliding pairs that can be resolved later. Again, the most important function for this chapter is the Clone:

ColliderComponent* ColliderComponent::Clone(void) const 
{
ColliderComponent* pNew = new ColliderComponent;
pNew->m_radius = m_radius;
pNew->m_pObj = m_pObj;
M5Phy::RegisterCollider(pNew);

return pNew;
}

Just like the GfxComponent, this component first creates a new version of itself, then copies the important information into the new component. Before returning the new component, it first registers itself with the physics engine. Since it is registered, it must be unregistered when it is destroyed, so we do that in the destructor:

ColliderComponent::ColliderComponent(void) : 
M5Component(CT_ColliderComponent), m_radius(0)
{
}
ColliderComponent::~ColliderComponent(void)
{
M5Phy::UnregisterCollider(this);
}

There are a few things to point out with both classes. First, notice that we don't clone the m_type, the m_id, or the isDead variables. This isn't necessary. The type is set by the constructor in the M5Component base class when we call the constructor. The id is also set in the base class, but it is important to point out that the purpose of the m_id is to be unique. It wouldn't serve the correct purpose if we also copied the id. Instead, we are copying the rest of the important data but we recognize that this is a separate component, not just an exact copy. For the same reason, we also don't copy the isDead variable. We are creating a new component that is like the old one, but still a unique component. If we copied the isDead, this component would be deleted in this frame or the next.

Next, both classes register themselves with engines in the clone method instead of the constructor. This is because of how they are intended to be used. Our object manager will hold a collection of these pre-created prototype components at the start of the game so they are ready to be cloned. We don't want these initial components to pollute the graphics or physics engine.

However, we are assuming that an object is being cloned and it also needs to live in the game world, so we register at that time. This seems like the most standard reason for cloning. It is better for the user to only worry about cloning, than to worry about cloning, registering, and then unregistering. If the user wishes to do something non-standard, they are free to unregister after cloning.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.219.249.210