Цель работы: Научиться разрабатывать приложения с использованием шаблона Приложение
В некоторых из предыдущих проектов мы использовали отдельные библиотеки и объекты XNA в
Рассмотрим особенности таких приложений. Создадим новый проект, в окне создания проекта выберем шаблон Приложение
(рис 35.1) Создание проекта
Посмотрим на Обозреватель решений этого проекта, рис. 35.2
(рис 35.2) Обозреватель решений для проекта P28_1
Если рассмотреть это решение в первом приближении, то окажется, что мы имеем
Рассмотрим решение подробнее. Оно состоит из трёх проектов:
MainPage – это обычная Рассмотрим файлы, которые входят в решение. Начнём с файла
<Application
x:Class="P28_1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:xna="clr-namespace:Microsoft.Xna.Framework;assembly=Microsoft.Xna.Framework.Interop">
<!--Ресурсы приложения-->
<Application.Resources>
</Application.Resources>
<Application.ApplicationLifetimeObjects>
<!--Обязательный объект, обрабатывающий события времени жизни приложения-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
<!--SharedGraphicsDeviceManager используется для отображения с помощью графических API XNA-->
<xna:SharedGraphicsDeviceManager />
</Application.ApplicationLifetimeObjects>
</Application>
Этот файл, как и в случае с обычным xna:SharedGraphicsDeviceManager – объект, который используется для вывода XNA-графики.
В листинге 35.2 приведен файл
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace P28_1
{
public partial class App : Application
{
/// <summary>
/// Обеспечивает быстрый доступ к корневому кадру приложения телефона.
/// </summary>
/// <returns>Корневой кадр приложения телефона.</returns>
public PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Предоставляет приложению доступ к ContentManager.
/// </summary>
public ContentManager Content { get; private set; }
/// <summary>
/// Предоставляет доступ к GameTimer, настроенному для передачи данных в FrameworkDispatcher.
/// </summary>
public GameTimer FrameworkDispatcherTimer { get; private set; }
/// <summary>
/// Предоставляет доступ к AppServiceProvider для приложения.
/// </summary>
public AppServiceProvider Services { get; private set; }
/// <summary>
/// Конструктор объекта приложения.
/// </summary>
public App()
{
// Глобальный обработчик неперехваченных исключений.
UnhandledException += Application_UnhandledException;
// Стандартная инициализация Silverlight
InitializeComponent();
// Инициализация телефона
InitializePhoneApplication();
// Инициализация XNA
InitializeXnaApplication();
// Отображение сведений о профиле графики во время отладки.
if (System.Diagnostics.Debugger.IsAttached)
{
// Отображение текущих счетчиков частоты смены кадров.
Application.Current.Host.Settings.EnableFrameRateCounter = true;
// Отображение областей приложения, перерисовываемых в каждом кадре.
//Application.Current.Host.Settings.EnableRedrawRegions = true;
// Включение режима визуализации анализа нерабочего кода
// для отображения областей страницы, переданных в GPU, с цветным наложением.
//Application.Current.Host.Settings.EnableCacheVisualization = true;
// Отключите обнаружение простоя приложения, установив для свойства UserIdleDetectionMode
// объекта PhoneApplicationService приложения значение Disabled.
// Внимание! Используйте только в режиме отладки. Приложение, в котором отключено обнаружение
бездействия пользователя, будет продолжать работать
// и потреблять энергию батареи, когда телефон не будет использоваться.
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}
}
// Код для выполнения при запуске приложения (например, из меню "Пуск")
// Этот код не будет выполняться при повторной активации приложения
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
// Код для выполнения при активации приложения (переводится в основной режим)
// Этот код не будет выполняться при первом запуске приложения
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
// Код для выполнения при деактивации приложения (отправляется в фоновый режим)
// Этот код не будет выполняться при закрытии приложения
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
// Код для выполнения при закрытии приложения (например, при нажатии пользователем кнопки "Назад")
// Этот код не будет выполняться при деактивации приложения
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
// Код для выполнения в случае ошибки навигации
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// Ошибка навигации; перейти в отладчик
System.Diagnostics.Debugger.Break();
}
}
// Код для выполнения на необработанных исключениях
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// Произошло необработанное исключение; перейти в отладчик
System.Diagnostics.Debugger.Break();
}
}
#region Инициализация приложения телефона
// Избегайте двойной инициализации
private bool phoneApplicationInitialized = false;
// Не добавляйте в этот метод дополнительный код
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Создайте кадр, но не задавайте для него значение RootVisual; это позволит
// экрану-заставке оставаться активным, пока приложение не будет готово для визуализации.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Обработка сбоев навигации
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Убедитесь, что инициализация не выполняется повторно
phoneApplicationInitialized = true;
}
// Не добавляйте в этот метод дополнительный код
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Задайте корневой визуальный элемент для визуализации приложения
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Удалите этот обработчик, т.к. он больше не нужен
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
#endregion
#region Инициализация приложения XNA
// Выполняет инициализацию типов XNA, необходимых для приложения.
private void InitializeXnaApplication()
{
// Создание поставщика услуг
Services = new AppServiceProvider();
// Добавить SharedGraphicsDeviceManager к службам в качестве IGraphicsDeviceService для приложения
foreach (object obj in ApplicationLifetimeObjects)
{
if (obj is IGraphicsDeviceService)
Services.AddService(typeof(IGraphicsDeviceService), obj);
}
// Создайте ContentManager для загрузки в приложение предварительно скомпилированных ресурсов
Content = new ContentManager(Services, "Content");
// Создайте GameTimer для получения XNA FrameworkDispatcher
FrameworkDispatcherTimer = new GameTimer();
FrameworkDispatcherTimer.FrameAction += FrameworkDispatcherFrameAction;
FrameworkDispatcherTimer.Start();
}
// Обработчик событий передает FrameworkDispatcher во все кадры.
// FrameworkDispatcher требуется для большинства событий XNA и
// некоторых функций, таких как воспроизведение SoundEffect.
private void FrameworkDispatcherFrameAction(object sender, EventArgs e)
{
FrameworkDispatcher.Update();
}
#endregion
}
}
Это всё тот же ConetntManager для работы с XNA-контентом, создаётся FrameworkDispatcher.Update().
Страница MainPage.
(рис 35.3) Страница MainPage.xaml
Здесь располагается кнопка "Изменить на страницу игры". Правильнее будет подписать её как "Перейти на страницу игры", так как именно эту функцию она и выполняет. В листинге 35.3 приведен её код.
<phone:PhoneApplicationPage
x:Class="P28_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="имя страницы" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!--Создание одной кнопки для перехода ко второй странице, визуализация которой выполнена XNA Framework-->
<Button Height="100" Content="Изменить на страницу игры" Click="Button_Click" />
</Grid>
</Grid>
<!--Пример кода, иллюстрирующий использование ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Кнопка 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Кнопка 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="Элемент меню 1"/>
<shell:ApplicationBarMenuItem Text="Элемент меню 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
Страница стандартна, она по умолчанию содержит настройку, скрывающую системные значки, что приводит к отображению страницы на полный экран. В её CS-файле, листинг 35.4., содержится обработчик нажатия на кнопку, открывающий страницу GamePage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
namespace P28_1
{
public partial class MainPage : PhoneApplicationPage
{
// Конструктор
public MainPage()
{
InitializeComponent();
}
// Простая кнопка Щелкните обработчик событий, чтобы перейти на вторую страницу
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/GamePage.xaml", UriKind.Relative));
}
}
}
Собственно говоря, мы можем пользоваться всем арсеналом инструментов и приёмов MainPage и с другими
GamePage, листинг 35.5., задаёт лишь свойства PhoneApplicationPage. В частности, по умолчанию игровой экран будет выводиться в портретной ориентации.
<phone:PhoneApplicationPage
x:Class="P28_1.GamePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480"
shell:SystemTray.IsVisible="False">
<!--Содержимое XAML не требуется, поскольку полная визуализация страницы выполнена XNA Framework-->
</phone:PhoneApplicationPage>
Другой нагрузки он, в шаблоне проекта, не несет, так как вся работа по созданию игры ведется в файле GamePage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace P28_1
{
public partial class GamePage : PhoneApplicationPage
{
ContentManager contentManager;
GameTimer timer;
SpriteBatch spriteBatch;
public GamePage()
{
InitializeComponent();
// Получить диспетчер содержимого из приложения
contentManager = (Application.Current as App).Content;
// Создайте таймер для этой страницы
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// В графическом устройстве включите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
// Создание нового SpriteBatch, который может использоваться для рисования текстур.
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
// TODO: загрузите сюда содержимое игры с помощью this.content
// Запуск таймера
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Остановка таймера
timer.Stop();
// В графическом устройстве выключите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Позволяет странице выполнять логику, такую как обновление окружения,
/// поиск конфликтов, сбор входных данных и воспроизведение звука.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
// TODO: добавьте здесь логику обновления
}
/// <summary>
/// Разрешает автоматическую прорисовку страницы.
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: добавьте здесь свой код прорисовки
}
}
}
В конструкторе (GamePage) мы получаем диспетчер содержимого, что позволяет нам работать с контентом, кроме того, в классе определено несколько переменных. Это – вышеупомянутый ContentManager, это – объект типа GameTimer, на основе которого на странице работают методы, аналогичные методам Update и Draw в обычных XNA-проектах. Это хорошо знакомый вам по XNA-проектам объект типа SpriteBatch, который используется для визуализации двумерных графических объектов.
В конструкторе, кроме того, мы создаём игровой Update и Draw – то есть – готовим всё для работы игрового цикла XNA.
В методе OnNavigatedTo, выполняющемся при переходе на страницу, мы запускаем XNA-визуализацию, создаём новый SpriteBatch, и, кроме того, здесь мы будем загружать ресурсы игры. Здесь же мы запускаем
Когда мы уходим со страницы, игровой
Метод OnUpdate – это аналог метода Update из XNA. Здесь мы будем реализовывать игровую логику. Метод OnDraw – аналог метода Draw. Он предназначен для отрисовки игровых объектов. По умолчанию здесь присутствует лишь команда очистки экрана.
Посмотрим теперь, как пользоваться вышеописанными механизмами на практике.
Реализуем в новом окружении пример, аналогичный проекту P5_1, который мы рассматривали на пятом практическом занятии. Здесь игровой объект перемещался по экрану в позицию касания экрана.
Создадим новый проект по шаблону Приложение
На странице MainPage.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="первая игра" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!--Создание одной кнопки для перехода ко второй странице, визуализация которой выполнена XNA Framework-->
<Button Height="100" Content="Играть!" Click="Button_Click" Margin="0,410,0,129" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Теперь добавим в проект P28_2LibContent изображение для визуализации. Делается это командой контекстного меню проекта Добавить > Существующий элемент. В итоге наш проект будет выглядеть следующим образом, рис. 35.4.
(рис 35.4) Игровой ресурс, добавленный в проект Content
Вся остальная работа над игрой ведется в файле GamePage.
using System;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
namespace P28_2
{
public partial class GamePage : PhoneApplicationPage
{
// Менеджер контента
ContentManager contentManager;
// Таймер для организации игрового цикла
GameTimer timer;
// Объект для вывода 2D-графики
SpriteBatch spriteBatch;
// Текстура для хранения изображения
private Texture2D mySprite;
//Позиция вывода изображения
private Vector2 position = new Vector2(0, 200);
public GamePage()
{
InitializeComponent();
// Получить диспетчер содержимого из приложения
contentManager = (Application.Current as App).Content;
// Создайте таймер для этой страницы
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// В графическом устройстве включите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
// Создание нового SpriteBatch, который может использоваться для рисования текстур.
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
// Загружаем игровой контент
mySprite = contentManager.Load<Texture2D>("ball");
// Запуск таймера
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Остановка таймера
timer.Stop();
// В графическом устройстве выключите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Позволяет странице выполнять логику, такую как обновление окружения,
/// поиск конфликтов, сбор входных данных и воспроизведение звука.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
//Получаем коллекцию объектов, содержащих информацию о касаниях экрана
TouchCollection touchLocations = TouchPanel.GetState();
//Перебираем коллекцию, присваивая объекту координаты касания
foreach (TouchLocation touchLocation in touchLocations)
{
if (touchLocation.State == TouchLocationState.Pressed)
{
position = touchLocation.Position;
}
}
}
/// <summary>
/// Разрешает автоматическую прорисовку страницы.
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
// Очищаем экран
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
// Выводим изображение
spriteBatch.Begin();
spriteBatch.Draw(mySprite, position, Color.White);
spriteBatch.End();
}
}
}
Обратите внимание на то, что для работы с сенсорным вводом нам понадобилось подключить пространство имён Microsoft.Xna.Framework.Input.
Если сопоставить этот код с кодом на XNA, выполняющим схожую функциональность, то окажется, что в данном случае иначе выглядит лишь код загрузки игрового ресурса (всё дело лишь в другом имени объекта):
mySprite = contentManager.Load<Texture2D>("ball");
В целом же наш простой пример в модификации практически не нуждался (если не учитывать необходимость располагать соответствующий код в методах, выполняющих ту же функциональность, что и в XNA, но с другими именами). В итоге мы получили следующее, рис. 35.5.
(рис 35.5) Игровой экран проекта
Обратите внимание на то, что начало координат расположено в левом верхнем углу экрана, экран по умолчанию расположен в портретной ориентации.
В данной лабораторной работе мы рассмотрели шаблон Приложение Silverlignt и XNA для Windows Phone. Он позволяет объединять в одном приложении возможности XNA и
Выберите один из проектов, которыми мы занимались ранее, изучая построение приложений для XNA, реализуйте его с помощью шаблона Приложение Silverlignt и XNA для Windows Phone.
К данной лабораторной работе прилагается видеоурок
и демонстрационный проект.
Цель работы: Научиться разрабатывать приложения с использованием шаблона Приложение
В некоторых из предыдущих проектов мы использовали отдельные библиотеки и объекты XNA в
Рассмотрим особенности таких приложений. Создадим новый проект, в окне создания проекта выберем шаблон Приложение
(рис 35.1) Создание проекта
Посмотрим на Обозреватель решений этого проекта, рис. 35.2
(рис 35.2) Обозреватель решений для проекта P28_1
Если рассмотреть это решение в первом приближении, то окажется, что мы имеем
Рассмотрим решение подробнее. Оно состоит из трёх проектов:
MainPage – это обычная Рассмотрим файлы, которые входят в решение. Начнём с файла
<Application
x:Class="P28_1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:xna="clr-namespace:Microsoft.Xna.Framework;assembly=Microsoft.Xna.Framework.Interop">
<!--Ресурсы приложения-->
<Application.Resources>
</Application.Resources>
<Application.ApplicationLifetimeObjects>
<!--Обязательный объект, обрабатывающий события времени жизни приложения-->
<shell:PhoneApplicationService
Launching="Application_Launching" Closing="Application_Closing"
Activated="Application_Activated" Deactivated="Application_Deactivated"/>
<!--SharedGraphicsDeviceManager используется для отображения с помощью графических API XNA-->
<xna:SharedGraphicsDeviceManager />
</Application.ApplicationLifetimeObjects>
</Application>
Этот файл, как и в случае с обычным xna:SharedGraphicsDeviceManager – объект, который используется для вывода XNA-графики.
В листинге 35.2 приведен файл
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace P28_1
{
public partial class App : Application
{
/// <summary>
/// Обеспечивает быстрый доступ к корневому кадру приложения телефона.
/// </summary>
/// <returns>Корневой кадр приложения телефона.</returns>
public PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Предоставляет приложению доступ к ContentManager.
/// </summary>
public ContentManager Content { get; private set; }
/// <summary>
/// Предоставляет доступ к GameTimer, настроенному для передачи данных в FrameworkDispatcher.
/// </summary>
public GameTimer FrameworkDispatcherTimer { get; private set; }
/// <summary>
/// Предоставляет доступ к AppServiceProvider для приложения.
/// </summary>
public AppServiceProvider Services { get; private set; }
/// <summary>
/// Конструктор объекта приложения.
/// </summary>
public App()
{
// Глобальный обработчик неперехваченных исключений.
UnhandledException += Application_UnhandledException;
// Стандартная инициализация Silverlight
InitializeComponent();
// Инициализация телефона
InitializePhoneApplication();
// Инициализация XNA
InitializeXnaApplication();
// Отображение сведений о профиле графики во время отладки.
if (System.Diagnostics.Debugger.IsAttached)
{
// Отображение текущих счетчиков частоты смены кадров.
Application.Current.Host.Settings.EnableFrameRateCounter = true;
// Отображение областей приложения, перерисовываемых в каждом кадре.
//Application.Current.Host.Settings.EnableRedrawRegions = true;
// Включение режима визуализации анализа нерабочего кода
// для отображения областей страницы, переданных в GPU, с цветным наложением.
//Application.Current.Host.Settings.EnableCacheVisualization = true;
// Отключите обнаружение простоя приложения, установив для свойства UserIdleDetectionMode
// объекта PhoneApplicationService приложения значение Disabled.
// Внимание! Используйте только в режиме отладки. Приложение, в котором отключено обнаружение
бездействия пользователя, будет продолжать работать
// и потреблять энергию батареи, когда телефон не будет использоваться.
PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
}
}
// Код для выполнения при запуске приложения (например, из меню "Пуск")
// Этот код не будет выполняться при повторной активации приложения
private void Application_Launching(object sender, LaunchingEventArgs e)
{
}
// Код для выполнения при активации приложения (переводится в основной режим)
// Этот код не будет выполняться при первом запуске приложения
private void Application_Activated(object sender, ActivatedEventArgs e)
{
}
// Код для выполнения при деактивации приложения (отправляется в фоновый режим)
// Этот код не будет выполняться при закрытии приложения
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
}
// Код для выполнения при закрытии приложения (например, при нажатии пользователем кнопки "Назад")
// Этот код не будет выполняться при деактивации приложения
private void Application_Closing(object sender, ClosingEventArgs e)
{
}
// Код для выполнения в случае ошибки навигации
private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// Ошибка навигации; перейти в отладчик
System.Diagnostics.Debugger.Break();
}
}
// Код для выполнения на необработанных исключениях
private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
// Произошло необработанное исключение; перейти в отладчик
System.Diagnostics.Debugger.Break();
}
}
#region Инициализация приложения телефона
// Избегайте двойной инициализации
private bool phoneApplicationInitialized = false;
// Не добавляйте в этот метод дополнительный код
private void InitializePhoneApplication()
{
if (phoneApplicationInitialized)
return;
// Создайте кадр, но не задавайте для него значение RootVisual; это позволит
// экрану-заставке оставаться активным, пока приложение не будет готово для визуализации.
RootFrame = new PhoneApplicationFrame();
RootFrame.Navigated += CompleteInitializePhoneApplication;
// Обработка сбоев навигации
RootFrame.NavigationFailed += RootFrame_NavigationFailed;
// Убедитесь, что инициализация не выполняется повторно
phoneApplicationInitialized = true;
}
// Не добавляйте в этот метод дополнительный код
private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
{
// Задайте корневой визуальный элемент для визуализации приложения
if (RootVisual != RootFrame)
RootVisual = RootFrame;
// Удалите этот обработчик, т.к. он больше не нужен
RootFrame.Navigated -= CompleteInitializePhoneApplication;
}
#endregion
#region Инициализация приложения XNA
// Выполняет инициализацию типов XNA, необходимых для приложения.
private void InitializeXnaApplication()
{
// Создание поставщика услуг
Services = new AppServiceProvider();
// Добавить SharedGraphicsDeviceManager к службам в качестве IGraphicsDeviceService для приложения
foreach (object obj in ApplicationLifetimeObjects)
{
if (obj is IGraphicsDeviceService)
Services.AddService(typeof(IGraphicsDeviceService), obj);
}
// Создайте ContentManager для загрузки в приложение предварительно скомпилированных ресурсов
Content = new ContentManager(Services, "Content");
// Создайте GameTimer для получения XNA FrameworkDispatcher
FrameworkDispatcherTimer = new GameTimer();
FrameworkDispatcherTimer.FrameAction += FrameworkDispatcherFrameAction;
FrameworkDispatcherTimer.Start();
}
// Обработчик событий передает FrameworkDispatcher во все кадры.
// FrameworkDispatcher требуется для большинства событий XNA и
// некоторых функций, таких как воспроизведение SoundEffect.
private void FrameworkDispatcherFrameAction(object sender, EventArgs e)
{
FrameworkDispatcher.Update();
}
#endregion
}
}
Это всё тот же ConetntManager для работы с XNA-контентом, создаётся FrameworkDispatcher.Update().
Страница MainPage.
(рис 35.3) Страница MainPage.xaml
Здесь располагается кнопка "Изменить на страницу игры". Правильнее будет подписать её как "Перейти на страницу игры", так как именно эту функцию она и выполняет. В листинге 35.3 приведен её код.
<phone:PhoneApplicationPage
x:Class="P28_1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="имя страницы" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!--Создание одной кнопки для перехода ко второй странице, визуализация которой выполнена XNA Framework-->
<Button Height="100" Content="Изменить на страницу игры" Click="Button_Click" />
</Grid>
</Grid>
<!--Пример кода, иллюстрирующий использование ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Кнопка 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Кнопка 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="Элемент меню 1"/>
<shell:ApplicationBarMenuItem Text="Элемент меню 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
Страница стандартна, она по умолчанию содержит настройку, скрывающую системные значки, что приводит к отображению страницы на полный экран. В её CS-файле, листинг 35.4., содержится обработчик нажатия на кнопку, открывающий страницу GamePage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
namespace P28_1
{
public partial class MainPage : PhoneApplicationPage
{
// Конструктор
public MainPage()
{
InitializeComponent();
}
// Простая кнопка Щелкните обработчик событий, чтобы перейти на вторую страницу
private void Button_Click(object sender, RoutedEventArgs e)
{
NavigationService.Navigate(new Uri("/GamePage.xaml", UriKind.Relative));
}
}
}
Собственно говоря, мы можем пользоваться всем арсеналом инструментов и приёмов MainPage и с другими
GamePage, листинг 35.5., задаёт лишь свойства PhoneApplicationPage. В частности, по умолчанию игровой экран будет выводиться в портретной ориентации.
<phone:PhoneApplicationPage
x:Class="P28_1.GamePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d" d:DesignHeight="800" d:DesignWidth="480"
shell:SystemTray.IsVisible="False">
<!--Содержимое XAML не требуется, поскольку полная визуализация страницы выполнена XNA Framework-->
</phone:PhoneApplicationPage>
Другой нагрузки он, в шаблоне проекта, не несет, так как вся работа по созданию игры ведется в файле GamePage.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace P28_1
{
public partial class GamePage : PhoneApplicationPage
{
ContentManager contentManager;
GameTimer timer;
SpriteBatch spriteBatch;
public GamePage()
{
InitializeComponent();
// Получить диспетчер содержимого из приложения
contentManager = (Application.Current as App).Content;
// Создайте таймер для этой страницы
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// В графическом устройстве включите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
// Создание нового SpriteBatch, который может использоваться для рисования текстур.
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
// TODO: загрузите сюда содержимое игры с помощью this.content
// Запуск таймера
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Остановка таймера
timer.Stop();
// В графическом устройстве выключите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Позволяет странице выполнять логику, такую как обновление окружения,
/// поиск конфликтов, сбор входных данных и воспроизведение звука.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
// TODO: добавьте здесь логику обновления
}
/// <summary>
/// Разрешает автоматическую прорисовку страницы.
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: добавьте здесь свой код прорисовки
}
}
}
В конструкторе (GamePage) мы получаем диспетчер содержимого, что позволяет нам работать с контентом, кроме того, в классе определено несколько переменных. Это – вышеупомянутый ContentManager, это – объект типа GameTimer, на основе которого на странице работают методы, аналогичные методам Update и Draw в обычных XNA-проектах. Это хорошо знакомый вам по XNA-проектам объект типа SpriteBatch, который используется для визуализации двумерных графических объектов.
В конструкторе, кроме того, мы создаём игровой Update и Draw – то есть – готовим всё для работы игрового цикла XNA.
В методе OnNavigatedTo, выполняющемся при переходе на страницу, мы запускаем XNA-визуализацию, создаём новый SpriteBatch, и, кроме того, здесь мы будем загружать ресурсы игры. Здесь же мы запускаем
Когда мы уходим со страницы, игровой
Метод OnUpdate – это аналог метода Update из XNA. Здесь мы будем реализовывать игровую логику. Метод OnDraw – аналог метода Draw. Он предназначен для отрисовки игровых объектов. По умолчанию здесь присутствует лишь команда очистки экрана.
Посмотрим теперь, как пользоваться вышеописанными механизмами на практике.
Реализуем в новом окружении пример, аналогичный проекту P5_1, который мы рассматривали на пятом практическом занятии. Здесь игровой объект перемещался по экрану в позицию касания экрана.
Создадим новый проект по шаблону Приложение
На странице MainPage.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="800"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="False">
<!--LayoutRoot представляет корневую сетку, где размещается все содержимое страницы-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel содержит имя приложения и заголовок страницы-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="МОЕ ПРИЛОЖЕНИЕ"
Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="первая игра" Margin="9,-7,0,0"
Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel — поместите здесь дополнительное содержимое-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<!--Создание одной кнопки для перехода ко второй странице, визуализация которой выполнена XNA Framework-->
<Button Height="100" Content="Играть!" Click="Button_Click" Margin="0,410,0,129" />
</Grid>
</Grid>
</phone:PhoneApplicationPage>
Теперь добавим в проект P28_2LibContent изображение для визуализации. Делается это командой контекстного меню проекта Добавить > Существующий элемент. В итоге наш проект будет выглядеть следующим образом, рис. 35.4.
(рис 35.4) Игровой ресурс, добавленный в проект Content
Вся остальная работа над игрой ведется в файле GamePage.
using System;
using System.Windows;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input.Touch;
namespace P28_2
{
public partial class GamePage : PhoneApplicationPage
{
// Менеджер контента
ContentManager contentManager;
// Таймер для организации игрового цикла
GameTimer timer;
// Объект для вывода 2D-графики
SpriteBatch spriteBatch;
// Текстура для хранения изображения
private Texture2D mySprite;
//Позиция вывода изображения
private Vector2 position = new Vector2(0, 200);
public GamePage()
{
InitializeComponent();
// Получить диспетчер содержимого из приложения
contentManager = (Application.Current as App).Content;
// Создайте таймер для этой страницы
timer = new GameTimer();
timer.UpdateInterval = TimeSpan.FromTicks(333333);
timer.Update += OnUpdate;
timer.Draw += OnDraw;
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
// В графическом устройстве включите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(true);
// Создание нового SpriteBatch, который может использоваться для рисования текстур.
spriteBatch = new SpriteBatch(SharedGraphicsDeviceManager.Current.GraphicsDevice);
// Загружаем игровой контент
mySprite = contentManager.Load<Texture2D>("ball");
// Запуск таймера
timer.Start();
base.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
// Остановка таймера
timer.Stop();
// В графическом устройстве выключите визуализацию XNA для режима совместного использования
SharedGraphicsDeviceManager.Current.GraphicsDevice.SetSharingMode(false);
base.OnNavigatedFrom(e);
}
/// <summary>
/// Позволяет странице выполнять логику, такую как обновление окружения,
/// поиск конфликтов, сбор входных данных и воспроизведение звука.
/// </summary>
private void OnUpdate(object sender, GameTimerEventArgs e)
{
//Получаем коллекцию объектов, содержащих информацию о касаниях экрана
TouchCollection touchLocations = TouchPanel.GetState();
//Перебираем коллекцию, присваивая объекту координаты касания
foreach (TouchLocation touchLocation in touchLocations)
{
if (touchLocation.State == TouchLocationState.Pressed)
{
position = touchLocation.Position;
}
}
}
/// <summary>
/// Разрешает автоматическую прорисовку страницы.
/// </summary>
private void OnDraw(object sender, GameTimerEventArgs e)
{
// Очищаем экран
SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
// Выводим изображение
spriteBatch.Begin();
spriteBatch.Draw(mySprite, position, Color.White);
spriteBatch.End();
}
}
}
Обратите внимание на то, что для работы с сенсорным вводом нам понадобилось подключить пространство имён Microsoft.Xna.Framework.Input.
Если сопоставить этот код с кодом на XNA, выполняющим схожую функциональность, то окажется, что в данном случае иначе выглядит лишь код загрузки игрового ресурса (всё дело лишь в другом имени объекта):
mySprite = contentManager.Load<Texture2D>("ball");
В целом же наш простой пример в модификации практически не нуждался (если не учитывать необходимость располагать соответствующий код в методах, выполняющих ту же функциональность, что и в XNA, но с другими именами). В итоге мы получили следующее, рис. 35.5.
(рис 35.5) Игровой экран проекта
Обратите внимание на то, что начало координат расположено в левом верхнем углу экрана, экран по умолчанию расположен в портретной ориентации.
В данной лабораторной работе мы рассмотрели шаблон Приложение Silverlignt и XNA для Windows Phone. Он позволяет объединять в одном приложении возможности XNA и
Выберите один из проектов, которыми мы занимались ранее, изучая построение приложений для XNA, реализуйте его с помощью шаблона Приложение Silverlignt и XNA для Windows Phone.
К данной лабораторной работе прилагается видеоурок
и демонстрационный проект.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.