Основы разработки компьютерных игр в XNA Game Studio

Оформление игры

Разбить на страницы
Показывать лекцию целиком

Примеры к лабораторной работе

Задачи работы

  • Разработать стартовый экран игры
  • Разработать систему меню игры
  • Разработать игровой экран
  • Разработать систему игровой справки
  • Научиться работать с текстом
  • Работа с текстом

    Для организации вспомогательных подсистем игры важно уметь работать с текстами. В XNA можно использовать несколько подходов к выводу текстовых сообщений. Во-первых – можно воспользоваться стандартными средствами вывода текста одним из шрифтов, установленных в системе. Таким способом удобно выводить на экран большие объемы текстов.

    Во-вторых – можно создать нужные надписи в виде изображений и выводить их как обычные спрайты. Этот подход удобен при создании системы меню и других надписей, которые нет необходимости модифицировать в ходе работы игровой программы. Причем, такие надписи можно анимировать теми же способами, которые используются для анимации спрайтов. Обычно системы меню в компьютерных играх создаются именно таким способом.

    Третий способ вывода текста – это создать нечто вроде алфавита – спрайта, содержащего изображения букв и, разработав систему вывода нужных участков этого спрайта с необходимыми символами, выводить любые тексты таким способом.

    Рассмотрим способ вывода обычного текста в XNA. Создадим новый проект P9_1. Прежде чем заниматься выводом текста, нужно создать ресурс типа .SPRITEFONT. Это – XML-файл, который содержит параметры шрифта, используемого для вывода. Для того, чтобы создать этот файл, выполните следующие действия.

  • Щелкните правой кнопкой мыши на папке Content и выберите в появившемся меню пункт Add $$\Rightarrow$$ New Item.
  • В появившемся окне добавления нового элемента выберите объект (рис 13.1) Создание нового файла типа Spritefont
  • Дайте файлу имя, соответствующее имени одного из шрифтов, установленных в вашей системе. Например, обычно везде можно найти шрифт Courier New, Times New Roman, Arial. Мы выбрали шрифт Monotype Corsiva. Обратите внимание на то, что имя SPRITEFONT-файла должно соответствовать имени одного из установленных в системе шрифтов.
  • Нажмите кнопку Add и файл будет добавлен в проект. Для того, чтобы управлять свойствами шрифта, нам понадобится редактировать этот файл. Чтобы открыть его для редактирования в XML-редакторе, встроенном в Visual Studio, достаточно сделать двойной щелчок по этому файлу рис 13.2(рис 13.2) Редактирование Spritefont-файла
  • Этот файл состоит из множества документированных разделов, которые позволяют настраивать свойства шрифта. В частности, по умолчанию параметр Style установлен в Regular. Для шрифта Monotype Corsiva этот стиль отображения не определен, поэтому нам нужно заменить Regular на Italic.

    Теперь все готово для использования шрифта в программе. Ниже, в листинге 13.1. приведен код класса Game1, реализующий вывод на экран строки текста.

    using System;
    using System.Collections.Generic;
    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.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace P9_1
    {
       
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;
            SpriteBatch spriteBatch;
            //Для хранения шрифта
            SpriteFont MyFont;
            //Для хранения позиции вывода текста
            Vector2 StringPosition;
            //Строка для вывода
            string OutString;
            //Для нахождения центра строки
            Vector2 StringOrigin; 
            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                OutString = "Score: 12345";
                base.Initialize();
            }
    
            
            protected override void LoadContent()
            {
                spriteBatch = new SpriteBatch(GraphicsDevice);
                //Загружаем шрифт
                MyFont  = Content.Load<SpriteFont>("Monotype Corsiva");
                //Устанавливаем позицию вывода шрифта в центре экрана
                StringPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2,
            graphics.GraphicsDevice.Viewport.Height / 2);
                //Находим центр строки
               StringOrigin = MyFont.MeasureString(OutString) / 2;
            }
    
            protected override void UnloadContent()
            {
            }
    
            protected override void Update(GameTime gameTime)
            {
                // Allows the game to exit
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                    this.Exit();
    
                base.Update(gameTime);
            }
    
            
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
                spriteBatch.Begin();
                //Выводим строку
                spriteBatch.DrawString(MyFont , OutString , StringPosition, Color.Red,
                    0, StringOrigin, 1.0f, SpriteEffects.None, 0.5f);
                spriteBatch.End();
                base.Draw(gameTime);
            }
        }
    }

    Как видите, вывод текста очень похож на вывод изображений. Для хранения шрифта используется переменная типа SpriteFont, метод DrawString объекта типа SpriteBatch отвечает за прорисовку текста на игровом экране:

    spriteBatch.DrawString(MyFont, OutString, StringPosition, Color.Red, 0, StringOrigin, 1.0f, SpriteEffects.None, 0.5f);

    Рассмотрим параметры вызова этого метода в табл. 13.1.

    Параметры метода DrawString объекта SpriteBatch
    Параметр вызова метода Описание параметра
    MyFont Переменная типа SpriteFont – шрифт, которым выводится текст
    OutString Переменная типа String – текст для вывода
    StringPosition Переменная типа Vector2 – позиция текста
    Color.Red Цвет текста
    0 Поворот текста в радианах относительно точки привязки координат текста
    StringOrigin Переменная типа Vector2 – точка привязки координат объекта
    1.0f Коэффициент масштабирования объекта
    SpriteEffects.None Эффект спрайта
    0.5f Глубина текста – положение относительно других графических объектов

    На рис. 13.3. вы можете видеть игровой экран проекта P9_1.

    (рис 13.3) Экран проекта P9_1

    Оформление игры

    Создадим новый проект P9_2. На его примере рассмотрим приемы работы, освоение которых вынесено в задачи данной работы.

    Задача оформления игры сводится к созданию нескольких игровых экранов с определенной функциональностью и к разработке системы перемещения между этими экранами.

    На рис. 13.4. вы можете видеть окно Project Explorer проекта P9_2.

    (рис 13.4) Окно Project Explorer проекта P9_2

    В листинге 13.2. приведен код класса Game1.

    using System;
    using System.Collections.Generic;
    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.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace P9_2
    {
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;
            SpriteBatch spriteBatch;
            //Экран справки
            HelpScreen help;
            Texture2D helpBack;
            Texture2D helpText;
            //Экран меню
            MenuScreen menu;
            Texture2D menuBack;
            Texture2D menuGameName;
            Texture2D menuTxtStartGame;
            Texture2D menuTxtHelp;
            Texture2D menuTxtExit;
            //Текущий элемент меню
            int currentItem;
            //Интервал задержки переключения между элементами меню
            int interval;
            //Игровой экран
            GameScreen newGame;
            Texture2D gameBat;
    
            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                //При запуске игры активным устанавливается первый элемент меню
                currentItem = 1;
                interval = 0;
                base.Initialize();
            }
    
            protected override void LoadContent()
            {
                //Загружаем элементы игры и создаем игровые экраны
                spriteBatch = new SpriteBatch(GraphicsDevice);
                Services.AddService(typeof(SpriteBatch), spriteBatch);
                helpBack = Content.Load<Texture2D>("helpBack");
                helpText = Content.Load<Texture2D>("helpText");
                help = new HelpScreen(this, helpBack, helpText,
                    new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height),
                    new Rectangle((this.Window.ClientBounds.Width - helpText.Width)/2, 
                    (this.Window.ClientBounds.Height - helpText.Height)/2, 
                    helpText .Width , helpText .Height ));
                Components.Add(help);
                gameBat = Content.Load<Texture2D>("BallandBats");
                newGame = new GameScreen(this, ref gameBat, new Rectangle(18, 9, 17, 88), 
                    new Vector2(100, 100));
                Components.Add(newGame);
                menuBack = Content.Load<Texture2D>("menuBack");
                menuGameName = Content.Load<Texture2D>("menuGameName");
                menuTxtStartGame = Content.Load<Texture2D>("menuTxtStartGame");
                menuTxtHelp = Content.Load<Texture2D>("menuTxtHelp");
                menuTxtExit = Content.Load<Texture2D>("menuTxtExit");
                menu = new MenuScreen(this, menuBack, menuGameName, menuTxtStartGame, menuTxtHelp,
                    menuTxtExit, new Rectangle(100, 300, 400, 50), new Rectangle(100, 350, 400, 50), 
                    new Rectangle(100, 400, 400, 50));
                Components.Add(menu);
                //Отображаем меню, остальные элементы скрыты
                menu.Show();
            }
    
            protected override void UnloadContent()
            {
                // TODO: Unload any non ContentManager content here
            }
    
            //Обработка нажатий на клавиши
            void KeyboardHandle()
            {
                //считаем состояние клавиатуры
                  KeyboardState kbState = Keyboard.GetState();
                //Если прошло 4 игровых цикла
                interval ++;
                if (interval >=4)
                {
                    //Обнуляем счетичик
                    interval = 0;
    
                    //Если выбран экран меню
                    if (menu.Enabled)
                    {
                        //Если нажата клавиша вверх
                        if (kbState.IsKeyDown(Keys.Up))
                        {
                            //Уменьшить на 1 счетчик, отвечающий за
                            //смену пунктов меню
                            currentItem--;
                            //Если в момент нажатия был активен самый верхний
                            //пункт
                            if (currentItem < 1)
                            {   
                                //Делаем текущим третий пункт
                                currentItem = 3;
                                //Вызов процедуры для отображения смены пункта меню
                                menu.GetKey(currentItem);
                            }
                                //если при уменьшении номера пункта
                                //он не меньше 1, вызываем процедуру
                                //отображения смены пункта
                            else
                            {
                                menu.GetKey(currentItem);
                            }
                        }
                        //При нажатии клавиши вниз
                        if (kbState.IsKeyDown(Keys.Down))
                        {
                            //Увеличим счетчик
                            currentItem++;
                            //Если он больше 3 - активируем первый пункт
                            if (currentItem > 3)
                            {
                                currentItem = 1;
                                menu.GetKey(currentItem);
                            }
                                //Если не меньше 3 - переходим на следующий пункт
                            else
                            {
                                menu.GetKey(currentItem);
                            }
                        }
                        //При нажатии клавиши Enter
                        //выполняем команду, соответствующую текущему пункту меню
                        if (kbState .IsKeyDown (Keys .Enter))
                        {
                            //Первый пункт - скрываем экран меню
                            //и отображаем игровой экран
                            if (currentItem == 1)
                            {
                                menu.Hide();
                                newGame.Show();
                            }
                            //Скрываем меню и
                            //отображаем экран справки
                            if (currentItem ==2) 
                            {
                                menu.Hide();
                                help.Show ();
                            }
                            //Выходим из игры
                            if (currentItem == 3)
                            {
                                this.Exit();
                            }
                        }
                     
    
                    }
                }
                //Если активен экран справки
                    if (help.Enabled)
                    {
                        //При нажатии Esc закрываем экран справки
                        //И открываем меню
                        if (kbState.IsKeyDown(Keys.Escape))
                        {
                            help.Hide();
                            menu.Show();
                        }
                    }
                //Если активен игровой экран
                //При нажатии Esc закрываем его 
                //и открываем меню
                //Обработка нажатий клавиш, необходимых для работы игрового экрана
                //проводится в объекте, соответствующем этому экрану
                    if (newGame.Enabled)
                    {
                        if (kbState.IsKeyDown(Keys.Escape))
                        {
                            newGame.Hide();
                            menu.Show();
                        }
                    }
            }
    
            protected override void Update(GameTime gameTime)
            {
                //Обработка нажатий на клавиши
                KeyboardHandle();
                base.Update(gameTime);
            }
    
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
                spriteBatch.Begin();
                base.Draw(gameTime);
                spriteBatch.End();
            }
        }
    }

    В листинге 13.3. вы можете видеть код класса Classes.Menu

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2.Classes
    {
        //Класс для хранения данных об элементах меню
        public class Menu
        {
            public Texture2D imageTexture;
            public Rectangle imageRectangle;
            public Color color;
            public Menu(Game game, Texture2D _texture, Rectangle _rectangle, Color _color)
            {
                imageTexture = _texture;
                imageRectangle = _rectangle;
                color = _color;
            }
        
        }
    }

    В листинге 13.4. приведен код класса Screen – этот класс является базовым для классов игровых экранов. Он содержит методы для активации и деактивации игровых экранов.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
    //Базовый класс, на основе которого создаются игровые экраны
        public class Screen : Microsoft.Xna.Framework.DrawableGameComponent
        {
            //Для объекта SpriteBatch, который используется
            //для вывода изображений в классах-наследниках
            public SpriteBatch sprBatch;
    
            public Screen(Game game)
                : base(game)
            {
                //При создании объекта он по умолчанию 
                //невидим и неактивен
                Visible = false;
                Enabled = false;
                //получим объект SpriteBatch
                sprBatch =(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            }
            //Процедура для отображения и активации объекта
            public void Show()
            {
                Visible = true;
                Enabled = true;
            }
            //Процедура для скрытия и отключения объекта
            public void Hide()
            {
                Visible = false;
                Enabled = false;
            }
    
        }
    }

    На рис. 13.5. вы можете видеть экран игрового меню.

    (рис 13.5) Экран игрового меню

    В листинге 13.5. приведен код класса MenuScreen.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
    //Экран меню
        public class MenuScreen : Screen 
        {
            //Элементы меню будем хранить в массиве типа Menu
            //тип данных Menu хранит информацию об элементах меню
            Classes.Menu[] menu;
            //Текстуры и прямоугольники для вывода элементов меню
            Texture2D menuTxtStartGame;
            Texture2D menuTxtHelp;
            Texture2D menuTxtExit;
            Rectangle menuTxtStartGameR, menuTxtHelpR, menuTxtExitR;
            Texture2D menuBack, menuGameName;
            Rectangle menuBackR, menuGameNameR;
    
            public MenuScreen(Game game, Texture2D _menuBack, Texture2D _menuGameName, 
                Texture2D _menuTxtStartGame, Texture2D _menuTxtHelp, Texture2D _menuTxtExit,
                Rectangle _menuTxtStartGameR,Rectangle _menuTxtHelpR, Rectangle _menuTxtExitR)
                : base(game)
            {
                menuBack = _menuBack;
                menuGameName = _menuGameName;
                menuBackR = new Rectangle(0, 0, 800, 600);
                menuGameNameR = new Rectangle(0, 0, 800, 200);
                menuTxtStartGame = _menuTxtStartGame ;
                menuTxtStartGameR = _menuTxtStartGameR;
                menuTxtHelp = _menuTxtHelp;
                menuTxtHelpR = _menuTxtHelpR;
                menuTxtExit = _menuTxtExit;
                menuTxtExitR = _menuTxtExitR;
                //Создаем массив из 3-х элементов
                menu = new P9_2.Classes.Menu[3];
                //Инициализируем элементы массива
                //Элемент с номером 0 активен
                menu[0]=new P9_2.Classes.Menu (game, menuTxtStartGame, menuTxtStartGameR ,Color.Magenta);
                menu[1] = new P9_2.Classes.Menu(game, menuTxtHelp, menuTxtHelpR, Color.White);
                menu[2] = new P9_2.Classes.Menu(game, menuTxtExit, menuTxtExitR, Color.White);
    
            }
            //Метод вызывается для отображения смены пункта меню
            public void GetKey(int i)
            {
                //i=1 - первый пункт
                //i=2 - второй пункт
                //i=3 - третий пункт
                if (i == 1)
                {
                    menu[0].color = Color.Magenta;
                    menu[1].color = Color.White;
                    menu[2].color = Color.White;
    
                }
                if (i == 2)
                {
                  menu[0].color = Color.White;
                  menu[1].color = Color.Magenta;
                  menu[2].color = Color.White;
                }
                if (i == 3)
                {
                    menu[0].color = Color.White;
                    menu[1].color = Color.White;
                    menu[2].color = Color.Magenta;
    
                }
            }
            //Рисование элементов
            public override void Draw(GameTime gameTime)
            {
                //Вывод фона и названия игры
                sprBatch.Draw(menuBack, menuBackR, Color.White);
                sprBatch.Draw(menuGameName, menuGameNameR, Color.White);
                //Вывод элементов меню
                for (int i=0;i<3;i++)
                    sprBatch.Draw(menu[i].imageTexture, menu[i].imageRectangle, menu[i].color);
                base.Draw(gameTime);
            }
    
        }
    }

    На рис. 13.6. вы может видеть справочный экран.

    (рис 13.6) Экран игровой справки

    В листинге 13.6. вы можете найти код класса HelpScreen.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
        //Экран справки
        public class HelpScreen : Screen 
        {
            //Текстуры для фона и справочной надписи
            Texture2D backTexture;
            Rectangle backRectangle;
            Texture2D helpTexture;
            Rectangle helpRectangle;
    
            public HelpScreen(Game game, Texture2D _back, Texture2D _help, Rectangle _backRec, Rectangle _helpRec)
                : base(game)
            {
                backTexture = _back;
                helpTexture = _help;
                backRectangle = _backRec;
                helpRectangle = _helpRec;
            }
            public override void Draw(GameTime gameTime)
            {
                //Выводим изображения
                sprBatch.Draw(backTexture, backRectangle, Color.White);
                sprBatch.Draw(helpTexture, helpRectangle, Color.White);
                base.Draw(gameTime);
            }
            
        }
    }

    На рис. 13.7. вы можете видеть игровой экран.

    (рис 13.7) Игровой экран

    В листинге 13.7. приведен код игрового экрана – класса GameScreen. Мы использовали простой код для перемещения объекта из проекта P3_4.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
        //Игровой экран
    
        public class GameScreen :Screen
        {
            private Texture2D sprTexture;
            private Rectangle sprRectangle;
            private Vector2 sprPosition;
            private Rectangle scrBounds;
    
    
            public GameScreen(Game game, ref Texture2D newTexture,
                Rectangle newRectangle, Vector2 newPosition)
                : base(game)
            {
                sprTexture = newTexture;
                sprRectangle = newRectangle;
                sprPosition = newPosition;
                scrBounds = new Rectangle(0, 0,
                    game.Window.ClientBounds.Width,
                    game.Window.ClientBounds.Height);
            }
    
            public override void Initialize()
            {
                // TODO: Add your initialization code here
    
                base.Initialize();
            }
    
            public override void Update(GameTime gameTime)
            {
    
                    KeyboardState kbState = Keyboard.GetState();
                    if (kbState.IsKeyDown(Keys.Up))
                    {
                        sprPosition.Y -= 5;
                    }
                    if (kbState.IsKeyDown(Keys.Down))
                    {
                        sprPosition.Y += 5;
                    }
                    if (kbState.IsKeyDown(Keys.Left))
                    {
                        sprPosition.X -= 5;
                    }
                    if (kbState.IsKeyDown(Keys.Right))
                    {
                        sprPosition.X += 5;
                    }
    
                    if (sprPosition.X < scrBounds.Left)
                    {
                        sprPosition.X = scrBounds.Left;
                    }
                    if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
                    {
                        sprPosition.X = scrBounds.Width - sprRectangle.Width;
                    }
                    if (sprPosition.Y < scrBounds.Top)
                    {
                        sprPosition.Y = scrBounds.Top;
                    }
                    if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
                    {
                        sprPosition.Y = scrBounds.Height - sprRectangle.Height;
                    }
    
                    base.Update(gameTime);
                
            }
            public override void Draw(GameTime gameTime)
            {
                sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);
                base.Draw(gameTime);
            }
        }
    }

    Задание

    Создайте систему игровых экранов и меню, которым можно управлять с помощью мыши и клавиатуры.

    Страницы:

    Примеры к лабораторной работе

    Задачи работы

  • Разработать стартовый экран игры
  • Разработать систему меню игры
  • Разработать игровой экран
  • Разработать систему игровой справки
  • Научиться работать с текстом
  • Работа с текстом

    Для организации вспомогательных подсистем игры важно уметь работать с текстами. В XNA можно использовать несколько подходов к выводу текстовых сообщений. Во-первых – можно воспользоваться стандартными средствами вывода текста одним из шрифтов, установленных в системе. Таким способом удобно выводить на экран большие объемы текстов.

    Во-вторых – можно создать нужные надписи в виде изображений и выводить их как обычные спрайты. Этот подход удобен при создании системы меню и других надписей, которые нет необходимости модифицировать в ходе работы игровой программы. Причем, такие надписи можно анимировать теми же способами, которые используются для анимации спрайтов. Обычно системы меню в компьютерных играх создаются именно таким способом.

    Третий способ вывода текста – это создать нечто вроде алфавита – спрайта, содержащего изображения букв и, разработав систему вывода нужных участков этого спрайта с необходимыми символами, выводить любые тексты таким способом.

    Рассмотрим способ вывода обычного текста в XNA. Создадим новый проект P9_1. Прежде чем заниматься выводом текста, нужно создать ресурс типа .SPRITEFONT. Это – XML-файл, который содержит параметры шрифта, используемого для вывода. Для того, чтобы создать этот файл, выполните следующие действия.

  • Щелкните правой кнопкой мыши на папке Content и выберите в появившемся меню пункт Add $$\Rightarrow$$ New Item.
  • В появившемся окне добавления нового элемента выберите объект (рис 13.1) Создание нового файла типа Spritefont
  • Дайте файлу имя, соответствующее имени одного из шрифтов, установленных в вашей системе. Например, обычно везде можно найти шрифт Courier New, Times New Roman, Arial. Мы выбрали шрифт Monotype Corsiva. Обратите внимание на то, что имя SPRITEFONT-файла должно соответствовать имени одного из установленных в системе шрифтов.
  • Нажмите кнопку Add и файл будет добавлен в проект. Для того, чтобы управлять свойствами шрифта, нам понадобится редактировать этот файл. Чтобы открыть его для редактирования в XML-редакторе, встроенном в Visual Studio, достаточно сделать двойной щелчок по этому файлу рис 13.2(рис 13.2) Редактирование Spritefont-файла
  • Этот файл состоит из множества документированных разделов, которые позволяют настраивать свойства шрифта. В частности, по умолчанию параметр Style установлен в Regular. Для шрифта Monotype Corsiva этот стиль отображения не определен, поэтому нам нужно заменить Regular на Italic.

    Теперь все готово для использования шрифта в программе. Ниже, в листинге 13.1. приведен код класса Game1, реализующий вывод на экран строки текста.

    using System;
    using System.Collections.Generic;
    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.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace P9_1
    {
       
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;
            SpriteBatch spriteBatch;
            //Для хранения шрифта
            SpriteFont MyFont;
            //Для хранения позиции вывода текста
            Vector2 StringPosition;
            //Строка для вывода
            string OutString;
            //Для нахождения центра строки
            Vector2 StringOrigin; 
            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                OutString = "Score: 12345";
                base.Initialize();
            }
    
            
            protected override void LoadContent()
            {
                spriteBatch = new SpriteBatch(GraphicsDevice);
                //Загружаем шрифт
                MyFont  = Content.Load<SpriteFont>("Monotype Corsiva");
                //Устанавливаем позицию вывода шрифта в центре экрана
                StringPosition = new Vector2(graphics.GraphicsDevice.Viewport.Width / 2,
            graphics.GraphicsDevice.Viewport.Height / 2);
                //Находим центр строки
               StringOrigin = MyFont.MeasureString(OutString) / 2;
            }
    
            protected override void UnloadContent()
            {
            }
    
            protected override void Update(GameTime gameTime)
            {
                // Allows the game to exit
                if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                    this.Exit();
    
                base.Update(gameTime);
            }
    
            
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
    
                spriteBatch.Begin();
                //Выводим строку
                spriteBatch.DrawString(MyFont , OutString , StringPosition, Color.Red,
                    0, StringOrigin, 1.0f, SpriteEffects.None, 0.5f);
                spriteBatch.End();
                base.Draw(gameTime);
            }
        }
    }

    Как видите, вывод текста очень похож на вывод изображений. Для хранения шрифта используется переменная типа SpriteFont, метод DrawString объекта типа SpriteBatch отвечает за прорисовку текста на игровом экране:

    spriteBatch.DrawString(MyFont, OutString, StringPosition, Color.Red, 0, StringOrigin, 1.0f, SpriteEffects.None, 0.5f);

    Рассмотрим параметры вызова этого метода в табл. 13.1.

    Параметры метода DrawString объекта SpriteBatch
    Параметр вызова метода Описание параметра
    MyFont Переменная типа SpriteFont – шрифт, которым выводится текст
    OutString Переменная типа String – текст для вывода
    StringPosition Переменная типа Vector2 – позиция текста
    Color.Red Цвет текста
    0 Поворот текста в радианах относительно точки привязки координат текста
    StringOrigin Переменная типа Vector2 – точка привязки координат объекта
    1.0f Коэффициент масштабирования объекта
    SpriteEffects.None Эффект спрайта
    0.5f Глубина текста – положение относительно других графических объектов

    На рис. 13.3. вы можете видеть игровой экран проекта P9_1.

    (рис 13.3) Экран проекта P9_1

    Оформление игры

    Создадим новый проект P9_2. На его примере рассмотрим приемы работы, освоение которых вынесено в задачи данной работы.

    Задача оформления игры сводится к созданию нескольких игровых экранов с определенной функциональностью и к разработке системы перемещения между этими экранами.

    На рис. 13.4. вы можете видеть окно Project Explorer проекта P9_2.

    (рис 13.4) Окно Project Explorer проекта P9_2

    В листинге 13.2. приведен код класса Game1.

    using System;
    using System.Collections.Generic;
    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.Net;
    using Microsoft.Xna.Framework.Storage;
    
    namespace P9_2
    {
        public class Game1 : Microsoft.Xna.Framework.Game
        {
            GraphicsDeviceManager graphics;
            SpriteBatch spriteBatch;
            //Экран справки
            HelpScreen help;
            Texture2D helpBack;
            Texture2D helpText;
            //Экран меню
            MenuScreen menu;
            Texture2D menuBack;
            Texture2D menuGameName;
            Texture2D menuTxtStartGame;
            Texture2D menuTxtHelp;
            Texture2D menuTxtExit;
            //Текущий элемент меню
            int currentItem;
            //Интервал задержки переключения между элементами меню
            int interval;
            //Игровой экран
            GameScreen newGame;
            Texture2D gameBat;
    
            public Game1()
            {
                graphics = new GraphicsDeviceManager(this);
                Content.RootDirectory = "Content";
            }
    
            protected override void Initialize()
            {
                //При запуске игры активным устанавливается первый элемент меню
                currentItem = 1;
                interval = 0;
                base.Initialize();
            }
    
            protected override void LoadContent()
            {
                //Загружаем элементы игры и создаем игровые экраны
                spriteBatch = new SpriteBatch(GraphicsDevice);
                Services.AddService(typeof(SpriteBatch), spriteBatch);
                helpBack = Content.Load<Texture2D>("helpBack");
                helpText = Content.Load<Texture2D>("helpText");
                help = new HelpScreen(this, helpBack, helpText,
                    new Rectangle(0, 0, this.Window.ClientBounds.Width, this.Window.ClientBounds.Height),
                    new Rectangle((this.Window.ClientBounds.Width - helpText.Width)/2, 
                    (this.Window.ClientBounds.Height - helpText.Height)/2, 
                    helpText .Width , helpText .Height ));
                Components.Add(help);
                gameBat = Content.Load<Texture2D>("BallandBats");
                newGame = new GameScreen(this, ref gameBat, new Rectangle(18, 9, 17, 88), 
                    new Vector2(100, 100));
                Components.Add(newGame);
                menuBack = Content.Load<Texture2D>("menuBack");
                menuGameName = Content.Load<Texture2D>("menuGameName");
                menuTxtStartGame = Content.Load<Texture2D>("menuTxtStartGame");
                menuTxtHelp = Content.Load<Texture2D>("menuTxtHelp");
                menuTxtExit = Content.Load<Texture2D>("menuTxtExit");
                menu = new MenuScreen(this, menuBack, menuGameName, menuTxtStartGame, menuTxtHelp,
                    menuTxtExit, new Rectangle(100, 300, 400, 50), new Rectangle(100, 350, 400, 50), 
                    new Rectangle(100, 400, 400, 50));
                Components.Add(menu);
                //Отображаем меню, остальные элементы скрыты
                menu.Show();
            }
    
            protected override void UnloadContent()
            {
                // TODO: Unload any non ContentManager content here
            }
    
            //Обработка нажатий на клавиши
            void KeyboardHandle()
            {
                //считаем состояние клавиатуры
                  KeyboardState kbState = Keyboard.GetState();
                //Если прошло 4 игровых цикла
                interval ++;
                if (interval >=4)
                {
                    //Обнуляем счетичик
                    interval = 0;
    
                    //Если выбран экран меню
                    if (menu.Enabled)
                    {
                        //Если нажата клавиша вверх
                        if (kbState.IsKeyDown(Keys.Up))
                        {
                            //Уменьшить на 1 счетчик, отвечающий за
                            //смену пунктов меню
                            currentItem--;
                            //Если в момент нажатия был активен самый верхний
                            //пункт
                            if (currentItem < 1)
                            {   
                                //Делаем текущим третий пункт
                                currentItem = 3;
                                //Вызов процедуры для отображения смены пункта меню
                                menu.GetKey(currentItem);
                            }
                                //если при уменьшении номера пункта
                                //он не меньше 1, вызываем процедуру
                                //отображения смены пункта
                            else
                            {
                                menu.GetKey(currentItem);
                            }
                        }
                        //При нажатии клавиши вниз
                        if (kbState.IsKeyDown(Keys.Down))
                        {
                            //Увеличим счетчик
                            currentItem++;
                            //Если он больше 3 - активируем первый пункт
                            if (currentItem > 3)
                            {
                                currentItem = 1;
                                menu.GetKey(currentItem);
                            }
                                //Если не меньше 3 - переходим на следующий пункт
                            else
                            {
                                menu.GetKey(currentItem);
                            }
                        }
                        //При нажатии клавиши Enter
                        //выполняем команду, соответствующую текущему пункту меню
                        if (kbState .IsKeyDown (Keys .Enter))
                        {
                            //Первый пункт - скрываем экран меню
                            //и отображаем игровой экран
                            if (currentItem == 1)
                            {
                                menu.Hide();
                                newGame.Show();
                            }
                            //Скрываем меню и
                            //отображаем экран справки
                            if (currentItem ==2) 
                            {
                                menu.Hide();
                                help.Show ();
                            }
                            //Выходим из игры
                            if (currentItem == 3)
                            {
                                this.Exit();
                            }
                        }
                     
    
                    }
                }
                //Если активен экран справки
                    if (help.Enabled)
                    {
                        //При нажатии Esc закрываем экран справки
                        //И открываем меню
                        if (kbState.IsKeyDown(Keys.Escape))
                        {
                            help.Hide();
                            menu.Show();
                        }
                    }
                //Если активен игровой экран
                //При нажатии Esc закрываем его 
                //и открываем меню
                //Обработка нажатий клавиш, необходимых для работы игрового экрана
                //проводится в объекте, соответствующем этому экрану
                    if (newGame.Enabled)
                    {
                        if (kbState.IsKeyDown(Keys.Escape))
                        {
                            newGame.Hide();
                            menu.Show();
                        }
                    }
            }
    
            protected override void Update(GameTime gameTime)
            {
                //Обработка нажатий на клавиши
                KeyboardHandle();
                base.Update(gameTime);
            }
    
            protected override void Draw(GameTime gameTime)
            {
                graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
                spriteBatch.Begin();
                base.Draw(gameTime);
                spriteBatch.End();
            }
        }
    }

    В листинге 13.3. вы можете видеть код класса Classes.Menu

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2.Classes
    {
        //Класс для хранения данных об элементах меню
        public class Menu
        {
            public Texture2D imageTexture;
            public Rectangle imageRectangle;
            public Color color;
            public Menu(Game game, Texture2D _texture, Rectangle _rectangle, Color _color)
            {
                imageTexture = _texture;
                imageRectangle = _rectangle;
                color = _color;
            }
        
        }
    }

    В листинге 13.4. приведен код класса Screen – этот класс является базовым для классов игровых экранов. Он содержит методы для активации и деактивации игровых экранов.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
    //Базовый класс, на основе которого создаются игровые экраны
        public class Screen : Microsoft.Xna.Framework.DrawableGameComponent
        {
            //Для объекта SpriteBatch, который используется
            //для вывода изображений в классах-наследниках
            public SpriteBatch sprBatch;
    
            public Screen(Game game)
                : base(game)
            {
                //При создании объекта он по умолчанию 
                //невидим и неактивен
                Visible = false;
                Enabled = false;
                //получим объект SpriteBatch
                sprBatch =(SpriteBatch)Game.Services.GetService(typeof(SpriteBatch));
            }
            //Процедура для отображения и активации объекта
            public void Show()
            {
                Visible = true;
                Enabled = true;
            }
            //Процедура для скрытия и отключения объекта
            public void Hide()
            {
                Visible = false;
                Enabled = false;
            }
    
        }
    }

    На рис. 13.5. вы можете видеть экран игрового меню.

    (рис 13.5) Экран игрового меню

    В листинге 13.5. приведен код класса MenuScreen.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
    //Экран меню
        public class MenuScreen : Screen 
        {
            //Элементы меню будем хранить в массиве типа Menu
            //тип данных Menu хранит информацию об элементах меню
            Classes.Menu[] menu;
            //Текстуры и прямоугольники для вывода элементов меню
            Texture2D menuTxtStartGame;
            Texture2D menuTxtHelp;
            Texture2D menuTxtExit;
            Rectangle menuTxtStartGameR, menuTxtHelpR, menuTxtExitR;
            Texture2D menuBack, menuGameName;
            Rectangle menuBackR, menuGameNameR;
    
            public MenuScreen(Game game, Texture2D _menuBack, Texture2D _menuGameName, 
                Texture2D _menuTxtStartGame, Texture2D _menuTxtHelp, Texture2D _menuTxtExit,
                Rectangle _menuTxtStartGameR,Rectangle _menuTxtHelpR, Rectangle _menuTxtExitR)
                : base(game)
            {
                menuBack = _menuBack;
                menuGameName = _menuGameName;
                menuBackR = new Rectangle(0, 0, 800, 600);
                menuGameNameR = new Rectangle(0, 0, 800, 200);
                menuTxtStartGame = _menuTxtStartGame ;
                menuTxtStartGameR = _menuTxtStartGameR;
                menuTxtHelp = _menuTxtHelp;
                menuTxtHelpR = _menuTxtHelpR;
                menuTxtExit = _menuTxtExit;
                menuTxtExitR = _menuTxtExitR;
                //Создаем массив из 3-х элементов
                menu = new P9_2.Classes.Menu[3];
                //Инициализируем элементы массива
                //Элемент с номером 0 активен
                menu[0]=new P9_2.Classes.Menu (game, menuTxtStartGame, menuTxtStartGameR ,Color.Magenta);
                menu[1] = new P9_2.Classes.Menu(game, menuTxtHelp, menuTxtHelpR, Color.White);
                menu[2] = new P9_2.Classes.Menu(game, menuTxtExit, menuTxtExitR, Color.White);
    
            }
            //Метод вызывается для отображения смены пункта меню
            public void GetKey(int i)
            {
                //i=1 - первый пункт
                //i=2 - второй пункт
                //i=3 - третий пункт
                if (i == 1)
                {
                    menu[0].color = Color.Magenta;
                    menu[1].color = Color.White;
                    menu[2].color = Color.White;
    
                }
                if (i == 2)
                {
                  menu[0].color = Color.White;
                  menu[1].color = Color.Magenta;
                  menu[2].color = Color.White;
                }
                if (i == 3)
                {
                    menu[0].color = Color.White;
                    menu[1].color = Color.White;
                    menu[2].color = Color.Magenta;
    
                }
            }
            //Рисование элементов
            public override void Draw(GameTime gameTime)
            {
                //Вывод фона и названия игры
                sprBatch.Draw(menuBack, menuBackR, Color.White);
                sprBatch.Draw(menuGameName, menuGameNameR, Color.White);
                //Вывод элементов меню
                for (int i=0;i<3;i++)
                    sprBatch.Draw(menu[i].imageTexture, menu[i].imageRectangle, menu[i].color);
                base.Draw(gameTime);
            }
    
        }
    }

    На рис. 13.6. вы может видеть справочный экран.

    (рис 13.6) Экран игровой справки

    В листинге 13.6. вы можете найти код класса HelpScreen.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
        //Экран справки
        public class HelpScreen : Screen 
        {
            //Текстуры для фона и справочной надписи
            Texture2D backTexture;
            Rectangle backRectangle;
            Texture2D helpTexture;
            Rectangle helpRectangle;
    
            public HelpScreen(Game game, Texture2D _back, Texture2D _help, Rectangle _backRec, Rectangle _helpRec)
                : base(game)
            {
                backTexture = _back;
                helpTexture = _help;
                backRectangle = _backRec;
                helpRectangle = _helpRec;
            }
            public override void Draw(GameTime gameTime)
            {
                //Выводим изображения
                sprBatch.Draw(backTexture, backRectangle, Color.White);
                sprBatch.Draw(helpTexture, helpRectangle, Color.White);
                base.Draw(gameTime);
            }
            
        }
    }

    На рис. 13.7. вы можете видеть игровой экран.

    (рис 13.7) Игровой экран

    В листинге 13.7. приведен код игрового экрана – класса GameScreen. Мы использовали простой код для перемещения объекта из проекта P3_4.

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Audio;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using Microsoft.Xna.Framework.Storage;
    using Microsoft.Xna.Framework.Content;
    
    namespace P9_2
    {
        //Игровой экран
    
        public class GameScreen :Screen
        {
            private Texture2D sprTexture;
            private Rectangle sprRectangle;
            private Vector2 sprPosition;
            private Rectangle scrBounds;
    
    
            public GameScreen(Game game, ref Texture2D newTexture,
                Rectangle newRectangle, Vector2 newPosition)
                : base(game)
            {
                sprTexture = newTexture;
                sprRectangle = newRectangle;
                sprPosition = newPosition;
                scrBounds = new Rectangle(0, 0,
                    game.Window.ClientBounds.Width,
                    game.Window.ClientBounds.Height);
            }
    
            public override void Initialize()
            {
                // TODO: Add your initialization code here
    
                base.Initialize();
            }
    
            public override void Update(GameTime gameTime)
            {
    
                    KeyboardState kbState = Keyboard.GetState();
                    if (kbState.IsKeyDown(Keys.Up))
                    {
                        sprPosition.Y -= 5;
                    }
                    if (kbState.IsKeyDown(Keys.Down))
                    {
                        sprPosition.Y += 5;
                    }
                    if (kbState.IsKeyDown(Keys.Left))
                    {
                        sprPosition.X -= 5;
                    }
                    if (kbState.IsKeyDown(Keys.Right))
                    {
                        sprPosition.X += 5;
                    }
    
                    if (sprPosition.X < scrBounds.Left)
                    {
                        sprPosition.X = scrBounds.Left;
                    }
                    if (sprPosition.X > scrBounds.Width - sprRectangle.Width)
                    {
                        sprPosition.X = scrBounds.Width - sprRectangle.Width;
                    }
                    if (sprPosition.Y < scrBounds.Top)
                    {
                        sprPosition.Y = scrBounds.Top;
                    }
                    if (sprPosition.Y > scrBounds.Height - sprRectangle.Height)
                    {
                        sprPosition.Y = scrBounds.Height - sprRectangle.Height;
                    }
    
                    base.Update(gameTime);
                
            }
            public override void Draw(GameTime gameTime)
            {
                sprBatch.Draw(sprTexture, sprPosition, sprRectangle, Color.White);
                base.Draw(gameTime);
            }
        }
    }

    Задание

    Создайте систему игровых экранов и меню, которым можно управлять с помощью мыши и клавиатуры.

    Вернуться к учебному плану