Skip to content

Latest commit

 

History

History
307 lines (262 loc) · 11.5 KB

211218.md

File metadata and controls

307 lines (262 loc) · 11.5 KB

Day 73

파이썬으로 배우는 게임 개발 입문편

Pygame

pg_6.py

# 사운드 출력
import pygame
import sys

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
CYAN = (0, 255, 255)

def main():
    pygame.init()
    pygame.display.set_caption('Sound')
    screen = pygame.display.set_mode((800, 600))
    clock = pygame.time.Clock()
    font = pygame.font.Font(None, 40)

    try: # 예외 처리
        pygame.mixer.music.load('Python_workspace\mini_game\sound_dir\pygame_bgm.ogg') # BGM 로딩
        se = pygame.mixer.Sound('Python_workspace\mini_game\sound_dir\pygame_se.ogg') # SE 로딩
    except: # 예외 발생시
        print('Wrong ogg file or No Audio Found') # 메세지 출력

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
        
        key = pygame.key.get_pressed()
        if key[pygame.K_p] == 1: # P 키를 눌렀을 때
            if pygame.mixer.music.get_busy() == False: # BGM이 정지 중이라면
                pygame.mixer.music.play(-1) # BGM 재생
        if key[pygame.K_s] == 1: # S 키를 눌렀을 때
            if pygame.mixer.music.get_busy() == True: # BGM이 재생 중이라면
                pygame.mixer.music.stop() # BGM 정지
        if key[pygame.K_SPACE] == 1: # SPACE 키를 눌렀으면
            se.play() # SE 재생

        pos = pygame.mixer.music.get_pos() # 변수에 BGM 재생 시간 대입
        txt1 = font.render('BGM pos' + str(pos), True, WHITE) # 재생 시간을 표시할 Surface
        txt2 = font.render('[P]lay bgm : [S]top bgm : [SPACE] se', True, CYAN) # 조작 방법을 표시할 Surface
        screen.fill(BLACK)
        screen.blit(txt1, [100, 100])
        screen.blit(txt2, [100, 200])

        pygame.display.update()
        clock.tick(10)

if __name__ == '__main__':
    main()

사운드를 재생하는 방법에 대한 부분이다.

명령 코드 비고
파일 로딩 pygame.mixer.music.load(파일명)
재생 pygame.mixer.music.play(인수) 인수에 -1을 입력하면 반복 재생되고,
0을 입력하면 1회 재생되며,
5를 입력하면 6회 반복 재생된다.
정지 pygame.mixer.music.stop()
재생 시간 얻기 pygame.mixer.music.get_pos() 밀리초 값
재생 상태 확인 pygame.mixer.music.get_busy() 재생 중이면 True, 그렇지 않으면 False

pygame에서 한국어를 쓰려면 별도로 처리해줘야 한다.

font = pygame.font.SysFont('malgungothic', 80) # 시스템 폰트 사용시
font = pygame.font.Font('{font_path}/{font_name}.ttf', 80) # 별도의 폰트 사용시

RPG 게임 만들기

미로 자동 생성

미로 생성 알고리즘 중에 기둥 쓰러뜨리기 법이라는 것이 있다고 한다.

  1. 테두리를 벽으로 둘러 싼다.
  2. 내부에 1칸씩 기둥을 놓는다.
  3. 각 기둥에서 상하좌우 중 하나의 방향(무작위)으로 벽을 만들면 미로가 완성된다.
  4. 가장 왼쪽 열의 기둥에서부터 4방향 중 하나에 벽을 만들고 그다음 열에서부터는 상, 하, 우 3방향 중 하나에 벽을 만든다.

이 방법의 주의점은 무작위로 4개 방향으로 벽을 만들면 들어갈 수 없는 칸이 생길 가능성이 있다는 것이다. 이는 4번으로 해결할 수 있다.

import pygame
import sys
import random

CYAN = (0, 255, 255)
GRAY = (96, 96, 96)

MAZE_W = 11 # 미로 가로 칸 수
MAZE_H = 9 # 미로 세로 칸 수
maze = [] # 미로 데이터 관리 리스트
for y in range(MAZE_H): # 리스트 초기화
    maze.append([0] * MAZE_W)

def make_maze():
    XP = [0, 1, 0, -1] # 기둥에서 벽을 그리기 위한 값 정의
    YP = [-1, 0, 1, 0] # "

    # 미로 생성 알고리즘 1번
    # 주변 벽
    for x in range(MAZE_W):
        maze[0][x] = 1
        maze[MAZE_H - 1][x] = 1
    for y in range(1, MAZE_H - 1):
        maze[y][0] = 1
        maze[y][MAZE_W - 1] = 1

    # 안을 아무것도 없는 상태로
    for y in range(1, MAZE_H - 1):
        for x in range(1, MAZE_W - 1):
            maze[y][x] = 0

    # 미로 생성 알고리즘 2번
    # 기둥
    for y in range(2, MAZE_H - 2, 2):
        for x in range(2, MAZE_W - 2, 2):
            maze[y][x] = 1
    
    # 미로생성 알고리즘 3번
    # 기둥에서 상하좌우로 벽 생성
    for y in range(2, MAZE_H - 2, 2):
        for x in range(2, MAZE_W - 2, 2):
            d = random.randint(0, 3)
            # 미로 생성 알고리즘 4번
            if x > 2: # 2번째 열부터 왼쪽으로는 벽을 만들지 않음
                d = random.randint(0, 2)
            maze[y + YP[d]][x + XP[d]] = 1

def main():
    pygame.init()
    pygame.display.set_caption('Maze making')
    screen = pygame.display.set_mode((528, 432))
    clock = pygame.time.Clock()

    make_maze()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE: # 스페이스 키를 누르면
                    make_maze() # 미로 생성 함수 호출

        for y in range(MAZE_H):
            for x in range(MAZE_W):
                W = 48 # 1칸의 폭
                H = 48 # 1칸의 높이
                X = x * W # 표시할 X 좌표 계산
                Y = y * H # 표시할 Y 좌표 계산
                if maze[y][x] == 0: # 미로
                    pygame.draw.rect(screen, CYAN, [X, Y, W, H]) # 길이라면 하늘색으로 칸을 채움
                if maze[y][x] == 1: # 벽
                    pygame.draw.rect(screen, GRAY, [X, Y, W, H]) # 벽이라면 회색으로 칸을 채움
        
        pygame.display.update()
        clock.tick(2)

if __name__ == '__main__':
    main()

위에서 설명한 미로 생성 알고리즘을 이용하여 자동으로 미로를 생성해서 보여준다.

미로를 던전으로 변환

미로에서 던전으로 데이터 변환을 수행해야 한다.

  1. 던전을 정의하기 위한 2차원 리스트 dungeon을 준비한다.
  2. maze의 칸 상태를 조사하면서 dungeon에 값을 설정한다.
  3. dungeon의 내용을 먼저 모두 벽으로 바꾼다.
  4. maze[y][x] 값을 확인한 수가 0(길)이면 무작위로 던전에 방을 만든다.
  5. 방을 만들지 않는 경우 maze[y][x]의 상하좌우 칸을 확인하고 0이면 그 방향으로 던전에 통로를 만든다.
# 던전 생성
import pygame
import sys
import random

BLACK = (0, 0, 0)
CYAN = (0, 255, 255)
GRAY = (96, 96, 96)

MAZE_W = 11 # 미로 가로 칸 수
MAZE_H = 9 # 미로 세로 칸 수
maze = [] # 미로 데이터 관리 리스트
for y in range(MAZE_H): # 리스트 초기화
    maze.append([0] * MAZE_W)

DUNGEON_W = MAZE_W * 3
DUNGEON_H = MAZE_H * 3
dungeon = []
for y in range(DUNGEON_H):
    dungeon.append([0] * DUNGEON_W)

imgWall = pygame.image.load('Python_workspace\mini_game\image_dir\wall.png')
imgFloor = pygame.image.load('Python_workspace\mini_game\image_dir\\floor.png')

def make_dungeon(): # 던전 자동 생성
    # 26 ~ 58번 줄까지는 미로 생성
    XP = [0, 1, 0, -1] # 기둥에서 벽을 그리기 위한 값 정의
    YP = [-1, 0, 1, 0] # "

    # 미로 생성 알고리즘 1번
    # 주변 벽
    for x in range(MAZE_W):
        maze[0][x] = 1
        maze[MAZE_H - 1][x] = 1
    for y in range(1, MAZE_H - 1):
        maze[y][0] = 1
        maze[y][MAZE_W - 1] = 1

    # 안을 아무것도 없는 상태로
    for y in range(1, MAZE_H - 1):
        for x in range(1, MAZE_W - 1):
            maze[y][x] = 0

    # 미로 생성 알고리즘 2번
    # 기둥
    for y in range(2, MAZE_H - 2, 2):
        for x in range(2, MAZE_W - 2, 2):
            maze[y][x] = 1
    
    # 미로생성 알고리즘 3번
    # 기둥에서 상하좌우로 벽 생성
    for y in range(2, MAZE_H - 2, 2):
        for x in range(2, MAZE_W - 2, 2):
            d = random.randint(0, 3)
            # 미로 생성 알고리즘 4번
            if x > 2: # 2번째 열부터 왼쪽으로는 벽을 만들지 않음
                d = random.randint(0, 2)
            maze[y + YP[d]][x + XP[d]] = 1

    # 60 ~ 85번 줄까지는 미로를 던전 데이터로 변환
    # 미로에서 던전 생성
    # 전체를 벽으로 만듬
    for y in range(DUNGEON_H):
        for x in range(DUNGEON_W):
            dungeon[y][x] = 9 # dungeon 값을 모두 9(벽)으로 설정
    # 방과 미로 배치
    for y in range(1, MAZE_H - 1):
        for x in range(1, MAZE_W - 1):
            dx = x * 3 + 1
            dy = y * 3 + 1
            if maze[y][x] == 0: # 미로 데이터 확인, 칸이 길이라면
                if random.randint(0, 99) < 20: # 방 생성 여부를 무작위로 결정
                    for ry in range(-1, 2):
                        for rx in range(-1, 2):
                            dungeon[dy + ry][dx + rx] = 0 # 3 x 3 칸의 방으로 만듬
                else: # 방을 만들지 않는 경우 통로 생성
                    dungeon[dy][dx] = 0 # 3 x 3 칸 중앙을 통로로
                    if maze[y - 1][x] == 0: # 미로의 위 칸이 길이라면 통로를 위로 연징
                        dungeon[dy - 1][dx] = 0
                    if maze[y + 1][x] == 0: # 미로의 아래 칸이 길이라면 통로를 아래로 연장
                        dungeon[dy + 1][dx] = 0
                    if maze[y][x - 1] == 0: # 미로의 왼쪽 칸이 길이라면 통로를 왼쪽으로 연장
                        dungeon[dy][dx - 1] = 0
                    if maze[y][x + 1] == 0: # 미로 오른쪽 칸이 길이라면 통로를 오른쪽으로 연장
                        dungeon[dy][dx + 1] = 0

def main():
    pygame.init()
    pygame.display.set_caption('Maze making')
    screen = pygame.display.set_mode((1056, 432))
    clock = pygame.time.Clock()

    make_dungeon()

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE: # 스페이스 키를 누르면
                    make_dungeon() # 미로 생성 함수 호출

        # 확인용 미로 표시
        for y in range(MAZE_H):
            for x in range(MAZE_W):
                W = 48 # 1칸의 폭
                H = 48 # 1칸의 높이
                X = x * W # 표시할 X 좌표 계산
                Y = y * H # 표시할 Y 좌표 계산
                if maze[y][x] == 0: # 미로
                    pygame.draw.rect(screen, CYAN, [X, Y, W, H]) # 길이라면 하늘색으로 칸을 채움
                if maze[y][x] == 1: # 벽
                    pygame.draw.rect(screen, GRAY, [X, Y, W, H]) # 벽이라면 회색으로 칸을 채움

        # 던전 그리기
        for y in range(DUNGEON_H):
            for x in range(DUNGEON_W):
                X = x * 16 + 528
                Y = y * 16
                if dungeon[y][x] == 0:
                    screen.blit(imgFloor, [X, Y])
                if dungeon[y][x] == 9:
                    screen.blit(imgWall, [X, Y])
        
        pygame.display.update()
        clock.tick(2)

if __name__ == '__main__':
    main()

여기서 벽을 9로 표현한 이유는 추후에 보물상자와 같은 다른 것들을 배치하기 위해서이다.

던전 생성에서 미로 1칸은 던전에서 3 x 3 칸에 해당한다. 그래서 'dx = x * 3 +1', 'dy = y * 3 + 1'로 변수를 준비한 것이고, 각 변수에 +1을 한 건 dx, dy를 3 x 3칸의 중앙 좌표 값으로 하기 위해서이다.