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

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

1. Sơ lược:

bài viết trước, mình đã hướng dẫn các bạn lập trình tạo ra giao diện đơn giản để thể hiện một bảng/ma trận có chứa các pokemon đc sinh ngẫu nhiên, chưa có thao tác gì với cái bảng đó cả. Hôm nay chúng ta sẽ làm thêm 1 chút ít nâng cao, bao gồm:

  1. Cải tiến model game (ghi nhận thêm trường hợp các ô bị xóa, trường hợp đang click)
  2. Liên kết, đón nhận các sự kiện click, mouse hover và mouse leave trên picturebox
  3. Tạo animation đơn giản (làm mờ ảnh, đóng khung đỏ các ô)

Mất thêm 30 phút ngày hôm nay, bạn sẽ tạo ra một giao diện game giống như hình bên dưới (hãy chú ý tới việc có 1 số ô bỏ trống (giả lập là đã bị xóa), một ô được bôi đen/làm mờ (giả lập vừa click vào đó) và một ô đc bôi đỏ (là ô có chuột trỏ vào đó)

Vẫn như đã nói trước, mình chỉ hướng dẫn sơ sơ và ý tưởng (dù mình có đủ code và ko ngại gì việc share). Mình nghĩ nếu các bạn chịu mày mò, đọc ý tưởng và tự làm sẽ giúp các bạn tốt lên rất nhiều

2. Ý tưởng:

Ở bài trước, các bạn đã học về gameModel để tạo ra ma trận chứa pokemon (mỗi 1 ô trong ma trận là 1 số dương, đại diện cho một con pokemon). Vậy nếu khi 2 con pokemon được chọn và bị xóa, chúng ta dùng cái gì để biểu diễn. Tất nhiên trường hợp này dùng số 0 là hay nhất

Trường hợp có 1 con pokemon bị click vào (ví dụ click vào con pikachu ở ô (1,1)), làm thế nào để biểu diễn nó??? Tất nhiên, bạn có thể dùng 1 chỉ số phụ để biểu diễn tọa độ của ô bị click. Nhưng với tư duy và cách làm của mình, bạn hãy đổi giá trị ô bị click đó thành số âm là xong:

Tóm lại, table của bạn là một ma trận chứa các số nguyên theo luật sau:

  • 0 là ko có con pokemon nào cả (Đã bị xóa)
  • Số dương là có pokemon, ko bị click (trạng thái bình thường)
  • Số âm là có pokemon, đã bị click. Chú ý rằng trong 1 ma trận sẽ chỉ có tối đa 2 số âm (trường hợp click 2 con giống nhau sau đó sẽ xóa chúng, hoặc click vào 2 con khác nhau). 
  • Chúng ta dùng mô hình này để biểu diễn logic của game, tính toán và xử lí các logic như ăn điểm, hoặc kết thúc game (khi các số trong ô đều là 0). Các class thể hiện giao diện sẽ nên chỉ thể hiện giao diện, hình ảnh, ko nên có xử lí logic trong các class làm nhiệm vụ giao diện nhá (đọc thêm 1 chút về MVC đi)

Về giao diện, chúng ta cần suy nghĩ tới các công việc:

  • Làm sao để biết chúng ta click, mouse hover hay mouse leave vào cái ô khỉ gió nào để xử lý sự kiện
  • Làm sao để làm mờ tấm ảnh trong picture box
  • Làm sao để đóng khung màu đỏ cái ô trong picture box (thể hiện là mouse hover)?

Và bây giờ chúng ta sẽ cùng giải quyết từng vấn đề đó nhé

3. Thay đổi modelGame:

Ở bài 1, đã có đoạn code tạo random các pokemon rồi

Giờ mình cần các bạn sửa đoạn code đó trong 5 phút vs yêu cầu:

  • Trong table có 1 số các ô là số 0 (thể hiện đã bị xóa)
  • Trong table có 1 hoặc 2 số là số âm (thể hiện là ô này bị click). Sau đó chúng ta sẽ tới phần chỉnh sửa giao diện

4. Xử lí sự kiện mouse click/mouse hover/mouse leave

Rất đơn giản, ở đoạn tạo ra 1 danh sách các pictureBox, bạn hãy liên kết sự kiện cho các pictureBox này như sau:

px[idx].Click += new EventHandler(pictureBoxClickEventhandle);
px[idx].MouseHover += new EventHandler(pictureBoxMouseHoverEventhandle);
px[idx].MouseLeave += new EventHandler(pictureBoxMouseLeaveEventhandle);

Bạn thấy rằng Click, MouseHoverMouseLeave là 3 sự kiện mà bạn muốn xử lí. Theo đó, khi đối tượng này đc nhận sự kiện, code của bạn sẽ tự chạy vào hàm tương ứng là pictureBoxClickEventhandle

Code mẫu của các hàm xử lý sự kiện sẽ có format sau:

public void pictureBoxMouseLeaveEventhandle(object sender, EventArgs e)
{
}

Trong đó sender chính là đối tượng nhận đc sự kiện (click vào button thì là button, click vào label thì là label), còn e chính là thông tin chi tiết về sự kiện (có thể là click chuột trái hay phải, gõ phím A hay phím B, ...)

Ở đoạn bên trên, bạn sẽ thấy có 1 điểm khác vs code hôm trước. Đó là mình đưa px (các picture box) vào 1 mảng, thay vì hôm trước là tạo mới các đối tượng ko nằm trong mảng

Cụ thể mình đã sửa thế này này:

px = new PictureBox[gameModel.Height * gameModel.Width];
for (int i = 0; i < gameModel.Height; i++)
   for (int j = 0; j < gameModel.Width; j++)
   {
      int idx = i * gameModel.Width + j;
      px[idx] = new PictureBox();
      ...
   }

Sở dĩ mình cần làm như vậy, là để sau này khi xử lí animation, mình cần biết cái picturebox nào đang được nhấp nháy, cái picturebox nào đang được làm mờ ảnh. Và với cách làm của lần trước thì sẽ rất khó để lưu trữ thông tin

Khi bạn chuyển toàn bộ các pictureBox ra 1 mảng pictureBox có đánh số từ 0->n, khi đó bạn chỉ cần lưu hoặc lấy index của pictureBox mà bạn cần xử lí rồi thao tác là xong

5. Xử lý đóng khung màu đỏ khi mouse hover ntn?

Bạn tưởng tượng pictureBox là 1 cái khung ảnh có chứa ảnh bên trong. Đóng khung màu đỏ thì chúng ta cần tạo border (đường viền) là xong

Tuy nhiên pictureBox lại không làm đc việc này. Cho nên action là thế này:

  • Chúng ta tạo pictureBox có độ dài và rộng rộng hơn cái ảnh (ví dụ ảnh là 40*50 thì ta tạo cái khung là 44*54 hay 42*52)
  • Đặt ảnh vào vị trí chính giữa của pictureBox:
    px[idx].SizeMode = PictureBoxSizeMode.CenterImage;​
  • Thay đổi màu nền của pictureBox thành màu mà bạn muốn:
    • Trường hợp ko có màu nền, sẽ lấy màu ở phía dưới
      px[idx].BackColor = Color.Transparent;​
    • Trường hợp màu đỏ thì thế này:
      px[idx].BackColor = Color.Red;​

Vậy hàm xử lí mouseHover chỉ cần thế này là xong:

public void pictureBoxMouseHoverEventhandle(object sender, EventArgs e)
{
   (sender as PictureBox).BackColor = Color.Red;
}

Chú ý nhá, nhớ đặt cái khung ảnh (pictureBox) to hơn cái ảnh. Nếu ko bạn sẽ ko thấy đc màu đỏ đâu :D

Và hãy tự xử lý trường hợp MouseLeave nha

6. Cách xóa ảnh trong pictureBox thì ntn?

Với các ô không có ảnh, biểu diễn bằng số 0 ở dưới gameModel, tương đương vs các pokemon đã bị xóa khỏi table, chúng ta cần làm cho pictureBox không có ảnh

Đơn giản lắm, bạn gán ảnh cho pictureBox bằng null là xong

px[idx].Image = null;

7. Và cuối cùng, làm mờ ảnh trong PictureBox thì thế nào?

Bạn có thể gõ cụm từ opacity image C# picturebox nhé

Ở trong PictureBox, ko có sẵn hàm làm mờ ảnh. Có 1 gợi ý nhỏ, đó là chúng ta tạo ra 1 cái ảnh mới bằng cách làm mờ cái ảnh cũ (thay đổi pixel của ảnh cũ theo 1 hệ số chung nào đó), rồi gán cái ảnh mới vào chính cái PictureBox. Hãy tham khảo đoạn code của mình code dưới đây:

public void makeClickedCell(PictureBox px)
{
   float ratio = 0.27f;
         
   //convert image in picture to bitmap
   Bitmap bmp = (Bitmap)px.Image;

   for (int w = 0; w < bmp.Width; w++)
   {
      for (int h = 0; h < bmp.Height; h++)
      {
         // get the pixel at position (w, h) of image
         Color c = bmp.GetPixel(w, h);

         // create new pixel
         Color newC = Color.FromArgb((int)(ratio * c.A), (int)(ratio * c.R), (int)(ratio * c.G), (int)(ratio * c.B));

         // change the value of pixel at posion (w, h)
         bmp.SetPixel(w, h, newC);
      }
   }

   // update image in picture box
   px.Image = bmp;
}

Dựa vào hàm này và tham khảo phần xử lí sự kiện, hãy tự viết hàm để làm mờ cái ô vuông khi click vào pictureBox nhé bạn

Bài hôm nay dừng ở đây thôi. Tạm biệt và chúc các bạn có kết quả tốt. 

NHỚ LIKE, SHARE VÀ GÓP Ý CHO MÌNH NHÉ