728x90
이번 글은 4번째 Note, KeyListener 클래스에 대한 부분입니다.
1. 구현 기능, 목표 및 Main, Dynamic 클래스
2. Game 클래스
3. Track, Music, Beat클래스
4. Note, KeyListener 클래스
1. Note
- note 클래스는 곡의 Note 들에 대한 정보와 노트 판정 정보를 저장하는 클래스로 다른 클래스들과 마찬가지로 Thread 를 상속받는다.
- 생성자 Note 메소드에서는 게임에서의 키보드 위치에따라 note의 x 좌표가 어디에 위치해야하는지를 알려준다.
- screenDraw 는 많이 보아왔던 메소드로 그래픽적 요소를 그려내기 위한 메소드이다. drawImage 메소드를 사용해 어떤 그림을 어떤 위치에 그리고 싶은지 정해준다.
- drop 메소드는 노트를 떨어뜨리는 메소드이다. 노트를 떨어뜨린다 라는 행위의 의미는 노트의 y 좌표가 일정한 양만큼 지속적으로 증가한다 라는 것을 의미한다.
- 이에따라서 drop 메소드가 실행될 때마다 y+=Main.Note_SPEED 를 해준 것이다.
- 만약 y 좌표가 620 을 넘어간다면, 즉 노트 판정 위치를 넘어간다면 Close 를 통해 proceeded = false 로 바뀌고, 해당 노트 스레드를 지워주게 된다.
- run 메소드는 Thread 를 실행시키는 메소드로 drop 메소드를 지속적을 실행시킨다. 이때 proceeded 가 true 인 동안에만 노트를 떨어지고, false 인 경우 스레드가 종료된다.
- 이때 노트가 미친듯이 떨어지지않게 하기 위해서 Thread.sleep 를 통해 노트가 정해진 Main.SLEEP_TIME 만큼 딜레이를 주면서 떨어지게 된다.
- judge 메소드에서는 버튼을 눌렀을 때의 y 좌표에 따라서 해당 판정을 String 타입으로 리턴한다. 이때 판정된 노트의 스레드는 종료된다.
- 여기서 y 좌표를 조절해주면 판정이 좀더 여유로워지거나 빡빡해지거나 하는듯 하다.
package DynamicMusic;
import javax.swing.*;
import java.awt.*;
// 각각의 떨어지는 노트 모두 Thread
/*
1. 노트 판정 과정
노트판정은 Queue 형식으로 구현한다. 즉 first in first out 형식.
이는 당연히도 먼저 출현 - 들어온 - 노트가 가장 먼저 판정이 되어야하기 때문이다.
큐는 특별한 코드로 만들기보다는 ArrayList 로 구현하면 된다.
*/
public class Note extends Thread{
private Image noteBasicImage = new ImageIcon(getClass().getResource("/menu_images/noteBasic_new.jpg")).getImage();
// 현재 노트 위치 확인을 위한 x, y 좌표
// 이 중 y 값의 시작위치를 특정값으로 고정으로 해두어서
// 노트가 생성된 지 1초 뒤 노트 판정위치인 580에 도달 할 수 있도록 함
private int x;
private int y = 580 - 1000 / Main.SLEEP_TIME * Main.NOTE_SPEED;
private String noteType;
// 현재 노트의 진행 여부 -> 즉 노트가 판정이 필요한 범위를 넘어가는지 확인할 수 있또록
private boolean proceeded = true;
// 노트 판정 -> 현재 노트 타입 - 키보드 어떤 버튼인지 - 반환
public String getNoteType(){
return noteType;
}
public boolean isProceeded(){
return proceeded;
}
// 노트를 더이상 사용할 필요가 없다면 false
public void close(){
proceeded = false;
}
public Note(String noteType){
if(noteType.equals("A")){
x = 228;
}else if(noteType.equals("S")){
x = 332;
}else if(noteType.equals("D")){
x = 436;
}else if(noteType.equals("Space")){
x = 540;
}else if(noteType.equals("J")){
x = 744;
}else if(noteType.equals("K")){
x = 848;
}else if(noteType.equals("L")){
x = 952;
}
this.noteType = noteType;
}
// 노트가 내려오는 그래픽을 그리기 위한 screenDraw
public void screenDraw(Graphics2D g){
// 짧은 노트라면 현재 x, y 좌표에 노트를 그려냄
// 노트 타입 - 키 - 에 따라서 다른 그래픽 그림을 그려냄 출력함
if(!noteType.equals("Space")){
g.drawImage(noteBasicImage, x, y, null);
// 긴 노트라면 노트 이미지 2개를 그려냄, 이때 겹치면 안되니까 한쪽은 x+100
}else{
g.drawImage(noteBasicImage, x, y, null);
g.drawImage(noteBasicImage, x+100, y, null);
}
}
// 노트가 떨어지도록 만드는 함수
public void drop(){
// 노트가 떨어진다 -> 노트가 아래로 움직인다 -> y 축으로 일정한 좌표만큼 움직인다
y += Main.NOTE_SPEED;
if(y>620){ // y 좌표가 620 이상이라면 Miss 판정
// System.out.println("Miss");
close();
}
}
// 스레드 실행 함수
@Override
public void run(){
try{
// 노트 떨어지는것은 무한반복
// 1초에 Main.NOTE_SPEED * 100 정도만큼 움직임
while(true) {
drop();
if (proceeded == true) {
// 떨어질때 Main.Sleep_Time 에 설정된 시간만큼 딜레이를 주면서 떨어짐
// 현재 노트가 계속해서 움직이고 있다면 반복적으로 내려옴
// 해당 노트에대한 작업처리가 끝나면 proceeded 가 false 로 변경
Thread.sleep(Main.SLEEP_TIME);
}else{
// 노트에 대한 전반적 작업 - 판정, 입력 등- 끝나서
// 더이상 해당 노트가 필요없어지면 스레드 종료(interrupt)
// proceeded = false
interrupt();
break;
}
}
}catch (Exception e){
System.err.println(e.getMessage());
}
}
public String judge() {
if(y >= 613){
// System.out.println("Late");
close();
return "Late";
}else if(y >= 600){
// System.out.println("Good");
close();
return "Good";
}else if(y>= 587){
// System.out.println("Great");
close();
return "Great";
}else if(y>= 573){
// System.out.println("Perfect");
close();
return "Perfect";
}else if(y>= 565){
// System.out.println("Great");
close();
return "Great";
}else if(y >= 550){
// System.out.println("Good");
close();
return "Good";
}else if(y>= 535){
// System.out.println("Early");
close();
return "Early";
}else{
// 범위 안에서 안누르고 막 눌렀을 경우 miss
return "Miss";
}
}
public int getY(){
return y;
}
}
2. KeyListener
- 지금까지 보았던 모든 클래스와 메소드 중에서 가장 최고로 간단한 클래스로 말 그대로 내가 키보드를 눌렀을 때 실행할 이벤트를 정의하는 클래스이다.
- KeyAdapter 을 상속받았으며, 각각 버튼을 누를때 - keyPressed - 와 버튼을 뗄 때 - key released - 를 정의한다.
- 만약 게임이 실행되고 있지 않은 상태인 game = null 인 경우라면 아래 if ~ else 문을 실행하지 않고 그냥 return 한다.
package DynamicMusic;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
// 키보드 이벤트 감지
public class KeyListener extends KeyAdapter {
@Override
public void keyPressed(KeyEvent e) {
// e.getKeyCode 는 사용자가 누르는 키
// KeyEvent.VK_@ 컴퓨터가 갖는 키
// static 으로 설정된 game 이 null 값을 갖는다면 => 어떤 게임도 실행되고 있지 않는다면
// return 해서 아래 키보드 이벤트 작업 실행되지 않도록 함
if(DynamicMusic.game == null){
return;
}
if(e.getKeyCode() == KeyEvent.VK_A){
DynamicMusic.game.pressA();
}
else if(e.getKeyCode() == KeyEvent.VK_S){
DynamicMusic.game.pressS();
}
else if(e.getKeyCode() == KeyEvent.VK_D){
DynamicMusic.game.pressD();
}
else if(e.getKeyCode() == KeyEvent.VK_SPACE){
DynamicMusic.game.pressSpace();
}
else if(e.getKeyCode() == KeyEvent.VK_J){
DynamicMusic.game.pressJ();
}
else if(e.getKeyCode() == KeyEvent.VK_K){
DynamicMusic.game.pressK();
}
else if(e.getKeyCode() == KeyEvent.VK_L){
DynamicMusic.game.pressL();
}
}
@Override
public void keyReleased(KeyEvent e) {
if(DynamicMusic.game == null){
return;
}
// 여기서 사용되는 e.getKeyCode() 는 키보드의 아스키 코드값을 가져옴
// KeyEvent.VK_% 도 출력해보면 해당 글자, 숫자의 아스키 코드값이 출력됨
if(e.getKeyCode() == KeyEvent.VK_A){
DynamicMusic.game.releaseA();
}
else if(e.getKeyCode() == KeyEvent.VK_S){
DynamicMusic.game.releaseS();
}
else if(e.getKeyCode() == KeyEvent.VK_D){
DynamicMusic.game.releaseD();
}
else if(e.getKeyCode() == KeyEvent.VK_SPACE){
DynamicMusic.game.releaseSpace();
}
else if(e.getKeyCode() == KeyEvent.VK_J){
DynamicMusic.game.releaseJ();
}
else if(e.getKeyCode() == KeyEvent.VK_K){
DynamicMusic.game.releaseK();
}
else if(e.getKeyCode() == KeyEvent.VK_L){
DynamicMusic.game.releaseL();
}
}
}
'토이 프로젝트 > Spring&Java 갖고놀기' 카테고리의 다른 글
DataPlay project - 1 : java로 Word Cloud 생성기- 크롤링, API, JSON (0) | 2022.05.04 |
---|---|
자바 리듬 게임 만들기(5) : 로그인 창, DAO, DTO, 점수창, score board (6) | 2022.04.19 |
자바 리듬 게임 만들기(3) (0) | 2022.03.22 |
자바 리듬 게임 만들기 (2) : Game(feat. 노트찍기모드) (0) | 2022.03.21 |
자바 리듬 게임 만들기 (1) : 구현목표, Main, DynamicMusic 클래스 (0) | 2022.03.20 |
댓글