JavaRush /Java Blog /Random-KO /JavaRush의 "게임" 섹션: 유용한 이론

JavaRush의 "게임" 섹션: 유용한 이론

Random-KO 그룹에 게시되었습니다
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 클래스 의 agename 변수를 마치 선언된 것처럼 사용할 수 있습니다 .
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. 메소드 재정의

때로는 매우 유용한 Parent 클래스로부터 Descendant 클래스와 모든 변수 및 메서드를 상속받았지만 일부 메서드는 우리가 원하는 방식으로 정확히 작동하지 않는 상황이 있습니다. 아니면 우리가 원하지 않는 방식으로 전혀 그렇지 않습니다. 이런 상황에서는 어떻게 해야 할까요? 마음에 들지 않는 메서드를 재정의할 수 있습니다. 이는 매우 간단하게 수행됩니다. 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";
  }
}
이 예에서는 Parent 클래스의 메서드가 printInfoDescendant 클래스에서 재정의되지 않은 경우 이 메서드가 Descendant 클래스의 개체에서 호출되면 해당 메서드가 호출되고 Parent 클래스가 호출 getName()되지 않습니다 .getName()
Родитель parent = new Родитель ();
parent.printnInfo();
이 코드는 화면에 "Luke" 라는 문구를 표시합니다 .
Потомок child = new Потомок ();
child.printnInfo();
이 코드는 "나는 당신의 아버지입니다, Luke"라는 문구를 표시합니다. .
컴파일러의 관점에서 보면 Descendant 클래스 는 다음과 같습니다.
public class Потомок extends Родитель {

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

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

3. 목록

아직 목록을 소개하지 않았다면 여기에 간단한 입문서가 있습니다. JavaRush 과정의 레벨 6-7 에 대한 전체 정보를 찾을 수 있습니다 . 목록은 배열과 공통점이 많습니다.
  • 특정 유형의 많은 데이터를 저장할 수 있습니다.
  • 색인/번호로 요소를 검색할 수 있습니다.
  • 요소 인덱스는 0부터 시작합니다.
목록의 장점: 배열과 달리 목록은 크기를 동적으로 변경할 수 있습니다. 생성 직후 목록의 크기는 0입니다. 목록에 요소를 추가하면 크기가 늘어납니다. 목록 생성 예:
ArrayList<String> myList = new ArrayList<String>(); // создание нового списка типа ArrayList
꺾쇠 괄호 안의 값은 목록이 저장할 수 있는 데이터 유형입니다. 목록 작업을 위한 몇 가지 방법은 다음과 같습니다.
암호 코드가 수행하는 작업에 대한 간략한 설명
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); 목록에서 항목 제거
다음 문서에서 목록에 대해 자세히 알아볼 수 있습니다.
  1. ArrayList 클래스
  2. 그림의 작업 ArrayList
  3. ArrayList에서 요소 제거

4. 배열

매트릭스란 무엇입니까? 행렬은 데이터를 채울 수 있는 직사각형 테이블에 지나지 않습니다. 즉, 2차원 배열입니다. 아시다시피 Java의 배열은 객체입니다. 표준 1차원 배열 유형은 int다음과 같습니다.
int [] array = {12, 32, 43, 54, 15, 36, 67, 28};
이것을 시각적으로 상상해 봅시다:
0 1 2 4 5 6 7
12 32 43 54 15 36 67 28
맨 윗줄은 셀 주소를 나타냅니다. 즉, 숫자 67을 얻으려면 인덱스 6이 있는 배열 요소에 액세스해야 합니다.
int number = array[6];
여기에서는 모든 것이 매우 간단합니다. 2차원 배열은 1차원 배열의 배열입니다. 이 말을 처음 듣는다면 잠시 멈추고 머릿속에 그려보세요. 2차원 배열은 다음과 같습니다.
0 1차원 배열 1차원 배열
1 1차원 배열
2 1차원 배열
1차원 배열
4 1차원 배열
5 1차원 배열
6 1차원 배열
7 1차원 배열
코드에서:
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 4 5 6 7
65 99 87 90 156 75 98 78
1 0 1 2 4 5 6 7
76 15 76 91 66 90 15 77
2 0 1 2 4 5 6 7
65 96 17 25 36 75 54 78
0 1 2 4 5 6 7
59 45 68 14 57 1 9 63
4 0 1 2 4 5 6 7
81 74 47 52 42 785 56 96
5 0 1 2 4 5 6 7
66 74 58 16 98 140 55 77
6 0 1 2 4 5 6 7
120 99 13 90 78 98 14 78
7 0 1 2 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를 사용합니다. 나머지 엔진 방법도 비슷한 방식으로 작동합니다. 게임을 개발할 때 매트릭스의 상태를 화면에 재현해야 하는 경우가 종종 있습니다. 어떻게 해야 하나요? 먼저, 루프에서 행렬의 모든 요소를 ​​반복해야 합니다. 둘째, 각각에 대해 반전된 좌표로 표시하는 메서드를 호출합니다. 예:
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]));
        }
    }
}
당연히 반전은 두 방향으로 작동합니다. (i, j)를 메소드에 setCellValue전달할 수 있지만 동시에 행렬에서 [j][i] 요소를 가져올 수 있습니다. 반전이 조금 어려워 보일 수도 있지만 명심해야 할 사항입니다. 그리고 항상 문제가 발생하면 펜으로 종이를 가져다가 매트릭스를 그리고 어떤 프로세스가 진행되고 있는지 재현하는 것이 좋습니다.

5. 난수

난수 생성기를 사용하는 방법은 무엇입니까? 클래스는 Game메소드를 정의합니다 getRandomNumber(int). 내부적으로는 Randomjava.util 패키지의 클래스를 사용하지만 이것이 난수 생성기 작업 원칙을 바꾸지는 않습니다. 인수로 getRandomNumber(int)정수를 사용합니다 . 이 숫자는 생성기가 반환할 수 있는 상한이 됩니다. 하한은 0이다. 중요한! 생성기는 절대 상한 숫자를 반환하지 않습니다. 예를 들어 getRandomNumber(3)무작위로 호출되면 0, 1, 2를 반환할 수 있습니다. 보시다시피 3을 반환할 수 없습니다. 이러한 발전기 사용은 매우 간단하지만 많은 경우에 매우 효과적입니다. 일부 제한 내에서 임의의 숫자를 얻어야 합니다. 세 자리 숫자(100..999)가 필요하다고 상상해 보십시오. 이미 알고 있듯이 반환되는 최소 개수는 0이므로 100을 더해야 하는데, 이 경우 상한을 초과하지 않도록 주의해야 합니다. 최대 난수 값으로 999를 얻으려면 인수 getRandomNumber(int)1000을 사용하여 메서드를 호출해야 합니다. 그러나 이후에 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%. 당신이 인간의 아침 에뮬레이터를 작성하고 있다고 상상해 보십시오. 특정 확률로 이벤트를 발생시켜야 합니다. 이렇게 하려면 다시 난수 생성기를 사용해야 합니다. 구현은 다를 수 있지만 가장 간단한 것은 다음 알고리즘을 따라야 합니다.
  1. 숫자를 생성하는 데 필요한 제한을 설정합니다.
  2. 난수를 생성합니다.
  3. 결과 숫자를 처리합니다.
따라서 이 경우 제한은 10이 됩니다. 메서드를 호출 getRandomNumber(10)하고 반환할 수 있는 내용을 분석해 보겠습니다. 10자리(0~9)를 반환할 수 있으며 각 숫자는 동일한 확률(10%)을 갖습니다. 이제 가능한 모든 결과를 결합하고 이를 가능한 이벤트와 일치시켜야 합니다. 상상력에 따라 많은 조합이 있을 수 있지만 가장 분명한 소리는 다음과 같습니다. "임의의 숫자가 [0..4] 내에 있는 경우 - 숫자가 [5..8 내에 있으면 "Overslept" 이벤트를 호출합니다. ] - "제 시간에 일어나세요", 숫자가 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("Встал на час раньше положенного ");
}
일반적으로 난수를 사용하는 데는 다양한 옵션이 있을 수 있습니다. 그것은 모두 당신의 상상력에만 달려 있습니다. 그러나 어떤 결과를 반복적으로 얻어야 ​​하는 경우에는 가장 효과적으로 사용됩니다. 그러면 이 결과는 이전 결과와 달라질 것입니다. 물론 어느 정도의 확률로요. 그게 다야! 게임 섹션에 대해 자세히 알아보려면 다음과 같은 유용한 문서를 참고하세요.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION