Lập trình game Winform với C# (Phần 1)

Lập trình game Winform với C# (Phần 1)

1. Lời nói đầu:

Hello anh em lập trình viên

Bắt đầu từ tuần này, mỗi 2 3 ngày mình sẽ dành khoảng 2 3 tiếng để viết các bài hướng dẫn anh em làm sản phẩm, ứng dụng hay game cụ thể. Mục tiêu là học đi đôi vs hành.

Do có khá nhiều bạn hướng dẫn làm game bằng C++ hay Java, nên mình sẽ ưu tiên chọn 1 số game cổ điển bằng C# để hướng dẫn mọi người.

Mình code thì khá là nhanh, chỉ 10 20'. Nhưng để viết thành 1 bài hướng dẫn thì khá là mất thời gian (do phải biên soạn, chụp ảnh, lời lẽ giải thích dài dòng), nên 1 bài blog khá là tốn công sức. Do đó, nếu tốc độ viết bài có chậm, cũng như câu cú lời lẽ còn lủng củng thì mong anh em thông cảm nhá :D

Mình có thể share hết toàn bộ code, nhưng với phương châm là hướng dẫn, giúp đỡ thay vì chia sẻ mã nguồn, nên nhiều đoạn sẽ nói vắn tắt hoặc bỏ qua, a em cần tìm hiểu thêm nhiều nhé

Và đặc biệt chú ý, trên tinh thần học hỏi, hãy thảo luận, góp ý vào dưới bài viết nếu có vấn đề khó hiểu nha

Đặc biệt chú ý, các bài hướng dẫn sẽ chỉ hướng dẫn và phù hợp cho các bạn đã có kiến thức cơ sở về ngôn ngữ lập trình và 1 chút ít thuật toán. Nếu chưa, hãy tự học các khóa học cơ bản trước đi nhé

2. Ý tưởng và giải pháp:

Game mình chọn lần này là game Pokemon. Có lẽ hơi nhiều bài hướng dẫn làm game này bằng C++ hay Java cũng trên blog này rồi, nhưng mà làm bằng C# winform thì chắc chỉ mất 20' thôi. Nên mình vẫn hướng dẫn lại game này 1 lần trong 1 seri độ khoảng 5-7 bài (a e chú ý theo dõi và ghi nhớ thực hành, rèn luyện là chủ yếu nha)

Trước hết, hãy tham khảo 1 game tương tự tại trang web này: https://gamevui.vn/pikachu/game

Bạn hãy để ý:

  • Phần game chính (phần table chứa các pokemon) là một ma trận chữ nhật, có kích thước là height *width
  • Số pokemon phải là chẵn, do đó height*width phải chia hết cho 2 (ko thể lẻ 1 con pokemon đc đúng không)
  • Mỗi một ô trong ma trận chữ nhật có kích thước bằng nhau, sẽ chứa 1 pokemon bất kì.
  • Mỗi 1 pokemon bất kỳ đều có số lượng chẵn (phải chẵn để đủ ghép cặp 2 con vs nhau chứ)
    Ví dụ sẽ có 4 pikachu, 8 con rồng lửa, 10 con sâu

Ý tưởng:

  • Chúng ta khởi tạo 1 bảng gồm width*height ô chữ nhật (width hoặc height phải chẵn). Mỗi ô trong bảng là 1 con số (ví dụ từ 1->20) đại diện cho một loại pokemon
  • Mỗi 1 con số (đại diện cho 1 loại pokemon) phải có số lượng là chẵn ở trong bảng (ví dụ 2 số 1, 20 số 2, 14 số 3, ...)
  • Không có ô nào trong bảng không có số (tức là ko có pokemon nào trong ô đó)
  • Về mặt giao diện, chúng ta sẽ dùng 1 cái gì đó để biểu diễn cho một hình chữ nhật có chứa pokemon. Và cái chúng ta cần sử dụng trong C# winform, phù hợp vs bài toán của chúng ta đó chính là PictureBox
    Sâu hơn về pictureBox, bạn có thể đọc thêm ở đây:https://csharpcanban.com/c-huong-dan-su-dung-picturebox-control.html#.XoMGACgzbZQ
    Đại khái chúng ta sẽ tưởng tượng rằng, chúng ta sẽ xếp liên tục các PictureBox (trong mỗi PictureBox chứa 1 ảnh đại diện cho 1 pokemon) vào vs nhau để tạo ra table như bên trên
  • Với bài tập này, chúng ta nên tạo ra lớp GameModel để biểu diễn dữ liệu (là chiều cao, chiều rộng và là cái bảng chứa các pikachu dưới dạng con số). Đối với giao diện, chúng ta dùng Form và add thẳng các PictureBox vào form

3. Bắt đầu làm:

  1. Chuẩn bị:
    - Hãy tự tìm và download bộ ảnh pokemon rồi đặt tên theo thứ tự của chúng. Ví dụ 1.png, 2.png, 3.png
    Hãy lên chính trang https://gamevui.vn/pikachu/game  để download các tấm ảnh của pokemon nhé (đã đc đặt tên theo số thứ tự rồi đó)
  2. Tạo dự án:
    Hãy tạo dự án winform bằng công cụ Visual Studio như ảnh dưới đây:
  3. Hãy tạo thêm lớp GameModel, thể hiện là dữ liệu bảng dữ liệu:
    1. Có thuộc tính là width, height, và int[,] table để thể hiện các pokemon nằm trong bảng
    2. Có phương thức khởi tạo để khởi tạo cái table. Hãy sinh ngẫu nhiên các pokemon nằm trong đó
    3. Thuật toán sinh ngẫu nhiên các pokemon và hàm khởi tạo, các bạn có thể tham khảo đoạn code này nha:
      class GameModel
          {
              private int[, ] table;
              private int width, height;
      
              public GameModel(int _width, int _height, int _numOfType)
              {
                  width = _width;
                  height = _height;
      
                  table = new int[height, width];
      
                  // su dung de luu cac o da sinh ra pokemon
                  HashSet<int> cellIndex = new HashSet<int>();
      
                  // su dung de sinh so ngau nhien
                  Random random = new Random();
      
                  for (int i=0; i<width*height/2; i++)
                  {
                      // sinh ngau nhien 1 loai pokemon, nam trong khoang tu 1 -> _numOfType
                      int typeOfPokemon = random.Next(1, _numOfType + 1);
                      Console.WriteLine(i);
                      // sinh ra o thu 1 chua pokemon, yeu cau la khong dc nam trong cellIndex
                      int cell1 = random.Next(random.Next(0, width * height+1));
                      while (cellIndex.Contains(cell1))
                          cell1 = random.Next(random.Next(0, width * height+1));
                      table[cell1 / width, cell1 % width] = typeOfPokemon;
                      cellIndex.Add(cell1);
      
                      // sinh ra o thu 2 chua pokemon, yeu cau la khong dc nam trong cellIndex
                      int cell2 = random.Next(random.Next(0, width * height+1));
                      while (cellIndex.Contains(cell2))
                          cell2 = random.Next(random.Next(0, width * height+1));
                      table[cell2 / width, cell2 % width] = typeOfPokemon;
                      cellIndex.Add(cell2);
      
                      Console.WriteLine(i);
                  }    
              }
      }​
    4. Bạn cần tự viết thêm các hàm/phương thức Width, Height và getCell(int row, col) để lấy ra được các dữ liệu tương ứng nha
    5. Hãy cố đọc hiểu và phân tích hàm sinh ma trận một cách ngẫu nhiên của mình nhé
  4. Cập nhật lại file Form1.cs có sẵn khi tạo dự án như thế này:
     public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                GameModel gameModel = new GameModel(20, 10, 10);
                for (int i = 0; i < gameModel.Height; i++)
                    for (int j = 0; j < gameModel.Width; j++)
                    {
                        PictureBox px = new PictureBox();
                        px.Width = 40;
                        px.Height = 50;
                        px.Top = 10 + i * 50;
                        px.Left = 10 + j * 40;
    
                        string name = "pieces" + gameModel.getCell(i, j).ToString() + ".png";
                        px.Image = Image.FromFile(name);
                        this.Controls.Add(px);
                    }
    
            }
        }​

  5. Hãy copy cái đống ảnh bạn đã chuẩn bị vào cùng thư mục chứa file *.exe sẽ biên dịch ra
    Sau đó hãy chạy thử và xem kết quả có giống như hình của mình ko nhé
  6. Hãy cải tiến 1 chút ít chương trình để màn hình hiển thị nhiều pokemon hơn, và các pokemon đc hiển thị chính giữa khung của cửa sổ xem sao nhé

Tạm kết

Nếu bài viết này có trên 100 shares, mình sẽ viết ngay tiếp phần 2 để bắt đầu hướng dẫn sử dụng các phần cài đặt sự kiện như click vào picture box, đổi màu khi click, ....