Главная » Разработка для Windows Phone 7 » Приложение для телефона на XNA

0

Далее у нас по плану приложение на XNA, отображающее небольшое приветствие в центре экрана. Тогда как в приложениях на Silverlight текст обычно превалирует, в видеоиграх его встретишь не часто. В играх роль текста сведена к описанию правил или отображению счета. Поэтому сама концепция приложения «здравствуй, мир» не вполне вписывается в общую идеологию программирования на XNA.

В XNA даже нет встроенных шрифтов. И приложение на XNA, выполняющееся на телефоне, не может использовать те же встроенные шрифты телефона, что и программы на Silverlight, как это можно было бы предположить. Silverlight применяет векторные шрифты TrueType, а XNA ничего не знает о таких экзотических концепциях. Для XNA все, включая шрифты, является растровыми изображениями.

Если в приложении на XNA требуется использовать определенный шрифт, он должен быть встроен в исполняемый файл как коллекция растровых изображений для каждого символа. XNA Game Studio (которая интегрирована в Visual Studio) очень упрощает сам процесс встраивания шрифта, но тут возникают некоторые серьезные правовые проблемы. Вы можете легально распространять приложение на XNA, использующее тот или иной шрифт, только при условии, если имеете право на распространение этого встроенного шрифта, а это невозможно для большинства шрифтов, поставляемых с самой Windows или приложениями Windows.

Чтобы помочь в решении этого правового затруднения, Майкрософт предоставляет лицензию на использование шрифтов Ascender Corporation именно в целях их применения в приложениях на XNA. Вот эти шрифты:

Kootenay                                                               Lindsay

Miramonte                                                            Pescadero

Miramonte Bold                                                  Pescadero Bold

Pericles                                               Segoe ui Mono

Pericles Light                                    Segoe ui Mono Bold

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

В Visual Studio в меню File выберите New и Project. В левой части диалогового окна выберите Visual C# и XNA Game Studio 4.0. В середине выберите Windows Phone Game (4.0). Задайте месторасположение и имя проекта, XnaHelloPhone.

Visual Studio создает два проекта, один для логики приложения и другой для его содержимого. Приложения на XNA обычно включают большой объем содержимого, которым преимущественно являются растровые изображения и трехмерные модели, но также и шрифты. Чтобы добавить шрифт в это приложение, щелкните правой кнопкой мыши проект, созданный для содержимого (он обозначен «XnaHelloPhoneContent (Content)»), и во всплывающем меню выберите Add (Добавить) и New Item (Новый элемент). Выберите Sprite Font, оставьте имя файла как есть, SpriteFont1.spritefont, и щелкните Add.

Слово «спрайт» («sprite» в переводе на русский означает «эльф») широко распространено в игровых приложениях и обычно обозначает небольшое растровое изображение, которое может очень быстро перемещаться (так же как эльфы, живущие в волшебном лесу). В XNA даже шрифты являются спрайтами.

SpriteFont1.spritefont появится в списке файлов каталога Content, и вы можете редактировать изобилующий комментариями XML-файл, описывающий шрифт.

Проект XNA: XnaHelloPhone Файл: SpriteFont1.spritefont (полностью за исключением комментариев)

<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics"> <Asset Type="Graphics:FontDescription"> <FontName>Segoe UI Mono</FontName> <Size>14</Size> <Spacing>0</Spacing> <UseKerning>true</UseKerning> <Style>Regular</Style> <CharacterRegions> <CharacterRegion>

<Start>&#32;</Start> <End>&#12 6;</End> </CharacterRegion> </CharacterRegions> </Asset> </XnaContent>

Между тегами FontName указан шрифт Segoe UI Mono, но его можно заменить любым другим шрифтом, из приведенных ранее. Если желаете использовать Pericles Light, укажите его полное имя, но для Miramonte Bold, Pescadero Bold или Segoe UI Mono Bold необходимо написать просто Miramonte, Pescadero или Segoe UI Mono и ввести Bold (Полужирный) между тегами Style (Стиль). Bold может использоваться и для других шрифтов, но для них он будет синтезирован, тогда как для Miramonte, Pescadero или Segoe UI Mono будет использоваться специально разработанный полужирный шрифт.

Теги Size (Размер) обозначают размер шрифта в пунктах. В XNA, как и в Silverlight, координаты и размеры задаются в пикселах, но в XNA за основу при преобразовании между пикселами и пунктами взято разрешение 96 DPI. Для XNA-приложения 14 шрифт равен 18- 2/3 пикселам. Это очень близко к шрифту размером 15 пунктов или с FontSize равным 20 пикселов в Silverlight для Windows Phone.

В разделе CharacterRegions (Диапазоны символов) файла указываются диапазоны шестнадцатеричных кодировок Unicode. По умолчанию используется диапазон от 0x32 до 0x126, что включает все обычные символы набора символов ASCII.

SpriteFont1.spritefont не является достаточно описательным именем. Я бы назвал файл так, чтобы было понятно, о каком шрифте идет речь. Если сохраняются стандартные настройки шрифта, можно переименовать файл в Segoe14.spritefont. Если взглянуть на свойства этого файла – щелкните правой кнопкой мыши имя файла и выберите Properties – можно увидеть, что значением Asset Name (Имя ресурса) является имя файла без расширения: Segoe14. Значение Asset Name используется для ссылки на шрифт в приложении. Если хотите запутать себя, можете изменить Asset Name и задать ему значение, отличное от имени файла.

Изначально проект XNAHelloPhone включает два C#-файла: Program.cs и Game1.cs. Первый очень простой и, как выясняется, не имеет отношения к играм для Windows Phone 7! Директива препроцессора активирует класс Program (Программа), только если определен символ WINDOWS или XBOX. При компиляции программ для Windows Phone вместо них задается символ WINDOWS_PHONE.

Чаще всего при создании небольших игр основная часть времени уходит на файл Game1.cs. Класс Game1 наследуется от Game (Игра). Первоначально в нем определены два поля: graphics (графические элементы) и spriteBatch (Пакет спрайтов). К этим двум полям я хочу добавить еще три:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент, демонстрирующий поля)

namespace XnaHelloPhone {

public class Gamel : Microsoft.Xna.Framework.Game {

GraphicsDeviceManager graphics; SpriteBatch spriteBatch;

string text = "Hello, Windows Phone 7!"; SpriteFont segoe14; Vector2 textPosition;

}

}

В этих трех новых полях просто указан текст для отображения, используемый для этого шрифт и месторасположения текста на экране. Координаты задаются в пикселах относительно верхнего левого угла экрана. Структура Vector2 имеет два поля: X и Y, типа float (число с плавающей точкой). В целях обеспечения лучшей производительности в XNA все значения с плавающей точкой берутся с одинарной точностью. (В Silverlight – с двойной точностью.) Структура Vector2 часто используется для задания точек, размеров и даже векторов в двухмерном пространстве.

При запуске игры на телефоне, создается экземпляр класса Game1 и выполняется конструктор Game1. Рассмотрим стандартный код:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

public Game1() {

graphics = new GraphicsDeviceManager(this);

Content.RootDirectory = "Content";

// По умолчанию частота кадров для Windows Phone составляет 30 кадр/с. TargetElapsedTime = TimeSpan.FromTicks(333333);

}

Первое выражение обеспечивает инициализацию поля graphics. Во втором выражении для Game определяется свойство Content (Содержимое) типа ContentManager (Диспетчер содержимого), и RootDirectory (Корневой каталог) является свойством этого класса. Значение «Content» этого свойства соответствует папке Content, в которой хранится шрифт Segoe размером 14 пунктов. В третьем выражении задается время игрового цикла программы, что управляет частотой обновления изображения. Экраны устройств Windows Phone 7 обновляются с частотой 30 кадров в секунду.

Когда экземпляр Game1 создан, вызывается его метод Run (Выполнить), и базовый класс Game инициирует процесс запуска игры. Один из первых шагов – вызов метода Initialize (Инициализировать), который может быть перегружен в производных от Game классах. XNA Game Studio автоматически формирует скелетный метод, в который я не буду ничего добавлять:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

protected override void Initialize() {

base.Initialize();

}

В методе Initialize шрифт или любое другое содержимое не должно загружаться. Это происходит несколько позже, когда базовый класс вызывает метод LoadContent (Загрузить содержимое).

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

protected override void LoadContent() {

spriteBatch = new SpriteBatch(GraphicsDevice);

segoe14 = this.Content.Load<SpriteFont>("Segoe14"); Vector2 textSize = segoe14.MeasureString(text); Viewport viewport = this.GraphicsDevice.Viewport;

textPosition = new Vector2((viewport.Width – textSize.X) / 2,

(viewport.Height – textSize.Y) / 2);

}

Первое выражение данного метода формируется автоматически. Вскоре мы увидим, как этот объект spriteBatch используется для вывода спрайтов на экран.

Все остальные выражения были добавлены мной. Как можно заметить, перед всеми именами свойств, таких как Content и GraphicsDevice (Графическое устройство), я поставил ключевое слово this, чтобы напомнить себе, что это свойства, а не статические классы. Как уже говорилось, свойство Content типа ContentManager. Универсальный метод Load (Загрузить) обеспечивает загрузку содержимого в приложение, в данном случае, это содержимое типа SpriteFont. Имя, указанное в кавычках, соответствует Asset Name, указанному в свойствах содержимого. Это выражение обеспечивает сохранение результата в поле segoe14 типа SpriteFont.

В XNA спрайты (включая текстовые строки) обычно позиционируются через задание координат в пикселах верхнего левого угла спрайта относительно верхнего левого угла экрана. Для расчета этих координат необходимо знать и размер экрана, и размер текста при отображении его конкретным шрифтом.

У класса SpriteFont есть чрезвычайно удобный метод MeasureString (Измерить строку), возвращающий объект Vector2 с размером конкретной текстовой строки в пикселах. (Для шрифта Segoe UI Mono размером 14 пунктов, высота которого эквивалентна 18-2/3 пикселам, метод MeasureString возвратит высоту 28 пикселов.)

Как правило, для получения размера экрана в приложении на XNA используется свойство Viewport (Окно просмотра) класса GraphicsDevice. Оно доступно через свойство GraphicsDevice класса Game и предоставляет свойства Width (Ширина) и Height (Высота).

После этого довольно просто вычислить textPosition (Положение текста) – координаты точки относительно верхнего левого угла окна просмотра, в которой будет располагаться верхний левый угол текстовой строки.

На этом этап инициализации программы завершается, и начинается фактическое действие. Приложение входит в игровой цикл. Синхронно с обновлением экрана, которое происходит с частотой 30 кадров в секунду, в приложении вызываются два метода: Update (Обновить) и за ним Draw (Рисовать). Снова и снова: Update, Draw, Update, Draw, Update, Draw…. (На самом деле, все несколько сложнее; методу Update необходимо 1/30 секунды для выполнения, но мы обсудим вопросы хронометража более подробно в одной из следующих глав.)

Метод Draw обеспечивает отрисовку образов на экране. И это все, что он может делать. Все подготовительные вычисления для отрисовки должны осуществляться в методе Update. Метод Update подготавливает программу к выполнению метода Draw. Очень часто приложения на XNA реализуют перемещение спрайтов по экрану на основании пользовательского ввода. Для телефона пользовательский ввод осуществляется преимущественно посредством сенсорного ввода. Вся обработка пользовательского ввода также должна происходить во время выполнения метода Update. Пример этому рассмотрим в главе 3.

Методы Update и Draw должны быть написаны так, чтобы они выполнялись максимально быстро. Полагаю, это само собой разумеется. Однако здесь имеются также некоторые очень важные моменты, которые могут быть не так очевидны.

Следует избегать включения в методы Update и Draw кода, выполняющего рутинные операции по распределению памяти из локальной кучи приложения. В определенный момент времени сборщик мусора .NET захочет вернуть часть этой памяти, и пока он будет выполнять свою работу, игра может немного притормаживать. В главах, посвященных разработке на XNA, будут представлены способы избежать распределения памяти из кучи.

Скорее всего, в методах Draw не будет возникать никаких проблем. Обычно все неприятности кроются в методе Update. Избегайте применения выражений new для классов. Это всегда приводит к распределению памяти. Однако нет ничего страшного в создании экземпляров структур, потому что эти экземпляры хранятся в стеке, а не в куче. (XNA использует структуры, а не классы, для многих типов объектов, необходимых в Update.) Но распределение памяти из кучи также может происходить и без явных выражений new. Например, конкатенация двух строк приводит к созданию новой строки в куче. Для выполнения каких-либо операций со строками в Update следует использовать StringBuilder (Построитель строк). Очень удобно, что XNA предоставляет для отображения текста методы, использующие объекты StringBuilder.

Но в нашем приложении XnaHelloPhone метод Update абсолютно тривиальный. Отображаемый текст зафиксирован в одной единственной точке. Все необходимые вычисления уже выполнены в методе LoadContent. Поэтому оставляем метод Update без изменений, просто в том виде, в каком он был изначально создан XNA Game Studio:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

protected override void Update(GameTime gameTime) {

if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();

base.Update(gameTime);

}

В формируемом по умолчанию коде для проверки события нажатия кнопки Back использует статический класс GamePad (Игровой планшет). Это событие является сигналом к выходу из игры.

И, наконец, метод Draw. Его автоматически созданная версия просто закрашивает фон голубым цветом:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

protected override void Draw(GameTime gameTime) {

GraphicsDevice.Clear(Color.CornflowerBlue); base.Draw(gameTime);

}

Васильковый цвет (CornflowerBlue) приобрел культовый статус в сообществе разработчиков на XNA. При работе над программой на XNA увидеть голубой экран очень утешительно, поскольку это означает, что программа, по крайней мере, дошла до метода Draw. Но в целях энергосбережения при использовании ОСИД-экранов желательно применять более темные фоны. Я нашел компромиссный вариант и сделал фон темно-синим. Как и Silverlight, XNA поддерживает 140 цветов, которые уже считаются стандартными. Выводимый текст будет белого цвета:

Проект XNA: XnaHelloPhone Файл: Game1.cs (фрагмент)

protected override void Draw(GameTime gameTime) {

GraphicsDevice.Clear(Color.Navy); spriteBatch.Begin();

spriteBatch.DrawString(segoe14, text, textPosition, Color.White); spriteBatch.End();

base.Draw(gameTime);

}

Спрайты выводятся на экран пакетами в составе объекта SpriteBatch, который был создан во время вызова метода LoadContent. Между вызовами Begin (Начало) и End (Конец) может осуществляться множество вызовов метода DrawString (Отрисовать строку) для отрисовки текста и Draw для отрисовки растровых изображений. Вызываться могут только эти методы. В данном конкретном вызове DrawString указан шрифт, выводимый текст, местоположение

верхнего левого угла текста относительно верхнего левого угла экрана и цвет. И вот, что мы получаем:

Намного лучше!

Но тут возникает вопрос: всегда ли приложения на Silverlight выполняются в портретном режиме, а приложения на XNA – в альбомном?

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии