이전 절에 예고된대로 게임의 시작처리와 공의 움직임 처리에 대해서 알아보도록 하자. 게임의 시작은 기존 처럼 누구든 먼저 스페이스 바를 누르면서 시작할 수 있다. 다만, 네트워크 게임의 특성상 그룹방 안에 게임 시작 가능 총 인원이 다 입장되어 있는지의 추가 확인이 필요하고(36라인) 확인된 이후에 게임시작이 가능한다. 게임의 시작하게 되면 원격에 떨어져 있는 상대도 곧바로 게임을 시작할 수 있도록 상대에게 game_start 메시지를 보낼 필요가 있다(39라인).
class Ball(Rect):
...
def collide_wall(self, sound_only=False):
# 위쪽 또는 아래쪽 벽
if self.top < 0 or self.bottom > HEIGHT:
if not sound_only:
self.vy = -self.vy # 속도의 y축 방향을 반대로하기
sounds.wall.play()
# 왼쪽 벽
if self.left < 0:
if not sound_only:
self.reset()
sounds.die.play()
return 'b2'
# 오른쪽 벽
if self.right > WIDTH:
if not sound_only:
self.reset()
sounds.die.play()
return 'b1'
return ''
...
def update():
global num_peers
...
# 게임시작 조건
if score.is_game_over():
# 게임 시작은 누구나 먼저 시작가능
if num_peers == MAX_PEERS and keyboard.space:
score.reset()
ball.reset()
net.send_msg(peer_id, 'game_start', True)
else: # 공
if is_host:
ball.move()
net.send_msg('Player2', 'ball_pos', {'center':ball.center, 'vel':(ball.vx, ball.vy)})
who_win = ball.collide_wall()
if who_win: # 점수
score.add(who_win)
net.send_msg('Player2', 'score', who_win)
ball.reset()
else: # Plyer2
ball.move()
ball.collide_wall(sound_only=True)
...
다음으로는 41라인 이후의 볼의 움직임에 관한 것으로, 이전에 살펴본 반사판의 움직임 처리와 많이 유사하다. 공의 움직임에 처리에 대한 책임은 호스트가 갖고 있으며 공의 현재 위치정보를 ball_pos 메시지를 통해 지속적으로 상대에게 보내상대도 자신의 화면에서 공의 위치를 실시간 업데이트 할 기회를 주고 있다(44라인). 공의 위치정보는 공의 중심좌표(ball.center)와 이동속도(ball.vx, ball.vy)에 대해 값을 {'center':ball.center, 'vel':(ball.vx, ball.vy)} 객체 형태로 제공하고 있음을 주의할 필요가 있다.
이제 공이 벽에 부딪히는 상황에 대한 처리로 기존에 collide_wall 멤버함수에 일부 변형이 적용되었는데, 공이 화면 좌우벽에 부딪힌 상황 즉, 승패가 난 상황에 대해 네트워크를 통한 원격 점수 업데이트(48라인)가 필요하기 때문에 그 부분에 대한 처리가 적용되었다. collide_wall 를 자세히 살펴보면, 기존에는 승점 발생시 객체 자신이 점수를 직접 업데이트 했었는데 이제는 승점을 획득한 편이 누구인지에 대한 값(b1 또는 b2)만을 리턴할 뿐(16, 23라인), 승점의 업데이트는 리턴값을 받은 쪽에서 업데이트 하도록 위임한다(45~47라인). 그리고, collide_wall 멤버함수에도 sound_only 라는 파마미터가 추가되었는데, 이전 반사판에서의 경우와 동일하게 호스트가 아닌 일반유저의 경우 공의 반사에 대해서 단지 효과음 처리만 하려는 의도이다.
다음으로는 살펴보는 것을 미뤄둔 수신 메시지의 처리에 대한 부분을 살펴보도록 하자.
def handle_recv_messages():
# 전체 메시지 수신
net.process_recv()
# 특정 메시지 구분
game_start = net.get_msg(peer_id, 'game_start')
bar_pos = net.get_msg(peer_id, 'bar_pos', clear=False)
ball_pos = net.get_msg("Player1", 'ball_pos', clear=False)
who_win = net.get_msg("Player1", 'score')
# 게임 시작
if game_start:
score.reset()
ball.reset()
# 공
if ball_pos:
ball.pos_update(ball_pos['center'], ball_pos['vel'])
# 바
if bar_pos:
if is_host:
bar2.pos_update(bar_pos)
else: # Plyer2
bar1.pos_update(bar_pos)
# 점수
if who_win:
score.add(who_win)
def update():
global num_peers
# 네트워크 수신 메시지 처리
handle_recv_messages()
...
우리가 송신 메시지 처리에서 상대에게 보내려는 메시지들을 버퍼라는 임시공간에 모았다가 주기적으로 한꺼번에 보내는 식으로 적용했던 것을 기억할 것이다. 수신하는 쪽에 처리에서도 이와 유사하게 메시지 수신 버퍼라는게 존재하여 네트워크를 통해서 계속 유입되는 메시지들을 수신 버퍼라는 공간에 계속 모아두었다가 주기적으로 한꺼번에 처리하는 방식을 사용하며, 따라서, 그러한 식의 처리를 위해서 NetNode 객체 안에 process_recv 멤버함수를 주기적으로 호출할 필요가 있다(3라인).