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

자바 리듬 게임 만들기(5) : 로그인 창, DAO, DTO, 점수창, score board

TerianP 2022. 4. 19. 01:13
728x90

이전까지는 사실 강의에서 나온대로 거의 그대로 만들었지만, 이번에는 나만의 기능들을 추가해서 만들어보았다.

 

게임에 추가되는 기능은 다음과 같다.

1. DAO, VO, DTO  :

  • DAO 는 DB 와 연결하기 위해서 만들어두는 클래스 DataBase Access Object
  • DTO 는 ValueObject DB 에서 가져온 데이터를 저장하고 db 레코드와 데이터를 매핑하기 위한 클래스이다.
    • 아래 코드에 전부 VO 로 되어있는데 공부하다보니 VO 가 아니라 DTO 를 사용했었던거네요ㅠ.ㅠ 확인하시고 봐주시면 감사하겠습니다
  • VO라는 것도 있는데 사실 VO 랑 비슷한 느낌으로 쓰인다. 이 3가지는 나중에 한번 더 정리하겠다.

2. 로그인 창 : 게임 실행 시 로그인 창이 존재하고 로그인 후 게임 실행이 가능하도록

3. 게임이 끝난 뒤 점수창 출력 : 본인의 점수와 콤보수 를 종합한 랭크를 출력한다.

4. 게임이 실행된 후 음악 선택 화면에서 SCORE BOARD 를 출력해서 각 음악별 가장 점수가 높은 5명을 출력한다.

 

코드에 대한 설명은 대부분 주석을 달아두었기 때문에 주석을 참고하는 것이 훨~~씬 좋을 것이고, 특히나 중요한 본인의 점수&콤보수 음악 선택 화면에서 score board 를 출력하는 부분만 추가적으로 설명하겠다.

혹은 아래의 git 에서 코드에 있는 주석과 readme 를 참고해주시기 바랍니다!!

https://github.com/SeJonJ/DynamicMusic.git

 

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

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

github.com

 

1. DAO

- DAO 에는 Spring 쪽 member table 과 연동하여 해당 table 정보를 끌고와서 로그인하고, 로그인한 사용자로 게임 진행 시 최종 점수를 저장하고, 게임 노래별로 점수를 출력할 수 있게 하는 등 전체적인 DB 연동 및 DB 에서 정보를 가져오는 역할을 한다.

- 상속받은 DB_info 클래스에는 DB에 관한 정보가 담겨있는 추상 클래스이다. 해당 글래스에 DB 주소, 로그인정보 등이 담겨있고, 그 정보들을 상속받아서 사용한다.

package DynamicMusic;

import java.sql.*;
import java.util.ArrayList;

public class LoginDAO extends DB_info {

    // DB 연결을 위한 statement, resultSet
    PreparedStatement pstmt = null;
    ResultSet rs = null;
    String sql;

    // 사용자가 입력한 ID, PW
    private String loginID;
    private String loginPW;

    // DB에서 가져온 ID PW
    private int getMemberCODE;
    private String getID;


    public LoginDAO() {

        try {
            conn = DriverManager.getConnection(getHjDB(), getUser(), getPasswd());
        } catch (SQLException e) {
            System.out.println("DB conn 에러 : " + e.getMessage());
            e.printStackTrace();
        }
    }

    public boolean loginStart() {
        sql = "SELECT MEMBER_CODE, MID, MPASSWD FROM MEMBER WHERE MID = ? AND MPASSWD = ?";


        try {
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, loginID);
            pstmt.setString(2, loginPW);

            rs = pstmt.executeQuery();

//                System.out.println("현재 멤버 코드 : "+getMemberCODE);
//                System.out.println("현재 아이디 : "+getID);
//                System.out.println("현재 패스워드 : "+getPW);


            if (rs.next()) {
                getMemberCODE = rs.getInt("MEMBER_CODE");
                getID = rs.getString("MID");

                System.out.println("로그인 정보 확인 로그인 완료");

                return true;
            }

            return false;

        } catch (Exception e) {
            System.out.println("SQL 에러 발생 : " + e.getMessage());

            return false;
        }

    }


    public void scoreUpdate(int highscore, int highcombo, String musicName) {

//        System.out.println("최고 스코어 : "+highscore);
//        System.out.println("최고 콤보 : "+highcombo);

        try{

            if (musicName.equals("DAYBREAK FRONTLINE")) {
                sql = "UPDATE DMUSIC SET DF_SCORE = ?, DF_COMBO = ? WHERE MEMBER_CODE = ?";


                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, highscore);
                pstmt.setInt(2, highcombo);
//                pstmt.setInt(3, getMemberCODE);
                pstmt.setInt(3, getMemberCODE);


                pstmt.executeUpdate();
//                conn.commit();
                System.out.println("점수 입력 완료");

            } else if (musicName.equals("Lose Yourself - Eminem")) {
                sql = "UPDATE DMUSIC SET LYS_SCORE = ?, LYS_COMBO = ? WHERE MEMBER_CODE = ?";


                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, highscore);
                pstmt.setInt(2, highcombo);
                pstmt.setInt(3, getMemberCODE);

                pstmt.executeUpdate();
//                conn.commit();
            } else if (musicName.equals("TheFatRat - The Calling")) {
                sql = "UPDATE DMUSIC SET TFR_SCORE = ?, TFR_COMBO = ? WHERE MEMBER_CODE = ?";


                pstmt = conn.prepareStatement(sql);
                pstmt.setInt(1, highscore);
                pstmt.setInt(2, highcombo);
                pstmt.setInt(3, getMemberCODE);

                pstmt.executeUpdate();
//                conn.commit();
            }


        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    public ArrayList<UserVO> scorePanel(String musicName){
        ArrayList<UserVO> list = new ArrayList<UserVO>();

        try {
            // 노래에 맞춰 SQL 변경
            if(musicName.equals("DAYBREAK FRONTLINE")) {

                sql = "SELECT M.MNAME AS NAME, D.DF_SCORE AS SCORE FROM MEMBER M NATURAL JOIN DMUSIC D ORDER BY DF_SCORE DESC LIMIT 5";

            }else if(musicName.equals("Lose Yourself - Eminem")){

                sql = "SELECT M.MNAME AS NAME, D.LYS_SCORE AS SCORE FROM MEMBER M NATURAL JOIN DMUSIC D ORDER BY LYS_SCORE DESC LIMIT 5";

            }else if(musicName.equals("TheFatRat - The Calling")){

                sql = "SELECT M.MNAME AS NAME, D.TFR_SCORE AS SCORE FROM MEMBER M NATURAL JOIN DMUSIC D ORDER BY TFR_SCORE DESC LIMIT 5";
            }

            pstmt = conn.prepareStatement(sql);

            rs = pstmt.executeQuery();
//                rs.next();
//                System.out.println(rs.getString("D.MEMBER_CODE"));

            while (rs.next()) {
                String name = rs.getString("NAME");
                int score = rs.getInt("SCORE");

                list.add(new UserVO(name, score));
//                System.out.println(name+" : "+score);
            }

            return list;

        } catch (SQLException e) {
            e.printStackTrace();
            return null;
        }

    }

    private void closeDB() {
        try {
            if (rs != null) {
                rs.close();
            }
            if (pstmt != null) {
                pstmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
//        System.out.println("자원 반납 완료");
    }


    public String getLoginID() {
        return loginID;
    }

    public void setLoginID(String loginID) {
        this.loginID = loginID;
    }

    public String getLoginPW() {
        return loginPW;
    }

    public void setLoginPW(String loginPW) {
        this.loginPW = loginPW;
    }

    public String getGetID() {
        return getID;
    }

//    public static void main(String[] args) {
//        LoginDAO dao = new LoginDAO();
////        dao.scoreUpdate(50,50,"DAYBREAK FRONTLINE");
//
//        ArrayList<UserVO> list = dao.scorePanel("TheFatRat - The Calling");
//        for(UserVO user : list){
//            System.out.println(user.getName() + " : "+user.getScoreTFR());
//        }
//
//    }

}

 

2. LoginFrame

- 로그인 프레임 클래스는 로그인 GUI 창을 띄우기 위한 클래스이다. 게임 실행 이전에 로그인 프레임 클래스가 먼저 실행되고, 해당 클래스에서 loginDAO 에 있는 loginStart() 클래스를 실행하고 true 를 반납할때만 게임이 실행되도록 한다.

- 만약 false 인 경우 "로그인 에러" 창이 뜨게된다.

package DynamicMusic;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class LoginFrame extends JFrame {

    // DB 접속 클래스
    LoginDAO loginDAO = new LoginDAO();

    JPanel jp = new JPanel();

    JButton jbtnLogin;
    JButton jbtnRegister;

    JTextField jtfID;

    // Password 암호화해서 보이게하는것은 JTextField 가 아니라 JPasswordField
    JPasswordField jtfPW;
    JLabel jlbID, jlbPW;

    private boolean loginResult;

    public void loginWindow(){
        jp.setLayout(null);

        jlbID = new JLabel("ID");
        jlbID.setBounds(180,50,50,20);
        jp.add(jlbID);

        jlbPW = new JLabel("Password");
        jlbPW.setBounds(150,100,60,20);
        jp.add(jlbPW);

        jtfID = new JTextField();
        jtfID.setBounds(230,50,100,20);
        jp.add(jtfID);

        jtfPW = new JPasswordField();
        jtfPW.setBounds(230,100,100,20);
        jp.add(jtfPW);

        jbtnLogin = new JButton("login");
        jbtnLogin.setBounds(150,175,100,50);
        jbtnLogin.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                loginDAO.setLoginID(jtfID.getText());
                loginDAO.setLoginPW(String.valueOf(jtfPW.getPassword()));

                if (loginDAO.loginStart()){

                    // 한개의 창만 닫기 : 현재 창만 닫음
                    dispose();

                    // DynamicMusic 게임 시작
                    // DynamicMusic 클래스
                    DynamicMusic dynamicMusic = new DynamicMusic(loginDAO);
                    dynamicMusic.musicStart();

                }else{
                    System.out.println("로그인 실패");
                    JOptionPane.showConfirmDialog(jlbID,"로그인 정보를 확인해주세요","로그인 에러",JOptionPane.DEFAULT_OPTION);

                }
            }
        });
        jp.add(jbtnLogin);

        jbtnRegister = new JButton("회원가입");
        jbtnRegister.setBounds(300,175,100,50);
        jp.add(jbtnRegister);

        add(jp);

        setTitle("Login");
        setSize(600,300);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(null);
        setFocusable(true);

    }
}

 

3. UserDTO

- userDTO 는 DB 에서 정보를 가져오고 DB 에 정보를 저장하기위한 통로로서 사용된다. 즉 DB 의 각 엔티티와 매칭되어 엔티티에 정보를 저장하고, 꺼내와서 사용하기 위해 사용한다고 생각하면 편하다.

package DynamicMusic;

public class UserVO {
    String name;
    int scoreDF;
    int scoreLYS;
    int scoreTFR;

    public UserVO(String name, int score){
        this.name = name;
        this.scoreTFR = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScoreDF() {
        return scoreDF;
    }

    public void setScoreDF(int scoreDF) {
        this.scoreDF = scoreDF;
    }

    public int getScoreLYS() {
        return scoreLYS;
    }

    public void setScoreLYS(int scoreLYS) {
        this.scoreLYS = scoreLYS;
    }

    public int getScoreTFR() {
        return scoreTFR;
    }

    public void setScoreTFR(int scoreTFR) {
        this.scoreTFR = scoreTFR;
    }
}

 

4. scoreResult

- scoreResult 클래스는 게임이 종료 된 후 점수와 콤보를 출력하기 위한 클래스이다.

- Thread 를 사용해서 run 메소드가 실행되면 graphics 를 이용해서 점수 화면 그림과 최고 점수와 현재 콤보수, 점수에 해당하는 랭크를 출력한다 -> 최고 점수와 콤보를 저장하는 메소드는 DAO 클래스에 있고, DynamicMusic 클래스에서 실행한다.

package DynamicMusic;

import javax.swing.*;
import java.awt.*;

public class scoreResult extends Thread {
    private Image scoreResult = new ImageIcon(getClass().getResource("/menu_images/scoreResult.png")).getImage();

    Graphics2D g;

    public scoreResult(Graphics2D g){
        this.g = g;
    }

    public void screenDraw(Graphics2D g){
        String grade=null;


        int totalScore = DynamicMusic.game.score;
        int totalCombo = DynamicMusic.game.combo;
//        int totalScore = 1000;
//        int totalCombo = 50;

        if(totalScore > (300*100*0.9)) {
            grade = "S";
        }else if(totalScore > (300*100*0.6)) {
            grade = "A";
        }else if(totalScore > (300*100*0.4)) {
            grade = "B";
        }else if(totalScore >= 0) {
            grade = "C";
        }
        g.drawImage(scoreResult, 240, 70, null);

        g.setFont(new Font("Arial", Font.BOLD, 100));
        g.setColor(Color.white);
        g.drawString("Score : "+String.valueOf(totalScore), 350, 290);
        g.drawString("Combo : "+String.valueOf(totalCombo), 350,400);

        g.setColor(Color.pink);
        g.drawString(grade, 600, 500);

    }

    @Override
    public void run() {
        screenDraw(g);
    }

    public void close(){
        interrupt();
    }

}

 

5. DynamicMusic, Game 클래스

- 이 두 클래스는 각 기능들이 추가될 때마다 정말 많이 바뀌었다. 그에 따라서 코드도 많이 길어졌기 때문에 아무래도 블로그에 전체 코드를 올릴 수는 없을 듯 하다.

- 그나마 가장 크게 바뀐 것은 게임의 배경이 일반적인 jpg 가 아닌 gif 로 바뀌었다는 점이다. 이렇게 바뀌었기 때문에 코드도 바꾸었는데 이 부분만큼은 한번 정리하고 넘어가겠다 => 고치는데 너무 힘들었어서 기억하기 위해서ㅠㅠ

- Jframe 에서 GIF를 사용하는 방법은 URL 클래스를 사용하는 것이다. 물론 다른 방법도 있지만 URL 객체를 사용하는 가장 큰 이유는 JFrame 외에도 추후 Jar 파일로 빌드하였을 때에도 에러 없이 실행하기 위해서이다.

- 즉 바로 Image 를 사용해서 읽어오는 것이 아니라 URL 클래스를 이용해서 GIF 파일을 한 번 읽어온 후 다시 ImageIcon 으로 형변환하여 저장하면 무난하게 GIF 를 읽어올 수 있게 된다.

- 물론 이후 Graphic 를 사용해서 그래픽 작업은 하는 것은 Image 와 동일하다. 이것을 잘만 사용하면 게임이 실행되는 화면에서도 배경으로 gif 를 보여줄 수 있을 것 같은데 다만 원활하게 실행하기 위해서는 Thread 를 통해서 실행해야할 듯하고, 그 후 쓰레드를 좀 더 잘 쓰게? 재정리하는 과정이 필요할 듯 하다.

// URL 객체를 사용하면 gif 를 graphic 에 띄울 수 있다
URL bakgroundGIF = getClass().getResource("/menu_images/Intro_bakground.gif");
private Image introBackground= new ImageIcon(bakgroundGIF).getImage();

 

6. 실행 사진

- 원래는 동영상으로 찍고 싶었지만 사진으로 대체한다.

로그인 창
게임 메인 화면(뒤 배경은 gif)

- 내가 로그인한 super 라는 유저가 보이는 것을 확인할 수 있다

score board

- 해당 곡의 1~5위 까지를 보여준다.