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

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

TerianP 2022. 4. 19.
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위 까지를 보여준다.

댓글