두런두런 회고록 . . . =)

My 픽! 점뿌!

정땅미 2024. 11. 20. 02:54

내 생각 중요 코드 - LeaderBoard 중 DB

private void loadMySQLDriver() {
    try {
        Class.forName("com.mysql.cj.jdbc.Driver");
        System.out.println("MySQL JDBC Driver loaded successfully");
    } catch (ClassNotFoundException e) {
        System.out.println("Error loading MySQL JDBC Driver: " + e.getMessage());
        e.printStackTrace();
    }
}

 

이 메서드는 MySQL JDBC 드라이버를 로드해요!

만약 드라이버가 없으면 예외를 출력하는 코드예요!

private static final String DB_URL = "jdbc:mysql://localhost:3306/mypickjump";
private static final String USER = "newuser";
private static final String PASS = "password";

 

그리고 여기서는 데이터베이스 연결에 필요한 URL, 사용자명, 비밀번호를 설정해요!

제가 터미널에서 데이터베이스를 생성할 때 이름을 mypickjump 로 했고, 제 이름은 newuser 그리고 비번은 password 라고 했어요!

private void saveScore(String nickname, int distance) {
    String selectSql = "SELECT distance FROM scores WHERE nickname = ?";
    String insertSql = "INSERT INTO scores(nickname, distance) VALUES(?, ?)";
    String updateSql = "UPDATE scores SET distance = ? WHERE nickname = ?";

    try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
         PreparedStatement selectStmt = conn.prepareStatement(selectSql);
         PreparedStatement insertStmt = conn.prepareStatement(insertSql);
         PreparedStatement updateStmt = conn.prepareStatement(updateSql)) {

        selectStmt.setString(1, nickname);
        ResultSet rs = selectStmt.executeQuery();

        if (rs.next()) {
            int existingDistance = rs.getInt("distance");
            if (distance > existingDistance) {
                updateStmt.setInt(1, distance);
                updateStmt.setString(2, nickname);
                updateStmt.executeUpdate();
                System.out.println("Score updated for " + nickname);
            } else {
                System.out.println("Existing score is higher, not updated");
            }
        } else {
            insertStmt.setString(1, nickname);
            insertStmt.setInt(2, distance);
            insertStmt.executeUpdate();
            System.out.println("New score inserted for " + nickname);
        }
    } catch (SQLException e) {
        System.out.println("Error saving/updating score: " + e.getMessage());
        e.printStackTrace();
    }
}

 

닉네임을 기반으로 점수를 조회하고, 닉네임이 똑같으면 현재 점수와 기존 점수를 비교하여 더 큰 값이 들어가는 코드입니다.

SELECT 문으로 해당 닉네임의 기존 점수를 조회 후,

데이터가 있으면 두 개의 점수를 비교한 후 새 점수가 더 크면 UPDATE 를 실행합니다.

만약 데이터가 없다면 INSERT 문으로 새로운 닉네임과 점수를 삽입합니다.

private List<PlayerScore> getTopScores() {
    List<PlayerScore> scores = new ArrayList<>();
    String sql = "SELECT nickname, MAX(distance) as max_distance FROM scores GROUP BY nickname ORDER BY max_distance DESC";

    try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(sql)) {
        while (rs.next()) {
            String nickname = rs.getString("nickname");
            int distance = rs.getInt("max_distance");
            scores.add(new PlayerScore(nickname, distance));
            System.out.println("Retrieved: " + nickname + " - " + distance);
        }
    } catch (SQLException e) {
        System.out.println("Error getting top scores: " + e.getMessage());
        e.printStackTrace();
    }
    return scores;
}

 

데이터베이스에서 상위 점수를 가져옵니다.

GROUP BY 와 MAX(distance) 를 사용하여 닉네임별 최고 점수를 조회합니다.

점수를 내림차순으로 정렬합니다.


내 생각 중요 코드 - Gamemain

// 점프 로직
jumpVelocity -= GRAVITY; // 점프 속도 감소 (중력 효과)
jumpHeight += jumpVelocity; // 점프 높이 계산

if (jumpHeight <= 0) {
    // 캐릭터가 땅에 도달했을 때
    jumpHeight = 0; // 점프 높이를 초기화
    jumpVelocity = 0; // 속도 초기화
    jumpCount = 0; // 점프 횟수 초기화
    isJumping = false; // 점프 상태 해제
}

 

이 코드는 캐릭터가 점프하는 동작을 처리합니다.

중력 효과를 사용하여 캐릭터가 땅에 도달하면 점프 상태와 속도를 초기화합니다.

더블 점프도 가능하게 하였습니다. 위에서 최대 점수 횟수는 2 로 초기화 했답니다.

// 장애물 이동 및 충돌 검사
Rectangle characterBounds = getCharacterBounds();
for (Iterator<Rectangle> it = obstacles.iterator(); it.hasNext();) {
    Rectangle obstacle = it.next();
    obstacle.x -= backgroundSpeed; // 장애물 이동

    // 충돌 감지
    if (characterBounds.intersects(obstacle)) {
        gameOver(); // 게임 오버 처리
        return;
    }

    // 화면 밖으로 나간 장애물 제거
    if (obstacle.x + obstacle.width < 0) {
        it.remove();
    }
}

 

장애물 충돌 및 이동 코드로, 장애물의 위치를 업데이트하면서 캐릭터와의 충돌을 감지합니다.

충돌 시 gameOver() 메서드를 호출해 게임 오버 상태로 전환하고,

만약 장애물이 화면 밖으로 나가면 리스트에서 제거하여 메모리를 효율적으로 사용합니다.

private void gameOver() {
    timer.stop(); // 게임 루프 타이머 중지
    isGameOver = true; // 게임 오버 상태 설정
    repaint(); // 화면 갱신

    // 1초 후에 게임 재시작 화면으로 전환
    Timer restartTimer = new Timer(1000, e -> {
        ((Timer) e.getSource()).stop(); // 타이머 종료
        SwingUtilities.invokeLater(() -> {
            parentFrame.switchToRestart(distance); // 점수 데이터를 전달하며 재시작 화면으로 전환
        });
    });
    restartTimer.setRepeats(false); // 한 번만 실행
    restartTimer.start();
}

 

이 부분은 캐릭터와 장애물이 충돌했을 때 게임을 종료하는 로직입니다.

돌고 있던 게임 루프를 멈추고 게임 오버 상태를 true 로 만듭니다.

1초 후에 닉네임 입력 화면으로 전환되도록 타이머를 설정했습니다.


내 생각 중요 코드 - ChooseChr

private void handleMouseClick(int mouseX, int mouseY) {
    // 왼쪽 화살표 클릭
    if (mouseX >= (getWidth() / 2 - 230) && mouseX <= (getWidth() / 2 - 230 + leftarrowImage.getWidth() * 0.8)
            && mouseY >= (getHeight() / 2 - leftarrowImage.getHeight() * 0.8 / 2)
            && mouseY <= (getHeight() / 2 + leftarrowImage.getHeight() * 0.8 / 2)) {

        moveLeft(); 
        return; 
    }

    // 오른쪽 화살표 클릭
    if (mouseX >= (getWidth() / 2 + 230) && mouseX <= (getWidth() / 2 + 230 + rightarrowImage.getWidth() * 0.8)
            && mouseY >= (getHeight() / 2 - rightarrowImage.getHeight() * 0.8 / 2)
            && mouseY <= (getHeight() / 2 + rightarrowImage.getHeight() * 0.8 / 2)) {

        moveRight(); 
        return; 
    }

    // 캐릭터 이미지 클릭
    int imgX = (getWidth() - (int)(charactorImages.get(index).getWidth() * scaleFactor)) / 2; 
    int imgY = (getHeight() - (int)(charactorImages.get(index).getHeight() * scaleFactor)) / 2; 

    if (mouseX >= imgX && mouseX <= imgX + charactorImages.get(index).getWidth() * scaleFactor &&
            mouseY >= imgY && mouseY <= imgY + charactorImages.get(index).getHeight() * scaleFactor) {

        selectCharacter(); 
    }
}

 

이 코드는 캐릭터를 선택하는 코드입니다.

왼쪽, 오른쪽 화살표 클릭을 통해 캐릭터를 이동시켜 캐릭터 이미지를 클릭하여 선택할 수 있는 로직입니다.

왼쪽과 오른쪽 화살표 클릭 코드가 다른 이유는 왼쪽은 음수가 발생할 수도 있기 때문이에요!

 

handleMouseClick() 메서드는 마우스 클릭 이벤트에 따라 캐릭터 인덱스를 변경하거나 캐릭터를 선택합니다.

private void startZoomEffect() { 
    if (zoomTimer != null && zoomTimer.isRunning()) { 
        zoomTimer.stop(); 
    }

    zoomTimer = new Timer(20, e -> { 
        scaleFactor += 0.05; // 확대 속도 조절
        if (scaleFactor >= 1.0) { // 최대 확대 비율 설정
            scaleFactor = 1.0; 
            zoomTimer.stop(); 
            transitionTimer.start(); // 확대가 완료되면 전환 타이머 시작
        }
        repaint(); 
    });

    zoomTimer.start(); 
}

 

이 코드에서는 캐릭터 이미지가 선택될 때 확대되는 애니메이션을 구현한 코드입니다.

캐릭터가 확대되면 transitionTimer 을 통해 게임 메인 화면을 돌아갑니다.


내 생각 중요 코드 - Restart

nicknameField = new JTextField(10) {
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (getText().isEmpty()) {
            g.setColor(Color.GRAY);
            g.setFont(getFont().deriveFont(Font.ITALIC));
            g.drawString("6글자 이내", getInsets().left, g.getFontMetrics().getMaxAscent() + getInsets().top + 20);
        }
    }
};
nicknameField.setFont(parent.getGalmuriFont().deriveFont(24f));
nicknameField.setForeground(Color.BLACK);
nicknameField.setCaretColor(Color.BLACK); 
nicknameField.setOpaque(false); 
nicknameField.setBorder(BorderFactory.createEmptyBorder());
nicknameField.requestFocusInWindow();

// 6글자 제한 설정
((AbstractDocument)nicknameField.getDocument()).setDocumentFilter(new DocumentFilter() {
    @Override
    public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException {
        String string = fb.getDocument().getText(0, fb.getDocument().getLength()) + text;
        if (string.length() <= 6) {
            super.replace(fb, offset, length, text, attrs);
        }
    }
});

 

닉네임 필드에 6글자 이내 라는 힌트를 표시하고, JTextField 를 커스터마이징하여 텍스트가 없는 상태에서만 힌트가 보이도록 하였습니다.

DocumentFilter 를 사용하여 입력된 닉네임이 6글자를 초과하지 않도록 제한하였습니다.


내 생각 중요 코드 - MypickJump

public void switchToChooseChr() {
    getContentPane().removeAll();

    chooseChrPanel.resetSelection(); 
    chooseChrPanel.setInitialIndex(0); 
    
    add(chooseChrPanel, BorderLayout.CENTER);
    revalidate();
    repaint();
    
    chooseChrPanel.requestFocusInWindow();
}

 

다양한 화면 간의 전환을 책임지는 메소드입니다.

이전에 선택한 캐릭터 정보를 초기화할 수 있도록 하였습니다.

// BGMPlayer 인스턴스 생성 및 음악 재생
bgmPlayer = new BGMPlayer();
bgmPlayer.playMusic(); // 음악 재생 호출

 

BGMPlayer 클래스를 통해 배경 음악을 재생합니다.