CodeGym /Blog Java /Ngẫu nhiên /Phần trò chơi trên CodeGym: Lý thuyết hữu ích
John Squirrels
Mức độ
San Francisco

Phần trò chơi trên CodeGym: Lý thuyết hữu ích

Xuất bản trong nhóm
Trong phần "Trò chơi" trên CodeGym, bạn sẽ tìm thấy các dự án thú vị liên quan đến việc viết các trò chơi phổ biến trên máy tính. Bạn muốn tạo phiên bản của riêng mình cho các trò chơi phổ biến 2048, Minesweeper, Snake và các trò chơi khác? Nó đơn giản. Chúng tôi đã biến việc viết trò chơi thành một quy trình từng bước. Mục "Trò chơi" trên CodeGym: Lý thuyết hữu ích - 1Để kiểm tra khả năng của mình với tư cách là nhà phát triển trò chơi, bạn không cần phải là một lập trình viên cao cấp, nhưng cần có một bộ kiến ​​thức cụ thể về Java. Ở đây bạn sẽ tìm thấy thông tin hữu ích trong việc viết trò chơi .

1. Thừa kế

Làm việc với công cụ trò chơi CodeGym liên quan đến việc sử dụng tính kế thừa. Nhưng nếu bạn không biết đó là gì thì sao? Một mặt, bạn cần hiểu chủ đề này: nó được học ở Cấp độ 11. Mặt khác, động cơ được thiết kế đặc biệt rất đơn giản, vì vậy bạn có thể bỏ qua kiến ​​thức hời hợt về kế thừa. Vậy thừa kế là gì? Nói một cách đơn giản, thừa kế là mối quan hệ giữa hai lớp. Một trong số họ trở thành cha mẹ và người kia trở thành con (hậu duệ). Hơn nữa, lớp cha thậm chí có thể không biết rằng nó có lớp con. Nói cách khác, nó không đạt được bất kỳ lợi thế cụ thể nào khi có con cháu. Nhưng thừa kế mang lại cho con cháu nhiều lợi thế. Và điều quan trọng nhất là tất cả các biến và phương thức của lớp cha đều xuất hiện trong lớp con như thể mã của lớp cha được sao chép sang lớp con. Đây không phải là một mô tả hoàn toàn chính xác, nhưng nó sẽ đủ để hiểu một cách đơn giản về thừa kế. Ví dụ 1: Kế thừa đơn giản nhất.

public class Parent {

}
Lớp Con kế thừa lớp Cha bằng cách sử dụng từ khóa mở rộng .

public class Child extends Parent {

}
Ví dụ 2: Sử dụng biến của lớp cha.

public class Parent {

   public int age;
   public String name;
}
Lớp Con có thể sử dụng các biến tuổitên của lớp Cha như thể chúng được khai báo trong lớp Cha .

public class Child extends Parent {

   public void printInfo() {

     System.out.println(name+" "+age);
   }
}
Ví dụ 3: Sử dụng các phương thức của lớp cha.

public class Parent {

   public int age;
   public String name;

   public getName() {
      return name;
  }
}
Lớp Con có thể sử dụng các biến và phương thức của lớp Cha như thể chúng được khai báo trong lớp Con. Trong ví dụ này, chúng tôi sử dụng phương thức getName() .

public class Child extends Parent {

   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}
Đây là giao diện của lớp Con đối với trình biên dịch:

public class Child extends Parent{

   public int age;  // Inherited variable
   public String name;  // Inherited variable

   public getName() {  // Inherited method.
      return name;
  }
   public void printInfo() {

     System.out.println(getName()+" "+age);
   }
}

2. Các phương thức ghi đè

Đôi khi có những tình huống mà chúng ta làm cho lớp Con của chúng ta kế thừa một số lớp Cha mẹ rất hữu ích, cùng với tất cả các biến và phương thức của nó, nhưng một số phương thức không hoạt động theo cách chúng ta muốn. Hoặc hoàn toàn không như cách chúng ta muốn. Chúng ta có thể làm gì trong tình huống này? Chúng ta có thể override phương thức mà chúng ta không thích. Điều này rất dễ thực hiện: trong lớp Con của chúng ta, chúng ta chỉ cần khai báo một phương thức có cùng chữ ký với phương thức trong lớp Cha và sau đó chúng ta viết mã của riêng mình vào đó. Ví dụ 1: Ghi đè một phương thức.

public class Parent {

   public String name;

   public void setName(String nameNew) {
       name = nameNew;
  }

   public getName() {
      return name;
  }
}
Phương thức printInfo() sẽ hiển thị "Luke, No!!!"

public class Child extends Parent{

   public void setName(String nameNew) {
       name = nameNew + ", No!!!";
  }

   public void printInfo() {
      setName("Luke");
      System.out.println(getName());
   }
}
Đây là giao diện của lớp Con đối với trình biên dịch:

public Child extends Parent {

   public String name;  // Inherited variable

   public void setName(String nameNew)  // Overridden method instead of the inherited method {

       name = nameNew + ", No!!!";
   }
   public getName() {  // Inherited method.

      return name;
   }
   public void printInfo() {

     setName("Luke");
     System.out.println( getName());
   }
}
Ví dụ 2: Một số phép kế thừa (và ghi đè phương thức).

public class Parent {

   public getName() {
      return "Luke";
  }
   public void printInfo() {

     System.out.println(getName());
   }
}

public class Child extends Parent {

   public getName() {
      return "Luke, I am your father";
  }
}
Trong ví dụ này, nếu printInfophương thức (từ lớp Cha) không bị ghi đè trong lớp Con, khi phương thức này được gọi trên một đối tượng Con, getName()phương thức của nó sẽ được gọi thay vì getName()phương thức của lớp Cha.

Parent parent = new Parent ();
parent.printnInfo();
Mã này hiển thị "Luke" trên màn hình.

Child child = new Child ();
child.printnInfo();
Mã này hiển thị "Luke, tôi là cha của bạn" trên màn hình.
Đây là giao diện của lớp Con đối với trình biên dịch:

public class Child extends Parent {

   public getName() {
      return "Luke, I am your father";
   }
   public void printInfo() {

     System.out.println(getName());
   }
}

3. Danh sách

Nếu bạn chưa gặp các danh sách (List), đây là tổng quan ngắn gọn. Bạn có thể tìm thấy thông tin đầy đủ ở Cấp độ 6-7 của khóa học CodeGym . Danh sách có nhiều điểm chung với mảng:
  • bạn có thể lưu trữ nhiều dữ liệu thuộc một loại cụ thể;
  • họ cho phép bạn lấy các mặt hàng theo chỉ mục của họ;
  • chỉ số phần tử bắt đầu từ 0.
Lợi ích của danh sách: Không giống như mảng, danh sách có thể thay đổi kích thước một cách linh hoạt. Khi một danh sách được tạo, kích thước của nó là 0. Khi bạn thêm các mục vào danh sách, kích thước của nó sẽ tăng lên. Đây là một ví dụ về việc tạo một danh sách:

ArrayList<String> myList = new ArrayList<String>(); // Create a new ArrayList
Giá trị trong dấu ngoặc nhọn cho biết kiểu dữ liệu mà danh sách có thể lưu trữ. Dưới đây là một số phương pháp để làm việc với danh sách:
Mã số Mô tả ngắn về chức năng của mã
ArrayList<String> list = new ArrayList<String>(); Tạo danh sách chuỗi mới
list.add("name"); Thêm một phần tử vào cuối danh sách
list.add(0, "name"); Thêm phần tử vào đầu danh sách
String name = list.get(5); Lấy một phần tử theo chỉ mục của nó
list.set(5, "new name"); Thay đổi một phần tử theo chỉ mục của nó
int count = list.size(); Lấy số phần tử trong danh sách
list.remove(4); Xóa một phần tử khỏi danh sách
Bạn có thể tìm hiểu thêm về danh sách từ các bài viết sau:
  1. lớp ArrayList
  2. ArrayList trong ảnh
  3. Xóa một phần tử khỏi ArrayList

4. Mảng

một ma trận là gì? Ma trận không gì khác hơn là một bảng hình chữ nhật có thể chứa đầy dữ liệu. Nói cách khác, đó là một mảng hai chiều. Như bạn đã biết, mảng trong Java là các đối tượng. Mảng một chiều tiêu chuẩn inttrông như thế này:

int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
Chúng ta có thể hình dung nó như thế này:
0 1 2 3 4 5 6 7
12 32 43 54 15 36 67 28
Hàng trên cùng cho biết địa chỉ của các ô. Nói cách khác, để lấy số 67, bạn cần truy cập phần tử mảng có chỉ số 6:

int number = array[6];
Tất cả đều rất đơn giản. Mảng hai chiều là mảng của mảng một chiều. Nếu bạn nghe về điều này lần đầu tiên, hãy dừng lại và hình dung nó trong đầu. Một mảng hai chiều trông như thế này:
0 mảng một chiều mảng một chiều
1 mảng một chiều
2 mảng một chiều
3 mảng một chiều
4 mảng một chiều
5 mảng một chiều
6 mảng một chiều
7 mảng một chiều
Trong mã:

int [][] matrix = {
{65, 99, 87, 90, 156, 75, 98, 78}, {76, 15, 76, 91, 66, 90, 15, 77}, {65, 96, 17, 25, 36, 75, 54, 78}, {59, 45, 68, 14, 57, 1, 9, 63}, {81, 74, 47, 52, 42, 785, 56, 96}, {66, 74, 58, 16, 98, 140, 55, 77}, {120, 99, 13, 90, 78, 98, 14, 78}, {20, 18, 74, 91, 96, 104, 105, 77} }
0 0 1 2 3 4 5 6 7
65 99 87 90 156 75 98 78
1 0 1 2 3 4 5 6 7
76 15 76 91 66 90 15 77
2 0 1 2 3 4 5 6 7
65 96 17 25 36 75 54 78
3 0 1 2 3 4 5 6 7
59 45 68 14 57 1 9 63
4 0 1 2 3 4 5 6 7
81 74 47 52 42 785 56 96
5 0 1 2 3 4 5 6 7
66 74 58 16 98 140 55 77
6 0 1 2 3 4 5 6 7
120 99 13 90 78 98 14 78
7 0 1 2 3 4 5 6 7
20 18 74 91 96 104 105 77
Để lấy giá trị 47, bạn cần tham khảo phần tử ma trận tại [4][2].

int number = matrix[4][2];
Bạn có thể nhận thấy rằng tọa độ ma trận khác với hệ tọa độ hình chữ nhật cổ điển (hệ tọa độ Descartes). Khi bạn truy cập ma trận, trước tiên bạn chỉ định tọa độ y và sau đó là tọa độ x. Trong toán học, theo thông lệ, tọa độ x được chỉ định trước, tức là (x, y). Bạn có thể tự hỏi: "Chà, tại sao không xoay biểu diễn ma trận của bạn và sau đó truy cập các phần tử theo cách thông thường bằng cách sử dụng (x, y)? Làm điều này sẽ không thay đổi nội dung của ma trận". Vâng, sẽ không có gì thay đổi. Nhưng trong thế giới lập trình, thông lệ được chấp nhận là truy cập ma trận "đầu tiên theo y, sau đó theo x". Bạn nên chấp nhận điều này như một cách thích hợp. Bây giờ hãy nói về việc chiếu ma trận vào công cụ của chúng ta (Gamelớp học). Như bạn đã biết, động cơ có nhiều phương pháp thay đổi các ô của sân chơi ở các tọa độ cụ thể. Ví dụ, setCellValue(int x, int y, String value)phương pháp. Nó đặt một ô cụ thể có tọa độ (x, y) bằng tham số giá trị. Bạn có thể nhận thấy rằng phương pháp này lấy x trước, giống như trong hệ tọa độ cổ điển. Các phương pháp khác của động cơ hoạt động theo cách tương tự. Khi phát triển trò chơi, thường sẽ cần phải tái tạo trạng thái của ma trận trên màn hình. làm sao chúng ta làm việc đó bây giờ? Trước tiên, bạn cần lặp qua tất cả các phần tử của ma trận trong một vòng lặp. Thứ hai, gọi phương thức hiển thị cho từng người trong số họ, sử dụng tọa độ REVERSED. Ví dụ:

private void drawScene() {
    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[i].length; j++) {
            setCellValue(j, i, String.valueOf(matrix[i][j]));
        }
    }
}
Đương nhiên, sự đảo ngược hoạt động theo cả hai hướng. Bạn có thể chuyển (i, j) cho setCellValuephương thức và đồng thời lấy phần tử [j][i] từ ma trận. Việc đảo ngược tọa độ nghe có vẻ hơi khó nhưng bạn cần nhớ nó. Và luôn luôn, nếu bạn gặp bất kỳ vấn đề nào, bạn nên lấy một tờ giấy và một cây bút, vẽ ma trận và tái tạo các quy trình liên quan đến ma trận.

5. Số ngẫu nhiên

Làm thế nào để bạn làm việc với một trình tạo số ngẫu nhiên? Lớp Gameđịnh nghĩa getRandomNumber(int)phương thức. Về cơ bản, nó sử dụng Randomlớp từ gói java.util, nhưng cách bạn làm việc với trình tạo số ngẫu nhiên không thay đổi. getRandomNumber(int)lấy một số nguyên làm đối số. Con số này sẽ là giới hạn trên đối với những gì trình tạo có thể trả về. Giới hạn dưới là 0. Quan trọng! Trình tạo sẽ KHÔNG BAO GIỜ trả về số giới hạn trên. Ví dụ: nếu bạn gọi getRandomNumber(3), nó sẽ ngẫu nhiên trả về 0, 1 hoặc 2. Như bạn thấy, nó không thể trả về 3. Sử dụng trình tạo theo cách này khá đơn giản nhưng hiệu quả cao trong nhiều trường hợp. Giả sử bạn cần lấy một số ngẫu nhiên trong một số phạm vi: Hãy tưởng tượng bạn cần một số có ba chữ số trong phạm vi [100..999]. Như bạn đã biết, số tối thiểu được trả về là 0. Vì vậy, bạn sẽ cần thêm 100. Nhưng trong trường hợp này, bạn cần chú ý để không vượt quá giới hạn trên. Để lấy 999 làm giá trị ngẫu nhiên tối đa, hãy gọigetRandomNumber(int)phương thức với đối số 1000. Nhưng bây giờ chúng ta nhớ rằng chúng ta đang thêm 100 vào kết quả: điều này có nghĩa là giới hạn trên phải giảm đi 100. Nói cách khác, mã để lấy số có ba chữ số ngẫu nhiên của chúng ta sẽ như thế này :

int number = 100 + getRandomNumber(900);
Nhưng để đơn giản hóa quy trình này, công cụ cung cấp getRandomNumber(int, int)phương thức có tham số đầu tiên là số tối thiểu để trả về. Sử dụng phương pháp này, ví dụ trước có thể được viết lại như sau:

int number = getRandomNumber(100, 1000);
Các số ngẫu nhiên có thể được sử dụng để lấy một phần tử mảng ngẫu nhiên:

String [] names = {"Sarah", "Val", "Sergey"};
String randomName = names[getRandomNumber(names.length)]
Tạo ra các sự kiện nhất định với một số xác suất. Đối với con người, buổi sáng bắt đầu với một số tình huống có thể xảy ra: Ngủ quên – 50% cơ hội; Thức dậy đúng giờ – 40% cơ hội; Dậy sớm một tiếng – 10% cơ hội. Hãy tưởng tượng rằng bạn đang viết một trình tạo kết quả buổi sáng. Bạn cần tạo các sự kiện với một xác suất nhất định. Để làm điều này, một lần nữa bạn cần sử dụng trình tạo số ngẫu nhiên. Có thể triển khai khác nhau, nhưng cách đơn giản nhất nên dựa trên thuật toán sau:
  1. đặt giới hạn được sử dụng để tạo số;
  2. tạo một số ngẫu nhiên;
  3. xử lý số thu được.
Trong trường hợp này, tối đa sẽ là 10. GọigetRandomNumber(10)phương pháp và phân tích rằng chúng ta có thể trả lại. Nó có thể trả về 10 số (từ 0 đến 9), mỗi số có cùng xác suất — 10%. Bây giờ chúng ta cần kết hợp tất cả các kết quả có thể và ánh xạ chúng tới các sự kiện có thể xảy ra. Trí tưởng tượng của bạn có thể nghĩ ra rất nhiều cách kết hợp khả thi, nhưng đây là cách kết hợp rõ ràng nhất: "Nếu số ngẫu nhiên nằm trong khoảng [0..4], chúng ta có sự kiện "Ngủ quên"; nếu số đó nằm trong khoảng [5 0 ..4], mỗi trong số đó có thể được trả về với xác suất 10%, tổng cộng là 50%; có 4 số trong phạm vi [5..8], và 9 chỉ là một số xuất hiện cùng với xác suất là 10%.

int randomNumber = getRandomNumber(10);
if (randomNumber < 5) {
    System.out.println("Overslept");
} else if (randomNumber < 9) {
    System.out.println("Woke up on time");
} else {
    System.out.println("Woke up an hour early");
}
Nói chung, có rất nhiều cách để sử dụng số ngẫu nhiên. Bạn chỉ bị giới hạn bởi trí tưởng tượng của mình. Nhưng chúng được sử dụng hiệu quả nhất nếu bạn cần liên tục đạt được một số kết quả. Sau đó, kết quả mới sẽ khác với kết quả trước đó. Với một số xác suất, tất nhiên. Đó là tất cả cho bây giờ! Nếu bạn muốn tìm hiểu thêm về phần "Trò chơi", đây là một số tài liệu hữu ích có thể giúp:
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION