#region File Description
//-----------------------------------------------------------------------------
// DemoGame.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
#endregion
namespace XnaGraphicsDemo
{
///
/// The main game class.
///
public class DemoGame : Microsoft.Xna.Framework.Game
{
// Constants.
const float TransitionSpeed = 1.5f;
const float ZoomyTextLifespan = 0.75f;
// Properties.
public GraphicsDeviceManager Graphics { get; private set; }
public SpriteBatch SpriteBatch { get; private set; }
public SpriteFont Font { get; private set; }
public SpriteFont BigFont { get; private set; }
public Texture2D BlankTexture { get; private set; }
public Matrix ScaleMatrix { get; private set; }
// Fields.
List menuComponents = new List();
GameTime currentGameTime;
// Transition effects provide swooshy crossfades when moving from one screen to another.
float transitionTimer = float.MaxValue;
int transitionMode;
RenderTarget2D transitionRenderTarget;
// Zoomy text provides visual feedback when selecting menu items.
// This is implemented by the main game, rather than any individual menu
// screen, because the zoomy effect from selecting a menu item needs to
// display across the transition while that menu makes way for a new one.
class ZoomyText
{
public string Text;
public Vector2 Position;
public float Age;
}
static List zoomyTexts = new List();
///
/// Constructor.
///
public DemoGame()
{
Content.RootDirectory = "Content";
Graphics = new GraphicsDeviceManager(this);
Graphics.PreferredBackBufferWidth = 480;
Graphics.PreferredBackBufferHeight = 800;
#if WINDOWS_PHONE
Graphics.IsFullScreen = true;
#else
IsMouseVisible = true;
#endif
TargetElapsedTime = TimeSpan.FromSeconds(1 / 30.0);
// Create all the different menu screens.
menuComponents.Add(new TitleMenu(this));
menuComponents.Add(new BasicDemo(this));
menuComponents.Add(new DualDemo(this));
menuComponents.Add(new AlphaDemo(this));
menuComponents.Add(new SkinnedDemo(this));
menuComponents.Add(new EnvmapDemo(this));
menuComponents.Add(new ParticleDemo(this));
// Set all the menu screens except the first to hidden and inactive.
foreach (MenuComponent component in menuComponents)
{
component.Enabled = component.Visible = false;
Components.Add(component);
}
// Make the title menu active and visible.
menuComponents[0].Enabled = menuComponents[0].Visible = true;
}
///
/// Changes which menu screen is currently active.
///
public void SetActiveMenu(int index)
{
// Trigger the transition effect.
for (int i = 0; i < menuComponents.Count; i++)
{
if (menuComponents[i].Visible)
{
BeginTransition(i, index);
break;
}
}
// Mark the previous menu as inactive, and the new one as active.
for (int i = 0; i < menuComponents.Count; i++)
{
menuComponents[i].Enabled = menuComponents[i].Visible = (i == index);
menuComponents[i].Reset();
}
}
///
/// Loads content and creates graphics resources.
///
protected override void LoadContent()
{
SpriteBatch = new SpriteBatch(GraphicsDevice);
Font = Content.Load("font");
BigFont = Content.Load("BigFont");
BlankTexture = new Texture2D(GraphicsDevice, 1, 1);
BlankTexture.SetData(new Color[] { Color.White });
transitionRenderTarget = new RenderTarget2D(GraphicsDevice, 480, 800, false, SurfaceFormat.Color, DepthFormat.Depth24, 0, 0);
}
///
/// Updates the transition effect and zoomy text animations.
///
protected override void Update(GameTime gameTime)
{
currentGameTime = gameTime;
UpdateZoomyText(gameTime);
if (transitionTimer < float.MaxValue)
transitionTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
// This updates game components, including the currently active menu screen.
base.Update(gameTime);
}
///
/// Draws the game.
///
protected override void Draw(GameTime gameTime)
{
ScaleMatrix = Matrix.CreateScale(Graphics.PreferredBackBufferWidth / 480f, Graphics.PreferredBackBufferHeight / 800f, 1);
// This draws game components, including the currently active menu screen.
base.Draw(gameTime);
DrawTransitionEffect();
DrawZoomyText();
}
///
/// Begins a transition effect, capturing a copy of the current screen into the transitionRenderTarget.
///
void BeginTransition(int oldMenuIndex, int newMenuIndex)
{
ScaleMatrix = Matrix.Identity;
GraphicsDevice.SetRenderTarget(transitionRenderTarget);
// Draw the old menu screen into the rendertarget.
menuComponents[oldMenuIndex].Draw(currentGameTime);
// Force the rendertarget alpha channel to fully opaque.
SpriteBatch.Begin(0, BlendState.Additive);
SpriteBatch.Draw(BlankTexture, new Rectangle(0, 0, 480, 800), new Color(0, 0, 0, 255));
SpriteBatch.End();
GraphicsDevice.SetRenderTarget(null);
// Initialize the transition state.
transitionTimer = (float)TargetElapsedTime.TotalSeconds;
transitionMode = newMenuIndex;
}
///
/// Draws the transition effect, displaying various animating pieces of the rendertarget
/// which contains the previous scene image over the top of the new scene. There are
/// various different effects which animate these pieces in different ways.
///
void DrawTransitionEffect()
{
if (transitionTimer >= TransitionSpeed)
return;
SpriteBatch.Begin();
float mu = transitionTimer / TransitionSpeed;
float alpha = 1 - mu;
switch (transitionMode)
{
case 1:
// BasicEffect
DrawOpenCurtainsTransition(alpha);
break;
case 2:
case 5:
// DualTexture
// EnvironmentMap
DrawSpinningSquaresTransition(mu, alpha);
break;
case 3:
case 4:
// AlphaTest and Skinning
DrawChequeredAppearTransition(mu);
break;
case 6:
// Particles
DrawFallingLinesTransition(mu);
break;
default:
// Returning to menu.
DrawShrinkAndSpinTransition(mu, alpha);
break;
}
SpriteBatch.End();
}
///
/// Transition effect where the screen splits in half, opening down the middle.
///
void DrawOpenCurtainsTransition(float alpha)
{
int w = (int)(240 * alpha * alpha);
SpriteBatch.Draw(transitionRenderTarget, new Rectangle(0, 0, w, 800), new Rectangle(0, 0, 240, 800), Color.White * alpha);
SpriteBatch.Draw(transitionRenderTarget, new Rectangle(480 - w, 0, w, 800), new Rectangle(240, 0, 240, 800), Color.White * alpha);
}
///
/// Transition effect where the screen splits into pieces, each spinning off in a different direction.
///
void DrawSpinningSquaresTransition(float mu, float alpha)
{
Random random = new Random(23);
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 8; y++)
{
Rectangle rect = new Rectangle(480 * x / 4, 800 * y / 8, 480 / 4, 800 / 8);
Vector2 origin = new Vector2(rect.Width, rect.Height) / 2;
float rotation = (float)(random.NextDouble() - 0.5) * mu * mu * 2;
float scale = 1 + (float)(random.NextDouble() - 0.5f) * mu * mu;
Vector2 pos = new Vector2(rect.Center.X, rect.Center.Y);
pos.X += (float)(random.NextDouble() - 0.5) * mu * mu * 400;
pos.Y += (float)(random.NextDouble() - 0.5) * mu * mu * 400;
SpriteBatch.Draw(transitionRenderTarget, pos, rect, Color.White * alpha, rotation, origin, scale, 0, 0);
}
}
}
///
/// Transition effect where each square of the image appears at a different time.
///
void DrawChequeredAppearTransition(float mu)
{
Random random = new Random(23);
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 16; y++)
{
Rectangle rect = new Rectangle(480 * x / 8, 800 * y / 16, 480 / 8, 800 / 16);
if (random.NextDouble() > mu * mu)
SpriteBatch.Draw(transitionRenderTarget, rect, rect, Color.White);
}
}
// The zoomy text effect doesn't look so good with this
// particular transition effect, so we temporarily disable it.
zoomyTexts.Clear();
}
///
/// Transition effect where the image dissolves into a sequence of vertically falling lines.
///
void DrawFallingLinesTransition(float mu)
{
Random random = new Random(23);
const int segments = 60;
for (int x = 0; x < segments; x++)
{
Rectangle rect = new Rectangle(480 * x / segments, 0, 480 / segments, 800);
Vector2 pos = new Vector2(rect.X, 0);
pos.Y += 800 * (float)Math.Pow(mu, random.NextDouble() * 10);
SpriteBatch.Draw(transitionRenderTarget, pos, rect, Color.White);
}
}
///
/// Transition effect where the image spins off toward the bottom left of the screen.
///
void DrawShrinkAndSpinTransition(float mu, float alpha)
{
Vector2 origin = new Vector2(240, 400);
Vector2 translate = (new Vector2(32, 800 - 32) - origin) * mu * mu;
float rotation = mu * mu * -4;
float scale = alpha * alpha;
Color tint = Color.White * (float)Math.Sqrt(alpha);
SpriteBatch.Draw(transitionRenderTarget, origin + translate, null, tint, rotation, origin, scale, 0, 0);
}
///
/// Creates a new zoomy text menu item selection effect.
///
public static void SpawnZoomyText(string text, Vector2 position)
{
zoomyTexts.Add(new ZoomyText { Text = text, Position = position });
}
///
/// Updates the zoomy text animations.
///
static void UpdateZoomyText(GameTime gameTime)
{
int i = 0;
while (i < zoomyTexts.Count)
{
zoomyTexts[i].Age += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (zoomyTexts[i].Age >= ZoomyTextLifespan)
zoomyTexts.RemoveAt(i);
else
i++;
}
}
///
/// Draws the zoomy text animations.
///
void DrawZoomyText()
{
if (zoomyTexts.Count <= 0)
return;
SpriteBatch.Begin(0, null, null, null, null, null, ScaleMatrix);
foreach (ZoomyText zoomyText in zoomyTexts)
{
Vector2 pos = zoomyText.Position + Font.MeasureString(zoomyText.Text) / 2;
float age = zoomyText.Age / ZoomyTextLifespan;
float sqrtAge = (float)Math.Sqrt(age);
float scale = 0.333f + sqrtAge * 2f;
float alpha = 1 - age;
SpriteFont font = BigFont;
// Our BigFont only contains characters a-z, so if the text
// contains any numbers, we have to use the other font instead.
foreach (char ch in zoomyText.Text)
{
if (char.IsDigit(ch))
{
font = Font;
scale *= 2;
break;
}
}
Vector2 origin = font.MeasureString(zoomyText.Text) / 2;
SpriteBatch.DrawString(font, zoomyText.Text, pos, Color.Lerp(new Color(64, 64, 255), Color.White, sqrtAge) * alpha, 0, origin, scale, 0, 0);
}
SpriteBatch.End();
}
}
}