Понеслась! Пора описывать алгоритмы в играх. Сам я не профессиональный программист, просто в последнее время мне стала интересна тема игр, всегда хотелось написать что то, как и каждому «компьютерщику». Но, когда садишься что-то сделать, оказывается, что это ой как не просто.
Сделав несколько рабочих набросков игр, код становится плохо читаемым и много чего хочется переделать. Вот и настает тот момент, когда нужно читать книги по алгоритмам и разбираться в чужом коде.
Начнем разбираться со структурой игровых экранов и меню, походу поймете, что это некий костяк игры. Пример, расскажет, как организовано меню и окно игрового процесса. Я взял и разобрал пример http://creators.xna.com/en-US/samples/gamestatemanagement, выбросил из него эффекты экранов, эффекты меню, другие сложности, - осталась та основа, которую легко читать для понимания. Комментариями я не частил, буду некоторые моменты описывать после кода, подробно объяснять не буду – в основах вы должны разбираться. Использую я C# 2008 Express Edition, XNA 3.1.
Структура файлов в проекте:
Теперь краткая иерархия классов с основными параметрами:
GameScreenInput.cs
GameScreenLoading.cs
GameScreenManager.cs
GameScreenMenu.cs
Elements. В переопределяемом методе обновления Update – описывается, что нужно делать при нажатии клавиш – пускать курсор вверх, вниз, не допускать вывод индекса SelectedIndex за пределы списка элементов, а по нажатию на клавишу Fire выполнять event через метод OnSelect.
Game1.cs
Background.cs
Credits.cs
HowToPlay.cs
MainGame.cs
PauseMenu.cs
Options.cs
Вот и все в примере на creators.xna.com код серьезней, если вы осмыслили этот текст то можно приступить к модификации: добавить эффекты к тексту, плавная смена экранов, привязка меню к игроку который ее вызвал, добавление других устройств управление и т.д.
Исходный код проекта
Сделав несколько рабочих набросков игр, код становится плохо читаемым и много чего хочется переделать. Вот и настает тот момент, когда нужно читать книги по алгоритмам и разбираться в чужом коде.
Начнем разбираться со структурой игровых экранов и меню, походу поймете, что это некий костяк игры. Пример, расскажет, как организовано меню и окно игрового процесса. Я взял и разобрал пример http://creators.xna.com/en-US/samples/gamestatemanagement, выбросил из него эффекты экранов, эффекты меню, другие сложности, - осталась та основа, которую легко читать для понимания. Комментариями я не частил, буду некоторые моменты описывать после кода, подробно объяснять не буду – в основах вы должны разбираться. Использую я C# 2008 Express Edition, XNA 3.1.
Структура файлов в проекте:
Теперь краткая иерархия классов с основными параметрами:
namespace MenuGame.GameScreenManager
class GameScreen
public bool IsActive;
public CGameScreenManager gameScreenManager;
class GameScreenInput
class GameScreenMenuElement
public delegate void dHandler(object sender);
public event dHandler Selected;
class GameScreenLoading: GameScreen
GameScreen[] screensToLoad;
public static void Load(CGameScreenManager screenManager, params GameScreen[] screensToLoad)
class GameScreenMenu: GameScreen
public List<GameScreenMenuElement>Elements = new List<GameScreenMenuElement>();
class CGameScreenManager : DrawableGameComponent
public List<GameScreen> Screens = new List<GameScreen>();
public SpriteBatch spriteBatch;
public bool IsInitialized;
public SpriteFont Font;
public GameScreenInput input = new GameScreenInput();
public GraphicsDeviceManager graphicsDeviceManager;
namespace MenuGame.Screen
class Background: GameScreen
class MainGame:GameScreen
class Credits : GameScreenMenu
class HowToPlay : GameScreenMenu
class MainMenu:GameScreenMenu
class Options: GameScreenMenu
class PauseMenu : GameScreenMenu
Теперь перейдем к более подробному описанию. Основные директивы using я не буду давать для экономии глаз, учитывайте, что к каждому файлу прилагаются: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.Media; using Microsoft.Xna.Framework.Net; using Microsoft.Xna.Framework.Storage;GameScreen.cs
namespace MenuGame.GameScreenManager
{
class GameScreen
{
public bool IsActive;
public CGameScreenManager gameScreenManager;
public GameScreen()
{
IsActive = true;
}
public virtual void LoadContent()
{ }
public virtual void UnLoadContent()
{ }
public virtual void Update(GameTime gameTime)
{ }
public virtual void Draw(GameTime gameTime)
{ }
public virtual void HandleInput(GameScreenInput input)
{ }
public void ExitGameScreen()
{
UnLoadContent();
IsActive = false;
gameScreenManager.RemoveScreen(this);
}
}
}Базовый класс, самый основной представляет единицу экрана. Параметр IsActive говорит об активном окне, если активно - то обновляем и рисуем его, при создании окна оно активно (см. Конструктор). Все методы кроме конструктора и ExitGameScreen - выход виртуальны и в наследуемом классе будут переопределяться по желанию. Каждый экран будет иметь ссылку на менеджера экранов gameScreenManager для указания явной и неявной связи, чтобы была видна иерархия, и не собирался мусор в программе, по ходу поймете…GameScreenInput.cs
namespace MenuGame.GameScreenManager
{
class GameScreenInput
{
public bool Up;
public bool Down;
public bool Left;
public bool Right;
public bool Fire;
public bool Menu;
private Keys keyUp;
private Keys keyDown;
private Keys keyLeft;
private Keys keyRight;
private Keys keyFire;
private Keys keyMenu;
private KeyboardState keyboardState;
private KeyboardState oldKeyboardState;
public GameScreenInput()
{
keyUp = Keys.Up;
keyDown = Keys.Down;
keyLeft = Keys.Left;
keyRight = Keys.Right;
keyFire = Keys.Enter;
keyMenu = Keys.Escape;
}
public void Update()
{
keyboardState = Keyboard.GetState();
if (keyboardState.IsKeyDown(keyUp) && !oldKeyboardState.IsKeyDown(keyUp)) Up = true; else Up = false;
if (keyboardState.IsKeyDown(keyDown) && !oldKeyboardState.IsKeyDown(keyDown)) Down = true; else Down = false;
if (keyboardState.IsKeyDown(keyLeft) && !oldKeyboardState.IsKeyDown(keyLeft)) Left = true; else Left = false;
if (keyboardState.IsKeyDown(keyRight) && !oldKeyboardState.IsKeyDown(keyRight)) Right = true; else Right = false;
if (keyboardState.IsKeyDown(keyFire) && !oldKeyboardState.IsKeyDown(keyFire)) Fire = true; else Fire = false;
if (keyboardState.IsKeyDown(keyMenu) && !oldKeyboardState.IsKeyDown(keyMenu)) Menu = true; else Menu = false;
oldKeyboardState = keyboardState;
}
}
}Независимый класс, здесь я постарался представить методику использования разных контроллеров. Имеются булевые переменные (Up, Down, Left, …) основных действий, устанавливает их в соответствии с нажатыми клавишами метод контроллера (клавиатура, мышка, гейпад). Тут только клавиатура, додумать (если вам нужно) другие контроллеры не сложно.GameScreenLoading.cs
namespace MenuGame.GameScreenManager
{
class GameScreenLoading: GameScreen
{
GameScreen[] screensToLoad;
bool IsExit=false;
int ExTime = 500;
public static void Load(CGameScreenManager screenManager, params GameScreen[] screensToLoad)
{
foreach (GameScreen screen in screenManager.GetScreens())
screen.ExitGameScreen();
GameScreenLoading loading = new GameScreenLoading();
screenManager.AddScreen(loading);
loading.screensToLoad = screensToLoad;
}
public override void Update(GameTime gameTime)
{
if (screensToLoad != null)
{
foreach (GameScreen screen in screensToLoad)
{
screen.IsActive = false;
gameScreenManager.AddScreen(screen);
}
screensToLoad = null;
}
ExTime -= gameTime.ElapsedGameTime.Milliseconds;
if (ExTime < 0)
{
IsExit = true;
gameScreenManager.RemoveScreen(this);
foreach (GameScreen screen in gameScreenManager.Screens)
screen.IsActive = true;
gameScreenManager.Game.ResetElapsedTime();
}
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
if (!IsExit)
{
gameScreenManager.Game.GraphicsDevice.Clear(Color.Black);
gameScreenManager.spriteBatch.Begin();
gameScreenManager.spriteBatch.DrawString(gameScreenManager.Font, "Loading...", new Vector2(100, 100), Color.White);
gameScreenManager.spriteBatch.End();
}
}
}
}Окно загрузки. Если вам нужно загрузить один или несколько экранов, которые содержат много контента - используйте этот класс. В основном используется для перехода от меню к игре. Обязательно статический метод Load, сюда передается родитель CGameScreenManager screenManager и массив экранов которые нужно загрузить. Перед тем как загрузить переданные экраны выгружаем все экраны что есть, и ставим на показ собственный экран (Loading…). Чтобы все не происходило мгновенно, и пользователь мог полюбоваться «шикарной» надписью, я сделал минимальную задержку в миллисекундах ExTime. После загрузки всех экранов нужно обязательно выгрузить себя из списка экранов и сделать gameScreenManager.Game.ResetElapsedTime() – обнулить счетчик прошедшего времени, чтобы загруженные экраны не «ринулись» если они зависят от времени.GameScreenManager.cs
namespace MenuGame.GameScreenManager
{
class CGameScreenManager : DrawableGameComponent
{
public List<GameScreen> Screens = new List<GameScreen>();
public SpriteBatch spriteBatch;
public bool IsInitialized;
public SpriteFont Font;
public GameScreenInput input = new GameScreenInput();
public GraphicsDeviceManager graphicsDeviceManager;
public CGameScreenManager(Game game, GraphicsDeviceManager graphicDeviceManager)
: base(game)
{
graphicsDeviceManager = graphicDeviceManager;
}
public override void Initialize()
{
base.Initialize();
IsInitialized = true;
}
protected override void LoadContent()
{
ContentManager content = Game.Content;
spriteBatch = new SpriteBatch(GraphicsDevice);
Font = content.Load<SpriteFont>("Font\\FontMenu");
foreach (GameScreen screen in Screens)
{
screen.LoadContent();
}
}
protected override void UnloadContent()
{
foreach (GameScreen screen in Screens)
{
screen.UnLoadContent();
}
}
public override void Update(GameTime gameTime)
{
for (int i = 0; i < Screens.Count; i++) if (Screens[i].IsActive) Screens[i].Update(gameTime);
}
public override void Draw(GameTime gameTime)
{
foreach (GameScreen screen in Screens)
{
if (screen.IsActive)
screen.Draw(gameTime);
}
}
public void AddScreen(GameScreen gameScreen)
{
gameScreen.gameScreenManager = this;
if (IsInitialized)
gameScreen.LoadContent();
Screens.Add(gameScreen);
}
public void RemoveScreen(GameScreen gameScreen)
{
gameScreen.UnLoadContent();
Screens.Remove(gameScreen);
}
public GameScreen[] GetScreens()
{
return Screens.ToArray();
}
}
}Главный класс – менеджер экранов. Содержит список экранов Screens. Некоторые данные: графическое устройство, шрифт, SpriteBatch. Принцип любого метода не сложный – перебрать все экраны, которые есть в коллекции, и сделать с ними действие, соответствующее методу. Есть нюанс в методе AddScreen нужно проверить, был ли сам менеджер инициализирован перед загрузкой контента экранов, нужен для этих строк gameScreenManager = new CGameScreenManager(this, graphics); Components.Add(gameScreenManager); gameScreenManager.AddScreen(new Background()); gameScreenManager.AddScreen(new MainMenu());GameScreenMenuElement.cs
namespace MenuGame.GameScreenManager
{
class GameScreenMenuElement
{
public string Text;
public delegate void dHandler(object sender);
public event dHandler Selected;
public GameScreenMenuElement(string text)
{
Text = text;
}
public virtual void Update()
{ }
public virtual void Draw(GameScreen screen, Vector2 Position, GameTime gameTime, bool IsSelected)
{
Color color;
if (IsSelected)
color = Color.Red;
else
color = Color.White;
screen.gameScreenManager.spriteBatch.DrawString(screen.gameScreenManager.Font, Text, Position, color);
}
public void OnSelect()
{
if (Selected != null) Selected(this);
}
}
}Независимый класс элемента меню. Элемент основан на тексте – то есть меню будет из текстовых строчек. Модифицировать под спрайты не сложно. Принцип такой – если курсор на позиции меню то оно отображается красным цветов, если нет – белым. Если вы нажали на меню - то будет вызываться метод OnSelect, который в свою очередь будет вызывать event по описанию делегата dHandler. Методы обработки элемента здесь нет – в этом то и смысл event что вы добавите его, например: GameScreenMenuElement BackGameMenu = new GameScreenMenuElement("Back");
BackGameMenu.Selected += Back;
Elements.Add(BackGameMenu);
}
public void Back(object sender)
{
gameScreenManager.AddScreen(new MainMenu());
this.ExitGameScreen();
}
Нужно проверять Selected != null если элемент меню создан, но в event ничего не добавлено.GameScreenMenu.cs
namespace MenuGame.GameScreenManager
{
class GameScreenMenu:GameScreen
{
public List<GameScreenMenuElement>Elements = new List<GameScreenMenuElement>();
public string ScreenTitle;
public int SelectedIndex=0;
public GameScreenMenu(string screenTitle)
{
ScreenTitle = screenTitle;
}
public override void Update(GameTime gameTime)
{
gameScreenManager.input.Update();
if (gameScreenManager.input.Up) SelectedIndex--;
if (gameScreenManager.input.Down) SelectedIndex++;
if (SelectedIndex < 0) SelectedIndex = 0;
if (SelectedIndex > Elements.Count-1) SelectedIndex = Elements.Count-1;
if (gameScreenManager.input.Fire) Elements[SelectedIndex].OnSelect();
}
public override void Draw(GameTime gameTime)
{
gameScreenManager.spriteBatch.Begin();
gameScreenManager.spriteBatch.DrawString(gameScreenManager.Font, ScreenTitle, new Vector2(100, 10), Color.DarkRed);
for (int i=0;i<Elements.Count;i++)
{
Elements[i].Draw(this, new Vector2(100, 100)+ new Vector2(0,i*gameScreenManager.Font.LineSpacing), gameTime, i==SelectedIndex);
}
gameScreenManager.spriteBatch.End();
}
}
}Класс меню содержит список элементов меню ListGame1.cs
using MenuGame.GameScreenManager;
using MenuGame.Screen;
namespace MenuGame
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
CGameScreenManager gameScreenManager;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1280;
graphics.PreferredBackBufferHeight = 720;
gameScreenManager = new CGameScreenManager(this, graphics);
Components.Add(gameScreenManager);
gameScreenManager.AddScreen(new Background());
gameScreenManager.AddScreen(new MainMenu());
}
protected override void Initialize()
{
base.Initialize();
gameScreenManager.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
}
}
}Файлы основной программы, генериться самой XNA, изменения сделаны в конструкторе и списке свойств класса. Теперь базовые классы созданы, нужно показать, как их использовать.Background.cs
using MenuGame.GameScreenManager;
namespace MenuGame.Screen
{
class Background: GameScreen
{
public Texture2D backgroundTexture;
public byte Alpha=160;
private ContentManager Content;
public Background()
: base()
{ IsActive = true; }
public override void LoadContent()
{
base.LoadContent();
if (Content==null)
Content = new ContentManager(gameScreenManager.Game.Services, "Content");
backgroundTexture = Content.Load<Texture2D>("Sprite\\Background\\bg");
}
public override void UnLoadContent()
{
base.UnLoadContent();
if (Content!=null)
Content.Unload();
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
SpriteBatch spriteBatch = gameScreenManager.spriteBatch;
Viewport viewport = gameScreenManager.GraphicsDevice.Viewport;
Rectangle fullscreen = new Rectangle(0, 0, viewport.Width,viewport.Height);
spriteBatch.Begin(SpriteBlendMode.None);
spriteBatch.Draw(backgroundTexture, fullscreen, new Color(255, 255, 255, Alpha));
spriteBatch.End();
}
}
}Экран, который ничего не делает кроме как отображает фоновый рисунок 1280х720, является наследником базового GameScreen. Credits.cs
using MenuGame.GameScreenManager;
namespace MenuGame.Screen
{
class Credits : GameScreenMenu
{
string creditsText;
public Credits()
: base("Credits")
{
creditsText = "maded by Vitukhin Sergey\nbla bla bla\nbla";
GameScreenMenuElement BackGameMenu = new GameScreenMenuElement("Back");
BackGameMenu.Selected += Back;
Elements.Add(BackGameMenu);
}
public void Back(object sender)
{
gameScreenManager.AddScreen(new MainMenu());
this.ExitGameScreen();
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
gameScreenManager.spriteBatch.Begin();
gameScreenManager.spriteBatch.DrawString(gameScreenManager.Font,creditsText,new Vector2(200,150),Color.White);
gameScreenManager.spriteBatch.End();
}
}
}Класс «О себе любимом» Заголовок Credits, который передаем в базовый класс GameScreenMenu. Содержит текст creditsText, который и должен отображать, кроме меню. По элементу BackGameMenu будет выполняться выход в главное меню (удаление самого себя и добавление в менеджер MainMenu()).HowToPlay.cs
namespace MenuGame.Screen
{
class HowToPlay : GameScreenMenu
{
string creditsText;
public HowToPlay()
: base("How To Play")
{
creditsText = "To play to win ... \nYou can do it!";
GameScreenMenuElement BackGameMenu = new GameScreenMenuElement("Back");
BackGameMenu.Selected += Back;
Elements.Add(BackGameMenu);
}
public void Back(object sender)
{
gameScreenManager.AddScreen(new MainMenu());
this.ExitGameScreen();
}
public override void Draw(GameTime gameTime)
{
base.Draw(gameTime);
gameScreenManager.spriteBatch.Begin();
gameScreenManager.spriteBatch.DrawString(gameScreenManager.Font,creditsText,new Vector2(200,150),Color.White);
gameScreenManager.spriteBatch.End();
}
}
}Полная аналогия с предыдущим классом. Можете добавить картинки и т.д. MainGame.cs
namespace MenuGame.Screen
{
class MainGame:GameScreen
{
Random random;
GameScreenInput GInput;
public MainGame()
{
random = new Random();
GInput = new GameScreenInput();
}
public override void Update(GameTime gameTime)
{
GInput.Update(); //если использовать инпут менеджера то затираються данные в апдейте (вызов со второго скрина)
if (GInput.Menu)
{
gameScreenManager.AddScreen(new PauseMenu());
IsActive = false;
}
}
public override void Draw(GameTime gameTime)
{
gameScreenManager.Game.GraphicsDevice.Clear(Color.Black);
gameScreenManager.spriteBatch.Begin();
gameScreenManager.spriteBatch.DrawString(gameScreenManager.Font, "Game", new Vector2(random.Next(100, 200), random.Next(100, 200)), Color.Yellow);
gameScreenManager.spriteBatch.End();
}
}
}Экран игры. Тут собственно ничего особенного не происходит – просто в случайном порядке выводиться надпись "Game" – это показательная программа, а не полноценная игра. По нажатию на Menu будет происходить вызов меню паузы. Нюанс в том, что нужно использовать свой GameScreenInput GInput иначе после двойного вызова обновления нажатая клавиша пропадет после вызова второго метода из-за строчки: oldKeyboardState = keyboardState;MainMenu.cs
using MenuGame.GameScreenManager;
namespace MenuGame.Screen
{
class MainMenu:GameScreenMenu
{
public MainMenu()
: base("Main menu")
{
GameScreenMenuElement PlayGameMenu = new GameScreenMenuElement("Play Game");
GameScreenMenuElement OptionsGameMenu = new GameScreenMenuElement("Optons");
GameScreenMenuElement HowToPlayGameMenu = new GameScreenMenuElement("How to play");
GameScreenMenuElement CreditsGameMenu = new GameScreenMenuElement("Credits");
GameScreenMenuElement ExitGameMenu = new GameScreenMenuElement("Exit Game");
ExitGameMenu.Selected += ExitGame;
CreditsGameMenu.Selected += Credits;
HowToPlayGameMenu.Selected += HowToPlay;
OptionsGameMenu.Selected += Options;
PlayGameMenu.Selected += NewGame;
Elements.Add(PlayGameMenu);
Elements.Add(OptionsGameMenu);
Elements.Add(HowToPlayGameMenu);
Elements.Add(CreditsGameMenu);
Elements.Add(ExitGameMenu);
}
void Options(object sender)
{
this.ExitGameScreen();
gameScreenManager.AddScreen(new Options());
}
void HowToPlay(object sender)
{
this.ExitGameScreen();
gameScreenManager.AddScreen(new HowToPlay());
}
void Credits(object sender)
{
this.ExitGameScreen();
gameScreenManager.AddScreen(new Credits());
}
void ExitGame(object sender)
{
gameScreenManager.Game.Exit();
}
void NewGame(object sender)
{
GameScreenLoading.Load(gameScreenManager, new MainGame());
}
}
}Главное меню. Хоть оно и главное тут ничего особенного, - создаются элементы меню, добавляются к event описанные методы. Каждый метод описывает удаление этого окна и добавление соответствующего экрана по пункту меню к менеджеру.PauseMenu.cs
using MenuGame.GameScreenManager;
namespace MenuGame.Screen
{
class PauseMenu : GameScreenMenu
{
public PauseMenu()
: base("Pause Menu")
{
GameScreenMenuElement ResumeGameMenu = new GameScreenMenuElement("Resume game");
GameScreenMenuElement ToMainMenuMenu = new GameScreenMenuElement("Main menu");
ResumeGameMenu.Selected += ResumeGame;
ToMainMenuMenu.Selected += ToMainMenu;
Elements.Add(ResumeGameMenu);
Elements.Add(ToMainMenuMenu);
IsActive = true;
}
void ResumeGame(object sender)
{
foreach (GameScreen screen in gameScreenManager.Screens)
screen.IsActive = true;
this.ExitGameScreen();
}
void ToMainMenu(object sender)
{
GameScreenLoading.Load(gameScreenManager, new GameScreen[] { new Background(), new MainMenu() });
}
}
}Меню паузы, наследник GameScreenMenu. Два метода выход в главное меню через GameScreenLoading.Load - потому что экрану игры нужно тоже сделать немаленький выгруз контента. Продолжить игру ResumeGame – делает все окна активными (игра на паузе – значит не обновляется и не рисуется), а себя выгружает.Options.cs
using MenuGame.GameScreenManager;
namespace MenuGame.Screen
{
class Options: GameScreenMenu
{
public bool IsFullScreen=false;
GameScreenMenuElement FullScreenGameMenu;
GameScreenMenuElement ScreenResolutionMenu;
GameScreenMenuElement ApplyMenu;
string tFullScreenGameMenu="Full Screen: ";
string tReSolution="Resolution: ";
string[] Resolutions = { "480p (640x480)", "720p (1280x720)", "1080i/1080p (1920x1080)" };
int[] ResolutionWidth = { 640, 1280, 1920 };
int[] ResolutionHeight = {480, 720, 1080};
int CurrentResolution = 1;
public Options()
: base("Options")
{
ScreenResolutionMenu = new GameScreenMenuElement(tReSolution + Resolutions[CurrentResolution]);
FullScreenGameMenu = new GameScreenMenuElement(tFullScreenGameMenu+(IsFullScreen?"yes":"no"));
ApplyMenu = new GameScreenMenuElement("Apply");
GameScreenMenuElement BackGameMenu = new GameScreenMenuElement("Back");
ScreenResolutionMenu.Selected += ChangeResolution;
FullScreenGameMenu.Selected += FullScreen;
ApplyMenu.Selected += Apply;
BackGameMenu.Selected += Back;
Elements.Add(ScreenResolutionMenu);
Elements.Add(FullScreenGameMenu);
Elements.Add(ApplyMenu);
Elements.Add(BackGameMenu);
}
public void Back(object sender)
{
gameScreenManager.AddScreen(new MainMenu());
this.ExitGameScreen();
}
public void Apply(object sender)
{
gameScreenManager.graphicsDeviceManager.PreferredBackBufferWidth = ResolutionWidth[CurrentResolution];
gameScreenManager.graphicsDeviceManager.PreferredBackBufferHeight = ResolutionHeight[CurrentResolution];
gameScreenManager.graphicsDeviceManager.ApplyChanges();
if (IsFullScreen != gameScreenManager.graphicsDeviceManager.IsFullScreen)
gameScreenManager.graphicsDeviceManager.ToggleFullScreen();
}
public void ChangeResolution(object sender)
{
CurrentResolution = (CurrentResolution + 1) % Resolutions.Length;
ScreenResolutionMenu.Text = tReSolution + Resolutions[CurrentResolution];
}
public void FullScreen(object sender)
{
if (!IsFullScreen)
{
IsFullScreen = true;
FullScreenGameMenu.Text = tFullScreenGameMenu + "yes";
}
else
{
IsFullScreen = false;
FullScreenGameMenu.Text = tFullScreenGameMenu + "no";
}
}
}
}Экран опции: задаем разрешение экрана, делаем игру полноэкранной, для списка разрешений несколько массивов с данными о разрешениях.Вот и все в примере на creators.xna.com код серьезней, если вы осмыслили этот текст то можно приступить к модификации: добавить эффекты к тексту, плавная смена экранов, привязка меню к игроку который ее вызвал, добавление других устройств управление и т.д.
Исходный код проекта

Комментарии
Отправить комментарий