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

자바 리듬 게임 만들기 (1) : 구현목표, Main, DynamicMusic 클래스

TerianP 2022. 3. 20.
728x90

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

 

그래서 생각한게 바로 따라하면서 공부하기 였습니다.

안타깝게도 아직은 Java 를 능숙하게 따라가지 못하고 있고, 각 라이브러리의 메서드나 그런 것들에 대해서 무지하다고 생각하였습니다. 그래도 하고 싶은게 많았으니...결국 다른분들이 만든 것들을 보고, 강의를 보고 열심히 따라하면서 배우고자 합니다

 

물론 무조건 따라하는 것은 아니고, 따라하면서도 제 나름대로 구현 목표를 갖고, 기본적인 기능 외 목표로한 부분을 구현하는 것을 목표로 달려갈 예정입니다.

 

사실 하나의 글에 정리하고 싶었는데...생각보다 많아져서 여러번에 걸쳐 정리하기로 했습니다.

 


포스팅 순서

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

2. Game 클래스

3. Track, Music, Beat 클래스

4. Note,  KeyListener 클래스

 

목표 : 자바로 리듬게임 만들기

- 강의는 '동빈나' 선생님의자바 리듬게임 만들기를 참고하였습니다(좋은 강의 감사합니다)

https://www.youtube.com/playlist?list=PLRx0vPvlEmdDySO3wDqMYGKMVH4Qa4QhR

 

자바(JAVA) 리듬게임 만들기 강좌(How To Make Java Rhythm Game)

 

www.youtube.com

 

현재 구현된 기능

- 시작 화면에서 인트로 음악 재생

- 시작 화면에 시작하기, 종료하기 버튼

- 시작하기 버튼 클릭 시 게임 메인 화면으로 전환, 종료하기 버튼 클릭 시 게임 종료

- 게임 메인 화면에서 음악 선택하면 해당 음악의 하이라이트 부분 재생

- 왼쪽 오른쪽 버튼에 따라서 음악 선택 가능

- 음악에 맞춰 노트 내려오고 노트별 판정 출력

- 점수 및 콤보 시스템

- 노트 찍기 모드 : 노트 찍기 모드가 활성화 된 경우 readNote 경로 안에 곡이름_난이도.txt 로 파일이 생기고 해당 파일 안에 노트 찍은 시간과 어떤 키를 눌렀는지 표시됨. 이후 Game 안에 있는 dropNote 메소드를 통해 해당 파일을 읽어오고 노트가 출력

 

추가 구현 목표

- DB 와 연동해서 게임 시작 시 로그인 기능

- 내가 만들어둔 Web 과 연동해서 로그인 시 게임 실행 가능 : 추가로 해당 로그인 아이디로 highscore 와 combo 를 기억해두었다가 웹에서 출력, 확인

 

게임 플레이 영상

내가 노트를 찍었지만...드럽게 못하네

 

Git

- 전체 코드는 git 에 올려두었습니다.

- 제 git에는 본 음악게임에서 사용된 어떤 노래와 사진도 포함되어 있지 않습니다. 다만 간단한 버튼과 제가 직접 포토샵으로 만든!!! 글자 이미지는 포함되어 있습니다.

https://github.com/SeJonJ/DynamicMusic

 

GitHub - SeJonJ/DynamicMusic: Java 로 만드는 음악 게임

Java 로 만드는 음악 게임. Contribute to SeJonJ/DynamicMusic development by creating an account on GitHub.

github.com

 

클래스 UML 다이어그램

클래스 UML 다이어그램


 

1. Main.class

- 메인 클래스는 단순 그 자체!!

- DynamicMusic 클래스에 start 라는 메서드를 생성 후 해당 메서드를 실행하는 것으로 전체 프로그램이 실행된다.

- 만약 DynamicMusic 클래스의 생성자에 시작하는 코드를 넣어두면 굳이 dynamicMusic.start() 할 필요는 없다.

public class Main {
    // FINAL 은 한번 선언되면 변경X 변수 => 이런 상수들은 대문자로 정의

    public static final int SCREEN_WIDTH = 1280;
    public static final int SCREEN_HEIGHT = 720;

    public static void main(String[] args){

        DynamicMusic dynamicMusic = new DynamicMusic();
        dynamicMusic.start();


        System.out.println("실행 완료");
    }
}

 

2. DynamicMusic.class - 게임의 전반적인 부분에 대한 정의

해당 파일의 코드가 길어서 크게 여러 부분으로 나누어 설명하도록 하겠습니다.

1-1) 전역 변수 정리

- Image 와 Grapics 는 GUI 화면에 이미지를 띄우고 그려주기 위한 클래스이다. 

- 먼저 Image 클래스를 사용해서 이미지를 가져오고 JButtion 을 만들어서 해당 이미지를 버튼에 넣는다.

  • 이때 getClass().getResource 로 파일을 가져오는데 이것은 추후 jar 혹은 exe 로 파일을 만들었을 때에도 오류없이 게임을 실행시키기 위함이다.
// 더블버퍼링 기술 사용
private Image screenImage;
private Graphics screenGraphic;

// ImageIcon 생성자를 이용해서 Main.class 에 있는 위치에서 리소스를 가져옴
// 이후 다시 getter 를 이용해서 해당 이미지를 IntroBackground에 넣어줌
// jar 파일로 만들때는 getClass.getResource 를 사용한다!

// Background 객체는 이후 아래에서 화면이 전환될때 이곳에 다른 사진을 넣어서 배경경 사진 바뀔 수 있도록 함
// 시작화면 background
private Image Background = new ImageIcon(getClass().getResource("/menu_images/intro_background.jpg")).getImage();

// memuBar 객체 안에 memuBar 이미지가 들어가게 됨
private JLabel menuBar = new JLabel(new ImageIcon(getClass().getResource("/menu_images/menuBar.png")));

// 오른쪽 위 eixtButton 의 기본 이미지와 버튼에 마우스를 올렸을때 이미지가 변하도록
private ImageIcon exitButtonImage = new ImageIcon(getClass().getResource("/menu_images/exit.png"));
private ImageIcon exitButtonEnteredImage = new ImageIcon(getClass().getResource("/menu_images/exit_entered.png"));


    // Button 생성
    private JButton exitButton = new JButton(exitButtonImage);
    private JButton startButton = new JButton(startButtonBasic);
    private JButton quitButton = new JButton(quitButtonBasic);
    private JButton leftButton = new JButton(leftButtonBasic);
    private JButton rightButton = new JButton(rightButtonBasic);
    private JButton easyButton = new JButton(easyButtonBasic);
    private JButton hardButton = new JButton(hardButtonBasic);
    private JButton backButton = new JButton(backButtonBasic);
    private JButton noteWriteButton = new JButton(noteWriteMod);

 

1-2) 코드를 위한 전역 변수 설정

- 이 부분은 코드 주석을 참고하자

- 제일 중요한 것은 ArrayList<Track> 부분인데 여기서 Track 객체를 배열로 갖는 trackList 를 생성하는 것을 확인할 수 있다. 이때 인덱스 번호는  nowSelected 변수로 대신한다.

- trackList 에는 Track 를 객체로 갖는다. 예를 들어 0 번째 인덱스에 Track 객체가 들어가고 Track 객체는 특정한 노래의  해당 곡 명 이미지, 메인 메뉴 음악, 메인 메뉴 배경 화면, 인게임 음악, 인게임 화면, 해당 곡 명 을 갖는다. 

- 최종적으로는 nowSelected = 0 인 경우 index 0번째 Track 객체를 가져오고 해당 Track 객체가 갖고있는 해당 곡 명 이미지, 메인 메뉴 음악, 메인 메뉴 배경 화면, 인게임 음악, 인게임 화면, 해당 곡 명 불러오게 된다.

    // 윈도우 창 위치를 메뉴바를 끌어서 옮길 수 있도록 마우스 좌표 int
    private int MouseX, MouseY;

    // 게임에 맞춰 화면을 표시하기 위한 변수
    private boolean isMainScreen = false;

    // Ingame 으로 넘어왔는지 확인하기 위한 변수수
   private boolean isGameScreen = false;

    // ArrayList 어떠한 변수를 담을 수 있는 이미 만들어진 배열? => 하나의 음악의 정보를 배열로 담음
    ArrayList<Track> trackList = new ArrayList<Track>();

    // trackList 안에 있는 값에 따라서 아래 변수들의 값이 달라짐
    // nowSelected 값은 현재 선택된 트랙의 번호이자 arraylist 의 index 를 의미한다.
    private int nowSelected = 0;
    private Image selectedImage;
    private Image titleImage;
    private Music selectedMusic;

    // 인트로 음악 정의
    private Music Intromusic = new Music("Intro_Elektronomia_Energy.mp3", true, "menu");

    // Game 인스턴스 생성 && 초기화 :
    // 이때 game 변수는 단 하나의 게임만 진행가능하며 game 변수자체가 프로젝트 전체에서 사용되어야하기 때문에
    // static 으로 만들어줌
    public static Game game;

2) JFrame 설정 및 각 버튼별 이벤트 설정

- 윈도우 창의 타이틀 이름, 보이는 여부, 위치와 크기 등을 정해준다.

- addKeyListener 부분에는 따로 키보트 별 이벤트 설정을 위해 KeyListener 이라는 클래스를 만들어서 매개변수로 넣어준다.

- exitButton 에서 버튼을 눌렀을때 이벤트, 땠을 때 이벤트, 올렸을 때 이벤트 등을 설정해준다.

- startButton 은 해당 버튼을 눌렀을 때  game_menu() 메소드가 실행되는데 이 메소드는 게임 메뉴로 들어가는 메소드이다.

       setUndecorated(true); // 기본 메뉴바 삭제
        setTitle("Dynamic Music");
        setSize(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
        setResizable(false); // 한번 창이 생성되면 임의적으로 창 크기 변경 불가
        setLocationRelativeTo(null); // 창이 정중앙에 위치
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 게임 창을 종료 시 프로그램 전체 종료
        setVisible(true); // 창 보이는 여부 => 반드시 true
        setBackground(new Color(0, 0, 0, 0)); // paintcomponent 했을때 배경이 회색이 아니라 휜색으로 됨
        setLayout(null); // 버튼이나 JLabel 을 넣었을 때 그 위치 그대로 넣어짐

        // add해서 KeyListener 에 내가 만든 KeyListen 인식
        addKeyListener(new KeyListener());

        // 게임 시작시 인트로 음악 재생
       Intromusic.start();

        // exitButton
        exitButton.setBounds(1245, 0, 30, 30); // 왼쪽부터 x, y , 길이, 높이
        exitButton.setBorderPainted(false);
        exitButton.setContentAreaFilled(false);
        exitButton.setFocusPainted(false);

        // 아래처럼 추상 클래스, 인터페이스등을 상속, 구현하는 클래스를 직접 생성한는 것 대신
        // { ~~~ } 를 통해 생성하는 것을 익명 클래스라고 함
        // 즉 new MouseAdapter() 뒤에 부분은 익명클래스로 MouseAdapter() 를 구현한 클래스로 취급됨
        exitButton.addMouseListener(new MouseAdapter() {
            @Override // 버튼에 마무스 올렸을 때 이벤트
            public void mouseEntered(MouseEvent e) {
                exitButton.setIcon(exitButtonEnteredImage);
                exitButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            @Override // 버튼에 마우스 뗐을 때 이벤트
            public void mouseExited(MouseEvent e) {
                exitButton.setIcon(exitButtonImage);
                exitButton.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }

            @Override // 마우스 눌렀을 때 이벤트
            public void mousePressed(MouseEvent e) {
                System.exit(0);
            }
        });
        
                // 게임 시작 버튼
        startButton.setBounds(800, 200, 400, 100);
        startButton.setBorderPainted(false);
        startButton.setContentAreaFilled(false);
        startButton.setFocusPainted(false);
        startButton.addMouseListener(new MouseAdapter() {
            @Override // 버튼에 마무스 올렸을 때 이벤트
            public void mouseEntered(MouseEvent e) {
                startButton.setIcon(startButtonEntered);
                startButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            @Override // 버튼에 마우스 뗐을 때 이벤트
            public void mouseExited(MouseEvent e) {
                startButton.setIcon(startButtonBasic);
                startButton.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }

            @Override // 마우스 눌렀을 때 이벤트 => 즉 게임 시작버튼 눌렀을 때의 이벤트
            public void mousePressed(MouseEvent e) {
                // 게임 메뉴 시작
                game_menu();
            }
        });

3) 난이도 버튼

- 난이도 버튼에서는 버튼을 눌렀을 때 해당 난이도로 게임이 실행되도록 한다. 정확히는 gameStart 메소드에 현재 선택한 곡 - noewSelected - 과 해당 난이도 - Easy, Hard - 를 매개변수로 보내준다.

        // easy 난이도 버튼
        // 시작화면에서는 난이도버튼 보일 필요 없음
        easyButton.setVisible(false);
        easyButton.setBounds(375, 580, 250, 67);
        easyButton.setBorderPainted(false);
        easyButton.setContentAreaFilled(false);
        easyButton.setFocusPainted(false);
        easyButton.addMouseListener(new MouseAdapter() {
            @Override // 버튼에 마무스 올렸을 때 이벤트
            public void mouseEntered(MouseEvent e) {
                easyButton.setIcon(easyButtonBasic);
                easyButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            @Override // 버튼에 마우스 뗐을 때 이벤트
            public void mouseExited(MouseEvent e) {
                easyButton.setIcon(easyButtonEntered);
                easyButton.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }

            @Override // 마우스 눌렀을 때 이벤트
            public void mousePressed(MouseEvent e) {
                // 쉬움 난이도 이벤트
                gameStart(nowSelected, "Easy");
            }
        });

        // hard 난이도 버튼
        hardButton.setVisible(false);
        hardButton.setBounds(655, 580, 250, 67);
//        hardButton.setBounds(900, 350, 250, 67);
        hardButton.setBorderPainted(false);
        hardButton.setContentAreaFilled(false);
        hardButton.setFocusPainted(false);
        hardButton.addMouseListener(new MouseAdapter() {
            @Override // 버튼에 마무스 올렸을 때 이벤트
            public void mouseEntered(MouseEvent e) {
                hardButton.setIcon(hardButtonBasic);
                hardButton.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            @Override // 버튼에 마우스 뗐을 때 이벤트
            public void mouseExited(MouseEvent e) {
                hardButton.setIcon(hardButtonEntered);
                hardButton.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }

            @Override // 마우스 눌렀을 때 이벤트
            public void mousePressed(MouseEvent e) {
                // 어려움 난이도 이벤트
                gameStart(nowSelected, "Hard");
            }
        });

4) 화면을 그래픽을 표현해주는 paint 와 screenDraw 메소드

- 이 부분도 주석을 참고부탁드립니다. 내용이 많고..복잡해요ㅠㅠ

- 간단히 설명하자면 paint 메소드는 JFrame 에서 화면을 그릴때 실행되는 함수이다. 

  • 먼저 paint 메소드가 실행된다. 이 안에서 screenImage 인스턴스가 동작하면서 에 윈도우 화면 크기만큼 생성하고, 다시 이 image 를 screenGraphic 에 넣어서 동작지킨다. 이를 통해 그래픽 객체가 생성된다.
  • 이후 screenDraw 함수에 screenGraphic 를  Graphic 2D 로 형변환 후 매개변수로 넣어준다. screenImage 는 다시 drawImage 라는 메소드에 넣어주는데 이 매소드는 이미지를 띄워주는 메소드이다.
  • screenDraw 는 화면은 추가로 그려주기 위한 메소드이다. 이 안에서 게임 시작 시 기본 배경 화면, 메인 메뉴에서의 이미지를 그려준다.
  • 중요한 것은 screenDraw 에서의 repaint() 메소드인데 이 메소드를 통해서 paint , 즉 화면에 그래픽을 그려주는 것을 계속 반복하게 된다. 즉 그래픽은 한번만 띄워주고 끝내는 것이 아닌 paint 와 repaint 를 통해서 게임이 실행되는 동안 반복적이고, 지속적으로 그래픽을 그려준다. 이를 더블 버퍼링 기법이라고 하며 버퍼에 이미지를 담아서 계속 갱신해주는 기법이다.
    // paint 메서드는 JFrame 에서 상속받아서 화면을 그릴때 가장 먼저 실행되는 함수
    // 즉 아래 순서대로 실행된다고 생각하면 됨
    // 1. JFrame 를 실행하면 updated(Graphics g) 가 가장 먼저 실행됨
    // 2. 다음으로 paint(Graphics g) 가 실행됨
    // 3. 이때 paint 함수 안에서 repaint() 를 실행함으로써 paint() -> repaint() -> paint() 식으로 반복됨
    // 실행시켜보면 실제로 a -> c -> d -> b 순서로 반복됨
    // 이렇게 화면을 계속 띄워주는 이유는 한번만 화면을 띄워주게 되면 버퍼링 현상이 심해서 느려지는 문제가 있음
    // -> 이에 paint 와 repaint 를 통해 계속해서 introbackground 이미지를 띄워주게 됨
    // 이런 기법을 더블 버퍼링 기법!! 이라고 함 : 버퍼에 이미지를 담아서 계속해서 갱신해줌
    public void paint(Graphics g) {
        screenImage = createImage(Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT); // 윈도우 화면 크기 만큼 생성
        screenGraphic = screenImage.getGraphics(); // screenImage 를 이용해서 그래픽 객체를 얻어옴
//        System.out.println("a");
        // screenGraphic 에 그림을 그려주게됨 , 그래픽에 화면에 이미지를 그린다? 라는 느낌? => 이때 screenDraw 메서드를 통해 화면을 그림(Draw)
        screenDraw((Graphics2D) screenGraphic); // 그래픽스 2D 로 형변환
//        System.out.println("b");
        // 0,0 인 이유는 이미 screenImage 가 이미 화면 크기 그대로 이기 때문에 0,0에 띄워주는 것
        g.drawImage(screenImage, 0, 0, null); // 윈도우 창에 screenImage 를 뿌려줌
    }

    public void screenDraw(Graphics2D g) { // 매개변수를 Graphics 에서 Graphics2D 로 변경
//        System.out.println("c");
        // drawImage 메서드를 Introbackground 를  x, y 좌표에 그려줌
        // g.drawImage 부분은 paintComponents 처럼 화면에 추가된 요소를 그려주는 것이 아닌 단순히
        // 이미지를 그림 그리는 것
        g.drawImage(Background, 0, 0, null);

        // isMainScreen = true 면 selectedImage 를 보여줌
        if (isMainScreen) {
            g.drawImage(selectedImage, 350, 100, null);
            g.drawImage(titleImage, 340, 85, null);
        }

        // isGameScreen = true 인게임 화면에서의 그래픽
        // ingame 에 관한 그래픽 내용은 Game 클래스에서 관리
        if(isGameScreen){
            game.screeenDraw(g);
        }

        // paintComponents 는 이미지를 단순히 그려주는 것 이외에 JLabel 처럼 추가된 요소를 그리는 것
        // 즉 JFrame 위에 button 이나 라벨처럼 add() 된 부분에 대한 것
        paintComponents(g);
        try{
            Thread.sleep(5);
        }catch(Exception e){
            e.printStackTrace();
        }
        this.repaint();
//        System.out.println("d");
    }

 

5) 트랙 & 음악 선택 메소드

- selectTrack 메소드는 곡을 선택 후 해당 곡의 메인 배경 화면과 노래의 하이라이트를 들려주게 된다.

- 이때 trackList 를 사용하는데 이는 추후 Track 를 설명하면서 한번 더 설명하도록 하겠다.

- 참고로 selectMusic 의 true 는 loop 여부를 의미하며, game 부분은 해당 음악이 게임 음악인지 아니면 배경 음악인지를 확인하기 위한 매개변수이다.

- selectLeft는 왼쪽 버튼에 해당하는 메소드이다.

  • 이때 nowSelect 가 0 일 때 즉 index 가 0 일때 즉 List의 첫번째일때는 왼쪽 버튼을 누르면 전체 trackList 의 전체 크기-1 번째 곡을 선택한다. ArrayList 에서 전체크기-1 이란 List의 마지막 index 를 의미하며 이는 즉 마지막 곡을 가져오는 것이다.
  • 만약 index 가 0 이 아닌 경우 왼쪽 버튼을 누르면 인덱스-1 해서 하나 전 노래를 선택한다.

- selectRight 는 오른쪽 버튼에 해당하는 메소드이다.

  • 사실 Left 메소드와 비슷하다. 다만 Right 버튼은 List-1 번째 인덱스, 즉 마지막 인덱스인 경우 오른쪽 버튼을 누르면 리스트의 첫번째인 0 으로 돌아간다.
  • 만약 마지막 인덱스, 마지막 곡이 아닌 경우 현재 인덱스에서 +1 한다.
// 현재 선택된 곡의 번호를 넣어줌으로써 해당 곡이 선택됨을 알림
public void selectTrack(int nowSelected) {
    // 선택한 곡이 null 이 아니면 , 즉 어떠한 곡이라도 하나가 실행되고 있다면 해당 음악을 종료
    if (selectedMusic != null) {
        selectedMusic.close();
    }

    // 현재 선택된 곡이 갖고 있는 noewSelected 번호를 갖고 아래의 각 정보를 가져옴
    // 예를 들어서 arraylist 의 index 가 1이면 1에 해당하는 title, start, music 를 가져와서 뿌려줌
    titleImage = new ImageIcon(getClass().getResource("/game_images/" +
            trackList.get(nowSelected).getTitleImage())).getImage();
    selectedImage = new ImageIcon(getClass().getResource("/game_images/" +
            trackList.get(nowSelected).getMenuImage())).getImage();


    selectedMusic = new Music(trackList.get(nowSelected).getStartMusic(), true, "game");
    selectedMusic.start();
}

// 왼쪽 버튼 메서드
public void selectLelft() {
    // 0번째 곡일때는 전체 trackList 크기에서 -1 한다.
    // 이는 0번째 곡일때 왼쪽을 누르면 track 에 있는 마지막 곡이 나오게 됨
    if (nowSelected == 0) {
        nowSelected = trackList.size() - 1;
    } else { // 가장 왼쪽 아닐때는 현재 nowSelected 에서 -1
        nowSelected--;
    }
    selectTrack(nowSelected);
}

// 오른쪽 버튼 메서드
public void selectRight() {
    // 현재 곡이 track 의 가장 오른쪽에, 즉 마지막에 있는 곡이라면
    // 가장 처음으로 돌아가도록
    if (nowSelected == trackList.size() - 1) {
        nowSelected = 0;
    } else { // 가장 오른쪽이 아닌 경우는 +1
        nowSelected++;
    }
    selectTrack(nowSelected);
}

6) Game Start 메소드

- Game Start 메소드는 말 그대로 게임을 시작하는 메소드이다. 

- 이 메소드 안에서 track.get 을 통해서 현재 index 번호에 맞는 titleName, 난이도 , gameMusic, IngameImage 를 가져온 후 game 객체를 생성한다. 이후 (Tread 를 상속받은)game 안에 있는 Tread 를 시작 - start - 하게 된다.

- 이때 isMainScreen 은 false, isGameScreen 은 true 로 두어 게임 메뉴에서 실제 게임 화면으로 전환을 확인해준다. 또한 게임 화면을 전환해두었기 때문에 left, right, easy, hard 버튼은 보이지 않게 해둔다. 또한 back 버튼 뒤로가기 버튼을 보이게 해둔다.

- 마지막으로 combo 와 score 를 0 으로 초기화해둔다.

public void gameStart(int nowSelected, String difficulty) {
    // 현재 재생되고 있는 음악이 있다면 음악 종료
    if (selectedMusic != null) {
        selectedMusic.close();
    }

    // 메인 스크린을 false 로 => 이렇게되면 screenDraw 함수에서 isMainScreen 부분을 멈추게됨
    isMainScreen = false;

    // Ingame 전환 확인
    isGameScreen = true;

    // 게임 시작 시 해당 선택된 곡 이름과 난이도 가져옴
    game = new Game(trackList.get(nowSelected).getTitleName(), difficulty, trackList.get(nowSelected).getGameMusic());

    // game 인스턴스 안에 있는 run 함수 실행
    game.start();

    // 버튼 안보이게
    leftButton.setVisible(false);
    rightButton.setVisible(false);
    easyButton.setVisible(false);
    hardButton.setVisible(false);

    // 뒤로 돌아가기 버튼
    backButton.setVisible(true);

    // 백그라운드 이미지가 ingame 이미지로 바뀌어야함
    Background = new ImageIcon(getClass().getResource("/game_images/"+trackList.get(nowSelected).getIngameImage())).getImage();

    // 노트 찍기 모드 일때는 해당 이미지 추가
    // noteMaker = true 일 때만 버튼 보이게
    if(Game.noteMaker) {
        noteWriteButton.setVisible(true);
    }

    // 게임 시작 시 점수 & 콤보 초기화
    Game.combo = 0;
    Game.score = 0;

    // 키보드 이벤트 동작을 위한 메서드
    // 이는 Main 클래스에 포커스가 맞춰져있어야 키보드 이벤트가 정상적으로 동작하기 때문
    setFocusable(true);
    requestFocus();
}

7) game_menu 와 bakMain 메소드

- game menu 와 bakMain 메소드는 각각 시작하기 버튼을 눌렀을 때 즉 노래 선택 게임 화면으로 들어갔을 때 실행되는 기능과 뒤로가기 버튼을 눌렀을 때 노래 선택 게임 화면으로 왔을 때 실행되는 메소드이다.

- 그렇기 때문에 안에 있는 코드가 비슷한데 먼저 Background 를 통해 메뉴로 왔을 때 배경화면을 바뀌게 해준다. 또한 필요한 버튼을 보이게 하고, index 0 번째 노래를 선택한다.

- 중요한 부분은 game menu 로 왔을 때 intromusic.close 해서 - 인트로 음악을 종료 해주는 부분과 게임 도중 뒤로가기 버튼을 눌러서 뒤로 왔을 때 game.close 해서 게임을 종료해주는 부분이다.

    public void game_menu(){
        // 시작, 종료버튼 없애기
        startButton.setVisible(false);
        quitButton.setVisible(false);

        // 여기는 게임 메인 화면에 들어갔을 때 배경화면
        Background = new ImageIcon(getClass().getResource("/menu_images/main_Bakground.jpg")).getImage();

        // 게임 시작을 누르면 isMainScreen 을 true 로
        isMainScreen = true;

        //  left 와 right 버튼이 보이기
        leftButton.setVisible(true);
        rightButton.setVisible(true);

        // 난이도 버튼 표시
        easyButton.setVisible(true);
        hardButton.setVisible(true);

        // nowselected 번째 index 재생
        selectTrack(0);

        // 인트로 음악 종료
        Intromusic.close();
    }



public void bakMain(){
    // 메인 화면일때는 isMainScreen 이 true, GameScreen 은 false
    isMainScreen = true;
    isGameScreen = false;


    // 메인화면으로 돌아오면 다시 버튼 보이게
    leftButton.setVisible(true);
    rightButton.setVisible(true);
    easyButton.setVisible(true);
    hardButton.setVisible(true);

    // 뒤로 돌아가기 버튼
    backButton.setVisible(false);

    // 노트 찍기 버튼
    if(Game.noteMaker){
        noteWriteButton.setVisible(false);
    }

    // 다시 트랙 선택
    selectTrack(nowSelected);

    // 백그라운드 이미지가 track 의 nowSelected 에 맞는 이미지로
    Background = new ImageIcon(getClass().getResource("/menu_images/main_Bakground.jpg")).getImage();

    // 메뉴로 돌아왔을 때 게임 종료
    game.Close();
}

 


참고 사이트

- 저작권 없는 무료 배경 이미지

http://wallpaperswide.com/

 

WallpapersWide.com - Free 4K & 8K UltraHD Desktop Backgound Wallpapers for UHD TV, Ultra Widescreen Desktop, Tablet, Smartphone

 

wallpaperswide.com

 

- 배경 이미지 제거

https://www.remove.bg/ko/upload

 

이미지 업로드 – remove.bg

이미지 선택 & 무료로 – 한 번의 클릭도 없이 – 5초 안에 – 100% 자동으로 – 배경 삭제.

www.remove.bg

 

- mp3 음악 수정

https://mp3cut.net/ko/

 

Online MP3 Cutter - Cut Songs, Make Ringtones

결과 다운로드 필요한 경우 피치 이동, 볼륨 또는 속도 변경과 같은 여러 기능을 한 번에 사용하십시오. 필요 또는 기본 설정에 따라 사용 가능한 출력 형식 중 하나로 음악을 저장합니다.

mp3cut.net

 

댓글