This article has been localized into Vietnamese 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!
Chuyển động liên tục với DispatcherTimer
Trong các bài viết trước, chúng tôi đã tạo ra một khu vực trò chơi đẹp mắt cho Rắn của chúng tôi và sau đó chúng tôi đã thêm code để thực hiện việc tạo và di chuyển thực tế của con rắn. Tuy nhiên, như đã đề cập, mã chuyển động không phải là thứ nên được gọi chỉ một lần - thay vào đó, nó nên được gọi đi gọi lại, để giữ cho con rắn di chuyển chừng nào trò chơi đang chạy. Nói cách khác, chúng ta sẽ cần một Timer.
Nói chung, trong lập trình, Timer thường là một cơ chế cho phép lặp lại một tác vụ, dựa trên một khoảng thời gian. Nói cách khác, mỗi lần bộ đếm thời gian "tích tắc", một đoạn mã được thực thi và bộ đếm thời gian đánh dấu dựa trên khoảng thời gian xác định. Đây chính xác là những gì chúng ta cần để giữ cho con rắn của chúng ta di chuyển, vì vậy chúng ta sẽ thêm một DispatcherTimer vào Window của chúng ta:
public partial class SnakeWPFSample : Window
{
private System.Windows.Threading.DispatcherTimer gameTickTimer = new System.Windows.Threading.DispatcherTimer();
....
Với vị trí đó, bây giờ chúng ta cần đăng ký vào một sự kiện duy nhấtt: Sự kiện Tick. Chúng tôi sẽ làm điều đó trong hàm tạo của Window:
public SnakeWPFSample()
{
InitializeComponent();
gameTickTimer.Tick += GameTickTimer_Tick;
}
Và đây là việc thực hiện sự kiện:
private void GameTickTimer_Tick(object sender, EventArgs e)
{
MoveSnake();
}
Vì vậy, mỗi khi đồng hồ bấm giờ, sự kiện Tick được gọi, đổi lại gọi phương thức MoveSnake() mà chúng ta đã triển khai trước đó. Để cuối cùng thấy kết quả của tất cả lao động vất vả của chúng tôi và có một con rắn di chuyển trực quan, về cơ bản chúng tôi chỉ cần tạo ra các bộ phận rắn ban đầu và sau đó bắt đầu hẹn giờ. Chúng tôi sẽ tạo một phương thức gọi là StartNewGame(), chúng tôi sẽ sử dụng để bắt đầu cả trò chơi đầu tiên cũng như bất kỳ số lượng trò chơi mới bổ sung nào khi người chơi chết. Mặc dù vậy, chúng ta sẽ bắt đầu với một phiên bản rất cơ bản của nó, và sau đó tôi sẽ mở rộng nó với nhiều chức năng hơn khi chúng ta di chuyển - bây giờ, hãy để con rắn này di chuyển!
Bước đầu tiên là thêm một bộ hằng số khác, chúng ta sẽ sử dụng để bắt đầu trò chơi mới:
public partial class SnakeWPFSample : Window
{
const int SnakeSquareSize = 20;
const int SnakeStartLength = 3;
const int SnakeStartSpeed = 400;
const int SnakeSpeedThreshold = 100;
......
Chỉ có ba hằng số đầu tiên được sử dụng tại thời điểm này, để kiểm soát kích thước, chiều dài và tốc độ bắt đầu của Rắn. Chúng ta sẽ sử dụng SnakeSpeedThreshold sau, nhưng bây giờ, hãy thêm một cách thực hiện đơn giản phương thức StartNewGame() như đã hứa:
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
DrawSnake();
// Go!
gameTickTimer.IsEnabled = true;
}
Chúng tôi bắt đầu bằng cách đặt snakeLength và snakeDirection dựa trên các giá trị ban đầu. Sau đó, chúng tôi thêm một phần duy nhất vào Danh sách snakeParts(nhiều hơn về sau), tạo cho nó một vị trí bắt đầu tốt để di chuyển sang phải - một lần nữa chúng ta sẽ sử dụng hằng số SnakeSquareSize để giúp tính toán vị trí thích hợp. Với vị trí đó, chúng ta có thể vẽ con rắn bằng cách gọi phương thức DrawSnake() và sau đó kích hoạt bộ đếm thời gian, về cơ bản sẽ bắt đầu chuyển động của con rắn.
Bây giờ chúng ta cuối cùng đã đến lúc chúng ta gần như có thể ngồi lại và thưởng thức phiên bản đầu tiên của một thứ thực sự trông giống như một trò chơi - thực tế, tất cả những gì chúng ta phải làm bây giờ là gọi phương thức StartNewGame(). Tất nhiên điều này nên được thực hiện khi người dùng đã sẵn sàng, nhưng bây giờ, để kiểm tra xem mọi thứ có hoạt động không, chúng tôi sẽ đơn giản thực hiện ngay khi mọi thứ khác được khởi chạy - một lần nữa chúng tôi sẽ dựa vào sự kiện ContentRendered của Window, mà chúng tôi đã thêm vào một trong những bài viết đầu tiên. Chỉ cần thêm một cuộc gọi vào phương thức StartNewGame() của chúng tôi và cuối cùng chúng tôi đã sẵn sàng để biên dịch và chạy:
private void Window_ContentRendered(object sender, EventArgs e)
{
DrawGameArea();
StartNewGame();
}
Nếu bạn đã làm mọi thứ như mô tả, bây giờ bạn sẽ có thể bắt đầu trò chơi và thấy con rắn được tạo ra và ngay lập tức bắt đầu di chuyển:
Chú ý cách con rắn xuất hiện từ không có gì, như một hình vuông duy nhất, và sau đó phát triển đến chiều dài ba hình vuông. Điều đó xảy ra bởi vì chúng tôi chỉ thêm một phần vào danh sách snakeParts, nhưng mỗi lần phương thức MoveSnake() được gọi bởi timer, một phần mới được thêm vào (để làm cho nó phát triển), trong khi chỉ xóa phần đuôi nếu độ dài hiện tại là sắp vượt quá chiều dài mong muốn của con rắn, bắt đầu từ 3 (được quyết định bởi hằng số SnakeStartLength).
Tổng kết
Bây giờ chúng tôi có một con rắn di chuyển, đó là thực sự tuyệt vời! Nhưng như bạn có thể thấy từ hình ảnh hoạt hình ở trên, vẫn còn nhiều việc phải làm - không có thức ăn cho rắn ăn, và khi con rắn đập vào tường, không có gì xảy ra. Chúng tôi sẽ làm việc trên các khía cạnh này trong các bài viết tiếp theo.