스푸79 기록 보관소

운석 피하기 게임 개발(15) - 아이템효과(파워업, 방어막) 본문

게임공작소

운석 피하기 게임 개발(15) - 아이템효과(파워업, 방어막)

스푸79 2024. 10. 7. 07:00

 

아이템 효과를 구현해보자.

총 5개의 아이템을 게임에 추가할 것이다.

 

이번 포스트에서는 파워업과 방어막 아이템과 그 기능을 구현하겠다.

 

파워업 아이템을 먹게 되면

미사일이 한 개만 발사되다가 한번에 3개씩 발사된다.

 

방어막 아이템을 먹게 되면

동그란 실드가 기체 주변에 생성되고 일정 시간동안 운석의 데미지를 받지 않도록 해준다.

 

Player 클래스에 파워업과 방어막 처리를 확인하는 속성을 추가하자.

self.power_yn 과 self.shield_yn 이 True가 되면 아이템 효과가 발현되도록 한다.

import pygame
from util import Util
from enviroment import Enviroment as env

class Player(pygame.sprite.Sprite):
    def __init__(self, main, pos, sprites, missile_sprites):
        super().__init__()
        ... (중략) ...
        
        self.power_yn = False
        self.power_time = pygame.time.get_ticks()
        self.power_delay_time = 5000
        
        self.shield_yn = False

 

Player 클래스에 power_up와 fired 메서드를 추가한다.

 

power_up 메서드는 기체가 power item을 먹는 경우(충돌이 발생한 경우), 아래와 같이 동작한다.

power_yn 값을 True로 설정하고 power_time은 현재 시간으로 설정한다.

    def power_up(self):
        self.power_yn = True
        self.power_time = pygame.time.get_ticks()

 

fired 메서드는 미사일이 발사될 때(마우스 왼쪽 클릭), 아래와 같이 동작한다.

power_yn가 True 인 경우에는 각도를  ±5 간격으로 추가 미사일을 발사한다.

power_time이 power_delay_time 값보다 큰 경우, 아이템을 먹은 후 5초가 지나면 power_yn를 False로 바꿔서

원래대로 한발씩 나가도록 처리한다.

    def fired(self):        
        x3 = self.rect.centerx
        y3 = self.rect.centery
        missile = Missile(self.main, (x3, y3), self.dir, self.missile_sprites[self.dir])
        self.main.player_sprites.add(missile)
        
        if self.power_yn:
            dir1 = int(self.dir - 10 + 360) if self.dir - 10 >= 360 else self.dir - 10
            dir2 = int(self.dir + 10 - 360) if self.dir + 10 >= 360 else self.dir + 10
            missile = Missile(self.main, (x3, y3), dir1, self.missile_sprites[dir1])
            self.main.player_sprites.add(missile)
            missile = Missile(self.main, (x3, y3), dir2, self.missile_sprites[dir2])
            self.main.player_sprites.add(missile)
            
            if pygame.time.get_ticks() - self.power_time > self.power_delay_time:
                self.power_yn = False

 

 

기체 주위를 감싸게 될 방어막 Class를 구현해보자.

class Shield(pygame.sprite.Sprite):
    def __init__(self, pos, main):
        super().__init__()
        self.image = pygame.image.load(env.GAME_IMG_DIR + "player_shield.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.center = pos
        self.id = "player_shield"
        self.group_id = "player"
        self.shield_time = pygame.time.get_ticks()
        self.shield_delay_time = 5000
        self.speed = 0
        self.mass = 1000
        self.target = pygame.Vector2(0, 0)
        self.velocity = self.target * self.speed
        self.main = main
                
    def update(self):
        self.pos = self.main.player.pos
        self.rect.center = self.pos
        
        if pygame.time.get_ticks() - self.shield_time > self.shield_delay_time:
            self.main.player.shield_yn = False
            self.kill()
        
    def handle_collision(self, t_velocity, t_mass):
        pass

 

방어막은 기체를 중심으로 따라서 계속 이동하기 때문에

target값과 speed 값이 필요가 없다. 기체가 움직이는 좌표값을 그대로 사용하면 된다.

파워업과 마찬가지로 아이템을 먹은지 5초가 지나면 사라지도록 처리했다.

방어막이 사라지는 시점에 Player 개체의 shield_yn 값을 False로 바꾼다.

 

Player 클래스에서는 create_shield 메서드를 생성한다.

    def create_shield(self):
        if not(self.shield_yn):
            self.shield_yn = True
            shield = Shield(self.pos, self.main)  
            self.main.player_sprites.add(shield)

 

여기서 self.shield_yn 값을 체크하도록 했다.

False인 경우에만 실드가 생성되고 실드가 생성된 후에는 shield_yn 값을 True로 바로 바꿔준다.

 

Shield 클래스와 Player 클래스에서 shield_yn 값을 True와 False로 바꿔주는 이유가 있다.

만약 이런 체크가 없다면 shield_yn 값이 True가 되는 시점에

매 프레임마다 방어막이 생성되는 문제가 발생한다.

따라서 한번 방어막이 생성된 다음에는 생성되지 않도록 True값으로 설정하고

삭제된 시점에는 다음 번 아이템을 습득했을 때 아이템이 생성되도록 False로 설정한다.

 

이번에는 아이템 개체를 구현해보자.

아이템은 운석이 폭발할 때, 생성하고 방향은 생성 시점에 개체를 향해 나아가도록 설정했다.

Item이라는 상위 클래스를 하나 만들고

그 외에 ShieldItem, PowerItem 등등 하위 클래스에서 상속받도록 구현했다.

import pygame
import random
from enviroment import Enviroment as env

class Item(pygame.sprite.Sprite):
    def __init__(self, s_pos, d_pos, main):
        super().__init__()
        self.speed = 1
        self.target = (pygame.Vector2(d_pos) - pygame.Vector2(s_pos))
        self.target.normalize_ip()
        self.velocity = self.target * self.speed
        self.mass = 0  
        self.id = "item"      
        self.effect_id = "0"
        self.main = main
        
    def update(self):
        self.velocity = self.target * self.speed
        self.pos += self.velocity
        self.rect.center = (self.pos)   

class ItemShield(Item):
    def __init__(self, s_pos, d_pos, main):
        super().__init__(s_pos, d_pos, main)
        self.image = pygame.image.load(env.GAME_IMG_DIR + "item_shield.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.center = s_pos
        self.pos = s_pos
        
    def handle_collision(self, t_velocity, t_mass):
        self.main.player.create_shield()
        self.kill()

class ItemPower(Item):
    def __init__(self, s_pos, d_pos, main):
        super().__init__(s_pos, d_pos, main)
        self.image = pygame.image.load(env.GAME_IMG_DIR + "item_power.png").convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.center = s_pos
        self.pos = s_pos
        
    def handle_collision(self, t_velocity, t_mass):
        self.main.player.power_up()
        self.kill()

 

기체와 아이템이 충돌하는 시점에 handle_collision 메서드가 collider에 의해서 호출된다.

이때 각 아이템의 기능을 발현되는 player 개체의 메서드를 호출하도록 개발했다.

ItemShield는 create_shield 를 호출하고

ItemPower는 power_up을 호출한다.

 

그리고 아이템이 발생되는 중심에는 운석이 있다.

운석이 폭발하는 위치에서 아이템이 생성된다.

    def destroy(self, main):
        cnt = random.randint(6, 8)
        main.effect.circle_explode(self.pos.x, self.pos.y, self.mass, cnt)
        main.effect.radial_explode(self.effect_id, self.pos.x, self.pos.y, int(self.mass / 3), 100)
        
        if Util.is_probability(env.ITEM_CHANCE):
            s_pos = (self.pos.x, self.pos.y)
            d_pos = (main.player.pos.x, main.player.pos.y)
            if Util.is_probability(env.ITEM_CHANCE):                
                item_type = random.choice(['1', '2', '3', '4'])                
                if item_type == '1':
                    item = ItemShield(s_pos, d_pos, main)
                elif item_type == '2':
                    item = ItemPower(s_pos, d_pos, main)
                elif item_type == '3':
                    item = ItemEnergy(s_pos, d_pos, main)        
                else:
                    item = ItemBomb(s_pos, d_pos, main)
            else:
                item = ItemJewerly(s_pos, d_pos, main)
                
            main.item_sprites.add(item)
            
        self.kill()

 

운석이 폭발하는 시점에 일정한 확률로 아이템이 생성되는데

이때, 보석은 가장 확율이 높은 발생 아이템으로

그 이외 파워업, 실드, 에너지, 폭탄과 같은 아이템은

조금 더 낮은 확율로 발생되도록 처리했다.

 

소스코드를 첨부한다.

collider 부분에 설명할 부분이 남아 있는데

이 부분은 나머지 아이템, 폭탄, 에너지 그리고 보석 아이템 효과에 대해서 구현하면서

같이 설명하도록 하겠다.

소스코드를 첨부한다.

 

 

13. item.zip
0.06MB