파이썬-파이게임 제로 (순한맛)
매운맛으로 공부하기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. 9. 배틀 시티(Battle city) 게임 만들기

9.1 절차지향형으로 개발하기 1

Previous9. 배틀 시티(Battle city) 게임 만들기Next9.2 절차지향형으로 개발하기 2

Last updated 3 months ago

Was this helpful?

바로 아래 화면의 우리가 만들게 될 게임의 최종 결과물을 먼저 보고나면 코드 이해가 훨씬 쉬울 것이다. 다만, 이번 과의 맨 서두에서 언급했듯이 난이도 조절을 위해 오리지널 게임의 기능동작 전체 다를 구현하지 않을 예정이고, 대신 이 게임을 두 가지 프로그래밍 패러다임(, ) 각각을 사용한 두 가지 버전으로 만들어 볼 예정이다. 먼저 이전 절에서 예고했듯이 다시한번 나만의 객체를 만들고 활용하는 것으로 배워 봄으로써 이제는 보편화되어 사용되는 객체지향형이 기존의 절자지향형과 어떠한 현격한 차이를 가지는지를 한번 더 명확하게 이해하는 것이 이번 장의 과업이라고 할 수 있겠다.

우리는 이미 5-8장 통해 4개의 게임제작을 소화했기에 이미 튜토리얼 수준을 넘어섰다 볼 수 있다. 따라서, 이번 장에서도 이미 설명한 기본적인 내용에 대한 자세한 중복설명없이 빠르게 핵심적인 코딩에 대한 설명 위주로 진행하도록 하겠다.

참고로 이번 게임제작에 필요한 이미지, 사운드 등의 리소스들은 에서 다운로드 할 수 있으며 게임에 필요한 이미지 파일들은 코드에서 사용한 이름 그대로 imgaes 폴더에 이미 저장되어 있고, 효과음은 sounds 폴더에 저장되어 있다고 간주하고 시작하겠다. 또는 를 사용 중이라면 뮤 에디터의 작업 디렉토리인 (사용자계정)\mu_code\examples\pygame_zero 안에서 복사해 올 수 있다.

우선은 먼저 절차지향형으로성의 개발을 시작해 보자. 배틀시티게임은 이전에 만들어 본 블록격파 게임과 트윈비 게임을 섞어놓은 게임과 같다.

from pgzhelper import *
import random

WIDTH = 800
HEIGHT = 600

bullets = []
bullet_delay_cnt = 0
BULLET_DELAY = 50
enemy_bullets = []
explosions = []
winner = ''

tank = Actor("tank_blue", (400, 575))
tank.angle = 90

ENEMY_MOVE_DELAY = 20
MAX_ENEMIES = 3
enemies = []
for i in range(MAX_ENEMIES):
    enemy = Actor("tank_red")
    enemy.angle = 270
    enemy.x = (i + 1) * WIDTH / (MAX_ENEMIES + 1)
    enemy.y = 25
    enemy.move_cnt = 0
    enemies.append(enemy)

# 50x50 크기의 벽의 더미 생성
walls = []
WALL_SIZE = 50
for x in range(int(WIDTH / WALL_SIZE)):  
    for y in range(int(HEIGHT / WALL_SIZE - 2)):  # 맨 상/하단을 비워두기
        # 랜덤하게 벽없는 빈 공간 생성
        if random.randint(0, 100) < 50:  
            wall = Actor("wall", anchor=("left", "top"))
            wall.x = x * WALL_SIZE
            wall.y = y * WALL_SIZE + WALL_SIZE  # 맨 상단 비우기
            walls.append(wall)


def move_player(player):
    pass

def fire_bullets(player, bullets):
    pass

def collide_bullets(bullets):
    pass

def draw():
    screen.blit('grass', (0, 0))
    tank.draw()
    for enemy in enemies:
        enemy.draw()
    for wall in walls:
        wall.draw()
    for bullet in bullets:
        bullet.draw()
    for bullet in enemy_bullets:
        bullet.draw()
    for explosion in explosions:
        explosion.draw()

    if winner:
        screen.draw.text(winner + " Win!", \
            midbottom=(WIDTH / 2, HEIGHT / 2), fontsize=100)


def update():
    global bullet_delay_cnt, enemy_move_cnt

    # 주인공 탱크
    if winner == '':
        move_player(tank)
        if bullet_delay_cnt == 0:  # 지연시간 종료 후 재발사 가능
            if keyboard.space:
                sounds.sfx_exp_medium12.play()
                fire_bullets(tank, bullets)
                bullet_delay_cnt = BULLET_DELAY
        else:
            bullet_delay_cnt -= 1
        collide_bullets(bullets)

    # 적 탱크
    for enemy in enemies:
        choice = random.randint(0, 2)
        if enemy.move_cnt > 0:  # 탱크 움직이기
            enemy.move_cnt -= 1
            move_player(enemy)
        elif choice == 0:  # 움직임 지연시간 초기화
            enemy.move_cnt = ENEMY_MOVE_DELAY
        elif choice == 1:  # 탱크 방향전환
            enemy.angle = random.randint(0, 3) * 90
        else:  # 대포알 발사
            fire_bullets(enemy, enemy_bullets)
    collide_bullets(enemy_bullets)

벽더미의 구성을 자세히 살펴보자. 벽 한개의 크기는 가로세로 50픽셀(WALL_SIZE)의 정사각형이다. 이 벽이 가로세로 800x600픽셀 크기의 화면에 몇 개가 들어갈지는 WIDTH / WALL_SIZE 와 HEIGHT / WALL_SIZE 계산으로 쉽게 알 수 있다. 궁금한 점은 왜 그 계산식을 int()라는 내장함수에 넣어 사용했는가인데, 그 이유는 파이썬에서의 나눗셈 연산은 결과를 항상 실수형(float)으로 값을 되돌려 주기 때문이다. 우리는 그 연산결과를 다시 range() 내장함수에서 사용할 건데, 이 함수의 인자값(argument)은 또 항상 정수형(integer)을 넘겨주어야 한다. 결국 int() 함수의 목적은 실수값을 정수값으로 변환해 주는 함수인 것이다.

34번 라인에서 전체 세로열에서 2를 뺀 것은 위의 게임결과 화면에서 보면 알 수 있듯이, 화면 전체에 전부 벽을 놓을 게 아니라 적 탱크(enemy)가 위치할 화면 맨 윗 열(row)와 내 탱크(tank)가 위치할 화면 맨 아래 하단의 한 열(row)을 비워두기 위한 목적이다. 그리고, 17번 라인은 이 게임이 벽을 다 부시는 블록격파(?) 게임은 아니고, 상대 탱크를 포로 맞춰 없에는게 과업인 게임에서 일부러 화면전체에 벽을 가득채우지 않고, 군데군데 랜덤하게 비워두기 위해서 넣은 코드이다.

왜 지연이 필요한가? 그렇다. 근본적인 이유는 트윈비에서도 유사한 상황이 있었는데 바로 update 콜백함수의 호출속도 (60FPS) 때문이다. 따라서, bullet_delay_cnt 는 대포알의 너무 빨리도 많이도 나가지 않게 하기 위한 목적이고, enemy_move_cnt 는 적 탱크들은 사람이 조작하는게 아니라 컴퓨터에 의해 자동조작 운행이 될 예정인데 이 때 너무 빠른 속도로 운행되지 않게 하기 위한 목적이다. 결국, 대표알이든 탱크 움직임이든 게임성을 저해하지 않게 적당해야지 대전형 게임에서 어느 한쪽이 지나친 우위를 점하면 게임성이 떨어져 재미가 없어진다는 건 누구나 아는 상식이다.

마지막으로 컴퓨터에 의한 적 탱크의 자동조정의 아이디어는 3가지 행동(움직하기, 방향전환, 포쏘기)을 무작위화(램덤화)하는 것이고, 이 때 움직이는 시간에 대해서도 랜덤화함으로써 좀 더 변칙적인 행동을 유도한다. 그럼, 이제 다음 절에서부터 우리의 3가지 사용자 함수의 구현체를 구체적으로 채워보도록 하겠다.

위의 37번째 라인까지 코드는 게임무대의 배우객체들을 생성하는 것이다. 장애물 벽더미(walls), 주인공 탱크(tank)와 3대의 적 탱크(enemies)를 생성한다. 벽더미를 만드는 부분은 기존의 블록격파 게임에서 블록생성 부분과 크게 다르지 않다. 다만, 벽더미의 생성 알고리즘이 일부 다를 뿐이다.

현재는 절차지향형으로 개발하기로 했기때문에 필요한 기능들을 함수화(functionalize) 할 필요가 있다는 것을 배웠다. 이번 게임도 함수화를 진행해보자. 코딩에서 항상 염두해 두어야 할 점은 나와 남을 위해 가독성이 좋고, 추후 수정에 대비한 유지보수(maintenance) 측면을 늘 고려해야 한다. 사실상 함수도 그러한 이유로 출연했다고 할 수 있다. 함수의 주목적은 코드중복을 줄이는 것으로 이전 트위비 게임에서는 안타깝게도 공통 기능적으로 함수로 묶을 만한게 마땅지 않았지만, 이번 배틀시티 게임에서는 그 목적에 부합하는 함수 3개(move_player, fire_bullets, collide_bullets)를 만들 수 있다.

다음 절부터 위에 언급된 3개 함수의 구현체를 만들어보기로 하고, 지금은 그 함수들을 제외한 나머지 코드들에 대해 더 검토해보자. 지금까지 모든 과정을 함께 공부해 온 여러분의 실력상 대부분은 이전에 이미 배웠던 것들의 약간의 변형이라 찬찬히 드려다보면 대부분 이해가 될 것으로 믿는다. 약간 낯선 부분이 있다면, bullet_delay_cnt, enemy_move_cnt 두 변수에 관한 것일 것 같다. 두 변수값은 다시 BULLET_DELAY, ENEMY_MOVE_DELAY 이란 (Macro)값으로 채우고 있다(79, 91라인). 변수명이나 매크로명에서 유추할 수 있듯이 뭔가 의도적으로 코드실행의 지연(delay)을 주기 위한 것으로 유추할 수 있다.

🔢
🔢
매크로
이곳
커스텀 뮤 에디터
절자지향형
객체지향형