axiosengine/axios/ScreenSystem/MenuScreen.cs
2012-03-19 18:57:59 -05:00

338 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace FarseerPhysics.SamplesFramework
{
/// <summary>
/// Base class for screens that contain a menu of options. The user can
/// move up and down to select an entry, or cancel to back out of the screen.
/// </summary>
public class MenuScreen : GameScreen
{
#if WINDOWS || XBOX
protected const float NumEntries = 15;
#elif WINDOWS_PHONE
protected const float NumEntries = 9;
#endif
protected List<MenuEntry> _menuEntries = new List<MenuEntry>();
protected string _menuTitle;
protected Vector2 _titlePosition;
protected Vector2 _titleOrigin;
protected int _selectedEntry;
protected float _menuBorderTop;
protected float _menuBorderBottom;
protected float _menuBorderMargin;
protected float _menuOffset;
protected float _maxOffset;
protected Texture2D _texScrollButton;
protected Texture2D _texSlider;
protected MenuButton _scrollUp;
protected MenuButton _scrollDown;
protected MenuButton _scrollSlider;
protected bool _scrollLock;
/// <summary>
/// Constructor.
/// </summary>
public MenuScreen(string menuTitle)
{
_menuTitle = menuTitle;
TransitionOnTime = TimeSpan.FromSeconds(0.7);
TransitionOffTime = TimeSpan.FromSeconds(0.7);
HasCursor = true;
}
public void AddMenuItem(string name, EntryType type, GameScreen screen)
{
MenuEntry entry = new MenuEntry(this, name, type, screen);
_menuEntries.Add(entry);
}
public void AddMenuItem(MenuEntry me)
{
_menuEntries.Add(me);
}
public override void LoadContent()
{
base.LoadContent();
_texScrollButton = ScreenManager.Content.Load<Texture2D>("Common/arrow");
_texSlider = ScreenManager.Content.Load<Texture2D>("Common/slider");
//Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
//float scrollBarPos = viewport.Width / 2f;
//scrollBarPos -= _texScrollButton.Width + 2f;
InitMenu();
}
public void InitMenu()
{
Viewport viewport = ScreenManager.GraphicsDevice.Viewport;
SpriteFont font = ScreenManager.Fonts.MenuSpriteFont;
float scrollBarPos = viewport.Width / 2f;
for (int i = 0; i < _menuEntries.Count; ++i)
{
_menuEntries[i].Initialize();
scrollBarPos = Math.Min(scrollBarPos,
(viewport.Width - _menuEntries[i].GetWidth()) / 2f);
}
_titleOrigin = font.MeasureString(_menuTitle) / 2f;
_titlePosition = new Vector2(viewport.Width / 2f, font.MeasureString("M").Y / 2f + 10f);
_menuBorderMargin = font.MeasureString("M").Y * 0.8f;
_menuBorderTop = (viewport.Height - _menuBorderMargin * (NumEntries - 1)) / 2f;
_menuBorderBottom = (viewport.Height + _menuBorderMargin * (NumEntries - 1)) / 2f;
_menuOffset = 0f;
_maxOffset = Math.Max(0f, (_menuEntries.Count - NumEntries) * _menuBorderMargin);
_scrollUp = new MenuButton(_texScrollButton, false,
new Vector2(scrollBarPos, _menuBorderTop - _texScrollButton.Height), this);
_scrollDown = new MenuButton(_texScrollButton, true,
new Vector2(scrollBarPos, _menuBorderBottom + _texScrollButton.Height), this);
_scrollSlider = new MenuButton(_texSlider, false, new Vector2(scrollBarPos, _menuBorderTop), this);
_scrollLock = false;
}
/// <summary>
/// Returns the index of the menu entry at the position of the given mouse state.
/// </summary>
/// <returns>Index of menu entry if valid, -1 otherwise</returns>
private int GetMenuEntryAt(Vector2 position)
{
if (this.TransitionPosition == 0f && this.ScreenState == SamplesFramework.ScreenState.Active)
{
int index = 0;
foreach (MenuEntry entry in _menuEntries)
{
float width = entry.GetWidth();
float height = entry.GetHeight();
Rectangle rect = new Rectangle((int)(entry.Position.X - width / 2f),
(int)(entry.Position.Y - height / 2f),
(int)width, (int)height);
if (rect.Contains((int)position.X, (int)position.Y) && entry.Alpha > 0.1f)
{
return index;
}
++index;
}
}
return -1;
}
/// <summary>
/// Responds to user input, changing the selected entry and accepting
/// or cancelling the menu.
/// </summary>
public override void HandleInput(InputHelper input, GameTime gameTime)
{
// Mouse or touch on a menu item
int hoverIndex = GetMenuEntryAt(input.Cursor);
if (hoverIndex > -1 && _menuEntries[hoverIndex].IsSelectable() && !_scrollLock)
{
_selectedEntry = hoverIndex;
}
else
{
_selectedEntry = -1;
}
_scrollSlider.Hover = false;
if (input.IsCursorValid)
{
_scrollUp.Collide(input.Cursor);
_scrollDown.Collide(input.Cursor);
_scrollSlider.Collide(input.Cursor);
}
else
{
_scrollUp.Hover = false;
_scrollDown.Hover = false;
_scrollLock = false;
}
// Accept or cancel the menu?
if (input.IsMenuSelect() && _selectedEntry != -1)
{
if (_menuEntries[_selectedEntry].IsExitItem())
{
ScreenManager.Game.Exit();
}
else if (_menuEntries[_selectedEntry].IsBackItem())
{
this.ExitScreen();
}
else if (_menuEntries[_selectedEntry].Screen != null)
{
ScreenManager.AddScreen(_menuEntries[_selectedEntry].Screen);
if (_menuEntries[_selectedEntry].Screen is IDemoScreen)
{
ScreenManager.AddScreen(
new MessageBoxScreen((_menuEntries[_selectedEntry].Screen as IDemoScreen).GetDetails()));
}
}
}
else if (input.IsMenuCancel())
{
if (this.ScreenState == SamplesFramework.ScreenState.Active)
{
if (ScreenManager.GetScreens().Length == 2)
ScreenManager.Game.Exit();
else
this.ExitScreen();
}
//ScreenManager.Game.Exit();
}
if (input.IsMenuPressed())
{
if (_scrollUp.Hover)
{
_menuOffset = Math.Max(_menuOffset - 200f * (float)gameTime.ElapsedGameTime.TotalSeconds, 0f);
_scrollLock = false;
}
if (_scrollDown.Hover)
{
_menuOffset = Math.Min(_menuOffset + 200f * (float)gameTime.ElapsedGameTime.TotalSeconds, _maxOffset);
_scrollLock = false;
}
if (_scrollSlider.Hover)
{
_scrollLock = true;
}
}
if (input.IsMenuReleased())
{
_scrollLock = false;
}
if (_scrollLock)
{
_scrollSlider.Hover = true;
_menuOffset = Math.Max(Math.Min(((input.Cursor.Y - _menuBorderTop) / (_menuBorderBottom - _menuBorderTop)) * _maxOffset, _maxOffset), 0f);
}
}
/// <summary>
/// Allows the screen the chance to position the menu entries. By default
/// all menu entries are lined up in a vertical list, centered on the screen.
/// </summary>
protected virtual void UpdateMenuEntryLocations()
{
// Make the menu slide into place during transitions, using a
// power curve to make things look more interesting (this makes
// the movement slow down as it nears the end).
float transitionOffset = (float)Math.Pow(TransitionPosition, 2);
Vector2 position = Vector2.Zero;
position.Y = _menuBorderTop - _menuOffset;
// update each menu entry's location in turn
for (int i = 0; i < _menuEntries.Count; ++i)
{
position.X = ScreenManager.GraphicsDevice.Viewport.Width / 2f;
if (ScreenState == ScreenState.TransitionOn)
{
position.X -= transitionOffset * 256;
}
else
{
position.X += transitionOffset * 256;
}
// set the entry's position
_menuEntries[i].Position = position;
if (position.Y < _menuBorderTop)
{
_menuEntries[i].Alpha = 1f -
Math.Min(_menuBorderTop - position.Y, _menuBorderMargin) / _menuBorderMargin;
}
else if (position.Y > _menuBorderBottom)
{
_menuEntries[i].Alpha = 1f -
Math.Min(position.Y - _menuBorderBottom, _menuBorderMargin) /
_menuBorderMargin;
}
else
{
_menuEntries[i].Alpha = 1f;
}
// move down for the next entry the size of this entry
position.Y += _menuEntries[i].GetHeight();
}
Vector2 scrollPos = _scrollSlider.Position;
scrollPos.Y = MathHelper.Lerp(_menuBorderTop, _menuBorderBottom, _menuOffset / _maxOffset);
_scrollSlider.Position = scrollPos;
}
/// <summary>
/// Updates the menu.
/// </summary>
public override void Update(GameTime gameTime, bool otherScreenHasFocus,
bool coveredByOtherScreen)
{
base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);
// Update each nested MenuEntry object.
for (int i = 0; i < _menuEntries.Count; ++i)
{
bool isSelected = IsActive && (i == _selectedEntry);
_menuEntries[i].Update(isSelected, gameTime);
}
_scrollUp.Update(gameTime);
_scrollDown.Update(gameTime);
_scrollSlider.Update(gameTime);
}
/// <summary>
/// Draws the menu.
/// </summary>
public override void Draw(GameTime gameTime)
{
// make sure our entries are in the right place before we draw them
UpdateMenuEntryLocations();
SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
SpriteFont font = ScreenManager.Fonts.MenuSpriteFont;
spriteBatch.Begin();
// Draw each menu entry in turn.
for (int i = 0; i < _menuEntries.Count; ++i)
{
bool isSelected = IsActive && (i == _selectedEntry);
_menuEntries[i].Draw();
}
// Make the menu slide into place during transitions, using a
// power curve to make things look more interesting (this makes
// the movement slow down as it nears the end).
Vector2 transitionOffset = new Vector2(0f, (float)Math.Pow(TransitionPosition, 2) * 100f);
spriteBatch.DrawString(font, _menuTitle, _titlePosition - transitionOffset + Vector2.One * 2f, Color.Black, 0,
_titleOrigin, 1f, SpriteEffects.None, 0);
spriteBatch.DrawString(font, _menuTitle, _titlePosition - transitionOffset, new Color(255, 210, 0), 0,
_titleOrigin, 1f, SpriteEffects.None, 0);
_scrollUp.Draw();
_scrollSlider.Draw();
_scrollDown.Draw();
spriteBatch.End();
}
}
}