MessageDisplayComponent.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // MessageDisplayComponent.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Collections.Generic;
  12. using Microsoft.Xna.Framework;
  13. using Microsoft.Xna.Framework.Graphics;
  14. #endregion
  15. namespace CatapultGame
  16. {
  17. /// <summary>
  18. /// Component implements the IMessageDisplay interface. This is used to show
  19. /// notification messages when interesting events occur, for instance when
  20. /// gamers join or leave the network session
  21. /// </summary>
  22. class MessageDisplayComponent : DrawableGameComponent, IMessageDisplay
  23. {
  24. #region Fields
  25. SpriteBatch spriteBatch;
  26. SpriteFont font;
  27. // List of the currently visible notification messages.
  28. List<NotificationMessage> messages = new List<NotificationMessage>();
  29. // Coordinates threadsafe access to the message list.
  30. object syncObject = new object();
  31. // Tweakable settings control how long each message is visible.
  32. static readonly TimeSpan fadeInTime = TimeSpan.FromSeconds(0.25);
  33. static readonly TimeSpan showTime = TimeSpan.FromSeconds(5);
  34. static readonly TimeSpan fadeOutTime = TimeSpan.FromSeconds(0.5);
  35. #endregion
  36. #region Initialization
  37. /// <summary>
  38. /// Constructs a new message display component.
  39. /// </summary>
  40. public MessageDisplayComponent(Game game)
  41. : base(game)
  42. {
  43. // Register ourselves to implement the IMessageDisplay service.
  44. game.Services.AddService(typeof(IMessageDisplay), this);
  45. }
  46. /// <summary>
  47. /// Load graphics content for the message display.
  48. /// </summary>
  49. protected override void LoadContent()
  50. {
  51. spriteBatch = new SpriteBatch(GraphicsDevice);
  52. //font = Game.Content.Load<SpriteFont>("menufont");
  53. font = Game.Content.Load<SpriteFont>("Fonts/MenuFont");
  54. }
  55. #endregion
  56. #region Update and Draw
  57. /// <summary>
  58. /// Updates the message display component.
  59. /// </summary>
  60. public override void Update(GameTime gameTime)
  61. {
  62. lock (syncObject)
  63. {
  64. int index = 0;
  65. float targetPosition = 0;
  66. // Update each message in turn.
  67. while (index < messages.Count)
  68. {
  69. NotificationMessage message = messages[index];
  70. // Gradually slide the message toward its desired position.
  71. float positionDelta = targetPosition - message.Position;
  72. float velocity = (float)gameTime.ElapsedGameTime.TotalSeconds * 2;
  73. message.Position += positionDelta * Math.Min(velocity, 1);
  74. // Update the age of the message.
  75. message.Age += gameTime.ElapsedGameTime;
  76. if (message.Age < showTime + fadeOutTime)
  77. {
  78. // This message is still alive.
  79. index++;
  80. // Any subsequent messages should be positioned below
  81. // this one, unless it has started to fade out.
  82. if (message.Age < showTime)
  83. targetPosition++;
  84. }
  85. else
  86. {
  87. // This message is old, and should be removed.
  88. messages.RemoveAt(index);
  89. }
  90. }
  91. }
  92. }
  93. /// <summary>
  94. /// Draws the message display component.
  95. /// </summary>
  96. public override void Draw(GameTime gameTime)
  97. {
  98. lock (syncObject)
  99. {
  100. // Early out if there are no messages to display.
  101. if (messages.Count == 0)
  102. return;
  103. Vector2 position = new Vector2(GraphicsDevice.Viewport.Width - 100, 0);
  104. spriteBatch.Begin();
  105. // Draw each message in turn.
  106. foreach (NotificationMessage message in messages)
  107. {
  108. const float scale = 0.75f;
  109. // Compute the alpha of this message.
  110. float alpha = 1;
  111. if (message.Age < fadeInTime)
  112. {
  113. // Fading in.
  114. alpha = (float)(message.Age.TotalSeconds /
  115. fadeInTime.TotalSeconds);
  116. }
  117. else if (message.Age > showTime)
  118. {
  119. // Fading out.
  120. TimeSpan fadeOut = showTime + fadeOutTime - message.Age;
  121. alpha = (float)(fadeOut.TotalSeconds /
  122. fadeOutTime.TotalSeconds);
  123. }
  124. // Compute the message position.
  125. position.Y = 80 + message.Position * font.LineSpacing * scale;
  126. // Compute an origin value to right align each message.
  127. Vector2 origin = font.MeasureString(message.Text);
  128. origin.Y = 0;
  129. // Draw the message text, with a drop shadow.
  130. spriteBatch.DrawString(font, message.Text, position + Vector2.One,
  131. Color.Black * alpha, 0,
  132. origin, scale, SpriteEffects.None, 0);
  133. spriteBatch.DrawString(font, message.Text, position,
  134. Color.White * alpha, 0,
  135. origin, scale, SpriteEffects.None, 0);
  136. }
  137. spriteBatch.End();
  138. }
  139. }
  140. #endregion
  141. #region Implement IMessageDisplay
  142. /// <summary>
  143. /// Shows a new notification message.
  144. /// </summary>
  145. public void ShowMessage(string message, params object[] parameters)
  146. {
  147. string formattedMessage = string.Format(message, parameters);
  148. lock (syncObject)
  149. {
  150. float startPosition = messages.Count;
  151. messages.Add(new NotificationMessage(formattedMessage, startPosition));
  152. }
  153. }
  154. #endregion
  155. #region Nested Types
  156. /// <summary>
  157. /// Helper class stores the position and text of a single notification message.
  158. /// </summary>
  159. class NotificationMessage
  160. {
  161. public string Text;
  162. public float Position;
  163. public TimeSpan Age;
  164. public NotificationMessage(string text, float position)
  165. {
  166. Text = text;
  167. Position = position;
  168. Age = TimeSpan.Zero;
  169. }
  170. }
  171. #endregion
  172. }
  173. }