#region File Description //----------------------------------------------------------------------------- // MessageDisplayComponent.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; #endregion namespace NetworkStateManagement { /// /// Component implements the IMessageDisplay interface. This is used to show /// notification messages when interesting events occur, for instance when /// gamers join or leave the network session /// class MessageDisplayComponent : DrawableGameComponent, IMessageDisplay { #region Fields SpriteBatch spriteBatch; SpriteFont font; // List of the currently visible notification messages. List messages = new List(); // Coordinates threadsafe access to the message list. object syncObject = new object(); // Tweakable settings control how long each message is visible. static readonly TimeSpan fadeInTime = TimeSpan.FromSeconds(0.25); static readonly TimeSpan showTime = TimeSpan.FromSeconds(5); static readonly TimeSpan fadeOutTime = TimeSpan.FromSeconds(0.5); #endregion #region Initialization /// /// Constructs a new message display component. /// public MessageDisplayComponent(Game game) : base(game) { // Register ourselves to implement the IMessageDisplay service. game.Services.AddService(typeof(IMessageDisplay), this); } /// /// Load graphics content for the message display. /// protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice); font = Game.Content.Load("menufont"); } #endregion #region Update and Draw /// /// Updates the message display component. /// public override void Update(GameTime gameTime) { lock (syncObject) { int index = 0; float targetPosition = 0; // Update each message in turn. while (index < messages.Count) { NotificationMessage message = messages[index]; // Gradually slide the message toward its desired position. float positionDelta = targetPosition - message.Position; float velocity = (float)gameTime.ElapsedGameTime.TotalSeconds * 2; message.Position += positionDelta * Math.Min(velocity, 1); // Update the age of the message. message.Age += gameTime.ElapsedGameTime; if (message.Age < showTime + fadeOutTime) { // This message is still alive. index++; // Any subsequent messages should be positioned below // this one, unless it has started to fade out. if (message.Age < showTime) targetPosition++; } else { // This message is old, and should be removed. messages.RemoveAt(index); } } } } /// /// Draws the message display component. /// public override void Draw(GameTime gameTime) { lock (syncObject) { // Early out if there are no messages to display. if (messages.Count == 0) return; Vector2 position = new Vector2(GraphicsDevice.Viewport.Width - 100, 0); spriteBatch.Begin(); // Draw each message in turn. foreach (NotificationMessage message in messages) { const float scale = 0.75f; // Compute the alpha of this message. float alpha = 1; if (message.Age < fadeInTime) { // Fading in. alpha = (float)(message.Age.TotalSeconds / fadeInTime.TotalSeconds); } else if (message.Age > showTime) { // Fading out. TimeSpan fadeOut = showTime + fadeOutTime - message.Age; alpha = (float)(fadeOut.TotalSeconds / fadeOutTime.TotalSeconds); } // Compute the message position. position.Y = 80 + message.Position * font.LineSpacing * scale; // Compute an origin value to right align each message. Vector2 origin = font.MeasureString(message.Text); origin.Y = 0; // Draw the message text, with a drop shadow. spriteBatch.DrawString(font, message.Text, position + Vector2.One, Color.Black * alpha, 0, origin, scale, SpriteEffects.None, 0); spriteBatch.DrawString(font, message.Text, position, Color.White * alpha, 0, origin, scale, SpriteEffects.None, 0); } spriteBatch.End(); } } #endregion #region Implement IMessageDisplay /// /// Shows a new notification message. /// public void ShowMessage(string message, params object[] parameters) { string formattedMessage = string.Format(message, parameters); lock (syncObject) { float startPosition = messages.Count; messages.Add(new NotificationMessage(formattedMessage, startPosition)); } } #endregion #region Nested Types /// /// Helper class stores the position and text of a single notification message. /// class NotificationMessage { public string Text; public float Position; public TimeSpan Age; public NotificationMessage(string text, float position) { Text = text; Position = position; Age = TimeSpan.Zero; } } #endregion } }