using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MessagingInCSharp { class Program { static void Main( string[] args ) { // Create a scene manager SceneManager sceneMgr = new SceneManager(); // Have scene manager create an entity for us, which // automatically puts the object into the scene as well Entity myEntity = sceneMgr.CreateEntity(); // Create a render component RenderComponent renderComp = new RenderComponent(); // Attach render component to the entity we made myEntity.AddComponent(renderComp); // Set 'myEntity' position to (1, 2, 3) MsgSetPosition msgSetPos = new MsgSetPosition(myEntity.uniqueID, 1.0f, 2.0f, 3.0f); sceneMgr.SendMessage(msgSetPos); Console.WriteLine("Position set to (1, 2, 3) on entity with ID: " + myEntity.uniqueID); Console.WriteLine("Retreiving position from entity with ID: " + myEntity.uniqueID); // Get 'myEntity' position to verify it was set properly MsgGetPosition msgGetPos = new MsgGetPosition(myEntity.uniqueID); sceneMgr.SendMessage(msgGetPos); Console.WriteLine("X: " + msgGetPos.x); Console.WriteLine("Y: " + msgGetPos.y); Console.WriteLine("Z: " + msgGetPos.z); } } public enum MessageType { SetPosition, GetPosition } public class Vector3 { public float x = 0.0f; public float y = 0.0f; public float z = 0.0f; } public class BaseMessage { public int destEntityID; public MessageType messageType; protected BaseMessage( int destinationEntityID, MessageType messageType ) { this.destEntityID = destinationEntityID; this.messageType = messageType; } } public class PositionMessage : BaseMessage { public float x; public float y; public float z; protected PositionMessage( int destinationEntityID, MessageType messageType, float X = 0.0f, float Y = 0.0f, float Z = 0.0f) : base(destinationEntityID, messageType) { this.x = X; this.y = Y; this.z = Z; } } public class MsgSetPosition : PositionMessage { public MsgSetPosition( int destinationEntityID, float X, float Y, float Z ) : base(destinationEntityID, MessageType.SetPosition, X, Y, Z) {} } public class MsgGetPosition : PositionMessage { public MsgGetPosition( int destinationEntityID) : base(destinationEntityID, MessageType.GetPosition, 0.0f, 0.0f, 0.0f) {} } public abstract class BaseComponent { public virtual bool SendMessage( BaseMessage msg ) { return false; } } public class RenderComponent : BaseComponent { public override bool SendMessage( BaseMessage msg ) { // Entity has a switch for any messages it cares about switch (msg.messageType) { case MessageType.SetPosition: { // Update render mesh position/translation Console.WriteLine("RenderComponent handling SetPosition"); } break; default: return base.SendMessage(msg); } return true; } } public class Entity { public int uniqueID; public int UniqueID { get { return this.uniqueID; } set { this.uniqueID = value; } } private Vector3 position = new Vector3(); private List<BaseComponent> components = new List<BaseComponent>(); public Entity( int uniqueID ) { this.uniqueID = uniqueID; } public void AddComponent( BaseComponent component ) { this.components.Add(component); } public bool SendMessage( BaseMessage msg ) { bool messageHandled = false; // Entity has a switch for any messages it cares about switch (msg.messageType) { case MessageType.SetPosition: { MsgSetPosition msgSetPos = msg as MsgSetPosition; position.x = msgSetPos.x; position.y = msgSetPos.y; position.z = msgSetPos.z; messageHandled = true; Console.WriteLine("Entity handled SetPosition"); } break; case MessageType.GetPosition: { MsgGetPosition msgGetPos = msg as MsgGetPosition; msgGetPos.x = position.x; msgGetPos.y = position.y; msgGetPos.z = position.z; messageHandled = true; Console.WriteLine("Entity handled GetPosition"); } break; default: return PassMessageToComponents(msg); } // If the entity didn't handle the message but the component // did, we return true to signify it was handled by something. messageHandled |= PassMessageToComponents(msg); return messageHandled; } private bool PassMessageToComponents( BaseMessage msg ) { bool messageHandled = false; this.components.ForEach(c => messageHandled |= c.SendMessage(msg) ); return messageHandled; } } public class SceneManager { private Dictionary<int, Entity> entities = new Dictionary<int,Entity>(); private static int nextEntityID = 0; // Returns true if the entity or any components handled the message public bool SendMessage( BaseMessage msg ) { // We look for the entity in the scene by its ID Entity entity; if ( entities.TryGetValue(msg.destEntityID, out entity) ) { // Entity was found, so send it the message return entity.SendMessage(msg); } // Entity with the specified ID wasn't found return false; } public Entity CreateEntity() { Entity newEntity = new Entity(SceneManager.nextEntityID++); entities.Add(newEntity.UniqueID, newEntity); return newEntity; } } }
A blog by a professional game developer, about game programming and development. My posts will range in comments from beginners to game development to those at a more advanced level.
Saturday, February 11, 2012
Game Engine Architecture, C#
Here's the same architecture as my last two posts, but this time in C# (the last two were in Scala and C++).
Game Engine Architecture, now in Scala
In my previous post I discussed game engine architecture, object<->component hierarchy, etc. Here's the sample architecture as the last post, but instead of C++ this version is written in Scala. One thing worth noting, we get identical functionality in this Scala example, with 35% less code!
package MessagingInScala object GameMessageType extends Enumeration { type GameMessageType = Value val SetPosition = Value val GetPosition = Value } import GameMessageType._ class Vector3(var x: Float, var y: Float, var z: Float) abstract class GameMessage (val destinationID: Int) { def messageType: GameMessageType } abstract class PositionMessage (destinationID: Int, var x: Float, var y: Float, var z: Float) extends GameMessage(destinationID) { } class MsgSetPosition(destinationID: Int, x: Float, y: Float, z: Float) extends PositionMessage(destinationID, x, y, z) { override val messageType = SetPosition } object MsgSetPosition { def Apply(destinationID: Int, x: Float, y: Float, z: Float) = new MsgSetPosition(destinationID, x, y, z) } class MsgGetPosition(destinationID: Int) extends PositionMessage(destinationID, 0.0f, 0.0f, 0.0f) { val messageType = GetPosition } object MsgGetPosition { def Apply(destinationID: Int) = new MsgGetPosition(destinationID) } abstract class BaseComponent { def SendMessage(message: GameMessage) = false } class RenderComponent extends BaseComponent { override def SendMessage(message: GameMessage): Boolean = { message.messageType match { case GameMessageType.SetPosition => { // Update render mesh position println("RenderComponent received SetPosition") true // Return value } case _ => super.SendMessage(message) } } } class Entity(ID: Int) { private var Components: List[BaseComponent] = List() var position: Vector3 = new Vector3(0.0f, 0.0f, 0.0f) val uniqueID: Int = ID def AddComponent(component: BaseComponent) { Components = component :: Components } def SendMessage(message: GameMessage): Boolean = { message.messageType match { case GameMessageType.SetPosition => { println("Entity received SetPosition") var msgSetPos: MsgSetPosition = message.asInstanceOf[MsgSetPosition] position.x = msgSetPos.x position.y = msgSetPos.y position.z = msgSetPos.z PassMessageToComponents(message) // This is also the return value } case GameMessageType.GetPosition => { println("Entity received GetPosition") var msgGetPos: MsgGetPosition = message.asInstanceOf[MsgGetPosition] msgGetPos.x = position.x msgGetPos.y = position.y msgGetPos.z = position.z PassMessageToComponents(message) // This is also the return value } case _ => PassMessageToComponents(message) // This is also the return value } } def PassMessageToComponents(message: GameMessage): Boolean = { var messageHandled = false Components.foreach(c => { messageHandled |= c.SendMessage(message) }) messageHandled } } object Entity { var nextUUID: Int = 0 def apply() = new Entity(nextUUID + 1) } class SceneManager { // You don't need to type the entire HashMap path like this, I'm // doing this so the reader understands this is not a Java HashMap var entities: Map[Int, Entity] = Map.empty[Int, Entity] def SendMessage(message: GameMessage): Boolean = { if ( entities.contains(message.destinationID) ) { entities(message.destinationID).SendMessage(message) } else { false } } def CreateEntity(): Entity = { val newEntity: Entity = Entity() entities += newEntity.uniqueID -> newEntity newEntity } } object Main extends App { val sceneMgr: SceneManager = new SceneManager val testEntity = sceneMgr.CreateEntity() val testRenderComp = new RenderComponent testEntity.AddComponent(testRenderComp) val msgSetPos: MsgSetPosition = new MsgSetPosition(testEntity.uniqueID, 1.0f, 2.0f, 3.0f) sceneMgr.SendMessage(msgSetPos) println("Position set to (1, 2, 3) on entity with ID " + testEntity.uniqueID) println("Retreiving position from object with ID: " + testEntity.uniqueID) val msgGetPos: MsgGetPosition = new MsgGetPosition(testEntity.uniqueID) sceneMgr.SendMessage(msgGetPos) println("X: " + msgGetPos.x) println("Y: " + msgGetPos.y) println("Z: " + msgGetPos.z) }
Thursday, February 9, 2012
Game Engine Architecture, C++
I recently saw a question on Stack Overflow about engine architecture, specifically how to layout systems and communicate between them. My favorite engine architecture by far is an object<->component architecture. I posted a full working code sample as an answer to the question, along with details. Here's the link to the question, if you don't wish to follow the link, here's the full answer along with code. Enjoy!
My favorite game engine structure is the interface and object<->component model using messaging for communication between almost all parts.
You have multiple interfaces for main engine parts such as your scene manager, resource loader, audio, renderer, physics, etc.
I have the scene manager in charge of all objects in the 3D scene/world.
Object is a very atomic class, containing only a few things that are common to almost everything in your scene, in my engine the object class holds only position, rotation, a list of components, and a unique ID. Every object's ID is generated by a static int, so that no two objects will every have the same ID, this allows you to send messages to an object by its ID, rather than having to have a pointer to the object.
The list of components on the object is what gives that objects is main properties. For example, for something that you can see in the 3D world, you would give your object a render component that contains the information about the render mesh. If you want an object to have physics you would give it a physics component. If you want something to act as a camera, give it a camera component. The list of components can go on and on.
Communication between interfaces, objects, and components is key. In my engine I have a generic message class that contains only a unique ID, and a message type ID. The unique ID is the ID of the object you want the message to go to, and the message type ID is used by the object receiving the message so it knows what type of message it is.
Objects can handle the message if they need, and they can pass the message on to each of their components, and components will often do important things with the message. For example, if you want to change and object's position you send the object a SetPosition message, the object may update its position variable when it gets the message, but the render component may need to message to update the position of the render mesh, and the physics component may need the message to update the physics body's position.
Here is a very simple layout of scene manager, object, and component, and message flow, that I whipped up in about an hour, written in C++. When run it sets the position on an object, and the message passes through the render component, then retrieves the position from the object. Enjoy!
My favorite game engine structure is the interface and object<->component model using messaging for communication between almost all parts.
You have multiple interfaces for main engine parts such as your scene manager, resource loader, audio, renderer, physics, etc.
I have the scene manager in charge of all objects in the 3D scene/world.
Object is a very atomic class, containing only a few things that are common to almost everything in your scene, in my engine the object class holds only position, rotation, a list of components, and a unique ID. Every object's ID is generated by a static int, so that no two objects will every have the same ID, this allows you to send messages to an object by its ID, rather than having to have a pointer to the object.
The list of components on the object is what gives that objects is main properties. For example, for something that you can see in the 3D world, you would give your object a render component that contains the information about the render mesh. If you want an object to have physics you would give it a physics component. If you want something to act as a camera, give it a camera component. The list of components can go on and on.
Communication between interfaces, objects, and components is key. In my engine I have a generic message class that contains only a unique ID, and a message type ID. The unique ID is the ID of the object you want the message to go to, and the message type ID is used by the object receiving the message so it knows what type of message it is.
Objects can handle the message if they need, and they can pass the message on to each of their components, and components will often do important things with the message. For example, if you want to change and object's position you send the object a SetPosition message, the object may update its position variable when it gets the message, but the render component may need to message to update the position of the render mesh, and the physics component may need the message to update the physics body's position.
Here is a very simple layout of scene manager, object, and component, and message flow, that I whipped up in about an hour, written in C++. When run it sets the position on an object, and the message passes through the render component, then retrieves the position from the object. Enjoy!
#include <iostream> #include <stdio.h> #include <list> #include <map> using namespace std; struct Vector3 { public: Vector3() : x(0.0f), y(0.0f), z(0.0f) {} float x, y, z; }; enum eMessageType { SetPosition, GetPosition, }; class BaseMessage { protected: // Abstract class, constructor is protected BaseMessage(int destinationObjectID, eMessageType messageTypeID) : m_destObjectID(destinationObjectID) , m_messageTypeID(messageTypeID) {} public: // Normally this isn't public, just doing it to keep code small int m_destObjectID; eMessageType m_messageTypeID; }; class PositionMessage : public BaseMessage { protected: // Abstract class, constructor is protected PositionMessage(int destinationObjectID, eMessageType messageTypeID, float X = 0.0f, float Y = 0.0f, float Z = 0.0f) : BaseMessage(destinationObjectID, messageTypeID) , x(X) , y(Y) , z(Z) { } public: float x, y, z; }; class MsgSetPosition : public PositionMessage { public: MsgSetPosition(int destinationObjectID, float X, float Y, float Z) : PositionMessage(destinationObjectID, SetPosition, X, Y, Z) {} }; class MsgGetPosition : public PositionMessage { public: MsgGetPosition(int destinationObjectID) : PositionMessage(destinationObjectID, GetPosition) {} }; class BaseComponent { public: virtual bool SendMessage(BaseMessage* msg) { return false; } }; class RenderComponent : public BaseComponent { public: /*override*/ bool SendMessage(BaseMessage* msg) { switch(msg->m_messageTypeID) { case SetPosition: { // Update render mesh position/translation cout << "RenderComponent handling SetPosition\n"; } break; default: return BaseComponent::SendMessage(msg); } return true; } }; class Object { public: Object(int uniqueID) : m_UniqueID(uniqueID) { } int GetObjectID() const { return m_UniqueID; } void AddComponent(BaseComponent* comp) { m_Components.push_back(comp); } bool SendMessage(BaseMessage* msg) { bool messageHandled = false; // Object has a switch for any messages it cares about switch(msg->m_messageTypeID) { case SetPosition: { MsgSetPosition* msgSetPos = static_cast<MsgSetPosition*>(msg); m_Position.x = msgSetPos->x; m_Position.y = msgSetPos->y; m_Position.z = msgSetPos->z; messageHandled = true; cout << "Object handled SetPosition\n"; } break; case GetPosition: { MsgGetPosition* msgSetPos = static_cast<MsgGetPosition*>(msg); msgSetPos->x = m_Position.x; msgSetPos->y = m_Position.y; msgSetPos->z = m_Position.z; messageHandled = true; cout << "Object handling GetPosition\n"; } break; default: return PassMessageToComponents(msg); } // If the object didn't handle the message but the component // did, we return true to signify it was handled by something. messageHandled |= PassMessageToComponents(msg); return messageHandled; } private: // Methods bool PassMessageToComponents(BaseMessage* msg) { bool messageHandled = false; std::list<BaseComponent*>::iterator compIt = m_Components.begin(); for ( compIt; compIt != m_Components.end(); ++compIt ) { messageHandled |= (*compIt)->SendMessage(msg); } return messageHandled; } private: // Members int m_UniqueID; std::list<BaseComponent*> m_Components; Vector3 m_Position; }; class SceneManager { public: // Returns true if the object or any components handled the message bool SendMessage(BaseMessage* msg) { // We look for the object in the scene by its ID std::map<int, Object*>::iterator objIt = m_Objects.find(msg->m_destObjectID); if ( objIt != m_Objects.end() ) { // Object was found, so send it the message return objIt->second->SendMessage(msg); } // Object with the specified ID wasn't found return false; } Object* CreateObject() { Object* newObj = new Object(nextObjectID++); m_Objects[newObj->GetObjectID()] = newObj; return newObj; } private: std::map<int, Object*> m_Objects; static int nextObjectID; }; // Initialize our static unique objectID generator int SceneManager::nextObjectID = 0; int main() { // Create a scene manager SceneManager sceneMgr; // Have scene manager create an object for us, which // automatically puts the object into the scene as well Object* myObj = sceneMgr.CreateObject(); // Create a render component RenderComponent* renderComp = new RenderComponent(); // Attach render component to the object we made myObj->AddComponent(renderComp); // Set 'myObj' position to (1, 2, 3) MsgSetPosition msgSetPos(myObj->GetObjectID(), 1.0f, 2.0f, 3.0f); sceneMgr.SendMessage(&msgSetPos); cout << "Position set to (1, 2, 3) on object with ID: " << myObj->GetObjectID() << '\n'; cout << "Retreiving position from object with ID: " << myObj->GetObjectID() << '\n'; // Get 'myObj' position to verify it was set properly MsgGetPosition msgGetPos(myObj->GetObjectID()); sceneMgr.SendMessage(&msgGetPos); cout << "X: " << msgGetPos.x << '\n'; cout << "Y: " << msgGetPos.y << '\n'; cout << "Z: " << msgGetPos.z << '\n'; }
Tuesday, February 7, 2012
LINQ and Lambda in C# 4.0 for cleaner, leaner code
There are times when less is more. If you want clean and concise code, or if readability is more important than finely tuned high-performance, then LINQ and Lambda expressions might just be for you.
First let's look at a small code sample that does NOT use LINQ or Lambda that where we want to filter through some physics collision results for objects of a certain ID, and then sort our results by the lower IDs first.
First let's look at a small code sample that does NOT use LINQ or Lambda that where we want to filter through some physics collision results for objects of a certain ID, and then sort our results by the lower IDs first.
struct CollisionPair : IComparable, IComparer { public int first; public int second; // Since we're sorting we'll need to write our own Comparer int IComparer.Compare( object one, object two ) { CollisionPair pairOne = (CollisionPair)one; CollisionPair pairTwo = (CollisionPair)two; if (pairOne.first < pairTwo.first) return -1; else if (pairTwo.first < pairOne.first) return 1; else return 0; } // ...and our own comparable int IComparable.CompareTo( object two ) { CollisionPair pairTwo = (CollisionPair)two; if (this.first < pairTwo.first) return -1; else if (pairTwo.first < this.first) return 1; else return 0; } } static void Main( string[] args ) { List<CollisionPair> collisions = new List<CollisionPair> { new CollisionPair { first = 1, second = 5 }, new CollisionPair { first = 2, second = 3 }, new CollisionPair { first = 5, second = 4 } }; List<CollisionPair> sortedCollisionsWithFive = new List<CollisionPair>(); foreach (CollisionPair c in collisions) { if (c.first == 5 || c.second == 5) { sortedCollisionsWithFive.Add(c); } } sortedCollisionsWithFive.Sort(); foreach (CollisionPair c in sortedCollisionsWithFive) { Console.WriteLine("Collision between " + c.first + "and " + c.second); } }Now let's take a look at what we can do when we use LINQ and Lambda expressions to clean things up a bit.
struct CollisionPair { public int first; public int second; } static void Main( string[] args ) { List<CollisionPair> collisions = new List<CollisionPair> { new CollisionPair { first = 1, second = 5 }, new CollisionPair { first = 2, second = 3 }, new CollisionPair { first = 5, second = 4 } }; (from c in collisions where ( c.first == 5 || c.second == 5 ) orderby c.first select c).ForEach(c => Console.WriteLine("Collision between " + c.first + "and " + c.second)); }So which part is LINQ, and which part is Lambda? Here's the LINQ part:
(from c in collisions where ( c.first == 5 || c.second == 5 ) orderby c.first select c)This loops through 'collisions', and you can reference each element in the container by 'c'. Then it filters the list so only elements with either a first or second ID of 5 remain. Then it orders the list by the first element, and then selects all elements remaining and creates a list out of them. In this case the list may be hard to see, it's formed by wrapping the entire LINQ expression in parentheses. The sample below might make it easier to see:
// LINQ var filteredCollisions = from c in collisions where ( c.first == 5 || c.second == 5 ) orderby c.first select cAnd here is the Lambda expression:
// Lambda ForEach(c => Console.WriteLine("Collision between " + c.first + "and " + c.second));This performs a ForEach on a list, the list formed by the LINQ expression that comes before it in the original code. For each element in the list, it calls each element 'c', and then performs a Console.Writeline on each element. Here's the original sample, simplified a bit:
// LINQ var filteredCollisions = from c in collisions where ( c.first == 5 || c.second == 5 ) orderby c.first select c // Lambda ForEach(c => Console.WriteLine("Collision between " + c.first + "and " + c.second));If you want to use LINQ you'll want to make sure to use System.Linq:
using System.Linq;
Subscribe to:
Posts (Atom)