TOC

This article has been localized into Vietnamese by the community.

Tạo trò chơi: SnakeWPF:
Chapter introduction:

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!

Thêm thức ăn cho rắn

Ở giai đoạn này của loạt bài viết của SnakeWPF, giờ đây chúng ta đã có một hình nền bàn cờ là khu vực trò chơi, cũng như một con rắn màu xanh lá cây trông đáng yêu di chuyển xung quanh nó. Tuy nhiên, như đã đề cập trong phần giới thiệu, mục đích của trò chơi là cho rắn ăn một số thức ăn - trong phiên bản của chúng tôi sẽ là táo đỏ!

Vì vậy, bây giờ là lúc để bắt đầu thêm một số thực phẩm vào khu vực trò chơi. Chúng tôi sẽ làm điều đó bằng cách thêm ngẫu nhiên một vòng tròn màu đỏ ở đâu đó trong ranh giới của Canvas GameArea, nhưng chúng tôi cần đảm bảo rằng chúng tôi sẽ không đặt nó vào một trong những hình vuông đã bị con rắn không ngừng phát triển chiếm giữ. Nói cách khác, một trong những khía cạnh quan trọng nhất của việc đặt một quả táo vào khu vực trò chơi là mã quyết định vị trí tiếp theo. Đây là mã chúng tôi sẽ sử dụng để làm việc đó:

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);
}

Đảm bảo cũng thêm dòng này vào đầu khai báo lớp Window, cùng với các trường/hằng số còn lại:

public partial class SnakeWPFSample : Window
{
    private Random rnd = new Random();
    ......

Vì vậy, để giải thích nhanh code: Một lần nữa chúng tôi sử dụng hằng số SnakeSquareSize để giúp chúng tôi tính toán vị trí tiếp theo cho thực phẩm của mình, kết hợp với lớp Random, sẽ cho chúng tôi vị trí X và Y ngẫu nhiên. Khi chúng tôi có nó, chúng tôi chạy qua tất cả các bộ phận rắn hiện tại và kiểm tra xem vị trí của chúng có khớp với tọa độ X và Y mà chúng tôi vừa tạo hay không - có nghĩa là chúng tôi đã tấn công một khu vực hiện đang bị rắn chiếm giữ và sau đó chúng tôi yêu cầu một vị trí mới bằng cách đơn giản gọi lại phương thức (làm cho phương thức này đệ quy).

Điều này cũng có nghĩa là phương pháp này có thể tự gọi mình là một số lần không giới hạn và theo lý thuyết, dẫn đến một vòng lặp vô tận. Chúng tôi có thể thực hiện một số kiểm tra cho điều đó, nhưng không cần thiết, vì nó sẽ yêu cầu con rắn quá dài để không còn chỗ trống - tôi cá là trò chơi sẽ kết thúc trước khi điều này xảy ra.

Với điều đó, chúng tôi đã sẵn sàng để thêm code sẽ thêm thực phẩm vào vị trí mới được tính toán - chúng tôi sẽ thực hiện từ phương thức có tên DrawSnakeFood(). Nhờ tất cả các công việc đã được xử lý bởi GetNextFoodPosition(), khá đơn giản, nhưng trước tiên, hãy chắc chắn khai báo trường được sử dụng để lưu tham chiếu đến thực phẩm, cũng như SolidColorBrush được sử dụng để vẽ quả táo, cùng với trường khác/khai báo không đổi:

public partial class SnakeWPFSample : Window  
{  
    private UIElement snakeFood = null;  
    private SolidColorBrush foodBrush = Brushes.Red;
    ......

Đây là việc thực hiện phương pháp:

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);
}

Như đã hứa, rất đơn giản - ngay khi chúng tôi có vị trí, chúng tôi chỉ cần tạo một thể hiện Ellipse mới và một lần nữa chúng tôi sử dụng hằng số SnakeSquareSize để đảm bảo rằng nó có cùng kích thước với các ô nền cũng như từng phần của con rắn. Chúng tôi lưu một tham chiếu đến thể hiện Ellipse trong trường snakeFood, vì chúng ta cần nó sau này.

Với vị trí đó, chúng tôi thực sự chỉ cần gọi phương thức DrawSnakeFood() để xem kết quả công việc của chúng tôi. Điều này sẽ được thực hiện trong hai tình huống: Vào đầu trò chơi và khi con rắn "ăn" thức ăn (nhiều hơn về sau). Hiện tại, hãy thêm một cuộc gọi đến nó trong phương thức StartNewGame() của chúng tôi:

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;
}

Đó là nó! Nếu bạn chạy trò chơi bây giờ, bạn sẽ thấy rằng con rắn cuối cùng cũng có một số thức ăn để đuổi theo:

Một chút việc nhẹ nhàng để đặt một chấm đỏ trên màn hình, phải không?

Tổng kết

Trong bài viết này, cuối cùng chúng tôi đã thêm một số thức ăn vào bàn để con rắn bắt được, nhưng vẫn còn nhiều việc phải làm: Chúng ta cần phải kiểm soát con rắn, và chúng ta cần biết khi nào nó chạm phải thứ gì đó (một bức tường, đuôi của nó hoặc thức ăn). Thêm vào đó trong bài viết tiếp theo.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!