퉁퉁퉁퉁퉁퉁퉁퉁퉁 사후르 게임을 만들어 보자

2025. 4. 28. 19:17개발자 과정/Kotlin

버스를 타고 가던 중 머리에 울리는 퉁퉁퉁퉁퉁퉁퉁퉁퉁 사후르...


아, 이걸로 간단한 게임이나 만들어 볼까? 싶었다. 

모바일이면 좋겠다 싶었다. 참고로 안드로이드의 기본 기능만 활용한다. 

간단한 터치액션 게임이 엔진까지 필요한가 싶더라고~

 

 

사실 생각할게 없는 게임이다. 내 턴이 되는 5초동안 열심이 터치만 하면 된다. 뇌를 녹인다는 밈의 뜻과 일치하는 면이 있는 듯?

 

 

개발 과정에서 여러가지를 배울 수 있었다. 

우선 첫번째로 연속 터치마다 사운드가 다시 재생되도록 했는데, 이렇게 하니 아무 소리도 안났다. 

소리가 나기도 전에 다시 처음부터 재생하니까 그런 것이었다. 

fun playSoundWithMinDuration(minDurationMs: Long) {
    uiCallbacks.gameManager.userAction()
    val currentTime = System.currentTimeMillis()

    // 마지막 재생 시작 후 경과 시간 계산
    val elapsed = currentTime - lastPlayTime

    if (elapsed >= minDurationMs || mediaPlayer == null || mediaPlayer?.isPlaying == false) {
        // 최소 재생 시간 지났거나, 재생 중이 아니면 재생 시작
        playSound()
        lastPlayTime = currentTime
    } else {
        // 0.3초 안 됐으면 무시하거나 대기 - 여기선 무시
        // 필요하면 핸들러 등으로 지연 재생 구현 가능
    }
}

그래서 "퉁 퉁 퉁" 이 계속 이어질 수 있도록 최소 재생시간을 보장했다. 

 

그 다음은 카운트 다운을 어떻게 ui로 편하게 전달할 수 있을까를 고민했다. 

따로 타이머나, 바를 만들기 보다, 버튼이 카운트 다운 표시까지 역할까지 해주는 방식이 좋을거 같았다. 

// 5초 카운트다운 애니메이터 (오른쪽->왼쪽 색 변화)
fun animateButtonCoverFor5Seconds() {
    button.post {
        val buttonWidth = button.width.toFloat()
        countDownAnimator?.cancel()

        countDownAnimator = ValueAnimator.ofFloat(-buttonWidth,0f).apply {
            duration = gameManager.delayTime*2 // 5초
            addUpdateListener { animation ->
                val value = animation.animatedValue as Float
                buttonCover.translationX = value
            }
            start()
        }
    }
}

 

하지만 이상하게 딜레이 타임의 *2 를 해줘야 제대로 원하는 시간만큼 카운트 다운이 된다. 원인은 모르겠다. 

색을 채워주는 투명 버튼커버가 버튼보다 2배 큰가...? 

 

그리고 유저 강화 요소를 넣으면 더 재밌을거 같았다. 

기본적으로 레벨 클리어시 유저의 스테이터스도 상승을 하지만, 유저가 추가로 더 포인트를 줄 수 있는 것이다.

fun showUpgradeSelectionDialog() {
    GameStatus.isDialog=true
    val upgrades = arrayOf("HP HEAL", "ATK +15", "CRITICAL +2%")

    AlertDialog.Builder(this)
        .setTitle("강화 요소를 선택하세요")
        .setItems(upgrades) { dialog, which ->
            when (which) {
                0 -> gameManager.increaseHp()
                1 -> gameManager.increaseAtk()
                2 -> gameManager.increaseCritical()
            }
            dialog.dismiss()
            updateStatus()
            GameStatus.isDialog=false
            gameManager.resumeGame()
        }
        .setCancelable(false)
        .show()
}

그리고 다이얼로그 팝업 상태를 확인하기 위해 isDialog를 준다. 

 

게임이 멈춘 상태인지, 다이얼로그가 팝업 상태인지 등 알아야 할 스테이터스가 늘어나는거 같아, 

전역으로 관리 가능한 공유 자원이 있으면 편할거 같았다. 

object GameStatus {
    var isGamePaused = false
    var isDialog=false
}

싱글톤을 쓰면 아주 야물딱질거 같아서 싱글 톤을 썼다. 

아키텍쳐를 막짜놓은 감이 있어, 어차피 객체끼리 참조하고 스테이터스를 변동시켜야 하기에,

싱글톤이 그나마 복잡성을 줄여줄거 같았다. 

 

우선 글이 길어지니 여기까지만 하고 나머지는 다음에 2편으로 올린다.