ใน
ส่วน "เกม"ของ JavaRush คุณจะพบกับโปรเจ็กต์ที่น่าตื่นเต้นสำหรับการเขียนเกมคอมพิวเตอร์ยอดนิยม คุณต้องการสร้างเกมยอดนิยม "2048", "Sapper", "Snake" และเกมอื่น ๆ ในเวอร์ชันของคุณเองหรือไม่? มันง่ายมาก เราได้เปลี่ยนการเขียนเกมให้เป็นกระบวนการทีละขั้นตอน
หากต้องการลองตัวเองในฐานะนักพัฒนาเกม คุณไม่จำเป็นต้องเป็นโปรแกรมเมอร์ขั้นสูง แต่ยังจำเป็นต้องมีความรู้ Java บางชุด ที่นี่คุณจะพบ
ข้อมูลที่เป็นประโยชน์ในการเขียนเกม
1. มรดก
การทำงานกับเอ็นจิ้นเกม JavaRush เกี่ยวข้องกับการใช้การสืบทอด แต่ถ้าคุณไม่รู้ว่ามันคืออะไร? ในอีกด้านหนึ่ง คุณต้องเข้าใจหัวข้อนี้: มีการศึกษาที่
ระดับ 11 . ในทางกลับกัน เครื่องยนต์ได้รับการออกแบบอย่างจงใจให้เรียบง่ายมาก ดังนั้นคุณจึงสามารถเรียนรู้เกี่ยวกับมรดกได้อย่างผิวเผิน แล้วมรดกคืออะไร? พูดง่ายๆ ก็คือ มรดกคือความสัมพันธ์ระหว่างสองคลาส หนึ่งในนั้นกลายเป็นพ่อแม่ และคนที่สองกลายเป็นลูก (ชั้นสืบทอด) ในกรณีนี้ คลาสแม่อาจไม่รู้ด้วยซ้ำว่ามีคลาสที่สืบทอดมา เหล่านั้น. มันไม่ได้รับผลประโยชน์ใด ๆ เป็นพิเศษจากการมีคลาสผู้สืบทอด แต่การสืบทอดนั้นให้ข้อดีหลายประการแก่คลาสผู้สืบทอด และสิ่งสำคัญคือตัวแปรและวิธีการทั้งหมดของคลาสพาเรนต์จะปรากฏในคลาสลูก ราวกับว่าโค้ดของคลาสพาเรนต์ถูกคัดลอกไปยังคลาสลูก สิ่งนี้ไม่เป็นความจริงทั้งหมด แต่เพื่อให้เข้าใจถึงมรดกได้ง่ายขึ้น ต่อไปนี้เป็นตัวอย่างบางส่วนเพื่อให้เข้าใจถึงมรดกได้ดียิ่งขึ้น
ตัวอย่างที่ 1:มรดกที่ง่ายที่สุด
public class Родитель {
}
|
คลาสChildสืบทอดมาจากคลาสParentโดยใช้ คีย์เวิร์ด ขยาย |
public class Потомок extends Родитель {
}
|
ตัวอย่างที่ 2:การใช้ตัวแปรคลาสพาเรนต์
public class Родитель {
public int age;
public String name;
}
|
คลาสChildสามารถใช้ ตัวแปร อายุและชื่อ ของ คลาสParentเหมือนกับว่ามีการประกาศไว้ |
public class Потомок extends Родитель {
public void printInfo() {
System.out.println(name+" "+age);
}
}
|
ตัวอย่างที่ 3:การใช้เมธอดคลาสพาเรนต์
public class Родитель {
public int age;
public String name;
public getName() {
return name;
}
}
|
คลาสChildสามารถใช้ตัวแปรและวิธีการของ คลาส Parentเหมือนกับว่ามีการประกาศไว้ ในตัวอย่างนี้ เราใช้getName () วิธีการ |
public class Потомок extends Родитель {
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
|
นี่คือลักษณะของ คลาส
Descendantจากมุมมองของคอมไพเลอร์:
public class Потомок extends Родитель {
public int age;
public String name;
public getName() {
return name;
}
public void printInfo() {
System.out.println(getName()+" "+age);
}
}
2. วิธีการเอาชนะ
บางครั้งมีสถานการณ์ที่เราสืบทอดคลาส Descendant จากคลาส Parent ที่มีประโยชน์มาก พร้อมด้วยตัวแปรและวิธีการทั้งหมด แต่วิธีการบางอย่างไม่ทำงานอย่างที่เราต้องการ หรือไม่เป็นแบบที่เราไม่ต้องการเลย สิ่งที่ต้องทำในสถานการณ์เช่นนี้? เราสามารถแทนที่วิธีการที่เราไม่ชอบได้ สิ่งนี้ทำได้ง่ายมาก: ในคลาส Descendant ของเรา เราเพียงแค่ประกาศวิธีการที่มีลายเซ็น (ส่วนหัว) เหมือนกับวิธีการของคลาส Parent และเขียนโค้ดของเราลงไป
ตัวอย่างที่ 1:การเอาชนะวิธีการ
public class Родитель {
public String name;
public void setName (String nameNew) {
name = nameNew;
}
public getName() {
return name;
}
}
|
เมธอด printInfo() จะพิมพ์วลี"Luke, No!!!" |
public class Потомок extends Родитель {
public void setName (String nameNew) {
name = nameNew + ",No!!!";
}
public void printInfo() {
setName("Luke");
System.out.println( getName());
}
}
|
นี่คือลักษณะของ คลาส
Descendantจากมุมมองของคอมไพเลอร์:
public Потомок extends Родитель {
public String name;
public void setName (String nameNew) {
name = nameNew + ", No!!!";
}
public getName() {
return name;
}
public void printInfo() {
setName("Luke");
System.out.println(getName());
}
}
ตัวอย่างที่ 2:ความมหัศจรรย์เล็กน้อยของการสืบทอด (และการเอาชนะวิธีการ)
public class Родитель {
public getName() {
return "Luke";
}
public void printInfo() {
System.out.println(getName());
}
}
|
public class Потомок extends Родитель {
public getName() {
return "I'm your father, Luke";
}
}
|
ในตัวอย่างนี้: ถ้าเมธอด
printInfo
(จากคลาส Parent) ไม่ได้ถูกแทนที่ในคลาส Descendant เมื่อเมธอดนี้ถูกเรียกบนอ็อบเจ็กต์ของคลาส Descendant เมธอดของมันจะถูกเรียก
getName()
ว่า ไม่ใช่
getName()
คลาส Parent
Родитель parent = new Родитель ();
parent.printnInfo();
|
รหัสนี้แสดงข้อความ"ลุค"บน หน้าจอ |
Потомок child = new Потомок ();
child.printnInfo();
|
รหัสนี้แสดงข้อความว่า"ฉันเป็นพ่อของคุณ ลุค" . |
นี่คือลักษณะของ คลาส
Descendantจากมุมมองของคอมไพเลอร์:
public class Потомок extends Родитель {
public getName() {
return "I'm your father, Luke";
}
public void printInfo() {
System.out.println(getName());
}
}
3. รายการ
หากคุณยังไม่เคยพบกับ Lists นี่เป็นข้อมูลเบื้องต้นสั้นๆ คุณสามารถค้นหาข้อมูลที่สมบูรณ์เกี่ยวกับ ระดับ 6-7
ของหลักสูตร JavaRush รายการมีอะไรหลายอย่างเหมือนกันกับอาร์เรย์:
- สามารถจัดเก็บข้อมูลบางประเภทได้จำนวนมาก
- อนุญาตให้คุณดึงข้อมูลองค์ประกอบตามดัชนี/หมายเลข
- ดัชนีองค์ประกอบเริ่มต้นที่ 0
ข้อดีของรายการ: รายการสามารถเปลี่ยนขนาดแบบไดนามิกได้ ซึ่งต่างจากอาร์เรย์ตรง ทันทีหลังจากการสร้าง รายการจะมีขนาด 0 เมื่อคุณเพิ่มองค์ประกอบลงในรายการ ขนาดของมันจะเพิ่มขึ้น ตัวอย่างการสร้างรายการ:
ArrayList<String> myList = new ArrayList<String>();
ค่าในวงเล็บมุมคือประเภทของข้อมูลที่รายการสามารถจัดเก็บได้ ต่อไปนี้เป็นวิธีการบางอย่างในการทำงานกับรายการ:
รหัส |
คำอธิบายสั้น ๆ ว่าโค้ดทำอะไร |
ArrayList<String> list = new ArrayList<String>(); |
การสร้างรายการสตริงใหม่ |
list.add("name"); |
เพิ่มองค์ประกอบที่ส่วนท้ายของรายการ |
list.add(0, "name"); |
เพิ่มองค์ประกอบที่จุดเริ่มต้นของรายการ |
String name = list.get(5); |
รับองค์ประกอบตามดัชนีของมัน |
list.set(5, "new name"); |
เปลี่ยนองค์ประกอบตามดัชนี |
int count = list.size(); |
รับจำนวนองค์ประกอบในรายการ |
list.remove(4); |
ลบรายการออกจากรายการ |
คุณสามารถเรียนรู้เพิ่มเติมเกี่ยวกับรายการจากบทความเหล่านี้:
- คลาส ArrayList
- การทำงานของ ArrayList ในรูปภาพ
- การลบองค์ประกอบออกจาก ArrayList
4. อาร์เรย์
เมทริกซ์คืออะไร? เมทริกซ์ไม่มีอะไรมากไปกว่าตารางสี่เหลี่ยมที่สามารถเติมข้อมูลได้ กล่าวอีกนัยหนึ่ง มันคืออาร์เรย์สองมิติ ดังที่คุณคงทราบแล้วว่าอาร์เรย์ใน Java เป็นวัตถุ ประเภทอาร์เรย์หนึ่งมิติมาตรฐาน
int
มีลักษณะดังนี้:
int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
ลองจินตนาการสิ่งนี้ด้วยสายตา:
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
32 |
43 |
54 |
15 |
36 |
67 |
28 |
บรรทัดบนสุดระบุที่อยู่ของเซลล์ นั่นคือเพื่อให้ได้หมายเลข 67 คุณต้องเข้าถึงองค์ประกอบอาร์เรย์ที่มีดัชนี 6:
int number = array[6];
ทุกอย่างง่ายมากที่นี่ อาร์เรย์สองมิติคืออาร์เรย์ของอาร์เรย์หนึ่งมิติ หากนี่เป็นครั้งแรกที่คุณได้ยินเรื่องนี้ ให้หยุดและนึกภาพมันในหัวของคุณ อาร์เรย์สองมิติมีลักษณะดังนี้:
0 |
อาร์เรย์หนึ่งมิติ |
อาร์เรย์หนึ่งมิติ |
1 |
อาร์เรย์หนึ่งมิติ |
2 |
อาร์เรย์หนึ่งมิติ |
3 |
อาร์เรย์หนึ่งมิติ |
4 |
อาร์เรย์หนึ่งมิติ |
5 |
อาร์เรย์หนึ่งมิติ |
6 |
อาร์เรย์หนึ่งมิติ |
7 |
อาร์เรย์หนึ่งมิติ |
ในรหัส:
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 |
หากต้องการรับค่า 47 คุณต้องเข้าถึงองค์ประกอบเมทริกซ์ที่ [4] [2]
int number = matrix[4][2];
หากคุณสังเกตเห็นว่าพิกัดเมทริกซ์แตกต่างจากระบบพิกัดสี่เหลี่ยมแบบคลาสสิก (ระบบพิกัดคาร์ทีเซียน)
เมื่อเข้าถึงเมทริกซ์ คุณจะต้องระบุ y ก่อนแล้วตามด้วย xในขณะที่ในทางคณิตศาสตร์ เป็นเรื่องปกติที่จะระบุ x(x, y) ก่อน คุณอาจจะถามตัวเองว่า “ทำไมไม่ลองกลับเมทริกซ์ในจินตนาการของคุณและเข้าถึงองค์ประกอบต่างๆ ด้วยวิธีปกติผ่าน (x, y)? สิ่งนี้จะไม่เปลี่ยนเนื้อหาของเมทริกซ์” ใช่จะไม่มีอะไรเปลี่ยนแปลง แต่ในโลกของการเขียนโปรแกรม เป็นเรื่องปกติที่จะอ้างถึงเมทริกซ์ในรูปแบบ “y ก่อน แล้ว x” สิ่งนี้จะต้องได้รับการพิจารณา ตอนนี้เรามาพูดถึงการฉายเมทริกซ์ลงบนเอ็นจิ้นของเรา (คลาส
Game
) ดังที่คุณทราบเครื่องยนต์มีหลายวิธีที่จะเปลี่ยนเซลล์ของสนามแข่งขันตามพิกัดที่กำหนด ตัวอย่างเช่น
setCellValue(int x, int y, String value)
. มันตั้งค่าเซลล์บางเซลล์ด้วยพิกัด (x, y) เป็น
value
ค่า ดังที่คุณสังเกตเห็นว่า วิธีนี้จะต้องใช้ x ก่อน เช่นเดียวกับในระบบพิกัดแบบคลาสสิก วิธีเครื่องยนต์ที่เหลือทำงานในลักษณะเดียวกัน เมื่อพัฒนาเกม มักจะจำเป็นต้องสร้างสถานะของเมทริกซ์บนหน้าจอขึ้นมาใหม่ วิธีการทำเช่นนี้? ขั้นแรก ในการวนซ้ำ คุณต้องวนซ้ำองค์ประกอบทั้งหมดของเมทริกซ์ ประการที่สอง สำหรับแต่ละวิธี ให้เรียกเมธอดเพื่อแสดงด้วยพิกัด INVERTED ตัวอย่าง:
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]));
}
}
}
โดยธรรมชาติแล้ว การผกผันทำงานในสองทิศทาง
setCellValue
คุณสามารถส่งผ่าน (i, j) ไปยังเมธอดได้ แต่ในขณะเดียวกันก็รับองค์ประกอบ [j][i] จากเมทริกซ์ การผกผันอาจดูยากเล็กน้อย แต่เป็นสิ่งที่ต้องคำนึงถึง และหากเกิดปัญหาใด ๆ เกิดขึ้นก็คุ้มค่าที่จะเอากระดาษแผ่นหนึ่งมาด้วยปากกา วาดเมทริกซ์ และทำซ้ำกระบวนการที่กำลังเกิดขึ้น
5. ตัวเลขสุ่ม
วิธีการทำงานกับเครื่องกำเนิดตัวเลขสุ่ม? ชั้นเรียน
Game
กำหนดวิธี
getRandomNumber(int)
การ ภายใต้ประทุนนั้นจะใช้คลาส
Random
จากแพ็คเกจ java.util แต่ไม่ได้เปลี่ยนหลักการทำงานกับตัวสร้างตัวเลขสุ่ม
getRandomNumber(int)
รับจำนวนเต็มเป็นอาร์กิวเมนต์ หมายเลขนี้จะเป็นขอบเขตบนที่ตัวสร้างสามารถส่งคืนได้ ขีดจำกัดล่างคือ 0
สำคัญ! ตัวสร้างจะไม่ส่งคืนหมายเลขขอบเขตบน ตัวอย่างเช่น หากเรียก
getRandomNumber(3)
แบบสุ่มก็สามารถส่งคืน 0, 1, 2 ได้ อย่างที่คุณเห็น มันไม่สามารถส่งคืน 3 ได้ การใช้เครื่องกำเนิดไฟฟ้านี้ค่อนข้างง่าย แต่มีประสิทธิภาพมากในหลายกรณี
คุณต้องได้ตัวเลขสุ่มภายในขีดจำกัด: ลองนึกภาพว่าคุณต้องการตัวเลขสามหลัก (100..999) ดังที่คุณทราบแล้วว่าจำนวนขั้นต่ำที่ส่งคืนคือ 0 ดังนั้นคุณจะต้องบวก 100 แต่ในกรณีนี้ คุณต้องระวังไม่ให้เกินขีดจำกัดบน หากต้องการรับ 999 เป็นค่าสุ่มสูงสุด คุณควรเรียกเมธอด
getRandomNumber(int)
ด้วยอาร์กิวเมนต์ 1,000 แต่เราจำได้เกี่ยวกับการบวก 100 ในภายหลัง: นี่หมายความว่าขอบเขตบนควรลดลง 100 นั่นคือโค้ดที่จะได้รับ ตัวเลขสามหลักแบบสุ่มจะมีลักษณะดังนี้:
int number = 100 + getRandomNumber(900);
แต่เพื่อทำให้ขั้นตอนดังกล่าวง่ายขึ้น กลไกจัดการจึงมีวิธีการ
getRandomNumber(int, int)
ที่รับจำนวนขั้นต่ำเพื่อส่งกลับเป็นอาร์กิวเมนต์แรก เมื่อใช้วิธีนี้ ตัวอย่างก่อนหน้านี้สามารถเขียนใหม่ได้:
int number = getRandomNumber(100, 1000);
ตัวเลขสุ่มสามารถใช้เพื่อรับองค์ประกอบอาร์เรย์สุ่ม:
String [] names = {"Andrey", "Валентин", "Сергей"};
String randomName = names[getRandomNumber(names.length)]
ทำให้เกิดเหตุการณ์บางอย่างด้วยความน่าจะเป็นที่แน่นอน เช้าของบุคคลจะเริ่มต้นตามสถานการณ์ที่เป็นไปได้: นอนหลับมากเกินไป – 50%; ตื่นตรงเวลา – 40%; ตื่นเร็วกว่าที่คาดหนึ่งชั่วโมง – 10% ลองนึกภาพว่าคุณกำลังเขียนโปรแกรมจำลองตอนเช้าของมนุษย์ คุณต้องทริกเกอร์เหตุการณ์ด้วยความน่าจะเป็นที่แน่นอน หากต้องการทำเช่นนี้ คุณต้องใช้เครื่องสร้างตัวเลขสุ่มอีกครั้ง การใช้งานอาจแตกต่างกัน แต่วิธีที่ง่ายที่สุดควรเป็นไปตามอัลกอริทึมต่อไปนี้:
- เรากำหนดขีดจำกัดภายในที่ต้องสร้างจำนวน
- สร้างตัวเลขสุ่ม
- เราประมวลผลหมายเลขผลลัพธ์
ดังนั้นในกรณีนี้ ขีดจำกัดจะเป็น 10 ลองเรียกเมธอด
getRandomNumber(10)
แล้ววิเคราะห์ว่ามันจะส่งคืนอะไรให้เราได้บ้าง สามารถส่งคืนตัวเลข 10 หลัก (ตั้งแต่ 0 ถึง 9) และแต่ละตัวมีความน่าจะเป็นเท่ากัน - 10% ตอนนี้เราจำเป็นต้องรวมผลลัพธ์ที่เป็นไปได้ทั้งหมดและจับคู่กับเหตุการณ์ที่เป็นไปได้ของเรา สามารถมีชุดค่าผสมได้มากมายขึ้นอยู่กับจินตนาการของคุณ แต่เสียงที่ชัดเจนที่สุด: “ หากตัวเลขสุ่มอยู่ภายใน [0..4] - เรียกเหตุการณ์ว่า “Overslept” หากตัวเลขอยู่ภายใน [5..8 ] - “ตื่น” ตรงเวลา” และเฉพาะในกรณีที่เลข 9 แสดงว่า “ฉันตื่นเร็วกว่าที่คาดไว้หนึ่งชั่วโมง” ทุกอย่างง่ายมาก: ภายใน [0..4] มี 5 หมายเลข ซึ่งแต่ละหมายเลขสามารถส่งคืนได้โดยมีความน่าจะเป็น 10% ซึ่งทั้งหมดจะเป็น 50% ภายใน [5..8] มีตัวเลข 4 ตัว และ 9 เป็นตัวเลขเดียวที่ปรากฏด้วยความน่าจะเป็น 10% ในโค้ด การออกแบบที่ชาญฉลาดทั้งหมดนี้ดูเรียบง่ายยิ่งขึ้น:
int randomNumber = getRandomNumber(10);
if (randomNumber < 5) {
System.out.println("Проспал ");
} else if (randomNumber < 9) {
System.out.println("Встал вовремя ");
} else {
System.out.println("Встал на час раньше положенного ");
}
โดยทั่วไปอาจมีตัวเลือกมากมายสำหรับการใช้ตัวเลขสุ่ม ทุกอย่างขึ้นอยู่กับจินตนาการของคุณเท่านั้น แต่จะใช้อย่างมีประสิทธิภาพมากที่สุดหากคุณต้องการได้รับผลลัพธ์ซ้ำ ๆ ผลลัพธ์นี้จะแตกต่างจากครั้งก่อน แน่นอนว่ามีความน่าจะเป็นอยู่บ้าง นั่นคือทั้งหมด! หากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับส่วนเกม นี่คือเอกสารที่เป็นประโยชน์บางส่วนที่สามารถช่วยได้:
GO TO FULL VERSION