파이썬-파이게임 제로 (순한맛)
매운맛으로 공부하기Mu에디터 커스텀 버전 다운로드Mu에디터 한글화 프로젝트 참여책관련 Q&A
  • 게임제작하며 프로젝트 기반으로 파이썬 배우기 (순한맛)
  • 1. 파이게임 제로(pygame zero) 라이브러리
  • 2. 개발환경 구축
  • 3. Hello World 프로그램에서 시작하자
  • 4. 게임 루프의 이해
  • 5. 플래피 버드(Flappy bird) 게임 만들기
    • 5.1 화면에 배경 이미지(오브젝트) 나타내기
    • 5.2 화면에 플래피버드(오브젝트) 나타내기와 움직이기
    • 5.3 화면에 플래피버드(오브젝트)의 자연스러운 움직임 만들기
    • 5.4 화면에 파이프(오브젝트) 나타내기와 움직이기
    • 5.5 플래피버드와 파이프의 충돌 구현하기
    • 5.6 플래피버드 게임 추가기능 구현하기 (점수기능)
    • 5.7 플래피버드 게임 추가기능 구현하기 (파이프 위치 랜덤화)
    • 5.8 플래피버드 게임 추가기능 구현하기 (플래피버드 애니메이션)
  • 6. 블록격파(Breakout) 게임만들기
    • 6.1 게임무대에 배경과 배우 등장시키기
    • 6.2 배우들의 움직임 구현하기
    • 6.3 공의 반사와 블록격파 구현하기
    • 6.4 충돌검사시 고려할 것들
    • 6.5 (보너스) 오리지널 게임처럼 만들어 보기
  • 7. 트윈비(TwinBee) 슈팅게임 만들기
    • 7.1 스크롤 배경객체 만들기, 배경음, 배우(적, 주인공) 등장시키기
    • 7.2 배우들의 움직임과 총알 공격 구현하기
    • 7.3 충돌처리 및 기타정보(점수 및 게임종료) 표기
    • 7.4 그 밖에 도전과제
  • 8. 퐁(Pong) 게임 만들기
    • 8.1 절차지향으로 개발하기 1
    • 8.2 절차지향형으로 개발하기 2
    • 8.3 객체지향 개발이론 (객제지향 디자인이란)
    • 8.4 객체지향 개발이론 (사용자 정의 객체 만들기, 상속)
    • 8.5 객체지향으로 개발하기 1
    • 8.6 객체지향으로 개발하기 2
    • 8.7 (보너스) 파이게임제로 예제버전 1
    • 8.8 (보너스) 파이게임제로 예제버전 2
  • 9. 배틀 시티(Battle city) 게임 만들기
    • 9.1 절차지향형으로 개발하기 1
    • 9.2 절차지향형으로 개발하기 2
    • 9.3 객체지향으로 개발하기 1
    • 9.5 객체지향 개발이론 (인터페이스)
    • 9.6 객체지향으로 개발하기 2
    • 9.7 객체지향으로 개발하기 3
    • 9.8 객체지향으로 개발하기 4
  • 10. 퐁(Pong)을 네트워크 게임으로 만들기
    • 10.1 네트워크 게임방식의 이해와 라이브러리 설치
    • 10.2 릴레이 서버 구동과 클라이언트 접속
    • 10.3 네트워크 게임으로 만들기 1
    • 10.4 네트워크 게임으로 만들기 2
  • 부록
    • 게임을 단 한 개의 실행파일로 패키징 하기
    • 뮤 에디터 단축키 모음
Powered by GitBook
On this page

Was this helpful?

  1. 10. 퐁(Pong)을 네트워크 게임으로 만들기

10.3 네트워크 게임으로 만들기 1

단 한대의 컴퓨터에서만 동작하는 게임(이를 지칭하는 용어로 standalone 게임이라 함)을 네트워크 게임으로 변모시키기 위해서는 기존 퐁 게임을 구성하는 객체들(점수, 반사판, 공)의 코드에 일부 수정이 필요하다. 우리가 처음부터 네트워크형 게임으로 만들 것을 작정하고 객체설계를 한 것이 아니기 때문에 네트워크 게임을 위해 미흡한 부분이 있고 이번 절에서는 이를 보완하도록 하자.

먼저, 점수(Score) 객체이다. 참고로 각 객체의 코드 전체가 아닌 추가/수정된 부분만 따로 떼내 살펴보도록 하겠다. reinit 멤버 함수가 추가로 필요한데 이 함수의 목적은 게임을 아예 최초 시작단계로 돌아가기 위한 목적이다. 이런 상황은 네트워크 게임에서만 발생할 수 있는 상황인데 언제 필요할 수 있을까? 그것은 한창 게임 진행 중에 갑자기 네트워크 불완전성으로 인한 또는 의도적인 상대방의 불특정 게임중단 상황을 대응하기 위함이다.

class Score():
    ...
    
    def reinit(self):
        self._b1_score = -1
        self._b2_score = -1
    ...

def update():
    global num_peers
    ...   
    
    # 상대 연결 끓김
    num_peers = len(net.get_peers())
    if num_peers < MAX_PEERS:
        if is_host:
            score.reinit()
            ball.reset()
        else:  # Plyer2 
            print("The host is disconnected.")
            exit()  # 호스트가 종료되면 방 폭파
    ...

릴레이 서버방식의 게임의 특징은 우리가 앞서 살펴봤듯이 그룹방에서 호스트가 중요하고, 호스트는 게임의 전체적인 상태의 관리를 통해 게임을 유지시키는 것을 담당하는 주인장 같은 역할이다. 이 호스트가 어떤 상황에서든 게임 도중에 그룹방에서 나가게 되면 당연히 더이상 게임은 진행될 수 없는 상황으로, 어떻게 처리할지는 여러 시나리오를 만들 수 있는데, 제일 간단히 처리하는 방법은 우리가 알고 있는 소위 방폭파(?)라는 방법으로 그룹안에 있는 모든 클라이언트들이 그룹방에서 내쫒기는 것으로, 즉 모든 클라이언트들의 기존 서버와의 네트워크 연결을 강제종료 시키는 것이다(18~20라인). 이와는 반대로 호스트가 아닌 일반 유저가 갑자기 그룹방에서 나간 상황에 대해서는 어떻게 처리해야 할까? 이 때가 바로 renint 함수를 호출해서 게임을 완전초기화해서 호스트만 혼자 그룹방에서 게임상대자의 접속을 기다리는 최초 상태로 돌리는 지점인 것이다(15~17라인).

그렇다면, 게임중간에 어떤 클라이언트가 그룹에서 빠져나갔는지를 감지할 수 있을까? 그 해답은 update 함수의 5번 라인에 NetNode 객체의 get_peers 멤버함수로 이 함수는 현재 그룹안에 있는 클라이언트들의 id목록을 리턴해주며 update 함수 안에 위치시켜 그 변동성을 실시간 확인함으로써 게임중간에 어떤 클라이언트가 그룹에서 빠져나갔는지 알 수 있다.

다음으로는 각 클라이언트들의 반사판의 조작과 움직임 처리하는 루틴을 살펴보자.

class Bar(Rect):
    ...
    def collide_ball(self, sound_only=False):
        if self.colliderect(self.ball):
            if not sound_only:
                # # 10픽셀 앞으로 먼저 튀어오르기
                if self.ball.vx < 0:  # bar1 이면
                    self.ball.x += 10
                else:  # bar2 이면
                    self.ball.x -= 10
                self.ball.vx = -self.ball.vx  * SPEED_UP # 속도의 x축 방향을 반대로하기
                ''' 공이 윗측 진입하면서 반사판 윗측에 부딪힐 때 또는
                    공이 아래측 진입하면서 반사판 아래측에 부딪힐 때는 진입방향 그대로 반사 '''
                if (self.ball.vy > 0 and self.ball.centery < self.centery) or \
                    (self.ball.vy < 0 and self.ball.centery > self.centery):
                    self.ball.vy = -self.ball.vy * SPEED_UP
            sounds.bar.play()
    ...

def update():
    ...
    
    # 반사판
    if keyboard.a or keyboard.up:
        my_bar.up()
    if keyboard.z or keyboard.down:
        my_bar.down()
    net.send_msg(peer_id, 'bar_pos', my_bar.center)

    for bar in bars:
        if is_host:
            bar.collide_ball()
        else:  # Plyer2
            bar.collide_ball(sound_only=True)     
    ...
    
    # 메시지 전송  
    net.process_send()

반사판의 조작은 호스트/일반 유저이든 상관없이 둘 다 본인이 선호하는 방식으로 키보드의 'a' 또는 '윗방향키' 로 윗방향으로 움직이고, 키보드의 'z' 또는 '아래방향키' 로 아래방향으로 움직일 수 있다(24~27라인). 참고로 my_bar 변수값은 이전 장의 connect_server 함수에서 살펴보았듯이 그룹방에 입장한 순서에 따라, bar1 또는 bar2값이 사전에 할당되어 있음을 알 수 있다. 여기까지 UI적으로 본인화면에 움직임을 만든 후에는 상대방에게도 나의 움직임에 대한 변경폭을 네트워크를 통해 알려주어야만 서로 상대방 화면에 상의 움직임이 실시간으로 보일 것이다. 그러한 목적의 멤버함수가 바로 그 다음 9번 라인에서 사용한 send_msg 함수이다.

이 함수는 3가지 파라미터 값을 갖는다. 첫번째로 이 (정보성)메시지의 수신자의 그룹방에서의 id값으로 여기서는 peer_id를 인자로 넘겼는데, 마찬가지로 이전 장의 connect_server 함수에서 이 값이 사전 설정되어 있다. 즉, 호스트가 된 게임유저는 상대인 일반유저에게/일반유저인 게임유저는 호스트에게 메시지를 보내게 되는 것이다. 두번째 값은 메시지 수신자가 자신에게 도착하면 여러 메시지들 중에서 특정 메시지를 구분할 수 있게 하려는 목적의 메시지 식별자(일종의 id)이다. 마지막으로는 실제 메시지의 내용으로 여기서는 반사판 객체의 중심좌표(my_bar.center)를 보내고 수신측에서는 이 값을 화면에 UI적으로 반영시킴으로서 상대방 반사판의 움직임을 표현한다. 그러나, send_msg 함수는 즉시 상대에게 메시지를 전송까지 하는 건 아니고, 해당 메시지를 메시지 송신 버퍼(buffer)라고 보낼 메시지들을 임시로 모아두는 기억장소에 저장해 놓는 것까지의 역할을 하고, 실제 메시지 전송은 38라인에 있는 NetNode 객체의 process_send 멤버함수에 의해서 송신 버퍼에 임시저장되어 모아진 메시지들을 일괄 전송하는 일을 주기적으로 하게 된다. 따라서, 이 함수를 upate 함수의 맨 마지막에 항상 위치시켜야 한다는 것을 잊지말자. 지금까지 메시지 송신과정의 전반을 이해했고, 메시지를 전송하는 부분이 있다면, 당연히 해당 메시지 수신하여 처리하는 루틴이 있을 것이고, 이 부분에 대해서는 다음 절에서 자세히 살펴보도록 하자.

마지막으로 반사판 조작해서 새롭게 추가된 부분을 더 살펴보면(31~34라인), 게임 호스트와 일반유저의 처리를 구분했고, 호스트의 경우, 단독게임(standalone) 때와 다를바가 없고 일반유저의 경우는 collide_ball 함수를 호출하면서 전에는 본적이 없는 sound_only 라는 새로운 인자값을 넘기고 있다. 그 이름에서 알 수 있듯이 공의 충돌감지에 대한 처리시 반사에 대한 직접 처리는 하지 않고, 효과음만 발생시키려는 목적이다(5번 라인). 왜 이런 처리가 필요할까? 그것은 네트워크 게임의 특성상 원격에 게임에 참여하는 모든 게임참여자의 실시간 일치(sync) 를 위해 각 클라이언트가 독립적으로 처리해야 하는 부분이 있고, 호스트가 대표해서 처리해야하는 루틴이 분리되게 된다. 따라서, 이 부분을 잘 고려해 역할분담해 코딩하는 것이 필요하다. 그러나, 이러한 역할분담에 대해서는 사전에 미리 한번에 미리 알 수 있기보다는 게임을 만들어가는 과정에서 실제 네트워크 환경에서 테스트를 해보며 발견되는 경우가 많다.

그럼 다음 절에 이이서 게임 제작의 남은 부분인 게임의 시작처리와 공의 움직임 처리, 송신 메시지의 수신 처리에 대해서 알아보도록 하자.

Previous10.2 릴레이 서버 구동과 클라이언트 접속Next10.4 네트워크 게임으로 만들기 2

Last updated 2 months ago

Was this helpful?

Page cover image