Showing posts with label messaging. Show all posts
Showing posts with label messaging. Show all posts

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++).

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;
        }
    }
}

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)
}