This article has been localized into Russian by the community.
In this article series, we're building a complete Snake game from scratch. It makes sense to start with the Introduction and then work your way through the articles one by one, to get the full understanding.
If you want to get the complete source code for the game at once, to get started modifying and learning from it right now, consider downloading all our samples!
Улучшаем SnakeWPF: делаем это больше похожим на игру
Мы уже сделали классную игру Snake в WPF. Реализовали всю игровую механику и в результате игра стала полностью функциональной. Однако, есть еще возможность для множества улучшений т.к. текущее состояние нашей игры это самый минимум. Поэтому в следующих главах я сделаю несколько улучшений нашей игры SnakeWPF - а в этой главе я сосредоточусь на том, чтобы наше создание выглядело как настоящая игра!
Сейчас у нас границы игровой панели и заголовок в Windows стиле, что не очень похоже на игру. Однако, ранее нам требовался заголовок, чтобы в нем отображать информацию о счете и скорости. И в качестве замечательного бонуса мы автоматически получили кнопки минимизации/максимизации/закрытий в стиле Windows:
На этом этапе я хочу полностью снести заголовок в Windows стиле, который мы получили по умолчанию. Вместо этого я хочу реализовать наш собственный заголовок, который будет показывать счет и скорость, а также будет содежать нашу специальную кнопку для закрывания экрана. Все это должно по стилю совпадать с нашей уже существующей игрой. К счастью для нас это достаточно легко сделать в WPF.
Добавляем наш заголовок
Первым делом добавим парочку новых свойств и новое событие (event) в декларацию Window. Это должно выглядеть следующим образом:
<Window x:Class="WpfTutorialSamples.Games.SnakeWPFSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfTutorialSamples.Games"
mc:Ignorable="d"
Title="SnakeWPF - Score: 0" SizeToContent="WidthAndHeight" ContentRendered="Window_ContentRendered" KeyUp="Window_KeyUp"
ResizeMode="NoResize" WindowStyle="None" Background="Black" MouseDown="Window_MouseDown">
Все изменения содержатся в последней строке. Мы устанавливаем ResizeMode в NoResize и WindowStyle в None. Это полностью удалит заголовок и все границы экрана вокруг Window - это для нас не является проблемой т.к. уже сейчас игровое поле имеет черную границу в 5 пикселей.
Вы также заметите, что я подписался на новое событие (event) MouseDown. Причина этого заключается в том, что раз мы потеряли стандартный заголовок с ним потеряли возможность двигать игру из одной части экрана в другую. К счастью, есть возможность легко восстановить эту возможность в нашем собственном заголовке. Однако, т.к. он не выглядит стандартным образом, наш пользователь может запутаться и не знать, за что хвататься, чтобы перетащить экран игры. Поэтому я решил сделать весь экран передвигаемым. Поэтому в коде C# определим событие event Window_MouseDown следующим образом:
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
Сделав это, мы позволяем игроку перемещать экран игры вне зависимости от того за что ухватиться мышкой. Следующим шагом я добавлю заголовок, который будет показывать счет и скорость, а также кнопку закрытия экрана. Внутренняя часть Window XAML должна выглядеть следующим образом:
<DockPanel Background="Black">
<Grid DockPanel.Dock="Top" Name="pnlTitleBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Consolas" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="24" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</Grid.Resources>
<WrapPanel Margin="10,0,0,0">
<TextBlock>Score:</TextBlock>
<TextBlock Name="tbStatusScore">0</TextBlock>
</WrapPanel>
<WrapPanel Grid.Column="1">
<TextBlock>Speed:</TextBlock>
<TextBlock Name="tbStatusSpeed">0</TextBlock>
</WrapPanel>
<Button Grid.Column="2" DockPanel.Dock="Right" Background="Transparent" Foreground="White" FontWeight="Bold" FontSize="20" BorderThickness="0" Name="btnClose" Click="BtnClose_Click" Padding="10,0">X</Button>
</Grid>
<Border BorderBrush="Black" BorderThickness="5">
<Canvas Name="GameArea" ClipToBounds="True" Width="400" Height="400">
</Canvas>
</Border>
</DockPanel>
Не забудьте определить обработчик события BtnClose_Click:
private void BtnClose_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
Ранее мы воплотили метод UpdateGameStatus(), который обновлял свойство Title окна Window - нам теперь нужно изменить этот метод, чтобы использовать новые TextBlock:
private void UpdateGameStatus()
{
this.tbStatusScore.Text = currentScore.ToString();
this.tbStatusSpeed.Text = gameTickTimer.Interval.TotalMilliseconds.ToString();
}
Я расскажу подробней, что мы сейчас сделали, а пока давайте посмотрим как теперь выглядит наша игра:
Заголовок выглядит гораздо круче, не правда ли? Но давайте обсудим, что было сделано: как вы видите, первоначальный элемент управления Border с находящимся внутри GameArea Canvas теперь окружен DockPanel. Это позволяет нам приложить наш собственный заголовок в форме панели Grid вверху Window.
Элемент Grid использует несколько замечательных возможностей WPF, которые уже обсуждались в этом руководстве: Мы используем ColumnDefinition для того, чтобы разделить все пространство на две равные части (для счета и скорости), и еще третью часть с автоматическим изменением размера для нашей кнопки закрытия. Заметьте, что мы используем WPF Style для того, чтобы все в нашей игре выглядело одинаково, все наши эелементы управления TextBlock - шрифты, размер шрифтов, цвет и т.д. все применено ко всем TextBlock(ам), все благодаря Style, который мы определили для Grid, для всех элементов управления TextBlock.
Также заметьте, как легко управлять настройками Button, которую мы использовали для кнопки закрытия экрана, для полного соответствия другим компонентам нашей игры, лишь используюя стандартные свойства - WPF реально гибкий!
Обобщая
В этой части мы сделали так, чтобы наша SnakeWPF выглядела настоящей игрой. Мы добились этого убрав стандартный вид Windows приложений, и используя наш собственный заголовок. Но это еще не конец улучшениям!