Понеслась! Пора описывать алгоритмы в играх. Сам я не профессиональный программист, просто в последнее время мне стала интересна тема игр, всегда хотелось написать что то, как и каждому «компьютерщику». Но, когда садишься что-то сделать, оказывается, что это ой как не просто.
Сделав несколько рабочих набросков игр, код становится плохо читаемым и много чего хочется переделать. Вот и настает тот момент, когда нужно читать книги по алгоритмам и разбираться в чужом коде.
Начнем разбираться со структурой игровых экранов и меню, походу поймете, что это некий костяк игры. Пример, расскажет, как организовано меню и окно игрового процесса. Я взял и разобрал пример 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(); } } }Класс меню содержит список элементов меню List
Game1.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 код серьезней, если вы осмыслили этот текст то можно приступить к модификации: добавить эффекты к тексту, плавная смена экранов, привязка меню к игроку который ее вызвал, добавление других устройств управление и т.д.
Исходный код проекта
Комментарии
Отправить комментарий