Language: C++
Graphics: OGRE3D
Physics: Havok Physics
Programming/Design/Menu Art: Daniel Zeligman
The Z Component Engine is an experiment in game architecture for me. I have taken zero courses related to games and have been mostly self-taught. This system is an alterantive to a deep traditional inheritance hiearchy.
The end goal of using a component design is to have increased modularity, greater flexibility, easier maintenance and overall a more polished code base.
The system I have created using Havok Physics and Ogre3D allows for a creation of a simple 3D game with all the key features one would expect: Collision detection response, Skeletal animation, dynamic textures, sound triggers, phantom triggers and its fully data driven.
I first chose to apply this approach in order to achieve greater flexibility when making design changes as well as give me an easier time when I need to refactor. In a Component-based system game objects are usually just a collection of the components that they need in order to function. Each component exists to provide a single piece of functionality to their parent game object. For instance, a simple Box would have a Render component, Physics component, AI component and maybe a Position component.
A Component-based architecture also allows easier data-driven approaches to the gameplay. Logic or specific values can be easily written in XML files to tie directly to individual components which can be loaded at run-time.
My Component and GameObject headers can be seen below in the Code Snippet section of this page. Each GameObject contains a map of its components. GameObjects can have multiple instances of a single component as long as they have unique IDs. I first only allowed a single instance of a family, but more complex objects such as a player may need many AnimationComponents and SoundComponents. Components usually do not know anything about other components. They do most contact with the environment by sending events to their parent subsystem which then sends events to the other subsystems who ahve registered to listen for that type of event. However, objects do have public accessors to their components for some cases where components require direct access for efficient use.
Individual objects are created initially through prototype xml files. The game will traverse the xml files feeding data about each component to the components corresponding subsystem. The subsystem will create the specific component, store a reference to it and then return it to be added to the GameObject.
Screenshots:
Code Snippets:
GameObject.h
GameObject.cpp
Component.h
PhysicsComponent.h
XML Keyframed Platform Example
Some brief discussions on a few of the classes are below.
Below are my interfaces for a simple GameObject container for various components. It is simple and can represent a variety of objects, ranging from Door's with triggers, static and dynamic platforms , scriptable physics crates, a dynamic light or anything else than can be made up of individual components. The Component header, an example xml representation of a platform and something else are shown below.
1 #pragma once
2 #include <string>
3 #include <vector>
4 #include <map>
5
6 class Component;
7
8 //Base GameObject Component holder
9 class GameObject
10 {
11 public:
12 /**
13 * Base constructor
14 * @param id the GO id
15 */
16 GameObject(const std::string& id);
17
18 /**
19 * Base destructor
20 */
21 ~GameObject(void);
22
23 /**
24 * Get the string ID of this Game Object
25 * @return the std::string ID
26 */
27 const std::string& getID();
28
29 /**
30 * set the string ID of this Game Object
31 * @param id std::string
32 * @throws IllegalParameterException if id is empty
33 */
34 void setID( const std::string& id );
35
36 /**
37 * Add a new component to this game object
38 * Will replace a old component of the same family if that family already exists
39 * @param component the new component to be added
40 * @throws IllegalParameterException if component is NULL
41 */
42 void addComponent(Component* component);
43
44 /**
45 * Remove the component of the corresponding family
46 * @param componentFamily the family type
47 * @throws IllegalParameterException if componentFamily is empty
48 */
49 void removeComponent(const std::string& componentFamily,const std::string& compID);
50
51 /**
52 * Get a component based off its familyID key
53 * @param familyID the family string ID
54 * @param compID the comp string ID
55 * @return the component if it exists, otherwise NULL
56 * @throws IllegalParameterException if familyID is empty
57 */
58 Component* getComponentByFamilyID( const std::string& familyID,const std::string& compID );
59
60 /**
61 * Create a new gameobject with the same type of components as this Game Object
62 * @param name the new GameObject name/id
63 * @return the newly created GameObject
64 * @throws IllegalParameterException if name is empty
65 */
66 GameObject* clone(const std::string& name);
67
68 /**
69 * ----USED for a few components that need a member pointer to another for efficient use
70 * Get a component based off its family ID
71 * This will return the component with its actual type
72 * thus not requiring a dynamic downcast
73 * @param id the component ID
74 * @return the component index
75 */
76 template<class T>
77 T* GetInterface(const std::string& id,const std::string& compID )
78 {
79 if(mComponents[id].size() == 0)
80 {
81 return reinterpret_cast<T*>(NULL);
82 }
83 return reinterpret_cast<T*>( mComponents[id][compID] );
84 }
85
86 /**
87 * Register a component of this object based off its ID
88 * @param id the family ID
89 * @param compID
90 * @param p the component
91 */
92 template<class T>
93 void RegisterInterface(const std::string& id,const std::string& compID, T* p )
94 {
95 mComponents[id][compID] = p;
96 }
97 /**
98 * clear and delete all Components
99 */
100 void clearGOCs();
101
102
103 private:
104 //uncopyable
105 explicit GameObject(const GameObject& go);
106 GameObject& operator=(const GameObject& go);
107
108 std::string mOID; //unique identifier for this object
109 //Key 1: Family ID, Key 2:Component ID
110 std::map<const std::string, std::map<const std::string,Component*> > mComponents;
111
112
113 };
114
115
1 #pragma once
2 #include <crtdbg.h>
3 #include "EventListener.h"
4 #include "Event.h"
5
6 class GameObject;
7 class SubSystem;
8
9 /// Base class for all components, contains pointers to its owner and subsystem
10 class Component : public EventListener
11 {
12 public:
13 /**
14 * Base constructor
15 */
16 Component(void);
17
18 Component(const std::string& id);
19
20 /**
21 * Base destructor
22 */
23 virtual ~Component(void);
24
25 /**
26 * virtual familyID to represent the component family as a key value in a hash
27 * @return the family ID
28 */
29 virtual const std::string& familyID() = 0;
30
31 /**
32 * set the GameObject owner
33 * @param owner The GO component owner
34 * @throws IllegalParametersException If owner is NULL
35 */
36 void setOwner(GameObject* owner);
37
38 /**
39 * Get the Owner of this component
40 * @return the GO owner
41 */
42 GameObject* getOwner() const;
43
44 /**
45 * Set the subsystem
46 * @param subSystem The subsystem of this component
47 * @throws IllegalParametersException If subSystem is NULL
48 */
49 void setSubSystem(SubSystem* subSystem);
50
51 /**
52 * Get the Subsystem of this component
53 * @return the component subsystem
54 */
55 SubSystem* getSubSystem();
56
57 const std::string getID();
58
59 /**
60 * Duplicates this components members
61 * Creates new Ogre objects when required
62 * @param name the new name of the component
63 * @return the new Cloned component
64 * @throws IllegalParametersException If name is NULL
65 */
66 virtual Component* clone(const std::string& name) = 0;
67
68 /**
69 * initialization function
70 * implemented by components that require secondary construction
71 */
72 virtual void init() = 0;
73
74 /**
75 * Used to receive events sent from other components/subsystems
76 * @param event the Event to Handle
77 */
78 virtual void handleEvent(Event* event){}
79
80 protected:
81 GameObject* mOwner; //Parent GameObject owner
82 SubSystem* mSubSystem; //Parent SubSystem
83 std::string mID;
84 private:
85 //Uncopyable
86 explicit Component(const Component& co);
87 Component& operator= (const Component& co);
88
89 };
90
1 #pragma once
2 #include <crtdbg.h>
3 #include <Ogre.h>
4 #include "PhysicsData.h"
5 #include "Component.h"
6
7 #include <Physics/Dynamics/Entity/hkpRigidBody.h>
8 #include <Physics/Dynamics/Collide/hkpCollisionListener.h>
9
10 // Represents a base level Havok Rigid Body
11 class PhysicsComponent :
12 public Component , public hkpCollisionListener
13 {
14 public:
15 /**
16 * Base physics constructor
17 * @param data the PhysicsData to populate the initial body / col
18 */
19 PhysicsComponent(PhysicsData data);
20
21 /**
22 *
23 * Base destructor
24 */
25 virtual ~PhysicsComponent(void);
26
27 /**
28 * Duplicates this components members
29 * Creates new Ogre objects when required
30 * @param name the new name of the component
31 * @return the new Cloned component
32 * @throws IllegalParametersException If name is NULL
33 */
34 Component* clone(const std::string& name);
35
36 /**
37 * initialization function
38 */
39 virtual void init();
40
41 /**
42 * virtual familyID to represent the component family as a key value in a hash
43 * @return the family ID, MeshComponent
44 */
45 virtual const std::string& familyID();
46
47 /**
48 * Get the physics body
49 * @return the Havok Rigid Body
50 */
51 hkpEntity* getBody();
52
53 //individual customized steps performed here
54 virtual void preUpdate(float dt);
55 virtual void postUpdate(float dt);
56
57 //helper method for player control
58 virtual void setTranslate(const Ogre::Vector3& translate,float angle);
59
60
61 // Called after a contact point was added
62 virtual void contactPointAddedCallback( hkpContactPointAddedEvent& event );
63
64 virtual void contactPointConfirmedCallback( hkpContactPointConfirmedEvent& event);
65
66
67 virtual void contactPointRemovedCallback( hkpContactPointRemovedEvent& event );
68
69 // Called just before the collisionResult is passed to the constraint system (solved).
70 virtual void contactProcessCallback( hkpContactProcessEvent& event );
71
72 void setMotionType(int motionType);
73
74 private:
75 //Uncopyable
76 /**
77 * Explicit private copy constructor making class uncopyable
78 */
79 explicit PhysicsComponent(const PhysicsComponent& co);
80 PhysicsComponent& operator=(const PhysicsComponent& co);
81
82 protected:
83 /**
84 * Base physics constructor
85 * @see clone()
86 */
87 PhysicsComponent();
88
89
90 static std::string mFamilyID;
91 hkpRigidBody* mBody;
92
93 //keyframed physics members
94 std::vector<Ogre::Vector3> mOffsets;
95 float mMoveSpeed;
96 float mWaitTime;
97 float mCurrentWaitTime;
98 int mCurrentIndex;
99 int mMotionType;
100 };
101
102
103
84 <MovingPlatform>
85 <MeshComponent>
86 <MeshData name="platform0" meshName="cube.mesh" materialName="Simple/RockWall" entName="platform0" castShadows="true"/>
87 </MeshComponent>
88 <RenderComponent>
89 <RenderData name="platform0" sizeX ="8" sizeY=".17" sizeZ ="8" posX="40" posY="25" posZ="10" visible="1" />
90 </RenderComponent>
91 <PhysicsComponent>
92 <PhysicsData
93 name="platform0"
94 collisionShape="Box"
95 mass ="50"
96 motionType="1"
97 posX="40" posY="25" posZ="10"
98 friction="1.0f"
99 restitution="0.0f"
100 colX="8" colY=".4" colZ="8"
101 moveSpeed="226.0f"
102 waitTime=".1f"
103 >
104 <offset posX="40" posY="5" posZ="10" />
105
106 </PhysicsData>
107 </PhysicsComponent>
108 </MovingPlatform>



