This article has been localized into Ukrainian 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.
Додавання власного рядка заголовка
Перший крок – додати кілька властивостей та нову подію до оголошення 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. Це повністю видалить рядок заголовка, а також будь-які стандартні межі навколо вікна — для нас це не проблема, оскільки основна область нашої гри вже має чорну рамку розміром 5 пікселів.
Ви також помітите, що я підписався на нову подію – подію MouseDown. Причина полягає в тому, що оскільки ми втрачаємо рядок заголовка за замовчуванням, користувач більше не може перетягувати гру з однієї точки екрана в іншу. На щастя для нас, таку поведінку легко відтворити, наприклад, на нашому власному рядку заголовка. Однак, оскільки він не схожий на звичайний рядок заголовка, користувач може бути незрозумілим, куди перетягувати, тому я вирішив просто зробити всю поверхню вікна перетягуваною. Отже, у вашому коді визначте обробник події Window_MouseDown таким чином:
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}Після цього ваше вікно можна перетягувати незалежно від того, де ви використовуєте мишу. Наступний крок – додати наш власний рядок заголовка, який має відображати рахунок і швидкість, а також кнопку закриття. Внутрішня частина 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 з Canvas GameArea всередині нього тепер оточений DockPanel. Це дозволяє нам легко прикріпити наш новий рядок заголовка у вигляді панелі Grid до верхньої частини вікна.
Сітка використовує кілька цікавих технік WPF, які вже обговорювалися в інших частинах цього посібника: ми використовуємо ColumnDefinition, щоб розділити область на дві області однакового розміру (для оцінки та швидкості), а також третій стовпець з автоматичним розміром для кнопки закриття. Ви також помітите, що ми використовуємо WPF Style, щоб застосувати однаковий візуальний вигляд до всіх елементів керування TextBlock – до всіх них застосовується однаковий власний шрифт, розмір шрифту, колір і товщина завдяки стилю, визначеному в сітці, що орієнтований на елементи керування TextBlock.
Також зверніть увагу, як легко налаштувати елемент керування Button, який використовується для закриття вікна, щоб він повністю відповідав решті зовнішнього вигляду гри, просто використовуючи стандартні властивості — WPF такий гнучкий!
Короткий зміст
У цій статті ми зробили нашу реалізацію SnakeWPF набагато більше схожою на гру, видаливши стандартний вигляд Windows та застосувавши власний рядок заголовка. У наступних статтях ми внесемо ще більше покращень!