Messaging system
  
  • Sending a standard message
    • Create an instance of a message
      • For example, if you want to get the position of an entity, there is already a message for that called MsgGetPosition, which is a custom message type. Feel free to create your own custom message types, and for more info on how to do so, I’ll discuss it fully later in the ‘Messaging System’ section.
      • MsgGetPosition msgGetPos = ObjectPool.Aquire<MsgGetPosition>();
         
      • Now that you have a message instance you can fill it with any needed data. In this case since this message is designed to return information, the only data you need to give it is the target of the message (the Entity you want to get the position of).
      • msgGetPos.UniqueTarget = this.sceneMgrRootEntity.UniqueID;
      • This specifies we want to send the message to the sceneMgrRootEntity. All entities have a unique 64-bit ID. This ID allows you to send anything entity a message directly, and immediately.
      • Now we have to actually send the message. If we’re sending a message to an entity we want to use QSGame’s SendMessage function.
      • this.Game.SendMessage(msgGetPos);
         
      • This sends the message immediately, and you by the next line of code you should already have your result returned.
      •  After SendMessage is called, ‘msgGetPos’ will now have the position of the entity. You can retrieve it simply by accessing the variable in ‘msgGetPos’
      • Vector3 sceneRootPosition = msgGetPos.Position;
         
      • If you're sending a message that doesn't need to occur immediately then you can use the QueueMessage() function instead of SendMessage(). This puts the message into a queue to get handled as soon as the engine has time. Currently the engine isn't very multi-threaded, but once it is this may be one of the things that is threaded out, so using QueueMessage makes your game more likely to be friendly for multi-threading.
        • Note: You cannot send messages with a 'request' protocol via QueueMessage(). A 'request' protocol means you need the result immediately returned to the message sender.
  • Sending an Interface-only Message
    • This allows you to send a message directly to a specific interface. 
    • To do this, send your message through SendInterfaceMessage() rather than SendMessage or QueueMessage.
    • SendInterfaceMessage() takes an InterfaceType value, which is a type that is unique to each interface. If I want to send a message to the CameraInterface I would do something like this:
      MsgGetRenderEntity camMsg = ObjectPool.Aquire<MsgGetRenderEntity>();
      this.game.SendInterfaceMessage(camMsg, InterfaceType.Camera);
    • Doing the above sends a MsgGetRenderEntity message directly to the CameraInterface. In this case, we’re using this message to return the UniqueID of the current Entity acting as the render camera.
  • Creating your own custom message class
    • Creating your own custom messages is actually pretty simple for most message types.
      • First, add a new message type to the MessageType file.
      • This file is located in (Messaging\MessageType.cs)
    • Now, to make your own message class.
    • Let's reference a message in the EntityMessages file for an example. Do a search for this line:
      public class MsgGamePadTrigger : Message<GamePadTrigger>
       
    • This is a good one to talk about here because you can see that this message is of a custom type (GamePadTrigger). All of the data contained within your custom message must be within the type inside of Message<>. So because this message is transmitting GamePadTrigger info, that is the type of the message.
    • Ok, now lets make one of the existing types from scratch. Lets see how MsgGetPosition was made.
    • Lets name our class, by convention it is good to start all messages with the letters Msg, this makes all messages easy to find by name. In this case we want a message that returns a position, so we can call it ‘GetPosition’ with ‘Msg’ on the front of it, so MsgGetPosition.
    • Positions in the QS Engine (and almost all XNA programs) are of the type Vector3, so we’ll make our message of that type as well.
      public class MsgGetPosition : Message<Vector3>
       
    • All messages have a member variable called ‘data’. This is always of the type that the message is made from, so ‘data’ in this case is Vector3. You can create accessors to ‘data’ to make it easy to use the message.
      public Vector3 Position
      {
          get { return this.data; }
          set { this.data = value; }
      }
       
    • You can see we made an accessor to this message’s ‘data’. Now whoever uses this message can simply access ‘Position’ which is very specific, rather than ‘data’, which is vague.
    • All custom message types must properly handle the Initialize() method. You must always call the base method’s initialize, and you must always initialize your message type in this method. The message type is the one you set up in the very first step in this section, in the MessageType.cs file.
      protected override void Initialize()
      {
          base.Initialize();
          this.type = MessageType.GetPosition;
          this.protocol = MessageProtocol.Request;
      }
       
    • All messages also have an Int64 called ‘UniqueTarget’. This is the unique ID of the entity that the  message will go to. This isn’t required when sending interface messages, only standard messages that are meant to go directly to an entity.
  • Message Protocol
    • Each message has an enum value called MessageProtocol, this tells the message what to expect in terms of handling. 'Request' is the default protocol, and this should be used for any message that needs to be handled and have data put into the message before returning to the sender, this message can only be handled by a single source before it will return to the sender. 'Broadcast' protocol is when the sender is letting the handler know about some data in the message and this message will continue through to all the components on an entity even if handled. 'Accumulate' is like request, except it can continue through to multiple components, giving each one a chance to make changes to the data in the message.
  • Message handling (better documentation pending on this section)
    • If you want to handle your own custom message, you need to listen for the message. If you look at the PhysicsInterface class as an example you can see how this is done.
    • You'll need to setup a delegate so that you'll receive a call whenever a game message is sent. Usually you just register your delegate from the constructor of your class.
    • When you receive a call to your delegate function you will then make a switch statement based on the messageType.
    • Within each case you'll cast the Message to the type of message you've received (which you know because of the switch). After casting it is a good thing to check to see if the type-cast worked.
    • Now you should have your message ready to read from.
  • So in summary:
    • Whatever data your message needs to carry is the type the message should be.
    • Create accessors to anything within that ‘data’ to make using the message easier.
    • If your message is made to send data out rather than request data back in, then make sure to set the protocol to 'Broadcast'.

Last edited Jan 11, 2012 at 6:11 PM by LordIkon, version 9

Comments

No comments yet.