이전의 포스트에서 슈팅게임의 뼈대를 만들어 두었다.

 

이제 그 위에 그럴듯한 옷을 입힐 차례다.

 

 

파이썬 게임 만들기 | 슈팅 게임 만들기 (SHMUP) | 파이게임 | 슈팅게임의 뼈대 | Sprite 객체 사용하

슈팅게임은 날아 오는 적을 무찌르는 게임이다. 캐릭터가 좌우로 움직이는게 있고 동서남북 방향으로 움직이는 게임도 있다. 아래에서 위를 보는 것을 종스크롤, 왼쪽에서 오른쪽으로 가는 것

digiconfactory.tistory.com

 

아래와 같은 게임에 스프라이트를 입히면

 

아래 처럼 변한다. 그래픽을 입히니까 그럴듯해 보인다

 

이런 게임 아트를 game asset 이라고 한다. 게임을 멋있게 만들기 위해서는 프로그래밍 하는 시간보다 자료 찾는 시간이 오래걸린다. 저작권이 없는(public) 자료도 있지만 많은 경우 크리에이티브 커먼즈 저작권이 걸려있다. 저작권 침해를 방지하기 위해 사용전에 어떤 조건인지 꼭 확인하도록 한다.

 

www.flaticon.com/ 벡터 아이콘이 많은 곳이다

 

Flaticon, the largest database of free vector icons

Download all icons in SVG, PSD, PNG, EPS format or as webfonts

www.flaticon.com

opengameart.org/

이런 벡터아이콘이 다운로드 가능하다

로얄티 프리 게임 애셋

 

OpenGameArt.org

 

opengameart.org

freemusicarchive.org/home 게임음악 스러운 Free Music

 

Free Music Archive

Update - Oct 21, 2020 After inviting the first FMA musicians back earlier this month it's now time to let more FMA friends in. Feel free to Log In or Sign Up. Some features might not be back so we love to hear your ideas, remarks and bug reports. Email you

freemusicarchive.org

www.bfxr.net/ 게임 효과음 만드는 프로그램

 

Bfxr. Make sound effects for your games.

To view this page ensure that Adobe Flash Player version 11.1.0 or greater is installed.

www.bfxr.net

원하는 game asset 을 모으다 보면 상당한 시간이 흘러갈 것이다. 이전 포스팅에 도형으로 뼈대를 만든 것은 그 때문이었다. game asset 을 찾는 것은 천천히 찾고 우선 뼈대를 만들어 진행을 시키는 것이다. 그래픽이야 언제든지 더 나은 asset 을 찾으면 바꿀 수 있다.

 


 

지난 포스팅의 연장이므로 전체 소스 보다는 부분적인 설명이 좋을 것이다.

 

전체 소스파일은 파일로 첨부한다.

 

shmupgame.py
0.01MB

 

 

게임루프를 보면 윗쪽 부분에 이미지와 사운드 파일을 로드한다. 배경으로 사용할 파일과 캐릭터 이미지 등 도형에 입힐 스프라이트 파일을 로드한다. 이미지를 리스트로 만드는 것은 적을 랜덤하게 생성하기 위해서이다.

 

사운드의 경우도 마찬가지이다. 사용할 것들을 미리 로드해놓는 것이다. 배경음악은 바로 틀어준다.

 

def game_loop(surface):

    # ----- load game graphics -----

    background = pygame.image.load(os.path.join(image_dir, 'milkyway.png')).convert()
    bg_rect = background.get_rect()
    player_image = pygame.image.load(os.path.join(image_dir, 'spikedship1.png')).convert()
    bullet_image = pygame.image.load(os.path.join(image_dir, 'blasterbolt.png')).convert()
    asteroid_image = []
    asteroid_list = ['asteroid1.png', 'asteroid2.png', 'asteroid3.png',
                     'asteroid4.png', 'asteroid5.png']
    for img in asteroid_list:
        asteroid_image.append(pygame.image.load(os.path.join(image_dir, img)).convert())

    shoot_sound = pygame.mixer.Sound(os.path.join(sound_dir, 'sfx3.wav'))
    shoot_sound.set_volume(0.1)
    explosion_sound = pygame.mixer.Sound(os.path.join(sound_dir, 'sfx4.wav'))
    explosion_sound.set_volume(0.1)

    pygame.mixer.music.load(os.path.join(sound_dir, 'stage1.mp3'))
    pygame.mixer.music.set_volume(0.1)
    pygame.mixer.music.play(loops=-1)

    clock = pygame.time.Clock()
    sprite_group = pygame.sprite.Group()
    mobs = pygame.sprite.Group()
    bullets = pygame.sprite.Group()
    player = PlayerShip(player_image)
    global player_health
    player_health= 100
    global score
    score = 0
    sprite_group.add(player)
    for i in range(7):
        enemy = Mob(asteroid_image)
        sprite_group.add(enemy)
        mobs.add(enemy)

    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                 running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    running = False
                if event.key == pygame.K_SPACE:
                    player.shoot(sprite_group, bullets, bullet_image, shoot_sound)
            if event.type == pygame.MOUSEBUTTONDOWN:
                player.shoot(sprite_group, bullets, bullet_image, shoot_sound)


        sprite_group.update()

        hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
        for hit in hits:
            explosion_sound.play()
            mob = Mob(asteroid_image)
            sprite_group.add(mobs)
            mobs.add(mob)
            score += 10

        hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
        if hits:
            print('a mob hits player!')
            player_health -= 1
            if player_health < 0:
                gameover(surface)
                close_game()
                restart()

        surface.fill(LIGHT_PINK1)
        surface.blit(background, bg_rect)
        sprite_group.draw(surface)
        score_update(surface)
        pygame.display.flip()
        clock.tick(FPS)
    pygame.quit()
    print('game played: ',playtime)

 

다음은 플레이어쉽이다.

 

여기서 self.image = pygame.Surface((40,30)) 의 도형을 주석처리하고 대신 위에서 로드한 image를 사용한다. transform.scale 을 사용하면 사이즈를 맞출 수 있다. 이미지를 조작하는 방법은 이전 포스트를 참고한다.

 

파이썬 게임만들기 9 | 잡설, 이미지 로드와 조작방법 | PYGAME

Pygame의 Rect 객체에 대하여 어느정도 조작이 가능하다면 다음 단계는 이미지를 불러와서 조작해볼 차례다. pygame모듀에는 이미지 조작 메소드들이 있다. 참조변수 transform 의 메소드들로 조작할

digiconfactory.tistory.com

set_colorkey를 하는 이유는 png 파일의 여백부분을 없애주기 때문이다. 그 외에 self.radius 를 사용하면 충돌감지기능을 향상시킬 수 있다. (메인루프의 collide 에서 나온다)

 

def shoot 에서 아까 로드한 sound들을 play() 하는 것을 볼 수 있다. 버튼이 눌러질 때 마다 무기가 발사되면서 소리가 난다.

class PlayerShip(pygame.sprite.Sprite):
    def __init__(self, image):
        pygame.sprite.Sprite.__init__(self)
        # self.image = pygame.Surface((40,30))
        self.image = pygame.transform.scale(image, (75, 40))
        self.image.set_colorkey(BLACK)
        self.rect = self.image.get_rect()
        self.radius = int(self.rect.width * .9/2)
        # pygame.draw.circle(self.image, RED, self.rect.center, self.radius, 1)
        # self.image.fill(RED)
        self.rect.centerx = int(SCREEN_WIDTH / 2)
        self.rect.centery = SCREEN_HEIGHT - 20
        self.speedx = 0
        self.speedy = 0

    def update(self):
        self.speedx = 0
        self.speedy = 0
        keystate = pygame.key.get_pressed()
        if keystate[pygame.K_a]:
            self.speedx = -10
        if keystate[pygame.K_d]:
            self.speedx = 10
        if keystate[pygame.K_w]:
            self.speedy = -10
        if keystate[pygame.K_s]:
            self.speedy = 10
        self.rect.x += self.speedx
        self.rect.y += self.speedy
        if self.rect.right > SCREEN_WIDTH:
            self.rect.right = SCREEN_WIDTH
        if self.rect.left < 0:
            self.rect.left = 0
        if self.rect.bottom > SCREEN_HEIGHT:
            self.rect.bottom = SCREEN_HEIGHT
        if self.rect.top < 0:
            self.rect.top = 0

    def shoot(self, all_sprites,bullets, image, sound):
        bullet = Bullet(self.rect.centerx, self.rect.top, image)
        all_sprites.add(bullet)
        bullets.add(bullet)
        sound.play()

다음은 몹과 총알이다. 이것도 같은 방식으로 image 를 로드한다.

 

몹을 만들 때 리스트로 파일들을 로드하고 random 함수로 선택한다. rotozoom 함수로 이미지를 원하는 형태로 바꾼다.

 

중간에 rotate 메서드를 추가했는데 이것은 날아오는 적들에게 움직임을 주기 위해서이다.

 

이런 기능들은 파이게임을 사용하지 않았다면 구현하기 쉽지 않은 것들이다. 비록 아마추어 수준의 게임이지만 파이게임은 상당히 장점이 많다. 파이썬과 프로그래밍을 학습하기에 상당히 좋은 내용이다.

class Mob(pygame.sprite.Sprite):
    def __init__(self, image):
        pygame.sprite.Sprite.__init__(self)
        self.image_origin = random.choice(image)
        self.image_origin = pygame.transform.rotozoom(random.choice(image), 0, 0.7)


        self.image = self.image_origin
        self.image.set_colorkey(WHITE)
        # self.image = pygame.Surface((30,30))
        # self.color = random.choice([RED, BLUE, RED, GREEN1, YELLOW])
        # self.image.fill(self.color)
        self.rect = self.image.get_rect()
        self.radius = int(self.rect.width * .9 / 2)
        # pygame.draw.circle(self.image, RED, self.rect.center, self.radius, 1)
        self.rect.x = random.randrange(SCREEN_WIDTH - self.rect.width)
        self.rect.y = random.randrange(-100, -40)
        self.speedy = random.randrange( 1, 8)
        self.speedx = random.randrange(-3, 3)
        self.rotation = 0
        self.rotation_speed = random.randrange(-10, 10)
        # self.rotation_speed = 7
        self.last_update = pygame.time.get_ticks()


    def rotate(self):
        now = pygame.time.get_ticks()
        if now - self.last_update > 50:
            self.last_update = now
            self.rotation = (self.rotation + self.rotation_speed) % 360
            new_image = pygame.transform.rotate(self.image_origin, self.rotation)
            old_center = self.rect.center
            self.image = new_image
            self.rect = self.image.get_rect()
            self.rect.center = old_center

    def update(self):
        self.rotate()
        self.rect.x += self.speedx
        self.rect.y += self.speedy
        if self.rect.top > SCREEN_HEIGHT + 10 or self.rect.left < -25 or self.rect.right > SCREEN_WIDTH + 20:
            self.rect.x = random.randrange(SCREEN_WIDTH - self.rect.width)
            self.rect.y = random.randrange(-100, -40)
            self.speedy = random.randrange(3, 8)

class Bullet(pygame.sprite.Sprite):
    def __init__(self, player_x, player_y, image):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.transform.scale(image, (40, 70))
        self.image.set_colorkey(WHITE)
        # self.image = pygame.Surface((10,20))
        # self.image.fill(GREEN1)
        self.rect = self.image.get_rect()
        # pygame.draw.ellipse(self.image, RED, self.rect,1)
        self.rect.bottom = player_y
        self.rect.centerx = player_x
        self.speedy = - 10

    def update(self):
        self.rect.y += self.speedy
        if self.rect.bottom < 0:
            self.kill()

이번 코드는 지난번 도형 슈팅게임 보다 좀 더 그래픽적으로 발전하고 소리와 배경음도 추가해봤다. 파이게임이 이런 복잡한 과정을 다 도와주기 때문에 손쉽게 할 수 있었다.

 

파이썬 게임에 관한 주제는 다음 포스팅에서도 다뤄보도록 하겠다.

 

 

*참고영상 유튜브 채널

 

공유하기

facebook twitter kakaoTalk kakaostory naver band