Commits

Anonymous committed 33b4b16

Adding switchboard / messaging. Some of the physics stuff is still weird, need to look into that.

Comments (0)

Files changed (12)

AngelXNA/AngelXNA.csproj

     <Compile Include="Infrastructure\World.cs" />
     <Compile Include="Input\InputBinding.cs" />
     <Compile Include="Input\InputManager.cs" />
+    <Compile Include="Messaging\Message.cs" />
+    <Compile Include="Messaging\Switchboard.cs" />
     <Compile Include="Physics\PhysicsActor.cs" />
     <Compile Include="Physics\PhysicsEventActor.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

AngelXNA/Infrastructure/World.cs

 using AngelXNA.AI;
 using Microsoft.Xna.Framework.Graphics;
 using AngelXNA.Infrastructure.Console;
+using AngelXNA.Messaging;
 
 namespace AngelXNA.Infrastructure
 {
 
             if (_simulateOn)
             {
+                // Deliver any messages that have been queued from the last frame. 
+                Switchboard.Instance.SendAllMessages();
+
                 //Clear out the collision contact points
                 // _contactPointCount = 0;
 
-                //Update gravity
-                //CONSOLE_ONCVARCHANGED( phys_gravity, Vector2, 
-                //{
-                //    GetPhysicsWorld().m_gravity = b2Vec2( _cvarNewVal.X, _cvarNewVal.Y );
-                //    if( _cvarOldVal == Vector2(0) )
-                //        WakeAllPhysics();
-                //})
-
                 //rb - Flag that the _elements array is locked so we don't try to add any
                 // new actors during the update.
                 _elementsLocked = true;
 
                 RunPhysics(aGameTime);
 
+                Switchboard.Instance.Update(aGameTime);
                 //if there are any system updates that still need to be run, put them here
             }
         }

AngelXNA/Messaging/Message.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace AngelXNA.Messaging
+{
+    public struct Message
+    {
+        private string _messageName;
+        // TODO: Angel used MessageListener, should we?
+        private object _sender;
+
+        public string MessageName
+        {
+            get { return _messageName; }
+        }
+
+        public object Sender
+        {
+            get { return _sender; }
+        }
+
+        public Message(string messageName)
+            : this(messageName, null)
+        {
+
+        }
+
+        public Message(string messageName, object sender)
+        {
+            _messageName = messageName;
+            _sender = sender;
+        }
+    }
+}

AngelXNA/Messaging/Switchboard.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+using Microsoft.Xna.Framework;
+
+namespace AngelXNA.Messaging
+{
+    public delegate void MessageHandler(Message message);
+
+    public class Switchboard
+    {
+        #region Singleton implementation
+        private static Switchboard s_Instance = new Switchboard();
+
+        public static Switchboard Instance
+        {
+            get { return s_Instance; }
+        }
+        #endregion
+
+        private struct MessageTimer
+        {
+            public Message _message;
+            public float _timeRemaining;
+
+            public MessageTimer(Message message, float timeRemaining)
+            {
+                _message = message;
+                _timeRemaining = Math.Max(0.0f, timeRemaining);
+            }
+
+            public void Tick(float dt)
+            {
+                _timeRemaining -= dt;
+            }
+        }
+
+        private bool _messagesLocked = false;
+        private Dictionary<string, MessageHandler> _subscriptions = new Dictionary<string,MessageHandler>();
+        private Queue<Message> _messages = new Queue<Message>();
+        private LinkedList<MessageTimer> _delayedMessages = new LinkedList<MessageTimer>();
+
+        public void Broadcast(Message message)
+        {
+            _messages.Enqueue(message);
+        }
+
+        public void DefferedBroadcast(Message message, float delay)
+        {
+	        _delayedMessages.AddLast(new MessageTimer(message, delay));
+        }
+
+        public void Update(GameTime gameTime)
+        {
+            float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
+            for (LinkedListNode<MessageTimer> node = _delayedMessages.First; node != null;)
+            {
+                node.Value.Tick(dt);
+                if (node.Value._timeRemaining <= 0.0f)
+                {
+                    Broadcast(node.Value._message);
+                    node = node.Next;
+                    _delayedMessages.Remove(node);
+                }
+                else
+                    node = node.Next;
+            }
+        }
+
+        public void SendAllMessages()
+        {
+            while (_messages.Count > 0)
+	        {
+                Message nextMessage = _messages.Dequeue();
+                if(_subscriptions.ContainsKey(nextMessage.MessageName))
+                {
+                    MessageHandler handler = _subscriptions[nextMessage.MessageName];
+                    if(handler != null)
+                        handler(nextMessage);
+                }
+	        }
+        }
+
+        public MessageHandler this[string asMessage]
+        {
+            get
+            {
+                if (_subscriptions.ContainsKey(asMessage))
+                    return _subscriptions[asMessage];
+                return null;
+            }
+            set
+            {
+                if (_subscriptions.ContainsKey(asMessage))
+                {
+                    if (value == null)
+                    {
+                        _subscriptions.Remove(asMessage);
+                    }
+                    else
+                    {
+                        _subscriptions[asMessage] = value;
+                    }
+                }
+                else if (value != null)
+                {
+                    _subscriptions.Add(asMessage, value);
+                }
+            }
+        }
+    }
+}

AngelXNA/Physics/PhysicsActor.cs

 using AngelXNA.Infrastructure;
 using FarseerGames.FarseerPhysics;
 using AngelXNA.Infrastructure.Console;
+using AngelXNA.Messaging;
 
 namespace AngelXNA.Physics
 {
             get { return _physBody; }
         }
 
+        public Geom Geom
+        {
+            get { return _physGeom; }
+        }
+
         public override Vector2 Size
         {
             set
 
         protected virtual bool InternalOnCollision(Geom geom1, Geom geom2, ContactList list)
         {
+            // Always broadcast to the switchboard first, so long as we're colliding with
+            // another physics actor.
+            if (geom2.Body.Tag is PhysicsActor)
+            {
+                // Only inform the switchboard if people care.
+                string messageName = "CollisionWith" + ((PhysicsActor)geom2.Body.Tag).Name;
+                if(Switchboard.Instance[messageName] != null)
+                {
+                    // On Collision for the other object will take care of the other side of things.
+                    Switchboard.Instance.Broadcast(new Message(messageName));
+                }
+            }
+            
+            // Now inform event subscribers who aren't on the switchboard.
+            // TODO: Should we even be doing this?  Or should we only rely on the switchboard?
             if (OnCollision != null)
                 OnCollision(geom1, geom2, list);
             return true;

IntroGame/Content/Content.contentproj

       <Processor>SoundEffectProcessor</Processor>
     </Compile>
   </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Sounds\click.wav">
+      <Name>click</Name>
+      <Importer>WavImporter</Importer>
+      <Processor>SoundEffectProcessor</Processor>
+    </Compile>
+  </ItemGroup>
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\XNA Game Studio\v3.0\Microsoft.Xna.GameStudio.ContentPipeline.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.

IntroGame/Content/Sounds/click.wav

Binary file added.

IntroGame/Content/Sounds/sprong.wav

Binary file modified.

IntroGame/DemoGameManager.cs

 using Microsoft.Xna.Framework.Graphics;
 using IntroGame.Screens;
 using AngelXNA.Infrastructure.Console;
+using Microsoft.Xna.Framework.Audio;
 
 namespace IntroGame
 {
     {
         private List<DemoScreen> _screens = new List<DemoScreen>();
         private int _current;
+        private SoundEffect _click;
 
         public DemoGameManager()
         {
             _screens.Add(new DemoScreenBindingInstructions());          // 7
             _screens.Add(new DemoScreenParticleActor());                // 8
             _screens.Add(new DemoScreenPhysicsActor());                 // 9
-            _screens.Add(new DemoScreenCollisions());                   // 10
-            _screens.Add(new DemoScreenCollisionLevelFile());	        // 11
-            _screens.Add(new DemoScreenLayeredCollisionLevelFile());	// 12
-            _screens.Add(new DemoScreenConsole());                      // 13
-            _screens.Add(new DemoScreenLogs());                         // 14
-            _screens.Add(new DemoScreenByeBye());                       // 15
+            _screens.Add(new DemoScreenMessagePassing());               // 10
+            _screens.Add(new DemoScreenCollisions());                   // 11
+            _screens.Add(new DemoScreenCollisionLevelFile());	        // 12
+            _screens.Add(new DemoScreenLayeredCollisionLevelFile());	// 13
+            _screens.Add(new DemoScreenConsole());                      // 14
+            _screens.Add(new DemoScreenLogs());                         // 15
+            _screens.Add(new DemoScreenByeBye());                       // 16
 
             int startingIndex = 0;
             if (_screens.Count > startingIndex)
             {
                 _current = -1;
             }
+
+            _click = World.Instance.Game.Content.Load<SoundEffect>("Sounds\\click");
         }
 
         [ConsoleMethod]
                 _screens[++_current].Start();
                 World.Instance.Add(_screens[_current]);
             }
+
+            _click.Play();
         }
 
         [ConsoleMethod]
                 _screens[--_current].Start();
                 World.Instance.Add(_screens[_current]);
             }
+
+            _click.Play();
         }
 
         [ConsoleMethod]

IntroGame/IntroGame.csproj

     <Compile Include="Screens\DemoScreenLayeredCollisionLevelFile.cs" />
     <Compile Include="Screens\DemoScreenLevelFile.cs" />
     <Compile Include="Screens\DemoScreenLogs.cs" />
+    <Compile Include="Screens\DemoScreenMessagePassing.cs" />
     <Compile Include="Screens\DemoScreenMovingActor.cs" />
     <Compile Include="Screens\DemoScreenParticleActor.cs" />
     <Compile Include="Screens\DemoScreenPhysicsActor.cs" />

IntroGame/Screens/DemoScreenCollisions.cs

 {
     public class DemoScreenCollisions : DemoScreen
     {
-        public SoundEffect sound;
-        public PhysicsActor p1;
-        public PhysicsActor p2;
-        public TextActor t1;
+        private SoundEffect sound;
+        private PhysicsActor p1;
+        private PhysicsActor p2;
+        private TextActor t1;
 
         public DemoScreenCollisions()
         {

IntroGame/Screens/DemoScreenMessagePassing.cs

+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using AngelXNA.Physics;
+using Microsoft.Xna.Framework;
+using Microsoft.Xna.Framework.Graphics;
+using AngelXNA.Infrastructure;
+using AngelXNA.Actors;
+using AngelXNA.Messaging;
+using Microsoft.Xna.Framework.Audio;
+
+namespace IntroGame.Screens
+{
+    public class DemoScreenMessagePassing : DemoScreen
+    {
+        private SoundEffect sound;
+        private PhysicsActor p1;
+        private PhysicsActor p2;
+        private PhysicsActor p3;
+
+        private TextActor t1;
+
+        public DemoScreenMessagePassing()
+        {
+            // Sets up the objects.
+            Setup();
+
+            //Subscribe the DemoScreen to some specific messages. We'll receive notification
+            // when these messages get broadcast. Look below at the ReceiveMessage() function,
+            // which is part of the MessageListener interface, to see how to handle the messages. 
+            Switchboard.Instance["ScreenStarted"] += new MessageHandler(ReceiveMessage);
+
+            sound = World.Instance.Game.Content.Load<SoundEffect>(@"Sounds\sprong");
+        }
+
+        public override void Start()
+        {
+            //Kick off one message -- it'll get physics going on this screen. 
+	        Switchboard.Instance.Broadcast(new Message("ScreenStarted"));
+
+	        //Add the ground actor so they'll have something to bounce off. 
+	        p3 = new PhysicsActor();
+	        p3.Position = new Vector2(0.0f, -11.0f);
+	        p3.Size = new Vector2(30.0f, 5.0f);
+	        p3.Color = new Color(0.0f, 1.0f, 0.0f);
+	        p3.InitPhysics(0.0f, 0.1f, 0.0f, PhysicsActor.ShapeType.Box); 
+
+	        World.Instance.Add(p3);
+
+	        //Demo housekeeping below this point. 
+            #region Demo Housekeeping
+	        String outputText = "These actors are responding to Messages\nthat we're sending through our central Switchboard.";
+	        outputText += "\n\nYou can have actors respond to and broadcast arbitrary messages,\nwhich makes it easy to handle events in your game.";
+	        outputText += "\n\n\n(Those actors have been hanging out up there this whole time,\nwaiting for the message that this screen had started\nbefore they dropped in.)";
+	        t1 = new TextActor("Console", outputText);
+	        t1.Position = new Vector2(0.0f, 3.5f);
+	        t1.TextAlignment = TextActor.Alignment.Center;
+	        World.Instance.Add(t1);
+	        TextActor fileLoc = new TextActor("ConsoleSmall", "DemoScreenMessagePassing.cpp");
+	        fileLoc.Position = World.Instance.Camera.ScreenToWorld(5, 755);
+	        fileLoc.Color = new Color(.3f, .3f, .3f);
+	        World.Instance.Add(fileLoc);
+            _objects.Add(fileLoc);
+            _objects.Add(t1);
+            _objects.Add(p1);
+            _objects.Add(p2);
+            _objects.Add(p3);
+            #endregion
+        }
+
+        public override void Stop()
+        {
+            base.Stop();
+            Setup();
+        }
+
+        private void Setup()
+        {
+            //Create a new physics actor, but don't initialize its physics just yet. We'll start 
+            // physics in response to some messages later on. 
+            p1 = new PhysicsActor();
+            p1.Size = new Vector2(1.0f, 1.0f);
+            p1.Color = new Color(1.0f, 0.0f, 1.0f);
+            p1.Position = new Vector2(-8.0f, 12.6f);
+            p1.Rotation = 1.0f;
+
+            //Make a friend for him, and don't initialize its physics just yet either. 
+            p2 = new PhysicsActor();
+            p2.Size = new Vector2(1.0f, 1.0f);
+            p2.Color = new Color(0.0f, 0.0f, 1.0f);
+            p2.Position = new Vector2(8.0f, 12.6f);
+
+            //Add them all to the world. When we add them they are assigned names, which are
+            // guaranteed to be unique. You can also assign your own name to any Actor with
+            // SetName(), which returns the unique name it was actually given. (Numbers will 
+            // be appended if the name was already taken.)
+            // 
+            //There's also a static function Actor::GetNamed() that you pass a string name
+            // and will return either the Actor with that name or NULL. 
+            World.Instance.Add(p1);
+            World.Instance.Add(p2);
+
+            //This message is interesting -- all collision messages take the form "CollisionWith"
+            // plus the name of the actor colliding. Note that this means if you change the name
+            // of the actor, you need to change your subscriptions if you want to hear about 
+            // collisions. 
+            Switchboard.Instance["CollisionWith" + p1.Name] += new MessageHandler(ReceiveMessage);
+            Switchboard.Instance["CollisionWith" + p2.Name] += new MessageHandler(ReceiveMessage);
+        }
+
+        private void ReceiveMessage(Message message)
+        {
+            //Respond to the ScreenStarted message that we sent when the screen started. 
+            if (message.MessageName == "ScreenStarted")
+            {
+                p1.InitPhysics(0.8f, 0.5f, 0.7f, PhysicsActor.ShapeType.Box);
+            }
+
+            //When the first actor collides, we kick off the physics for the second actor. 
+            if (message.MessageName == "CollisionWith" + p1.Name)
+            {
+                // Only init the physics if it isn't already initialized.
+                //   *weird* things happen if you initialize it a second time.
+                if (p2.Body == null)
+                {
+                    p2.InitPhysics(0.8f, 0.5f, 0.7f, PhysicsActor.ShapeType.Box);
+                }
+
+                //If you need more data about the collision, collision messages always come *from*
+                // the actor colliding with the one you care about, and carry a pointer to the 
+                // relevant Box2D contact point, which contains more information like the normal force,
+                // position, tangent, etc. 
+                // 
+                //You can get these from GetSender() and the GetValue() function, since the 
+                // message getting delivered is really a templated TypedMessage<b2ContactPoint*>
+                Vector2 vel = p1.Body.LinearVelocity;
+                if (Math.Abs(vel.Y) > 5.0f)
+                {
+                    //We do the check on the actor's speed so that it only makes a sound when dropping
+                    // at a certain rate. Otherwise, the bounce noise will play every time it "makes 
+                    // contact" with the ground as it settles. This leads to the bad kind of cacophany. 
+                    sound.Play();
+                }
+            }
+            else if (message.MessageName == "CollisionWith" + p2.Name)
+            {
+                Vector2 vel = p2.Body.LinearVelocity;
+                if (Math.Abs(vel.Y) > (5.0f * World.c_iPhysicsScale))
+                    sound.Play();
+            }
+        }
+    }
+}