파이썬을 이용해 핑퐁 게임을 만들어 보았다.
현재는 터미널에서 열리지만, html에 연동하여 웹브라우저에서도 게임을 할 수 있도록 업데이트 할 예정이다. (다음 공부 계획^^)
깃헙 코드 : https://github.com/byoungjupark/Pong-Game.git
참고 강의 : https://youtu.be/XGf2GcyHPhc
모듈(Module) 이란?
파이썬 파일(*.py) 형태로 자주 사용하는 변수, 함수, 클래스 등을 넣어 하나의 모듈로 만들 수 있다. 개인의 편의에 따라 사용자 정의 모듈을 만들 수도 있고, 타인이 만들어놓은 모듈을 사용할 수도 있다.
import turtle
처음부터 직접 모든 것을 만들 수 있지만, 그래픽을 표현할 때 유용한 'turtle' 모듈을 불러오기로 한다. 캔버스와 펜을 제공하여 원하는 그림이나 모양을 만들 수 있다. 보통은 'pygame' 모듈을 많이 사용한다.
이제 turtle 모듈에 있는 클래스와 함수들을 사용하여, 게임 초기화면의 나타날 아이템들을 만들어보자!
바탕화면(게임 창)과 패들 2개(탁구채), 공, 점수판이 보여져야 한다.
turtle 클래스와 함수를 불러 각각에게 객체를 만들어 주고 기본값을 설정한다.
바탕화면은 Screen 함수를 불러와서 wn 이라는 변수로 지정한다. Screen 함수 내의 다른 함수들을 이용해서 바탕화면 스타일을 만든다.
title : 게임 창이 켜질 때 나타나는 제목을 표시해준다.
bgcolor : 배경화면 색상
setup : 가로 세로 크기 (픽셀 단위)
tracer : 객체의 움직임을 화면에 표시해준다. 이 게임에서는 움직임을 일일히 남길 필요가 없기 때문에 값은 0으로 한다.
예) 화살표 모양의 객체를 생성하고 한 방향으로 이동하는 함수를 넣었을 때, 그 흔적을 남긴다.
왼쪽과 오른쪽에 위치한 패들 2개를 만든다. turtle 모듈의 Turtle 클래스를 불러와 각각 paddle_a와 paddle_b라는 변수로 지정해준다.
클래스로 찍어낸 변수이기 때문에 객체이기도 하다
speed : 움직이는 속도를 말한다.
shape : 모양은 square, circle, triangle 등 심지어 turtle 도 있다.
shapesize : 값1은 20px을 의미한다. wid = 5, len = 1 을 입력하여 세로 100px, 가로 20px의 패들을 만든다.
penup : turtle은 그림을 그릴 때 유용한 모듈로 penup과 pendown 메소드가 있다. penup은 캔버스에서 펜을 띄우는 것, pendown은 캔버스에 펜을 놓는 것을 말한다. 즉, penup인 상태에서 이동할 때 그림을 그리지 않을 것이다.
goto : 패들의 위치 초기값 설정. 바탕화면의 정중앙은 0,0으로 기준점이 된다. 처음 바탕화면의 가로는 800px, 세로는 600px로 설정하였다. 즉 가로의 양끝은 각각 400, -400 이 되고, 세로의 양끝은 300, -300이 된다. (아래 그림 참조)
paddle_a와 paddle_b가 위 아래로 이동하는 함수를 만든다. 각 패들 별로 위 아래 2개의 함수를 만들기에 총 4개의 함수가 될 것이다. (위 아래와 연관된 y축 관련 매소드가 사용될 것이다.)
paddle_a_up 이라는 함수를 만들어 패들a가 위로 이동하는 기능을 넣을 것이다.
패들a의 현재 y좌표 값을 알려주는 메소드 ycor를 사용하고, y라는 지역변수로 받아준다.
y자신에게 +20px을 한다.
sety 메소드를 사용하여 패들a의 y좌표 값을 y로 설정한다. 즉 괄호 안의 y로 이동하게 된다.
반대로 paddl_a_down은 패들 a가 아래로 이동하는 기능을 가진다.
다른 부분은 같고, y -= 20 을 입력하여 20px씩 아래로 이동하도록 한다.
이런 식으로 패들b의 함수도 만들 수 있다.
이제 키보드를 누를 때마다 위의 함수가 구현되도록 서로를 연결시켜보자!
Screen.listen : 키보드 관련 이벤트를 인식시키는 것이 첫 스텝. listen 함수가 인식시켜준다. Screen 함수를 wn 변수로 만들었기에 wn.lisen()으로 표현한다.
onkeypress(함수, "키보드값") : 패들a는 w, s를 누를 때 위, 아래로 움직인다. 패들b는 화살표 위, 아래를 누를 때 움직인다. 화살표는 Up, Down으로 표시한다. (첫글자는 대문자로 해야 한다)
w를 누르면 paddle_a_up 함수를 불러와 함수를 구현하게 된다.
while True:
wn.update()
while문을 사용하여 게임 아이템들(패들, 공, 점수판)의 변화를 반영할 수 있다.
Screen의 update 함수는 말그대로 업데이트 해준다. 화면에 변화가 발생하면 변화된 부분을 보여준다. 즉, while문이 True일 동안 Screen 업데이트가 연속적으로 발생할 것이다.
이전에 만들어놓은 공은 현재 정적인 상태이다(dx,dy 함수에서는 '어느 쪽으로 얼마만큼 이동할거야!' 라고 선언만 한 상태임). 동적인 상태가 되기 위해 아래 문장을 while문 안에 넣는다.
ball.setx(ball.xcor() + ball.dx())
현재 x좌표(xcor)에서 dx 방향값(dx)을 더한다. 더한 값(xcor+dx)으로 위치시킨다.
while문이 진행되는 동안, 공은 오른쪽으로 2px만큼 연속적으로 움직이게 된다.
공의 y좌표도 똑같이 입력한다.
위 단계까지 입력하고 실행을 하면 공은 우하향으로 이동하면서 화면에서 사라질 것이다. if문을 사용하여 화면의 끝에 닿았을 때 공이 반대 방향으로 이동하도록 만들어보자. 크게 세로 벽면과 가로 벽면에 닿는 경우 2가지로 나눌 수 있고, 실행할 반응이 다르다.
- 세로 벽면에 닿았을 때 : x좌표는 그대로, y좌표는 반대 방향으로 이동
- 가로 벽면에 닿았을 때 : 정중앙으로 이동(0,0), x좌표 반대 방향 이동, 점수 1점 추가
1. 세로 벽면에 닿았을 때
위의 첫 if문을 해석하면 다음과 같다.
2. 가로 벽면에 닿았을 때
우선 if문 입력 전에 점수 변수를 만들어 초기값을 0으로 설정한다.
score_a = 0, score_b = 0
공이 패들에 닿을 때의 반응도 if문을 사용하여 만들 수 있다.
벽면에 닿을 때 썼던 if문의 구조를 이해했다면, 공이 패들에 닿는다는 것을 다음과 같이 표현할 수도 있다.
공의 x, y 좌표 = 패들의 x, y 좌표
다만, 공과 패들은 1px이 아니다! 각각의 크기를 설정했기 때문에, 사이즈를 고려하여 if문을 작성하도록 한다.
if문 작성 시 참고할 것
공 사이즈 : 20 x 20
패들 사이즈 : 20 x 100
패들 x좌표 : +-350
패들 y좌표 : 수시로 움직인다
조건 1 : 공의 x좌표가 340~350이고,
(ball.xcor() > 340 and ball.xcor() < 350)
공은 패들 x좌표 350 보다 작고, 패들 가로 사이즈 20을 고려하여 뺀 x좌표 330보다는 커야할 것이다. 330으로 실행하면 공이 패들과 겹쳐져 340으로 설정했다.
조건 2 : 공의 y좌표가 패들 y좌표의 -40~40이면,
(ball.ycor() < paddle_b.ycor()+40 and ball.ycor() > paddle_b.ycor()-40)
패들 y좌표는 계속해서 움직이므로 paddle_a.ycor()를 넣어 패들의 현재 y좌표를 설정한다. 패들 세로 사이즈는 100으로 패들의 세로 범위 내에 들려면 -50 ~ +50 일 것이다. (강의를 진행한 유튜버는 값을 40으로 주었는데, 50으로 설정해도 자연스러웠다.)
공의 x위치를 340에 놓는다. ball.setx(340)
공을 반대 방향으로 이동시킨다. ball.dx *= -1