Основы разработки приложений беспроводных устройств

Курсовые работы

Показывать лекцию целиком

Курсовая работа №1. Игровое приложение средствами Silverlight

Задание

Используя технологию Silverlight, написать игру "Пятнашки" для Windows Phone 7.

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

Требования к работе:

  • использовать анимацию
  • использовать событие Manipulation
  • сохранять лучший результат в изолированном хранилище
  • Описание

    Создадим новый проект Silverlight for Windows Phone – Windows Phone Application.

    Рисование квадратиков с цифрами будем делать динамически в коде приложения. В роли квадратиков будут выступать элементы управления Border и TextBlock. Размещаться они будут в элементе управления Canvas. Нарисуем xaml-дизайн приложения:

      <!--LayoutRoot is the root grid where all page content is placed-->
        <Grid x:Name="LayoutRoot">
            <Grid.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="Black" Offset="0" />
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Grid.Background>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
     
            <!--TitlePanel contains the name of the application and page title-->
            <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 - place additional content here-->
            <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                    <RowDefinition Height="100"/>
                </Grid.RowDefinitions>
     
                <Border Name="brdrField"  BorderBrush="Gray" BorderThickness="3" 
                Background="Black" Grid.Row="0">
                    <Canvas Name="cnvField" ManipulationDelta="cnvField_ManipulationDelta" />
                </Border>
     
                <StackPanel Orientation="Horizontal" Grid.Row="1">
                    <TextBlock Name="tbTimer" Text="" FontSize="30" Foreground="Black" 
                    VerticalAlignment="Center" Width="230" MouseEnter="tbTimer_MouseEnter" />
                    <Button Name="btnStart" Content="Сначала" Width="240" Height="90" 
                    Background="Black" Click="btnStart_Click" />
                </StackPanel>
            </Grid>
        </Grid>
    

    На эмуляторе это будет выглядеть примерно следующим образом Рис. 6.1 :

    (рис 6.1) Внешний вид приложения

    Для улучшения дизайна приложения свойству Background элемента LayoutRoot мы задали значение:

      <Grid.Background>
                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                    <GradientStop Color="Black" Offset="0" />
                    <GradientStop Color="White" Offset="1" />
                </LinearGradientBrush>
            </Grid.Background>
    

    Объекту Canvas мы задали событие ManipulationDelta. Обработчик этого события будет срабатывать при изменении положения прикоснувшегося пальца. Таким образом мы будем перемещать квадратики движением пальца по экрану по горизонтали или по вертикали.

    Перейдем к коду.

    Определим метод Draw(), который будет вызываться в начале игры, случайно генерировать цифры, вырисовывать их на поле. Каждый квадратик представляет собой текстовый блок внутри элемента Border.

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

      private void Draw()
            {
                cnvField.Children.Clear();
     
                //заполняем список чисел от 0 до FIELD_SIZE*FIELD_SIZE
                List<int> listNumbers = new List<int>();
                for (int i = 0; i < (FIELD_SIZE * FIELD_SIZE); i++)
                {
                    listNumbers.Add(i);
                }
     
                //будем брать случайный элемент этого списка
                Random u = new Random(DateTime.Now.Millisecond);
     
                for (int j = 0; j < FIELD_SIZE; j++)
                {
                    for (int i = 0; i < FIELD_SIZE; i++)
                    {
                        Border brdElement = new Border();
                        TextBlock tbElement = new TextBlock();
     
                        double tmpX = i * (CELL_SIZE + FIELD_BORDER);
                        double tmpY = j * (CELL_SIZE + FIELD_BORDER);
     
                        int r = u.Next(0, listNumbers.Count - 1);
                        string strText = listNumbers[r].ToString();
                        listNumbers.RemoveAt(r);
     
                        if (strText.Equals("0"))
                        {
                            strText = "";
                        }
     
                        tbElement.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                        tbElement.TextAlignment = TextAlignment.Center;
                        tbElement.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
                        tbElement.FontSize = 30;
                        tbElement.Text = strText;
     
                        brdElement.VerticalAlignment = System.Windows.VerticalAlignment.Center;
                        brdElement.HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
                        brdElement.Width = CELL_SIZE;
                        brdElement.Height = CELL_SIZE;
                        brdElement.SetValue(Canvas.LeftProperty, tmpX);
                        brdElement.SetValue(Canvas.TopProperty, tmpY);
                        brdElement.BorderBrush = new SolidColorBrush(Color.FromArgb(255, 128, 128, 128));
                        brdElement.BorderThickness = new Thickness(3);
                        brdElement.Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
                        brdElement.Child = tbElement;
     
                        listElements[i, j] = brdElement;
                        cnvField.Children.Add(listElements[i, j]);
                    }
                }
            }
    

    На эмуляторе это будет выглядеть примерно следующим образом Рис. 6.2 :

    (рис 6.2) Внешний вид конечного приложения

    Теперь напишем обработчик нажатия. Поскольку квадратик – это элемент TextBlock внутри элемента Border, то нажатие может произойти как по одному, так и по другому элементу. В самом начале обработчика предусмотрим такую возможность. В случае, если нажатии на текстовый блок, будет брать его родительский элемент (т.е. Border) и дальше работать с ним. В случае если нажатие произошло по какому-либо другому элементу – выходим из обработчика.

    Прежде чем выполнять анимацию, выполним задержку. Это необходимо в случае, если анимация еще не закончилась, а обработчик снова был вызван.

    Далее определяем координаты нажатой клетки, выполняем перемещение (с анимацией) и проверяем на окончание игры:

     private void cnvField_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
            {
                if (m_IsPlay)
                {
                    Border brdr;
     
                    if (e.OriginalSource.GetType() == (new TextBlock()).GetType())
                    {
                        TextBlock tb = e.OriginalSource as TextBlock;
                        brdr = tb.Parent as Border;
                    }
                    else if (e.OriginalSource.GetType() == (new Border()).GetType())
                    {
                        brdr = e.OriginalSource as Border;
                    }
                    else
                    {
                        return;
                    }
     
                    //задержка выполнения анимации (предотвращает баг с неплоным перемещением)
                    if ((DateTime.Now - m_dtSleep).Milliseconds > (ANIMATION_DURATION * 1000))
                    {
                        //получаем координаты нажатого контрола
                        int left = (int)(((double)brdr.GetValue(Canvas.LeftProperty)) / CELL_SIZE);
                        int top = (int)(((double)brdr.GetValue(Canvas.TopProperty)) / CELL_SIZE);
     
                        //если движение по горизонтали
                        if (Math.Abs(e.DeltaManipulation.Translation.X) > Math.Abs(e.DeltaManipulation.Translation.Y))
                        {
                            if ((e.DeltaManipulation.Translation.X < 0)  (left > 0))
                            {
                                MoveCell(left, top, left - 1, top);
                                m_dtSleep = DateTime.Now;
     
                                if (CheckForWin()) Win();
                            }
     
                            if ((e.DeltaManipulation.Translation.X > 0)  (left < FIELD_SIZE - 1))
                            {
                                MoveCell(left, top, left + 1, top);
                                m_dtSleep = DateTime.Now;
     
                                if (CheckForWin()) Win();
                            }
                        }
                        //если движение по вертикали
                        else if (Math.Abs(e.DeltaManipulation.Translation.X) < Math.Abs(e.DeltaManipulation.Translation.Y))
                        {
                            if ((e.DeltaManipulation.Translation.Y < 0)  (top > 0))
                            {
                                MoveCell(left, top, left, top - 1);
                                m_dtSleep = DateTime.Now;
     
                                if (CheckForWin()) Win();
                            }
     
                            if ((e.DeltaManipulation.Translation.Y > 0)  (top < FIELD_SIZE - 1))
                            {
                                MoveCell(left, top, left, top + 1);
                                m_dtSleep = DateTime.Now;
     
                                if (CheckForWin()) Win();
                            }
                        }
                    }
                }
                
                e.Handled = true;
            }
    

    При перемещении клетки необходимо учесть свойство Z-индекс, которое определяет, какой элемент на Canvas находится ниже, какой выше.

    Создадим два объекта типа DoubleAnimation для анимации перемещения левой (на случай, если перемещение по горизонтали) и верхней границы (на случай, если перемещение по вертикали). Зададим им диапазон изменения и задержку. Затем установим, что анимация будет происходить над элементом listElements[fromX, fromY] (в этом массиве типа Border[,] хранятся квадратики), а именно над его свойствами Canvas.LeftProperty и Canvas.TopProperty. Затем добавим анимацию в элемент Storyboard и запустим ее на выполнение:

    //создаем и описываем анимацию - перемещение
                        DoubleAnimation anima1 = new DoubleAnimation();
                        anima1.From = (double)listElements[fromX, fromY].GetValue(Canvas.LeftProperty);
                        anima1.To = leftTo;
                        anima1.Duration = new Duration(TimeSpan.FromSeconds(ANIMATION_DURATION));
                        DoubleAnimation anima2 = new DoubleAnimation();
                        anima2.From = (double)listElements[fromX, fromY].GetValue(Canvas.TopProperty);
                        anima2.To = topTo;
                        anima2.Duration = new Duration(TimeSpan.FromSeconds(ANIMATION_DURATION));
     
                        Storyboard.SetTarget(anima1, listElements[fromX, fromY]);
                        Storyboard.SetTargetProperty(anima1, new PropertyPath(Canvas.LeftProperty));
                        Storyboard.SetTarget(anima2, listElements[fromX, fromY]);
                        Storyboard.SetTargetProperty(anima2, new PropertyPath(Canvas.TopProperty));
     
                        Storyboard storyboard = new Storyboard();
                        storyboard.Children.Add(anima1);
                        storyboard.Children.Add(anima2);
                        storyboard.Begin();
    

    Полностью функция MoveCell() выглядит следующим образом:

    private void MoveCell(int fromX, int fromY, int toX, int toY)
            {
                listElements[fromX, fromY].Background = new SolidColorBrush(Color.FromArgb(255, 0, 0, 0));
     
                if (IsCellFree(toX, toY))
                {
                    //если клетки соседние
                    if (NeighbourCell(fromX, fromY, toX, toY))
                    {
                        //у кого z-индекс больше, тот рисуется выше
                        listElements[fromX, fromY].SetValue(Canvas.ZIndexProperty, 2);
                        listElements[toX, toY].SetValue(Canvas.ZIndexProperty, 1);
     
                        //запоминаем координаты пустой клетки
                        double leftTo = (double)listElements[toX, toY].GetValue(Canvas.LeftProperty);
                        double topTo = (double)listElements[toX, toY].GetValue(Canvas.TopProperty);
     
                        //перемещаем пустую клетку
                        listElements[toX, toY].SetValue(Canvas.LeftProperty, listElements[fromX, fromY].GetValue(Canvas.LeftProperty));
                        listElements[toX, toY].SetValue(Canvas.TopProperty, listElements[fromX, fromY].GetValue(Canvas.TopProperty));
     
                        //создаем и описываем анимацию - перемещение
                        DoubleAnimation anima1 = new DoubleAnimation();
                        anima1.From = (double)listElements[fromX, fromY].GetValue(Canvas.LeftProperty);
                        anima1.To = leftTo;
                        anima1.Duration = new Duration(TimeSpan.FromSeconds(ANIMATION_DURATION));
                        DoubleAnimation anima2 = new DoubleAnimation();
                        anima2.From = (double)listElements[fromX, fromY].GetValue(Canvas.TopProperty);
                        anima2.To = topTo;
                        anima2.Duration = new Duration(TimeSpan.FromSeconds(ANIMATION_DURATION));
     
                        Storyboard.SetTarget(anima1, listElements[fromX, fromY]);
                        Storyboard.SetTargetProperty(anima1, new PropertyPath(Canvas.LeftProperty));
                        Storyboard.SetTarget(anima2, listElements[fromX, fromY]);
                        Storyboard.SetTargetProperty(anima2, new PropertyPath(Canvas.TopProperty));
     
                        Storyboard storyboard = new Storyboard();
                        storyboard.Children.Add(anima1);
                        storyboard.Children.Add(anima2);
                        storyboard.Begin();
     
                        //меняем индексы
                        Border tmp = listElements[fromX, fromY];
                        listElements[fromX, fromY] = listElements[toX, toY];
                        listElements[toX, toY] = tmp;
                    }
                }
            }
    

    Курсовая работа №2. Игровое приложение средствами XNA

    Задание

    Используя технологию XNA, написать игру "Тетрис" для Windows Phone 7.

    Допускается использование технологии Silverlight + XNA, если основная логика игры будет написана на XNA Framework.

    Требования к работе:

  • уровни сложности (изменение скорости игры)
  • подсчет очков и сохранение лучшего результата
  • меню игры
  • звуковые эффекты
  • вывод на экран текущего состояния игры (набранные очки, уровень сложности)
  • Описание

    Создадим новый проект XNA Game Studio 4.0 – Windows Phone Silverlight and XNA Application.

    Создадим абстрактный класс Figure, которые будет содержать основные методы для будущих классов фигур. Ниже представлен данный класс с перечнем функций без их реализации. Реализацию функций рекомендуется написать самостоятельно:

     public class Figure
        {
            protected List<Vector2> Elements;
            protected int Position;
    
            public Figure();
            public List<Vector2> GetElements();
            public List<Vector2> GetAfterLeft();
            public List<Vector2> GetAfterRight();
            public List<Vector2> GetAfterDown();
            public List<Vector2> GetAfterRotate();
            public virtual void Rotate();
            public void MoveLeft();
            public void MoveRight();
            public void MoveDown();
        }
    

    Функции GetAfterLeft() и аналогичные возвращают координаты фигуры после перемещения/поворота. Данные функции необходимы для того, чтобы определить, возможно ли перемещение/поворот – не выйдут ли части фигуры за границы и т.п. Поле Position хранит текущее положение фигуры. При повороте значение Position увеличивается на 1.

    Создадим классы всех фигур, унаследуем их от класса Figure. Получится 7 фигур. Их условные названия: I, J, L, O, S, T, Z. Ниже представлена реализация фигуры "I". Реализацию остальных классов рекомендуется написать самостоятельно:

    public class I : Figure
        {
            public I() : base()
            {
                Position1(4, -1);
            }
     
            public void Position1(float x, float y)
            {
                Elements.Clear();
                Elements.Add(new Vector2(x, y + 1));
                Elements.Add(new Vector2(x + 1, y + 1));
                Elements.Add(new Vector2(x + 2, y + 1));
                Elements.Add(new Vector2(x + 3, y + 1));
            }
     
            public void Position2(float x, float y)
            {
                Elements.Clear();
                Elements.Add(new Vector2(x + 1, y));
                Elements.Add(new Vector2(x + 1, y + 1));
                Elements.Add(new Vector2(x + 1, y + 2));
                Elements.Add(new Vector2(x + 1, y + 3));
            }
     
            public override void Rotate()
            {
                base.Rotate();
     
                if (Position > 2) Position = 1;
     
                switch (Position)
                {
                    case 1:
                        Position1(Elements[0].X - 1f, Elements[0].Y);
                        break;
                    case 2:
                        Position2(Elements[0].X, Elements[0].Y - 1f);
                        break;
                }
            }
        }
    

    Создадим класс поля. Определим в нем размеры, массив кирпичиков на поле и функции проверки перемещений, контакта фигуры с кирпичиками на поле, передачи фигуры массиву Bricks и удаления линии:

     public class Field
        {
            private const int FIELD_LEFT = 90;
            private const int FIELD_TOP = 180;
            private const int FIELD_WIDTH = 12;
            private const int FIELD_HEIGHT = 24;
            private const int CELL_SIZE = 25;
     
            private bool[,] Bricks;
     
            public Field();
    
            //свойства
            public int Left;
            public int Top;
            public int Width;
            public int Height;
            public int CellSize;
     
            public bool[,] GetBricks();
            public bool CanLeft(List<Vector2> figure);
            public bool CanRight(List<Vector2> figure);
            public bool CanUp(List<Vector2> figure);
            public bool CanDown(List<Vector2> figure);
            public bool ContactWithBricks(List<Vector2> figure);
            public void FigureToBricks(List<Vector2> figure);
            public void RemoveLine(int index);
        }
    

    На странице GamePage.xaml будем реализовывать основную логику игры, построенную на фреймворке XNA. В классе игры определим глобальные переменные типа Texture2D, SpriteFont и SoundEffect (для этого нужно использовать пространство имен Microsoft.Xna.Framework.Audio). Внутри метода OnNavigatedTo() будем загружать изображения, шрифты и звуковые эффекты из ресурсов (ресурсы добавляются в проект TetrisLibContent: Add – Existing Items). Также там будем получать параметр от страницы Main.xaml с номером уровня сложности (скорости игры):

     protected override void OnNavigatedTo(NavigationEventArgs e)
            {
                ...
    
                imgPause = contentManager.Load<Texture2D>("pause");
                fontText = contentManager.Load<SpriteFont>("textfont");
                audioDong = contentManager.Load<SoundEffect>("dong");
    
                int level = 1;
                if (NavigationContext.QueryString.ContainsKey("level"))
                {
                    level = int.Parse(NavigationContext.QueryString["level"].ToString());
                }
     
                StartGame(level);
                base.OnNavigatedTo(e);
            }
    

    В функции StartGame() будем инициализировать все переменные, необходимые для игры. Также будем там генерировать первую фигуру и определять, какие типы жестов будем считывать сенсором экрана (для этого нужно использовать пространство имен Microsoft.Xna.Framework.Input.Touch):

     TouchPanel.EnabledGestures = GestureType.Tap | GestureType.Hold;
    

    В методе OnUpdate() происходит обновление объектов, пересчет положений и обработка нажатий.

    Внутри данного метода будем перехватывать нажатия. При одиночном нажатии будем перемещать фигуру просто на 1 клетку. При задержанном – будем осуществлять быстрое перемещение. Поскольку в Windows Phone 7 нельзя перехватить событие отпускания нажатия, будем использовать следующий ход. Если они одиночные (Tap) –просто перемещаем фигуру. Если нажатие с задержкой (Hold) – задаем переменной dtTouchSpeed (типа DateTime) значение текущего времени. Таким образом мы сохраняем момент, когда было перехвачено задержанное нажатие. В течение определенного времени после этого будем перехватывать обычные касания:

    //обработка задержанных нажатий (по таймеру)
                if (dtTouchSpeed.AddMilliseconds(TOUCH_SPEED) > DateTime.Now)
                {
                    foreach (TouchLocation location in TouchPanel.GetState())
                    {
                        TouchButtons(location.Position);
                    }
                }
     
                //обработка одиночных нажатий
                while (TouchPanel.IsGestureAvailable)
                {
                    GestureSample gesture = TouchPanel.ReadGesture();
     
                    if (gesture.GestureType == GestureType.Tap)
                    {
                        TouchButtons(gesture.Position);
                    }
                    else
                    {
                        if (gesture.GestureType == GestureType.Hold)
                        {
                            dtTouchSpeed = DateTime.Now;
                        }
                    }
                }
    

    Если, например, не использовать обработчик жестов, а только обработчик касаний – то игроку будет трудно перемещать фигуру на 1 клетку.

    В обработчике нажатий TouchButtons() выполняются проверки на нажатие в нужную область экрана, и осуществляется соответствующая логика (перемещение фигуры, поворот, пауза).

    Таким же способом, как и с обработчиком нажатий, будем обрабатывать игровые такты. Переменная gameSpeed будет хранить время (в миллисекундах), через которое будет опускаться фигура. Чем выше уровень игры, тем меньше значение переменной gameSpeed:

    if (dtGameSpeed.AddMilliseconds(gameSpeed) < DateTime.Now)
    {
        //обработка падения
    }
    

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

    Полностью метод OnUpdate() будет выглядеть примерно так:

            private void OnUpdate(object sender, GameTimerEventArgs e)
            {
                // TODO: Add your update logic here
     
                //обработка задержанных нажатий (по таймеру)
                if (dtTouchSpeed.AddMilliseconds(TOUCH_SPEED) > DateTime.Now)
                {
                    foreach (TouchLocation location in TouchPanel.GetState())
                    {
                        TouchButtons(location.Position);
                    }
                }
     
                //обработка одиночных нажатий
                while (TouchPanel.IsGestureAvailable)
                {
                    GestureSample gesture = TouchPanel.ReadGesture();
     
                    if (gesture.GestureType == GestureType.Tap)
                    {
                        TouchButtons(gesture.Position);
                    }
                    else
                    {
                        if (gesture.GestureType == GestureType.Hold)
                        {
                            dtTouchSpeed = DateTime.Now;
                        }
                    }
                }
     
                if (!gamePaused)
                {
                    if (dtGameSpeed.AddMilliseconds(gameSpeed) < DateTime.Now)
                    {
                        if (field.CanDown(figure.GetAfterDown()))
                        {
                            figure.MoveDown();
                        }
                        else
                        {
                            field.FigureToBricks(figure.GetElements());
     
                            //убираем линии
                            int lines = 0;
                            int line = field.CheckForLine(); ;
                            while (-1 != line)
                            {
                                audioTinTinTin.Play();
                                lines++;
                                field.RemoveLine(line);
                                line = field.CheckForLine();
                            }
     
                            if (lines > 0)
                            {
                                //начислить очки
                                switch (lines)
                                {
                                    case 1:
                                        scoreCurr += SCORE_LINES_1;
                                        break;
                                    case 2:
                                        scoreCurr += SCORE_LINES_2;
                                        break;
                                    case 3:
                                        scoreCurr += SCORE_LINES_3;
                                        break;
                                    case 4:
                                        scoreCurr += SCORE_LINES_4;
                                        break;
                                }
     
                                //смена уровня сложности
                                gameLevel = gameStartLevel + (int)(scoreCurr / LEVEL_CHANGE);
     
                                if (gameLevel <= LEVEL_MAX)
                                {
                                    gameSpeed = GAME_SPEED_START - (gameLevel - 1) * GAME_SPEED_DELTA;
                                }
                                else
                                {
                                    //победа
                                    GameOver();
                                }
                            }
     
                            //новая фигура
                            audioDong.Play();
                            figure = figureNext;
                            figureNext = GenerateRandomFigure();
     
                            if (field.ContactWithBricks(figure.GetElements()))
                            {
                                //поражение
                                GameOver();
                            }
                        }
     
                        dtGameSpeed = DateTime.Now;
                    }
                }
            }
    

    Метод OnDraw() отвечает за перерисовку игрового пространства. Метод вызывается каждый игровой такт. Внутри него с помощью уже определенной переменной spriteBatch будем рисовать фон, кнопки, игровое поле, кирпичики на поле, фигуры и строки. Всю вырисовку необходимо заключать в функциональные "скобки" spriteBatch.Begin() и spriteBatch.End().

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

    private void DrawFigures()
            {
                Microsoft.Xna.Framework.Rectangle rect;
                List<Vector2> elmts = figure.GetElements();
     
                for (int i = 0; i < elmts.Count; i++)
                {
                    rect = new Microsoft.Xna.Framework.Rectangle();
                    rect.X = field.Left + (int)elmts[i].X * field.CellSize;
                    rect.Y = field.Top + (int)elmts[i].Y * field.CellSize;
                    rect.Width = field.CellSize;
                    rect.Height = field.CellSize;
     
                    spriteBatch.Draw(imgBrick, rect, Color.White);
                }
            }
    
            private void DrawStrings()
            {
                float x1 = 30f;
                float y1 = 20f;
     
                Microsoft.Xna.Framework.Rectangle rect = new Microsoft.Xna.Framework.Rectangle();
                rect.X = (int)x1 - 20;
                rect.Y = (int)y1 - 10;
                rect.Width = 460;
                rect.Height = 45;
                spriteBatch.Draw(imgField, rect, Color.White);
     
                spriteBatch.DrawString(fontText, "Level: " + gameLevel.ToString(), new Vector2(x1, y1), Color.DarkBlue);
                spriteBatch.DrawString(fontText, "Score: " + scoreCurr.ToString(), new Vector2(x1 + 120f, y1), Color.DarkBlue);
                spriteBatch.DrawString(fontText, "High score: " + scoreHigh.ToString(), new Vector2(x1 + 250f, y1), Color.DarkBlue);
     
                //статус игры
                if (gamePaused)
                {
                    spriteBatch.DrawString(fontText, "PAUSED", new Vector2(200, 390), Color.DarkRed);
                }
            }
    

    Метод OnDraw() будет иметь следующий вид:

    private void OnDraw(object sender, GameTimerEventArgs e)
    {
        SharedGraphicsDeviceManager.Current.GraphicsDevice.Clear(Color.CornflowerBlue);
     
        // TODO: Add your drawing code here
        spriteBatch.Begin();
     
        //фон
        spriteBatch.Draw(imgBack, new Vector2(0f), Color.White);
        spriteBatch.Draw(imgTitle, new Vector2(0f, 60f), Color.White);
     
        //кнопки
        DrawButtons();
     
        //поле
        DrawField();
        
        //строки
        DrawStrings();
     
        //следующая фигура
        DrawNextFigure();
     
        //мусор
        DrawBricks();
     
        //фигуры
        DrawFigures();
     
        spriteBatch.End();
    }
    

    В итоге, на эмуляторе игра будет выглядеть примерно следующим образом Рис. 6.3 :

    (рис 6.3) Внешний вид конечного приложения

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