← 목록으로
브라우저 게임 · PART 2

부수라고 쌓는 벽돌도 있다 - 오락실 두 번째 기계, 벽돌깨기

2026.07.09 7분 읽기
부수라고 쌓는 벽돌도 있다 - 오락실 두 번째 기계, 벽돌깨기

두 번째 기계는 뭘 들여놓을까

퐁 게임을 만들고 다음 단계를 결정하는데 내가 원한 건 퐁 게임에서 없었던 개념이나 기능이 추가된 게임이었어. 재미있어 보이는 걸 고르는 게 아니라 배울 게 있는 걸 고르는 거지. 이왕 만드는 거 하나라도 더 배워야 남는 장사잖아. 그게 내 목적이기도 하고.

문제는 기준을 세워놓고도 내 눈엔 셋 다 그놈이 그놈으로 보인다는 거였지. 어떤 게임에 어떤 개념이 들어있는지 내가 알 턱이 있나. 그래서 AI 조카한테 물었더니 벽돌깨기를 짚어줬어. 패들이랑 공은 퐁에서 이미 다뤄봤고, 벽돌 50개를 한꺼번에 다루는 법이 새로 배울 거라나. 아는 것 위에 새 것을 하나씩 얹는 게 제일 좋은 순서라기에 그렇게 벽돌깨기로 정했어.

벽돌이 50개인데, 변수를 50개 만들어야 하나?

퐁은 요소가 단출했어. 공 하나, 패들 둘. 근데 벽돌깨기는 벽돌이 5줄에 10개씩, 50개야. 나야 뭐 벽돌이 50개든 100개든 "많네" 싶었을 뿐인데, AI 녀석 말로는 바로 이게 이번 게임의 핵심이래.

"퐁에서는 공이랑 패들에 이름표(변수)를 하나씩 붙이면 됐어. 그런데 벽돌 50개에 brick1, brick2, brick3... 이러면서 이름표 50개를 붙일 순 없잖아. 그래서 배열을 쓰는거야."

배열이 뭐냐고 물었더니, 상자 50개를 방바닥에 늘어놓는 게 아니라 서랍장 하나에 칸칸이 넣어두는 거래.

const bricks = []; // 벽돌들을 담아둘 빈 서랍장(배열)

for (let row = 0; row < 5; row++) { // 바깥 반복문: 줄 (5줄)
  for (let col = 0; col < 10; col++) { // 안쪽 반복문: 그 줄의 벽돌 (10개)
    bricks.push({ x: ..., y: ..., color: ... }); // 벽돌 하나 만들어서 서랍장에 넣기
  }
}

반복문 안에 반복문이 들어간 게 처음엔 어지러웠는데, "바깥은 줄, 안쪽은 그 줄의 벽돌"이라고 생각하니 이해가 되더라고. 벽돌공이 한 줄 쌓고, 그 다음 줄 쌓고 하는 것처럼 말이야.

그럼 벽돌이 깨지는 건 어떻게 처리하냐고 물었더니, 이 서랍장의 진짜 묘미는 뺄 때라는 거야. 공이 벽돌을 맞히면 그 벽돌을 배열에서 빼버리는데(splice라는 걸 쓴대), 화면은 매번 "지금 배열에 있는 벽돌"만 다시 그리기 때문에 배열에서 빠지는 순간 화면에서도 알아서 사라진다더라고. 듣고 보니 장부에서 지우면 진열대에서도 치워지는 셈이잖아. 파이썬 배울 때 리스트라는 걸 스치듯 봤는데, 그게 실전에서 이렇게 쓰이는구나 싶었어.

이번 게임의 구조를 한 장으로 그리면 이래. 아래쪽 틀은 퐁에서 그대로 가져왔고, 그 위에 얹은 새 개념이 바로 이 서랍장이야.

퐁에서 가져온 틀(캔버스, 게임 루프, 키 입력, 패들+공+충돌) 위에 새 개념 하나를 얹은 구조 — 배열 하나로 벽돌 50개를 push(쌓기), forEach(그리기), splice(빼기)로 다루고, 배열이 비면 클리어

내친김에 살림을 몇 개 더 주문했어. 목숨 세 개를 하트(♥)로 표시해달라, 위쪽 줄 벽돌일수록 점수를 높게 쳐달라. 멀리 있는 벽돌이 더 비싼 건 옛날 오락실 벽돌깨기의 유서 깊은 규칙이래.

그런데, 무한루프가 또 왔다

퐁 때 나를 괴롭혔던 게 무한 랠리 버그였잖아. 공이 똑같은 각도로만 튕기면 게임이 영원히 안 끝난다는 것. 그런데 이번에 은근히 감동한 게 하나 있어. 내가 시키지도 않았는데, AI 녀석이 그때 일을 기억하고 코드를 짜면서

"지난번 퐁에서 겪었던 무한 랠리 버그를 예방하려고, 이번엔 처음부터 패들 어디에 맞았느냐에 따라 공이 꺾이는 각도가 달라지게 해뒀어."라는 거야.

기특하잖아. 그러면서 슬쩍 이런 생각이 들었어. 이런 식이면 만든 게 쌓일수록 다음 결과물은 알아서 좋아지고, 내 수고는 오히려 줄어드는 거 아닌가. 삽질도 기록해두니 자산이 되는 셈이지.

검증도 그럴싸하게 했어. AI 녀석이 "패들이 공을 완벽하게 따라다니는 가짜 플레이어"를 만들어서 게임을 12만 프레임, 게임 시간으로 33분어치를 순식간에 돌려보더라고. 사람이 33분 동안 게임할 걸 기계가 몇 초 만에 대신 해주는 거야. 그런데 결과가 이상했어.

벽돌이 6개만 깨지고, 게임이 영원히 안 끝나는 거지.

해결법을 요청했고 녀석이 답하기를 공이 패들 정중앙에 맞으면 반사각 공식에 따라 옆으로 가는 속도가 정확히 0이 된대. 그럼 공이 수직으로만 오르내리게 되고, 자기 세로줄의 벽돌을 다 깨고 나면 그 빈 통로에 갇혀서 영원히 위아래로만 튕기는 거지. 우물에 빠진 공이랄까.

퐁에서는 패들 "모서리"가 함정이었는데, 이번엔 "정중앙"이 함정이었어. 교훈을 알고 있어도 버그는 다른 얼굴을 하고 다시 온다는 걸 배웠지.

반사 후에 옆으로 가는 속도가 0에 너무 가까우면 최소한의 속도를 강제로 보장하는 한 줄을 넣었대. 다시 돌려보니 아까 그 조건으로도 2분 만에 클리어, 가짜 플레이어로 세 판을 더 돌렸는데 전부 클리어, 점수도 매판 벽돌 50개어치랑 정확히 일치했어.

기능은 됐고, 이제 때깔이다

게임이 돌아가고 나니 이번엔 다른 게 눈에 밟혔어.(게임이 너무 금방 만들어졌거든.)화면 문구가 너무 투박하기도 하고, 여기저기 디자인을 좀 고쳐보고 싶었어.

그래서 타이틀도 좀 넣어보고, 화면에 보이는 문구들이 조금 더 게임스러웠으면 좋겠다 싶었거든. 해서 생각했던 이런저런 내용의 수정을 요청했고 AI 조카가 웹 검색까지 해가며 근사한 단어를 하나 물어왔어.

어트랙트 모드(attract mode). 옛날 오락실 기계가 손님을 끌려고 제목이랑 "INSERT COIN"을 번쩍번쩍 틀어놓던 대기 화면을 그렇게 부른대. 구경만 해도 무슨 게임인지 알게 한다는 건데, 블로그를 스크롤하다 마주치는 우리 게임한테 딱이잖아.

이번엔 코드부터 고치게 하지 않고, 시안을 세 개 만들어 오라고 했어. 오락실 어트랙트 모드, 커서 메뉴, 레트로 터미널 부팅 화면. 실제로 업무를 하며 간혹 시안을 업체에 요청하면 적어도 며칠씩 걸리곤 했는데, 이 녀석은 순식간에 꽤 그럴듯한 시안을 보여줬어. 진짜 세상 좋아졌다.

새로 만든 벽돌깨기 타이틀 화면 — 어트랙트 모드 스타일의 제목과 점멸 문구, 우측 상단에 CONTROLS ON 표시가 켜져 있다

구경만 하면 감질나잖아. 여기서 바로 플레이해볼 수 있어.

퐁은 일부러 안 고쳤어

여기서 한 가지 결정을 했어. 이 단장을 퐁 게임에는 적용하지 않기로.

이상하게 들리겠지만 이유가 있어. 퐁은 첫 게임의 투박한 모습 그대로 두고, 벽돌깨기부터 새 옷을 입히면, 오락실을 둘러보는 것만으로 게임들이 성장해온 순서가 보이잖아. 진열장이 곧 성장 기록인 셈이지. 어차피 이 블로그가 결과물 자랑이 아니라 과정을 기록하는 곳이니까, 어설픈 과거도 전시품이야. 나중이 조금 더 기대되는 순간이었지.

두 번째 기계를 들이면서 배운 게 하나 있다면 이거야. 두 번째는 처음부터 다시 만드는 게 아니더라고. 아는 틀 위에 새 것 하나를 얹는 거지. 세 번째 기계는 또 뭘 얹게 될지, 벌써 조금 궁금해.

← 이전 글 휴업했던 오락실에서 인터넷으로 새 가게를 냈다 - 브라우저 게임 만들기