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

No comments:

Post a Comment