TIL 3. 핑퐁 게임 만들기

Byoungju Park·2021년 6월 16일
0
post-thumbnail

파이썬을 이용해 핑퐁 게임을 만들어 보았다.
현재는 터미널에서 열리지만, html에 연동하여 웹브라우저에서도 게임을 할 수 있도록 업데이트 할 예정이다. (다음 공부 계획^^)
깃헙 코드 : https://github.com/byoungjupark/Pong-Game.git
참고 강의 : https://youtu.be/XGf2GcyHPhc

1. turtle 모듈 불러오기

모듈(Module) 이란?
파이썬 파일(*.py) 형태로 자주 사용하는 변수, 함수, 클래스 등을 넣어 하나의 모듈로 만들 수 있다. 개인의 편의에 따라 사용자 정의 모듈을 만들 수도 있고, 타인이 만들어놓은 모듈을 사용할 수도 있다.

import turtle 

처음부터 직접 모든 것을 만들 수 있지만, 그래픽을 표현할 때 유용한 'turtle' 모듈을 불러오기로 한다. 캔버스와 펜을 제공하여 원하는 그림이나 모양을 만들 수 있다. 보통은 'pygame' 모듈을 많이 사용한다.

2. 게임 아이템 만들기

이제 turtle 모듈에 있는 클래스와 함수들을 사용하여, 게임 초기화면의 나타날 아이템들을 만들어보자!

바탕화면(게임 창)과 패들 2개(탁구채), 공, 점수판이 보여져야 한다.
turtle 클래스와 함수를 불러 각각에게 객체를 만들어 주고 기본값을 설정한다.

2-1 바탕화면


바탕화면은 Screen 함수를 불러와서 wn 이라는 변수로 지정한다. Screen 함수 내의 다른 함수들을 이용해서 바탕화면 스타일을 만든다.

title : 게임 창이 켜질 때 나타나는 제목을 표시해준다.
bgcolor : 배경화면 색상
setup : 가로 세로 크기 (픽셀 단위)
tracer : 객체의 움직임을 화면에 표시해준다. 이 게임에서는 움직임을 일일히 남길 필요가 없기 때문에 값은 0으로 한다.
예) 화살표 모양의 객체를 생성하고 한 방향으로 이동하는 함수를 넣었을 때, 그 흔적을 남긴다.

2-2 패들 2개


왼쪽과 오른쪽에 위치한 패들 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 : 왼쪽 위치, 벽에서 50px 떨어트린다. 세로는 중앙 위치 (-350, 0)
  • paddle_b : 오른쪽 위치, 벽에서 50px 떨어트린다. 세로는 중앙 위치 (350, 0)

2-3 공과 점수판


  • 공은 정중앙에 위치하여 goto(0,0)을 준다.
    dx, dy : 객체의 이동 방향과 이동 간격을 표시할 수 있다. +는 오른쪽, 위쪽 / -는 왼쪽, 아래쪽을 이미한다. 숫자는 이동될 간격을 의미한다. 따라서 dx = +2, dy = -2를 입력하면 2px 간격으로 우하향하게 된다.
  • 점수판
    write : 텍스트를 표시해준다. write에 내장된 파라미터(align, font 등)을 이용해 텍스트 편집을 할 수 있다. 초기값으로 Player A: 0 Player B: 0 을 입력한다.
    hideturtle : 점수판의 텍스트만 필요하기 때문에 turtle의 기본모양인 화살표는 숨기도록 한다.

3. 키보드 누를 때마다 패들 움직이기

3-1 패들 움직이는 함수 만들기

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의 함수도 만들 수 있다.

3-2 키보드 누를 때마다 패들함수 반응 시키기

이제 키보드를 누를 때마다 위의 함수가 구현되도록 서로를 연결시켜보자!

Screen.listen : 키보드 관련 이벤트를 인식시키는 것이 첫 스텝. listen 함수가 인식시켜준다. Screen 함수를 wn 변수로 만들었기에 wn.lisen()으로 표현한다.

onkeypress(함수, "키보드값") : 패들a는 w, s를 누를 때 위, 아래로 움직인다. 패들b는 화살표 위, 아래를 누를 때 움직인다. 화살표는 Up, Down으로 표시한다. (첫글자는 대문자로 해야 한다)
w를 누르면 paddle_a_up 함수를 불러와 함수를 구현하게 된다.

4. 게임 진행하는 동안 일어나는 상황들

while True:
	wn.update()

while문을 사용하여 게임 아이템들(패들, 공, 점수판)의 변화를 반영할 수 있다.
Screen의 update 함수는 말그대로 업데이트 해준다. 화면에 변화가 발생하면 변화된 부분을 보여준다. 즉, while문이 True일 동안 Screen 업데이트가 연속적으로 발생할 것이다.

4-1 공 움직이기


이전에 만들어놓은 공은 현재 정적인 상태이다(dx,dy 함수에서는 '어느 쪽으로 얼마만큼 이동할거야!' 라고 선언만 한 상태임). 동적인 상태가 되기 위해 아래 문장을 while문 안에 넣는다.

ball.setx(ball.xcor() + ball.dx())
현재 x좌표(xcor)에서 dx 방향값(dx)을 더한다. 더한 값(xcor+dx)으로 위치시킨다.

while문이 진행되는 동안, 공은 오른쪽으로 2px만큼 연속적으로 움직이게 된다.
공의 y좌표도 똑같이 입력한다.

4-2 공이 벽면에 닿았을 때


위 단계까지 입력하고 실행을 하면 공은 우하향으로 이동하면서 화면에서 사라질 것이다. if문을 사용하여 화면의 끝에 닿았을 때 공이 반대 방향으로 이동하도록 만들어보자. 크게 세로 벽면과 가로 벽면에 닿는 경우 2가지로 나눌 수 있고, 실행할 반응이 다르다.

  • 세로 벽면에 닿았을 때 : x좌표는 그대로, y좌표는 반대 방향으로 이동
  • 가로 벽면에 닿았을 때 : 정중앙으로 이동(0,0), x좌표 반대 방향 이동, 점수 1점 추가

1. 세로 벽면에 닿았을 때

위의 첫 if문을 해석하면 다음과 같다.

  • 조건 : 공의 y좌표 값이 290을 초과하면, ball.ycor() > 290
    세로(y) 끝 좌표값은 각각 +-300 이다. 공의 크기가 20 x 20 인 점을 참조해서 y좌표 값은 300보다 작은 290을 준다. 그 이유는 300을 한도로 설정하면 공이 반쯤 나간 상태에서 튕겨질 것이므로 부자연스럽다.
  • 공의 y위치를 290에 놓는다. ball.sety(290)
  • 공을 반대 방향으로 이동시킨다. ball.dy *= -1

2. 가로 벽면에 닿았을 때
우선 if문 입력 전에 점수 변수를 만들어 초기값을 0으로 설정한다.
score_a = 0, score_b = 0

  • 조건 : 공의 x좌표 값이 290을 초과할 때, ball.xcor() > 290
  • 공의 위치를 정중앙으로 놓는다. ball.goto(0,0)
    공을 정중앙으로 이동시키는 이유는 한쪽이 점수를 획득하며 새로운 게임이 시작되기 때문에 처음 위치로 되돌리는 것이다.
  • 공을 반대 방향으로 이동시킨다. ball.dx *= -1
  • 점수를 1점 더한다. score_a += 1
  • 점수판의 초기값을 지운다. pen.clear()
    점수판(pen)은 clear 메소드를 써서 지운 다음 다시 write하여야 한다. 지우지 않으면 계속 덮어씌워질 것이다.
  • 점수판을 새로 쓴다.
    pen.write("Player A: {} Player B: {}".format(score_a, score_b)

4-3 공이 패들에 닿았을 때

공이 패들에 닿을 때의 반응도 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

profile
wanna be good programmer

0개의 댓글