8.8 (보너스) 파이게임제로 예제버전 2
패들(Paddle)과 테니스공(TennisBall) 객체 먼저 살펴보자. 두 객체 모두 이전에 만들어 반사판(Bar), 공(Ball) 객체과 크게 다르지 않아 이해에 큰 어려움은 없을 것 같다. 필자의 것과 크게 다른 부분은 패들객체와 볼객체에서 각자에게서 충돌에 대한 처리를 하는 부분이 없고, 메인객체인 게임객체(Game)에서 그 부분을 처리하게 된다.
class Paddle(Rect):
"""
Paddle represents one player on the screen.
It is drawn like a long rectangle and positioned either left or
right on the screen.
Two helper methods move the paddle up or down.
"""
def __init__(self, start_x, start_y):
super().__init__(start_x, start_y, PADDLE_WIDTH, PADDLE_HEIGHT)
def up(self):
if self.y - 5 > 40:
self.y -= 5
def down(self):
if self.y + self.height + 5 < HEIGHT - 40:
self.y += 5
def draw(self):
screen.draw.filled_rect(self, MAIN_COLOR)
class TennisBall():
"""
Represents a tennis ball on the screen
"""
def __init__(self, start_pos, dt):
"""
Initialize the tennis ball position and set the movement rate
"""
self.x, self.y = start_pos
self.dx = self.dy = dt
@property
def pos(self):
return (self.x, self.y)
def move(self):
self.x += self.dx
self.y += self.dy
def draw(self):
screen.draw.filled_circle(self.pos, TENNIS_BALL_RADIUS, MAIN_COLOR)그 밖에 차이점으로는 테니스공 객체는 Rect를 상속받지 않았고, 그렇기 때문에 객체 스스로 자신의 위치정보를 관리해야 할 필요성이 있으며, 그를 위해 38~40라인에서 보는 바와 같이 pos 라는 자신의 현재 위치를 나타내는 속성값을 별도로 갖고 있는 것을 알 수 있다. 또한 property라는 데코레이터(decorator) 를 사용한 파이썬 문법은 처음 등장했기 때문에 낫설기도 할텐데, 간단히 설명하면 특정 데코레이터를 함수명 위에다 @와 함께 붙히면 기존 함수를 수정하지 않고 그 기능을 확장하는 방법을 제공하는 문법으로 여기서는 pos가 더이상 객체의 메소드 함수가 아니라 객체의 속성(property)처럼 간주되길 원한다는 목적에서 붙혔다고 간단히 생각하자. 다시말해 해당 코드(38~40라인)는 아래 5번 라인과 100% 동일하다.
그럼, 저렇게 코딩하면 될 것을 왜 구지 데코레이터를 붙혀서 따로 분리해서 코딩했는가 라고 했을 때, 게임을 만든 저자가 pos 라는 속성값이 중요하기 때문에 강조해서 눈의 띄게 하려는 의미정도로 이해하면 될 것 같다. 이제 다음으로 게임객체를 살펴볼텐데, 거기서 이 property 데코레이터가 한번 더 사용되고, 그제서야 이 데코레이터의 진정한 목적을 알 수 있을 것이다. 그럼, 본격적으로 가장 핵심이면서 덩치도 크고 무거운 게임객체를 살펴보도록 하자.
게임객체는 자신에게 필요한 구성품과 같은 각각의 객체를 직접 생성하고 소멸시키는 것까지 제어한다. 13~14라인에 패들객체를 직접 생성하여 내부 속성값으로 할당하고 있다. 그리고, 연이은 16번 라인의 set_ball 멤버함수를 통해 테니스공 객체를 생성하고 있다.
여기서 set ball 함수의 인자값으로 ball_pos 속성값을 넘기고 있는 것을 알 수 있다. 18~23번 라인의 ball_pos함수에 위에서 언급했던 그 데코레이터가 다시 등장하고 있는데, property 데코레이터를 사용해 본질상 함수인 것을 변수(속성값)처럼 활용할 수 있게 변경했다. 그래서, ball_pos가 더이상 함수가 아닌, self.ball_pos 로 호출하면서 마치 변수처럼 사용하고 있다. ball_pos 함수를 살펴보면 먼저와 테니스공의 pos와 같이 단순히 현재 위치값을 반환하는게 아니라, 반환 전에 공의 위치를 현재 패들의 위치에 따른 패들 중앙에 딱 달라붙은 공의 위치 값으로 위치를 최종 가공한 후에 반환하고 있는 것을 알 수 있다. 그렇다. 사실 property 데코레이터의 본래 활용목적은 이렇게 속성값이 반환시 어떤 추가적인 가공이 필요할 때 사용하기 위한게 주 목적인 것이다.
이제 다른 나머지의 멤버함수에 구현체 대해서는 차근히 하나하나 살펴보면서 알고리즘적인 부분을 이해하면 될 것 같고, 마지막으로 51, 61라인에 사용된 collidepoint 함수에 대해 추가설명하면, 이름에서 알 수 있듯이 충돌검사를 하되, 기존엔 Rect 기반, 즉 사격형 기반으로 충돌검사를 했다면, Point 기반 즉, 픽셀단위 기반으로 충돌을 체크한다는 것이다. 픽셀단위로 확인한다니 더 정밀하게 충돌검사 할 수 있게단 기대가 된다. 그러나, 세상이치가 모든게 다 좋을 수 만은 없고, 약점이 있듯이, 이 경우도 그런데 더 세밀한 검사를 위해 더 많은 연산으로 컴퓨터의 자원을 많이 사용하게 되고, 최악의 경우 게임 전체 실행속도가 늦어지는 단점이 발생할 수 있음을 염두해 둬야 한다. 그래서, 우리는 항상 어느정도의 타협점(tradeoff)에서 코딩을 하게 되는 것이다. 참고로 이 함수는 파이게임제로의 모태가 되는 파이게임 라이브러리 Rect 객체의 멤버함수 이다.
여기까지해서 퐁 게임에 대한 것은 최종 마치도록 하겠다. 이번 보너스 절의 경우, 여러분 수준에서 내용이해에 조금 어려웠을 수 있겠단 생각이 든다. 그러나 어디까지나 말그대로 보너스이다. 모두가 다 이해하길 바라는 것이 아니고 여러분들 중에 누군가에게는 다소 수준보다 높은 지식이 도움이 될 것이라 추가된 것이고, 여러분들 중에 당장 이해를 다 못했을지라도 걱정하지 말라. 그것은 자연스러운 현상이고, 후추에 좀 더 여러 방면에서 지식이 더 자라났을 때, 다시 드려다보기를 몇 차례 더 하다보면 완전히 다 자기 것으로 소화될 날이 올 것이 틀림없다. 그럼, 여기서 마무리 하고, 다음 과에서 게속 객체지향 공부를 이어나가도록 하겠다.
Last updated