- •Основы разработки приложений беспроводных устройств
- •Среда разработки
- •Шаблоны приложений
- •Создание простого приложения
- •Добавление элементов управления на страницу xaml
- •Добавление новых страниц в проект
- •Навигация между страницами приложения
- •Лабораторный практикум 1: версия для печати и pda Лабораторная работа №1. Телефонная книжка
- •Лабораторная работа №2. Блокнот
- •Лабораторная работа №3. Меню ресторана
- •Лабораторная работа №4. Калькулятор
- •Лабораторная работа №5. База паролей
- •Лабораторная работа №6. Учет затрат
- •Лабораторная работа №7. Список задач
- •Лабораторная работа №8. Веб-браузер
- •Лабораторная работа №9. Игра "Загони шар в лузу"
- •Лабораторная работа №10. Определение местоположения
Лабораторная работа №9. Игра "Загони шар в лузу"
Задание
Создать игру "Загони шар в лузу" для Windows Phone 7 с использованием акселерометра (шар меняет направление и скорость при наклоне телефона) и возможностью сохранения наилучшего времени в изолированном хранилище.
Освоение
акселерометр
работа с потоками
изолированное хранилище
Описание
Создадим новый проект Silverlight for Windows Phone – Windows Phone Application.
Откроем файл разметки главной страницы MainPage.xaml. Разместим элемент Canvas, в котором будут находиться два эллипса (элемент управления Ellipse). В зависимости от положения телефона будем менять свойства Canvas.Left и Canvas.Top. Добавим также текстовый блок для вывода времени игры и 3 кнопки: новая игра, просмотр рекорда и сброс рекорда.
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<StackPanel>
<Canvas Name="cnvMain" Height="700" Width="450" Background="White">
<Ellipse Name="ellBall" Height="30" Width="30" Canvas.Left="50" Canvas.Top="200" Canvas.ZIndex="2"
Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Fill="Red" />
<Ellipse Name="ellHole" Height="50" Width="50" Canvas.Left="200" Canvas.Top="50" Canvas.ZIndex="1"
Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Fill="Black" />
</Canvas>
<StackPanel Orientation="Horizontal">
<TextBlock Name="lblTime" Text="00:00" FontSize="26" VerticalAlignment="Center" />
<Button Name="btnNewGame" Content="Новая" Click="btnNewGame_Click" />
<Button Name="btnRecord" Content="Рекорд" Click="btnRecord_Click" />
<Button Name="btnResetRecord" Content="Сброс" Click="btnResetRecord_Click" />
</StackPanel>
</StackPanel>
</Grid>
Добавим константы для определения радиусов шарика и лузы:
private const double BALL_RAD = 15d;
private const double HOLE_RAD = 20d;
В конструкторе класса проинициализируем размеры элепсов:
ellBall.Width = BALL_RAD * 2d;
ellBall.Height = BALL_RAD * 2d;
ellHole.Width = HOLE_RAD * 2d;
ellHole.Height = HOLE_RAD * 2d;
Для работы с акселерометром добавим в код директиву:
using Microsoft.Devices.Sensors;
Создадим глобальную переменную акселерометра:
private Accelerometer myAccel;
В конструкторе класса страницы проинициализируем акселерометр и подпишемся на событие изменения положения телефона:
myAccel = new Accelerometer();
myAccel.ReadingChanged += new EventHandler<AccelerometerReadingEventArgs>(myAccel_ReadingChanged);
Для изменения скорости шарика определим глобальную константу и переменные в классе:
private const double KOEFF_ACCEL = 20d;
private double nSpeedUpZ, nSpeedUpX;
private bool bGameStopped;
При изменении положения телефона будем менять координаты шарика. Поскольку из отдельного потока нельзя напрямую работать с элементами управления, будем пользоваться диспетчерами. После изменения координат будем проверять игру на победу (попадание центра шарика в лузу).
void myAccel_ReadingChanged(object sender, AccelerometerReadingEventArgs e)
{
if (myAccel.IsDataValid && !bGameStopped)
{
nSpeedUpZ = e.Z * KOEFF_ACCEL;
nSpeedUpX = e.X * KOEFF_ACCEL;
if (e.Z < 0)
{
Dispatcher.BeginInvoke(() => HandleUp());
}
else
{
Dispatcher.BeginInvoke(() => HandleDown());
}
if (e.X < 0)
{
Dispatcher.BeginInvoke(() => HandleLeft());
}
else
{
Dispatcher.BeginInvoke(() => HandleRight());
}
Dispatcher.BeginInvoke(() => WinChecking());
}
}
private void HandleUp()
{
if ((double)ellBall.GetValue(Canvas.TopProperty) > 0d)
{
ellBall.SetValue(Canvas.TopProperty, (double)ellBall.GetValue(Canvas.TopProperty) + nSpeedUpZ);
}
}
private void HandleDown()
{
if ((double)ellBall.GetValue(Canvas.TopProperty) < (cnvMain.Height - ellBall.Height))
{
ellBall.SetValue(Canvas.TopProperty, (double)ellBall.GetValue(Canvas.TopProperty) + nSpeedUpZ);
}
}
private void HandleLeft()
{
if ((double)ellBall.GetValue(Canvas.LeftProperty) > 0d)
{
ellBall.SetValue(Canvas.LeftProperty, (double)ellBall.GetValue(Canvas.LeftProperty) + nSpeedUpX);
}
}
private void HandleRight()
{
if ((double)ellBall.GetValue(Canvas.LeftProperty) < (cnvMain.Width - ellBall.Width))
{
ellBall.SetValue(Canvas.LeftProperty, (double)ellBall.GetValue(Canvas.LeftProperty) + nSpeedUpX);
}
}
private void WinChecking()
{
if (IsWin())
{
bGameStopped = true;
dTimer.Stop();
myAccel.Stop();
if (IsRecord())
{
nTimeRecord = nTime;
MessageBox.Show("Победа!" + Environment.NewLine + "Новый рекорд: " + FormatTime(nTimeRecord) + "!");
SaveToIsolatedStorage(nTimeRecord.ToString());
}
else
{
MessageBox.Show("Победа!");
}
}
}
private bool IsWin()
{
bool bRes = false;
if ((((double)ellBall.GetValue(Canvas.TopProperty) + BALL_RAD) >
((double)ellHole.GetValue(Canvas.TopProperty))) &&
(((double)ellBall.GetValue(Canvas.TopProperty) + BALL_RAD) <
((double)ellHole.GetValue(Canvas.TopProperty) + HOLE_RAD + HOLE_RAD)))
{
if ((((double)ellBall.GetValue(Canvas.LeftProperty) + BALL_RAD) >
((double)ellHole.GetValue(Canvas.LeftProperty))) &&
(((double)ellBall.GetValue(Canvas.LeftProperty) + BALL_RAD) <
((double)ellHole.GetValue(Canvas.LeftProperty) + HOLE_RAD + HOLE_RAD)))
{
bRes = true;
}
}
return bRes;
}
Введем в игру счетчик времени. Добавим константу и переменные:
private const int MAX_TIME = 3599; //59:59
private int nTimeRecord;
private int nTime;
System.Windows.Threading.DispatcherTimer dTimer;
В конструкторе класса объявим новый поток и подпишемся на событие срабатывания таймера (каждую секунду):
dTimer = new System.Windows.Threading.DispatcherTimer();
dTimer.Interval = new TimeSpan(0, 0, 0, 1, 0); //1 сек
dTimer.Tick += new EventHandler(Timer_Tick);
По событию будем увеличивать таймер и с помощью диспетчера менять время в текстовом блоке на странице. А в случае, если максимальное время достигнуто, останавливаем игру.
private void Timer_Tick(object sender, EventArgs e)
{
nTime++;
Dispatcher.BeginInvoke(() => ChangeTimerLabel());
if (IsLose())
{
Dispatcher.BeginInvoke(() => Lose());
}
}
private void ChangeTimerLabel()
{
lblTime.Text = FormatTime(nTime);
}
private string FormatTime(int time)
{
return String.Format("{0:00}", (int)(time / 60)) + ":" + String.Format("{0:00}", (time % 60));
}
private bool IsLose()
{
bool bRes = false;
if (nTime >= MAX_TIME)
{
bRes = true;
}
return bRes;
}
private void Lose()
{
bGameStopped = true;
dTimer.Stop();
myAccel.Stop();
MessageBox.Show("Скучно...");
}
В случае успешного попадания шарика в лузу, если достигнут рекорд по времени, сохраняем его в файле в изолированном хранилище:
private const string strIStorageName = "Wp7IUSLab12.txt";
При этому в конструкторе класса, в случае если файл с рекордом существует, будем загружать рекорд:
if (IsIsolatedStorageExist())
{
nTimeRecord = int.Parse(LoadFromIsolatedStorage());
}
else
{
nTimeRecord = MAX_TIME;
}
При нажатии на кнопку "Сброс" будем очищать хранилище:
Для работы с хранилищем определим директивы:
using System.IO.IsolatedStorage;
using System.IO;
Создадим функции сохранения рекорда в хранилище, загрузки, удаления и проверки файла на существование:
private void SaveToIsolatedStorage(string histText)
{
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fileStream = fileStorage.CreateFile(strIStorageName);
StreamWriter sw = new StreamWriter(fileStream);
sw.Write(histText);
sw.Close();
fileStream.Close();
}
private string LoadFromIsolatedStorage()
{
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream fileStream = fileStorage.OpenFile(strIStorageName, System.IO.FileMode.Open);
StreamReader sr = new StreamReader(fileStream);
string strRes = sr.ReadToEnd();
sr.Close();
fileStream.Close();
return strRes;
}
private bool IsIsolatedStorageExist()
{
IsolatedStorageFile sileStorage = IsolatedStorageFile.GetUserStoreForApplication();
return sileStorage.FileExists(strIStorageName);
}
private void RemoveIsolatedStorage()
{
if (IsIsolatedStorageExist())
{
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
fileStorage.DeleteFile(strIStorageName);
}
}
Теперь можно скомпилировать приложение, запустить на эмуляторе или телефоне и проверить его функциональность.
