using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace FarseerPhysics.SamplesFramework { /// /// 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. /// public class MenuScreen : GameScreen { #if WINDOWS || XBOX protected const float NumEntries = 15; #elif WINDOWS_PHONE protected const float NumEntries = 9; #endif protected List _menuEntries = new List(); 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; /// /// Constructor. /// 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("Common/arrow"); _texSlider = ScreenManager.Content.Load("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; } /// /// Returns the index of the menu entry at the position of the given mouse state. /// /// Index of menu entry if valid, -1 otherwise 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; } /// /// Responds to user input, changing the selected entry and accepting /// or cancelling the menu. /// 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 && this.TransitionPosition == 0 && this.TransitionAlpha == 1) { //GameScreen[] screens = ScreenManager.GetScreens(); //if (screens[screens.Length - 1] is BackgroundScreen ||| screens.Length ) // ScreenManager.Game.Exit(); //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); } } /// /// 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. /// 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; } /// /// Updates the menu. /// 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); } /// /// Draws the menu. /// 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(); } } }