토이 프로젝트/Spring&Java 갖고놀기

자바 리듬 게임 만들기 (2) : Game(feat. 노트찍기모드)

TerianP 2022. 3. 21.
728x90

 

2022.03.20 - [토이 프로젝트/따라하면서 배우기] - 자바 리듬 게임 제작 (1) : Main, DynamicMusic 클래스

 

자바 리듬 게임 제작 (1) : Main, DynamicMusic 클래스

이제 자바 공부도 한지 몇개월정도 지난것 같습니다. 이제부터는 기초 공부와 겸해서 내가 원하는 것, 내가 하고 싶은 것을 좀 더 만들어보기로 하였습니다(지금까지도 그랬지만...ㅋㅋ) 그래서

terianp.tistory.com

 

이번 글은 2번째 Game 클래스에 대한 부분입니다.

1. 구현 기능, 목표 및 Main, Dynamic 클래스

2. Game 클래스

3. Track, Music, Beat 클래스

4. Note,  KeyListener 클래스


1. Game 클래스 : 리듬 게임을 동작시키기위한 내용

1) Game 클래스 전역 변수 선언

  • Game 클래스는 Thread 를 상속받아서 사용한다. 이를 통해서 오버라이드된 run() 메서드에 내가 실행할 코드를 넣어두고, Dynamic Music 클래스에서 Game.start() 를 사용하면 Thread 로 Game 클래스가 실행되는 것이다.
  • Game 클래스의 전역변수는 Main 클래스와 전체적으로 비슷하다. 가장 중요한 Image 부분을 먼저 선언해두었고, 점수를 출력하기 위한 score 와 combo 를 static 으로 선언하였다. 이는 Dynamic Music 에서 게임이 시작될때 score 와 combo 를 초기화해서 시작하여야 하기 때문에 편하게 불러다쓰려고(...) static 으로 만들었다.
    • 사실 보다 정석적으로 하려면 score 와 combo 모두 private 으로 만들어고 getter 와 setter 를 이용해 설정하는게 좋다고 생각된다.
  • NoteMaker 부분이 가장 중요하다. 이 부분은 강의에서 나왔던 부분이 아니고 직접 짠 부분인데, 바로 음악에 맞춰 노트를 보다 쉽게 찍기 위해서 만들었다. 기존 강의에서는 직접 노트의 코드를 짜서 만들었는데 그 부분은 조금 어레인지하여 NoteMaker = true 인 경우 내가 찍은 노트와 그 시간이 readNote 디렉토리 안 titleName(곡명)_난이도.txt 로 저장된다. 이후 NoteMaker=false 해둔 후 게임을 실행하면 내가 찍은 노트에 따라서 BufferedReader 를 사용해서 한줄씩 읽어오게 된다(아래에 영상과 함께 설명하도록 하겠다)
// 스레드는 프로그램 안에 있는 작은 프로그램
public class Game extends Thread {
    // 1. 전반적인 처리 과정
    // Main 메소드를 통해 게임이 실행되면 지금 이 클래스 - game - 에서
    // 만들어진 인스턴스 변수를 이용해서 게임 컨트롤 가능하도록
    // 즉 게임 클래스의 인스턴스가 생성되어 실행되면 아래 run() 메소드가 실행됨

    // ingame 노트 경로별 경계선(라인)
    private Image noteRouteLine = new ImageIcon(getClass().getResource("/menu_images/noteRouteLine.png")).getImage();

    // ingame 음악 노트 판정 배경
    private Image judgementLineImage = new ImageIcon(getClass().getResource("/menu_images/judgementLine.png")).getImage();

    // Ingame 게임 정보 표시를 위한 이미지
    private Image gameInfoImage = new ImageIcon(getClass().getResource("/menu_images/gameInfo.png")).getImage();

    // ingame 노트 경로 배경
    private Image noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();

    // 각 버튼별 이미지
    // 아래 space 이미지가 2개인 이유는 다른 노트보다 길기 때문에 2개의 이미지를 하나로 합쳐서 길게 만들기 위함
    private Image A_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image S_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image D_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image Space1_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image Space2_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image J_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image K_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
    private Image L_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();

    // 노트 판정 이미지 - flare
    private Image blueFlareImage;
    // 노트 판정 이미지 - 글자
    private Image judgeImage;

    // 게임 실행 시 그 게임에 맞는 음악을 플레이하기 위한 변수
    private String titleName;
    private String diffiCulity;
    private String selectedMusic;
    private Music gameMusic;

    // Note maker 변수
    static boolean noteMaker = false;
    FileWriter fw;

    // 점수 출력
    static int score = 0;
    // 콤보 출력
    static int combo = 0;
    private Image comboImage = new ImageIcon(getClass().getResource("/menu_images/combo.png")).getImage();

 

2) Game 생성자와 screenDraw

  • Game 생성자는 매개변수로 titleName, diffiCulity, selectedMusic 를 받아온다. 이때 받아온 것들 중 selectedMusic 의 경우 다시 Music 생성자에 넣어준 후 Music 클래스를 시작한다. Music 클래스도 마찬가지로 Thread 를 상속받았기 때문에 시작되면 Music 클래스가 Thread 로 시작된다.
  • noteMaker 은 기본적으로는 false 인 상태이며 만약 true 인 경우 FileWriter 객체가 생성되며 내가 지정한 디렉토리에 titleName_diffiCulity.txt 로 파일이 저장된다
  • 여기서의 screenDraw 는 Dynamic 쪽에서 isGameScreen=true 인 경우 그려지는 그래픽 요소들을 정해둔 메소드이다. screenDraw 의 대부분은 그래픽 요소를 어디에 배치할것인지, 글자 색깔은 어떻게 할 것인지 정해둔 것들이다.
  • 또한 note 는 noteList 를 통해 노트별로 각각 관리한다. 이는 for문을 돌면서 내가 가져오는 note 의 y 좌표가 노트 판정을 넘어가는 620 을 넘어간다면 Miss 이미지를 띄우고, score -10 , combo = 0 으로 둔다. 또한 아래 if 문을 통해서 내가 사용하지 않는, 이미 지나간 note 는 삭제한다.
public Game(String titleName, String diffiCulity, String selectedMusic) {
    this.titleName = titleName;
    this.diffiCulity = diffiCulity;
    this.selectedMusic = selectedMusic;
    gameMusic = new Music(this.selectedMusic, false, "game");
    gameMusic.start();

    // 노트 편하게 찍기
    // noteMaker 이 true 인 동안만 아래 내용 실행
    if (noteMaker) {
        try {
            // 작성자가 입력한 시간과 입력한 키를 노래명_난이도.txt 로 저장
            fw = new FileWriter(".\\src\\main\\resources\\readNote\\" + titleName + "_" + diffiCulity + ".txt");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

// 음악 노트를 개별적으로 관리할 ArrayList
ArrayList<Note> noteList = new ArrayList<Note>();

public void screeenDraw(Graphics2D g) {
    // INGAME 노트 경로 배경, 노트 경로별 경계선(라인), 어떤키 사용하는지, 점수 등 => 총 7키
    // 뒷부분에 그려지는 이미지 - 코드 일수록 - 앞쪽으로 튀어나와서 그려지게 됨, 마치 모드처럼

    // ingame 시 게임 정보 표시를 위한 파란 줄
    g.drawImage(gameInfoImage, 0, 660, null);

    // A 버튼
    g.drawImage(noteRouteLine, 224, 30, null);
    g.drawImage(A_noteRouteImage, 228, 30, null);

    // S 버튼
    g.drawImage(noteRouteLine, 328, 30, null);
    g.drawImage(S_noteRouteImage, 332, 30, null);

    // D 버튼
    g.drawImage(noteRouteLine, 432, 30, null);
    g.drawImage(D_noteRouteImage, 436, 30, null);

    // Space1
    g.drawImage(noteRouteLine, 536, 30, null);
    g.drawImage(Space1_noteRouteImage, 540, 30, null);

    // Space2
    g.drawImage(noteRouteLine, 740, 30, null);
    g.drawImage(Space2_noteRouteImage, 640, 30, null);

    // J 버튼
    g.drawImage(noteRouteLine, 844, 30, null);
    g.drawImage(J_noteRouteImage, 744, 30, null);

    // K 버튼
    g.drawImage(noteRouteLine, 948, 30, null);
    g.drawImage(K_noteRouteImage, 848, 30, null);

    // L 버튼
    g.drawImage(noteRouteLine, 1052, 30, null);
    g.drawImage(L_noteRouteImage, 952, 30, null);


    // noteList 에는 note 위치 - x, y - 가 저장되어 있음
    // for 문을 통해 noteList 안에 있는 내용을 하나하나 꺼내오면서 반복 출력
    // 반복 출력되면서 Graph 으로 만듦
    for (int i = 0; i < noteList.size(); i++) {
        Note note = noteList.get(i);

        // 노트 판정의 마지노선이 620 이기 때문에
        // 620 이 넘어가는 note 들에 대해서는 miss 이미지 출력
        if (note.getY() > 620) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeMiss.png")).getImage();
            score -= 10;
            combo = 0;

        }
        // 현재 노트가 동작 상태가 아니라면 - Proceeded 가 false 라면 -
        // 사용되지 않은 노트는 화면에서 지워짐 -> 해당 i 번째 노트를 삭제
        if (!note.isProceeded()) {
            noteList.remove(i);
            i--;
        } else {
            note.screenDraw(g);
        }
    }


    // 글씨 색깔 설정
    g.setColor(Color.white);
    // Graphics2D 설정 : 이 설정을 통해서 TEXT에 ANTIALIASING 설정을 할 수 있고, 글씨가 좀더 뚜렷하게 보임
    g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);


    // 폰트설정 : 아래 나오는 텍스트에 폰트 적용
    g.setFont(new Font("Arial", Font.BOLD, 30));

    // ingame 에서 노래 제목 출력
    g.drawString(titleName, 20, 702);

    // ingame 에서 노래 난이도 출력
    g.drawString(diffiCulity, 1190, 700);

    // 게임 점수 출력
    g.drawString(String.valueOf(score), 620, 702);

    // 게임 콤보 출력
    g.drawImage(comboImage, 1050,130,null);
    g.setColor(Color.CYAN);
    g.drawString(String.valueOf(combo), 1150, 270);

    // 폰트 설정2: 아래 나오는 텍스트에 폰트 적용용
    g.setFont(new Font("Elephant", Font.BOLD, 30));
    g.setColor(Color.white);

    // ingame 키패드 확인 출력
    g.drawString("A", 270, 609);
    g.drawString("S", 374, 609);
    g.drawString("D", 470, 609);
    g.drawString("SPACE", 580, 609);
    g.drawString("J", 784, 609);
    g.drawString("K", 889, 609);
    g.drawString("L", 993, 609);


    // 음악 노트판정을 위한 배경
    g.drawImage(judgementLineImage, 0, 580, null);

    // 노트 판정 시 이미지 - flare
    g.drawImage(blueFlareImage, 250, 150, null);

    // 노트 판정 시 이미지 - 글자
    g.drawImage(judgeImage, 570, 290, null);
}

 

3) press, release

  • press 메소드는 말 그대로 해당 키보드를 눌렀을 때 이벤트, release 는 키보드를 뗐을 때 이벤트 처리에 대한 메소드이다.
  • press 가 중요한데 여기서는 해당 키를 눌렀을 때 이미지 변화와 소리 출력을 정의해둔다. 또한 아래의 판정 메소드인 judge 메소드를 사용하기 위해 매개변수로 해당 키보드를 String 으로 넣어준다.
    • Game 클래스의 judge 메소드는 내가 키보드를 눌렀을 때 실행된다는 점을 기억하자! 
  • noteMaker == true 인 경우  gameMusic.getTime 를 통해 해당 키가 찍힌 시간 과 해당 버튼 A 를 파일에 쓴다.
// press 메서드 : 해당 버튼을 눌렀을 때 이미지변경
// release 메서드 : 해당 버튼에서 손을 뗐을 때 이미지 변경
public void pressA() {
    A_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoutePressed.png")).getImage();
    // 키보드 눌렀을 때 음악
    new Music("drumSmall1.mp3", false, "menu").start();

    if (noteMaker == true) {
        System.out.println(gameMusic.getTime() + " A");
        noteWriter(gameMusic.getTime(), "A");
    }

    // note 판정 함수
    judge("A");

}

public void releaseA() {
    A_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
}

public void pressS() {
    S_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoutePressed.png")).getImage();
    new Music("drumSmall22.mp3", false, "menu").start();

    if (noteMaker == true) {
        System.out.println(gameMusic.getTime() + " S");
        noteWriter(gameMusic.getTime(), "S");
    }
    // note 판정 함수
    judge("S");
}

public void releaseS() {
    S_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoute.png")).getImage();
}

public void pressD() {
    D_noteRouteImage = new ImageIcon(getClass().getResource("/menu_images/noteRoutePressed.png")).getImage();
    new Music("drumSmall33.mp3", false, "menu").start();

    if (noteMaker == true) {
        System.out.println(gameMusic.getTime() + " D");
        noteWriter(gameMusic.getTime(), "D");

    }
    // note 판정 함수
    judge("D");
}

 

4) judge, judgeEvent

이 부분은 Note 클래스와 관련이 많습니다. Note 클래스와 메소드는 추후 설명할 예정이니 대충 이렇구나...하고만 넘어가주시면 될 듯 합니다

  • 먼저 judge 메소드 코드에 대한 내용은 다음과 같다.
    • judge 메소드가 실행되면 noteList 에서 get 해서 현재 note 를 가져온다.
    • 이후 get 으로 가져온 note 객체를 Note 클래스의 judge() 메소드에 매개변수로 넣어준다.
    • Game 클래스의 judge 메소드는 내가 키보드를 눌렀을 때 실행된다!!! 이는 즉 내가 키보드를 눌러서 키를 String 타입으로 보내준 순간 Game 의 judge 메소드가 실행된다는 의미이다. judge 메소드 안에서는 noteList 에 담아져있던 현재 note 를 가져와 Note 객체를 만들고 이 객체를 다시 Note 클래스의 judge 메소드의 매개변수로 던져주게 된다
    • Note 클래스의 judge 메소드는 내가 매개변수로 보낸 note 객체의 y 값을 판정하고, 해당 y 값이 어느 위치인지에 따라서 perfect, great, good , Late, early, miss 등을 판정한 후 String 값으로 return 한다.
  • 다음으로 judgeEvent 에 대한 설명이다.
    • judgeEvent 메소드는 위에 있는 judge 메소드에서 실행되었던 Note 클래스의 judge 메소드에서 return 된 String 값을 매개 변수로 받는다.
    • 매개 변수로 받은 String 값이 어떤 것인지에 따라서 점수가 늘어나거나, 줄어들거나 콤보가 늘어나거나 0 으로 초기화되거나 하는 것을 정의한다.
    • 물론 판정에 따라서 이미지도 보여준다.
    public void judge(String input) {
        // for 문을 통해 전체 노트를 훑어보게됨
        for (int i = 0; i < noteList.size(); i++) {
            // 만약 noteList.get(i) 로 가져온 현재 노트 - nowNote.getNoteType - 가
            // 사용자가 입력한 input 과 일치한다면 note.judge() 메서드 실행
            // 만약 노트 입력이 안된거면 그냥 무시 => 추후 miss 판정
            Note nowNote = noteList.get(i);

            if (input.equals(nowNote.getNoteType())) {
                // judgeEvent 는 String 을 매개변수로 받음
                // Note 의 judge 메서드는 각 판정에 맞는 String 타입을 반환함
                judgeEvent(nowNote.judge());
                break;
            }
        }
    }

    public void judgeEvent(String judge) {
        System.out.println(judge);
        blueFlareImage = new ImageIcon(getClass().getResource("/menu_images/blue_flare.png")).getImage();
//        if (!judge.equals("None")) {
//            blueFlareImage = new ImageIcon(getClass().getResource("/menu_images/blue_flare.png")).getImage();
//        }
        // Miss : 점수 -10, 콤보 초기화
        // 나머지는 점수 +, 콤보+1
        if (judge.equals("Miss")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeMiss.png")).getImage();
            score -= 10;
            combo = 0;

        } else if (judge.equals("Late")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeLate.png")).getImage();
            score += 5;
            combo +=1;
        } else if (judge.equals("Early")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeEarly.png")).getImage();
            score += 10;
            combo +=1;

        } else if (judge.equals("Good")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeGood.png")).getImage();
            score += 20;
            combo +=1;

        } else if (judge.equals("Great")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgeGreat.png")).getImage();
            score += 30;
            combo +=1;

        } else if (judge.equals("Perfect")) {
            judgeImage = new ImageIcon(getClass().getResource("/menu_images/judgePerfect.png")).getImage();
            score += 50;
            combo +=1;

        }
    }

 

5) NoteMaker 모드 - 노트 찍기 모드

  • NoteMaker 모드는 이 부분은 강의에서 나온 부분이 아니라 요번에 공부하면서 직접 만든 부분으로 본래 직접 노트를 찍어야하는데 이러한 귀찮음을....해결하기 위해!! 해결하고 싶어서 만든 한번 만들어보았다.
  • 먼저 run, close 메소드는 각각 Thread 를 상속받은 클래스를 실행시키기 위한 메소드와 실행되는 음악을 종료시키고, 현재 스레드를 종료시키기 위한 클래스이다.

 

  • noteWriter 부분을 설명하겠다.
    • noteWriter 는 현재 시간 getTime 과 키보드 key 를 매개변수로 받는다. 이후 BufferedWriter 를 통해서 위에 선언해두었던 fw 를 가져오고 매개변수로 받았던 'getTime' 와 'key' 를 파일(fw)에 쓰게 된다.
    • 이때 write 가 아닌 append 를 쓰는 이유는 write 를 쓰게 되면 이전에 기록해두었던 내용들 위에 새로 받은 내용을 쓰기 때문에 문제가 되는데 append 는 이전에 기록해두었던 내용 뒤에 쓰게 되면서 내가 원하는대로 파일에 써지게 된다.
  • dropNote 부분이다.
    • dropNote 는 노트를 떨어뜨리는 메소드이다. 이 메소드에서는 noteWriter 를 통해서 만들어둔 파일을 읽어와서 노트를 떨어뜨리게 만드는 것이다.
    • 먼저 noteMaker 이 false 인지 true 인지 판단한다. false 인 경우 정상적으로 파일을 읽어오지만 true 인 경우에는 읽어오는 것 없이 아무것도 실행하지 않는다.
    • 다음으로 beat 객체를 배열로 만든 beats 와 ArrayList 로 time, noteType 을 각각 선언한다. 또한 파일을 읽어올 InputStream, BufferedReader, 임시로 읽어와서 저장하기 위한 readfile 이라는 String을 하나 선언한다.
    • 다음으로 in 과 br 을 통해서 noteWriter 로 만들어두었던 txt 를 읽어온다. 이후 br.readLine 와 while 문을 사용해서 파일이 끝날때까지 읽어온다. 가져오는 값은 time 과 key 총 2개 임으로 각각 String.nextToken 을 사용해서 각각 time 과 noteType ArrayList 에 저장한다.
    • 다음으로 아래에서 beats 배열의 전체 크기를 time 배열의 크기로 담아둔다. 이는 내가 찍은 노트가 하나도 빠짐없이 beats 배열에 담기도록 전체의 크기와 똑같이 만들어두게 된다. 이후 for 문을 통해서 time 배열에 저장해둔 값과 noteType 에 저장해둔 값을 가져온 후 beats 배열에 저장한다.
    • 마지막으로 while 문이 도는데 beats 배열에 담겼던 객체의 Time 값이 현재 음악의 시간보다 작은 경우 노트가 drop 되게 된다. 그리고 이렇게 drop 되는 노트를 다시 noteList 에 저장한다.
    @Override
    public void run() {
        dropNote();
    }

    public void Close() {
        // 게임 음악 종료
        gameMusic.close();
        // 스레드 종료
        this.interrupt();

    }

    // 노트를 떨어뜨리게 - 내려오게 - 만드는 메소드

    // Beat[] 로 만들어진 비트 배열에 만들어진 beat를 넣어둠
    // 이후 for 문이 동작하면서 betas 의 시간과 gameMusic 를 비교하면서 노트를 출력함
    // 노트 출력시에는 Note 인스턴스 객체 사용
    // => note 에는 Thread 가 달려있고, note가 실행되면 Thread 가 같이 실행됨
    // => Dynamic Music 클래스에서 screenDraw 가 실행되면서 for문 noteList가 돌게 됨
    public void dropNote() {

        if (noteMaker == false) {

            Beat[] beats = null; // beat 객체를 배열로

            // time : 파일에서 가져온 시간 배열
            // noteType : 파일에서 가져온 noteType => 버튼 배열
            ArrayList<Integer> time = new ArrayList<>();
            ArrayList<String> noteType = new ArrayList<>();

            InputStream in; // 파일 위치 확인
            BufferedReader br; // 파일 읽어오기
            String readfile = ""; // str 선언

            // 곡 명
            try {

                // jar 파일로 만들었을 때 파일을 불러오기 위한 코드
                // InputStream 을 통해서 파일을 가져오고 이후 ButteredRead 를 통해서 파일을 읽는다.
                in = getClass().getResourceAsStream("/readNote/" + titleName + "_" + diffiCulity + ".txt");
                br = new BufferedReader(new InputStreamReader((in)));

                // 파일을 한줄씩 읽어옴. 이때 1줄에 대하여 " " 공백으로 나눠서 각각 time, noteType Arraylist 에 저장함
                // 1. 1줄 : 1110 A 로 찍혀있고 이를 1110 과 A 로 나눔.
                // 2. 그후 시간은 time 에 A는 노트 타입에 저장함
                while ((readfile = br.readLine()) != null) {

                    StringTokenizer note_stk = new StringTokenizer(readfile, " ");
                    time.add(Integer.parseInt(note_stk.nextToken()));
                    noteType.add(note_stk.nextToken());

                }

//                System.out.println(time.size());
//                System.out.println("time : " + time.get(0));
//                System.out.println("notetype : " + noteType.get(0));


                // 노트 떨어지는 시간 갭
                int gap = 660/(Main.NOTE_SPEED) * (Main.SLEEP_TIME) - (Main.REACH_TIME);

                // beat 객체로 만든 beats 배열
                // 배열의 사이즈는 time 배열의 크기만큼
                // 즉 이로인해서 내가 찍은 전체 노트의 사이즈 = time 배열 사이즈 = noteType 사이즈 = beats 배열 사이즈
                beats = new Beat[time.size()];

                for (int j = 0; j < time.size(); j++) {
                    // beat 배열의 생성자 매개변수로 time 과 noteType을 던져줌
                    beats[j] = new Beat(time.get(j)-gap, noteType.get(j));
//                        System.out.println("time : " + time.get(j));
//                        System.out.println("notetype : " + noteType.get(j));
//                        System.out.println("시간 : "+gameMusic.getTime());
                }


            } catch (Exception e) {
                System.out.println(e.getMessage());
                e.printStackTrace();
            }


            // beats 배열안에 있는 노트에서의 시간 값(getTime)이
            // 즉 노트가 떨어지는 시간이 게임 시간보다 작다면 노트가 떨어짐
            // 현재 음악 시간을 실시간으로 파악해서 해당 시간에 알맞은 노트를 떨어뜨림
            int i = 0;

            while (true) {
                boolean dropped = false;

                if (beats[i].getTime() <= gameMusic.getTime()) {
//                    System.out.println("beat note: "+beats[i].getNoteName());
//                    System.out.println("i : "+i);
                    Note note = new Note(beats[i].getNoteName());
                    note.start();
                    noteList.add(note);
                    i++;

                    dropped = true;
                }
                if (dropped) {
                    try {
                        Thread.sleep(5);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        } else if (noteMaker == true) {
            System.out.println("노트 찍기 모드");
        }
    }

    // 노트 찍기 모드
    public void noteWriter(int getTime, String key) {
        try {

            BufferedWriter bw = new BufferedWriter(fw);

            bw.append(String.valueOf(getTime)).append(" ").append(key);
            bw.newLine();
            bw.flush();

        } catch (Exception e) {
            System.out.println(e.getStackTrace());
            System.out.println(e.getMessage());
        }
    }

2. NoteMaker 사용 모습

1) theFatRat 곡의 Hard 난이도는 현재 어떠한 노트도 찍히지 않은 상태이다.

theFatRat 곡의 Hard 난이도는 없다

2) 이제 NoteMaker=true 로 바꾸고 게임을 시작하겠다. 

오른쪽에 combo 대신 노트찍기 모드라고 나타는 것을 확인 할 수 있따.

 

 

3) 노트를 찍은 후 쨘!! 하고 Hard 난이도의 txt 가 생겨난다

마치 마법처럼 나의 노트가 생겨난다

4) 이제 바로 noteMaker = false 로 두고 다시 시작해보겠다

 

절대! 내가 못하는게 아니다! 내가 만들었지만 판정이 이상하다

 

댓글