스푸79 기록 보관소

운석 피하기 게임 개발(16) - 아이템효과(폭탄, 보석, 에너지 등등) 본문

게임공작소

운석 피하기 게임 개발(16) - 아이템효과(폭탄, 보석, 에너지 등등)

스푸79 2024. 10. 13. 06:00

 
이번에는 에너지 표시, 플레이 타임 표시, 플레이 중에 획득한 보석 갯수 표시와
Z키를 눌렀을 때 폭탄이 사용되면서 화면 내에 있는 모든 운석을 폭파시키는 기능을 구현해 보았다.
 
우선 에너지 표시부터 처리해보자.

class Player(pygame.sprite.Sprite):
    def __init__(self, main, pos, sprites, missile_sprites):
        super().__init__()
        .. (중략) ..        
        self.health = 8
        self.total_health = 8

 
멤버변수 health 와 total_health를 추가한다. health 값을 8로 설정했다. 운석과 총 8번 부딪쳐도 죽지 않는다.
total_health를 추가한 이유는 에너지 아이템을 아주 많이 먹더라도 health가 total_health를 넘지 않도록 하기 위해서다.

 
에너지를 표시하는 바는 위의 그림처럼 구성이 된다.
맨 아래에는 붉은색 바가 칠해지고 그 위로 노란색 체력바가 그려진다. 그리고 두 바 감싸는 흰색 테두리를 맨 위에 그린다.
노란색은 현재 기체의 체력을 기준으로 그린다. 부딪칠때마다 노란색 바의 폭을 줄이게 되면
위와 같이 에너지가 줄어드는 효과가 나온다.
아래는 에너지바를 그리는 코드이다.

    def energy_bar(self):   
        x  = int((155 * self.player.health) / self.player.total_health)
        bar = pygame.Rect(env.BAR_X, env.BAR_Y, env.BAR_WIDTH, env.BAR_HEIGHT)
        energy = pygame.Rect(env.BAR_X, env.BAR_Y, x, env.BAR_HEIGHT)
        pygame.draw.rect(self.screen, env.RED, bar)
        pygame.draw.rect(self.screen, env.YELLOW, energy)
        pygame.draw.rect(self.screen, env.WHITE, bar, 1)

 
노란색으로 그려지는 energy의 폭은 체력 전체와 남은 체력의 비율을 바의 원래 폭의 비율로 계산해 주면 된다.
(앗! 또 수학이...ㅎ)
 
 
기체가 생성될 때 폭탄을 총 3개를 기본으로 들고 시작한다. 폭탄을 누르면 화면 내에 있는 운석이 모두 폭파된다.
에너지 바 밑에 해당 폭탄을 그려주는 코드를 추가했다.

    def bomb_item(self):
        image = self.resources.bomb_item 
        for i in range(self.player.bomb_cnt):            
            self.screen.blit(image, (env.BOMB_ITEM_X + (env.BOMB_ITEM_MARGIN * i), env.BOMB_ITEM_Y))

 
기존의 폭탄 아이템의 크기를 좀 줄인 후 45도로 회전시켜서 위와 같이 기울어져서 보이도록 했다.

 
화면 중앙에는 타이머를 하나 두었다. 해당 시간은 나중에 게임 종료 후, 최고 기록 순위 매길때 사용할 생각이다.
화면 우측에는 보석을 먹은 갯수를 표시했다.
저 보석을 응용해서 다른 기체를 해금하거나 업그레이드를 위해 플레이어가 수집하도록 동기부여를 할 수 있다.

 
폭탄키는 Z키로 설정했다.
Z키를 눌렀을 때 화면 내에 있는 모든 운석을 폭파시키는 코드를 작성해보자.

    def fire_bomb(self):
        for meteor in self.meteor_sprites:
            meteor.destroy(self)

 
로직은 너무 간단하다. for문으로 sprites_group에 있는 모든 meteor객체의 destroy 함수를 호출해 주면 끝난다.
폭탄으로 터뜨리는 경우에 만약 아이템을 떨구지 않도록 처리하려고 한다면
destroy함수에 True, False값을 받도록 수정을 하면 된다.
여기서는 우선 폭탄으로 터지는 경우에도 아이템을 떨구도록 했다.
 
아래는 보석 먹은 숫자와 플레이 시간을 출력하는 코드이다.

    def total_jewerly(self):
        jewerly_text = f"{self.player.jewerly_cnt}"
        text = self.resources.digit_font_32.render(jewerly_text, True, env.WHITE)             
        text_rect = text.get_rect()
        text_rect.right = 790 
        text_rect.top = 5
        self.screen.blit(text, text_rect)
        
        image = self.resources.item_jewerly
        image_left = text_rect.left - (self.resources.item_jewerly.get_rect().width + 5)
        self.screen.blit(image, (image_left, 10))        
    
    def timer(self, microseconds):
        time_text = f"{microseconds:.2f}"  # 밀리초 단위로 표시
        text = self.resources.digit_font_48.render(time_text, True, env.WHITE)             
        text_rect = text.get_rect()
        text_rect.left = 360
        text_rect.top = 5
        self.screen.blit(text, text_rect)

 
밀리초 단위로 표기하기 위해 format을 .2f 소수점 2번째 자리까지 찍히도록 설정했다.
화면에 출력되는 숫자는 일반적인 font가 아니라 제작된 무료폰트이다.
 
외부 폰트를 사용할 때 사실 좀 걱정을 해야할 부분이 있다. 그건 바로 저작권인데
만약 배포 당시에 무료폰트라도 나중에 유로폰트로 바꿔버리면 꼼짝없이 저작권에 걸려 소송을 당하게 된다.
하지만 해당 폰트를 이용해서 상업적으로 사용하는 건 아니기 때문에 그냥 사용을 했다.
소스 코드상에서는 해당 폰트명을 우선 ?로 표시하겠다.

        self.digit_font_48 = pygame.font.Font(env.GAME_FONT_DIR + "??.ttf", 48)
        self.digit_font_32 = pygame.font.Font(env.GAME_FONT_DIR + "??.ttf", 32)

 
폰트도 resources.py에 생성되는 시점에 한번 로드하도록 처리했다.
역시 다른 이미지와 마찬가지로 I/O가 발생되는 코드는 최소화하는 것이 좋다.
 
main.py에는 set_grab이라는 새로운 코드를 추가했다.
마우스 커서가 창 밖으로 넘어가면
해당 포커싱이 외부로 빠지면 다른 창으로 넘어가는 문제를 막기 위해서다.

class Main():
    def __init__(self):        
        pygame.init()        
        .. 중략 ..
        pygame.event.set_grab(self.grab) #마우스를 스크린 안에 고정 처리

이렇게 초기화 시점에 코드를 넣어주면 마우스는 이제 항상 윈도우 창 안에서만 움직이다.
 
하지만 또 게임이 실행되는 창 안에서만 마우스를 쓰게 되면 종료를 시킬 수가 없다.
우선 ESC를 누른 경우에는 마우스가 다시 밖으로 갈 수 있도록 처리하고
다시 한번 더 누른 경우 또 윈도우 안으로 들어오도록 처리했다.
set_grap 메서드는 not() 문법을 사용해서 toggle로 처리했다.

                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        self.set_grab()                  
                        
                        ..(중략)..
                        
    def set_grab(self):
        self.grab = not(self.grab)
        pygame.event.set_grab(self.grab)

 
이제 거의 다 게임이 완성단계에 와 있는 것 같다.
배경음악과 효과음을 넣고 게임 데이터를 저장과 메뉴 처리만 하면 완성되는 것 같다.
앞으로 4장 정도면 운석피하기 게임은 완성이 되는 것 같다.
 
마지막 강좌에서는 개발된 게임을 exe로 만드는 과정도 설명할 예정이다.
그렇게 되면 이제 python IDE 환경을 따로 구축하지 않아도 실행파일만으로 게임을 해 볼 수가 있게 된다.
전체 소스 코드를 올린다.

14. energy
0.07MB