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 у нас є шахова дошка як ігрова область, а також гарна зелена змія, що рухається навколо неї. Однак, як згадувалося у вступі, мета гри полягає в тому, щоб змія з'їла трохи їжі — у нашій версії це будуть червоні яблука!
Отже, тепер час почати додавати їжу до ігрової області. Ми зробимо це, випадковим чином додавши червоне коло десь у межах полотна GameArea, але нам потрібно переконатися, що ми не розміщуємо його в одному з квадратів, які вже зайняті постійно зростаючою змією. Іншими словами, одним з найважливіших аспектів розміщення яблука на ігровій області є код, який визначає наступну позицію. Ось код, який ми будемо використовувати для цього:
private Point GetNextFoodPosition()
{
int maxX = (int)(GameArea.ActualWidth / SnakeSquareSize);
int maxY = (int)(GameArea.ActualHeight / SnakeSquareSize);
int foodX = rnd.Next(0, maxX) * SnakeSquareSize;
int foodY = rnd.Next(0, maxY) * SnakeSquareSize;
foreach(SnakePart snakePart in snakeParts)
{
if((snakePart.Position.X == foodX) && (snakePart.Position.Y == foodY))
return GetNextFoodPosition();
}
return new Point(foodX, foodY);
}Обов'язково додайте цей рядок у початок оголошення класу Window разом з рештою полів/констант:
public partial class SnakeWPFSample : Window
{
private Random rnd = new Random();
......Отже, щоб швидко пояснити код: ми знову використовуємо константу SnakeSquareSize, щоб допомогти нам обчислити наступну позицію для нашої їжі, у поєднанні з класом Random, який надасть нам випадкову позицію X та Y. Отримавши її, ми переглядаємо всі поточні частини змії та перевіряємо, чи їхня позиція відповідає щойно створеним нами координатам X та Y — якщо так, це означає, що ми потрапили в область, яку наразі займає змія, а потім ми запитуємо нову позицію, просто викликаючи метод ще раз (що робить це рекурсивним методом).
Це також означає, що цей метод може викликати себе необмежену кількість разів і, теоретично, призводити до нескінченного циклу. Ми могли б виконати деякі перевірки для цього, але це не повинно бути необхідним, оскільки для цього потрібно, щоб змійка була настільки довгою, щоб не залишалося порожнього місця — я гадаю, що гра закінчиться до того, як це станеться.
Після цього ми готові додати код, який додасть їжу в щойно обчислену позицію – ми зробимо це за допомогою методу під назвою DrawSnakeFood(). Завдяки всій роботі, яку вже виконав GetNextFoodPosition(), це досить просто, але спочатку обов’язково оголосіть поле, яке використовується для збереження посилання на їжу, а також SolidColorBrush, який використовується для малювання яблука, разом з іншими оголошеннями полів/констант:
public partial class SnakeWPFSample : Window
{
private UIElement snakeFood = null;
private SolidColorBrush foodBrush = Brushes.Red;
......Ось реалізація методу:
private void DrawSnakeFood()
{
Point foodPosition = GetNextFoodPosition();
snakeFood = new Ellipse()
{
Width = SnakeSquareSize,
Height = SnakeSquareSize,
Fill = foodBrush
};
GameArea.Children.Add(snakeFood);
Canvas.SetTop(snakeFood, foodPosition.Y);
Canvas.SetLeft(snakeFood, foodPosition.X);
}Як і обіцяли, це дуже просто – щойно ми визначимо позицію, ми просто створюємо новий екземпляр Ellipse та знову використовуємо константу SnakeSquareSize, щоб переконатися, що він має такий самий розмір, як і фонові плитки, а також кожна частина змії. Ми зберігаємо посилання на екземпляр Ellipse в полі snakeFood, оскільки воно нам знадобиться пізніше.
З огляду на це, нам просто потрібно викликати метод DrawSnakeFood(), щоб побачити результат нашої роботи. Це буде зроблено у двох ситуаціях: на початку гри та коли змія «з’їсть» їжу (про це пізніше). А поки що давайте додамо його виклик у нашому методі StartNewGame():
private void StartNewGame()
{
snakeLength = SnakeStartLength;
snakeDirection = SnakeDirection.Right;
snakeParts.Add(new SnakePart() { Position = new Point(SnakeSquareSize * 5, SnakeSquareSize * 5) });
gameTickTimer.Interval = TimeSpan.FromMilliseconds(SnakeStartSpeed);
// Draw the snake and the snake food
DrawSnake();
DrawSnakeFood();
// Go!
gameTickTimer.IsEnabled = true;
}Ось і все! Якщо ви зараз запустите гру, то побачите, що змія нарешті має якусь їжу для полювання:

Досить багато роботи, щоб розмістити червону крапку на екрані, чи не так?
Короткий зміст
У цій статті ми нарешті додали трохи їжі на стіл для змії, яку вона зможе зловити, але ще є над чим працювати: нам потрібно вміти контролювати змію та знати, коли вона об щось вдаряється (об стіну, власний хвіст чи їжу). Детальніше про це в наступній статті.