스푸79 기록 보관소

운석 피하기 게임 개발(8)- 운석 처리(3) 본문

게임개발

운석 피하기 게임 개발(8)- 운석 처리(3)

스푸79 2024. 8. 31. 09:53

 

 

 

지난 시간에 생성한 다양한 운석 덩어리들이 화면에 나오도록 처리했다.

위에서 보듯 다양한 사이즈의 돌이 회전하면서 움직이고 있다.

처음에 아무런 움직임없이 그냥 이동만 하는 것보다는 자연스럽다는 걸 알 수 있을 것이다.

 

우선 지난 번 코드 중에 실수한 부분부터 짚고 가겠다.

    def dispose(self):
        # 화면 밖으로 벗어나게 되면 객체를 삭제, memory leak 방지
        if self.rect.x + self.rect.width + env.MARGIN < 0:
            self.kill()
        elif self.rect.x + self.rect.width - env.MARGIN >= env.WIDTH:
            self.kill()
        elif self.rect.y + self.rect.height + env.MARGIN < 0:
            self.kill()
        elif self.rect.y + self.rect.height - env.MARGIN >= env.HEIGHT:
            self.kill()

 

지난 번 코드의 잘못된 부분은 margin을 안 준 것이다.

margin을 안 주게 되면 운석이 화면에 경계선에 걸쳐지는 순간 사라져 버리게 된다.

이것은 화면 밖으로 나갈 때 뿐만 아니라 생성될 때도 마찬가지가 되어 버려서 화면에서 나타났다가 사라지는

마법(?)과 같은 현상이 벌어진다.

 

 

그럼 이제부터는 오늘 수정할 부분 정리해 보도록 하겠다.

class Resources:
    def __init__(self):
        self.meteorA1 = [[] for _ in range(env.METEOR_SIZE)]
        -- (중략) ..
        self.render()

    def render(self):
        .. (중략) ..

    def get_meteor(self, type, color):
        # 속성 이름을 동적으로 조합
        attr_name = f'meteor{type}{color}'
        return getattr(self, attr_name)

 

우선 Resources 코드에 get_meteor라는 funtion을 하나 추가했다.

이 함수는 class 안에 있는 멤버변수명을 파라미터로 받아서 가져올 수 있도록 처리했다.

meteorA1 = meteorA{모양}1{숫자}

meteor부분은 공통명이고 뒤에 코드와 숫자가 따라오는데

코드는 운석의 모양

숫자는 색상이다.

다양한 운석을 처리하려면 이 두개를 무작위로 조합하면 된다.

운석은 총 2개, 색상은 총 4개로 8개의 경우의 수가 나온다.

 

여기에 지난 포스트에서 만든 30개 사이즈의 운석 크기를 나누게 되면

총 8 x 30 = 240 개의 운석이 생성이 되게 된다.

spawn_meteor 함수에 운석을 랜덤하게 뽑는 코드를 추가하자.

    def spawn_meteor(self):
        .. (중략) ..

        meteor_type = random.choice(['A', 'B']) # A와 B 둘 중 한개 선택
        meteor_color = random.choice(['1', '2', '3', '4']) # 1,2,3,4 중 한개 선택
        meteor_size  = random.randint(0, 29) # 30개 SIZE 중에서 한개 선택
        
        .. (중략) ..

 

운석을 생성하는 meteor 코드도 변경하자.

class Meteor(pygame.sprite.Sprite):
    def __init__(self, s_pos, d_pos, resource):
        super().__init__()
        meteor_color = random.choice(['01', '02', '03', '04']) 
        meteor_type = random.choice(['A', 'B'])
        image_info = env.GAME_IMG_DIR + "meteor_" + meteor_color + "_" + meteor_type + ".png"
        self.image = pygame.image.load(image_info).convert_alpha()

 

기존의 코드는 위와 같다.

운석이 생성되는 클래스 내에 랜덤하게 선택하도록 처리를 했다.

 

아래 코드를 보자.

self.image는 이미 생성된 resource 클래스에서 값을 받아오도록 처리를 했다.

이렇게 되면 운석이 생성 시점에 이미지 파일을 불러오는 처리를 생략하게 된다.

이 차이가 크지 않다고 생각할 수도 있지만

이미지를 불러오는 작업은 File IO 처리를 하기 때문에 처리 속도에 제법 영향을 미칠 수가 있다.

class Meteor(pygame.sprite.Sprite):
    def __init__(self, s_pos, d_pos, resource):
        super().__init__()
        self.sprites = resource                  # 미리 load한 resource를 맵핑
        self.image = self.sprites[0]             
        self.rect = self.image.get_rect()
        self.rect.center = (s_pos)
        self.pos = s_pos
        self.speed = random.randint(1, 5)
        self.velocity = self.speed
        self.target = (pygame.Vector2(d_pos) - pygame.Vector2(s_pos))
        self.target.normalize_ip()
        self.rotate_speed = random.randint(1, 3) # 회전 속도
        self.image_idx = 0                       # 회전에 따른 이미지 index

 

대부분의 게임 개발 강좌의 예제는 load를 불러서 그때그때 처리하게 끔 설명을 한다.

하지만 그건 교육용의 간단한 게임 개발할 때의 이야기다.

최소 인디게임을 개발한다고 해도 이렇게 Resource를 별도로 처리해주는 로직을 만드는 것이 좋다.

 

Meteor 클래스에는 기존에 없던 rotate 코드를 추가했다.

360도로 회전된 이미지가 self.sprites에 들어있다.

매 프레임마다 해당 이미지를 순서대로 교체하면 회전하는 느낌을 줄 수가 있다.

    def update(self):
        self.rotate()
        self.move()
        self.dispose()

    def rotate(self):        
        self.image_idx += self.rotate_speed        
        if self.image_idx >= 360:
            self.image_idx = 0
        
    def move(self):
        self.velocity = self.target * self.speed
        self.pos += self.velocity
        self.image = self.sprites[self.image_idx]
        self.rect = self.image.get_rect()
        self.rect.center = (self.pos)

 

Meteor클래스가 수정이 되었으니

기존의 spwan_meteor 함수에 meteor를 생성하는 코드를 다시 작성한다.

        meteor_type = random.choice(['A', 'B'])
        meteor_color = random.choice(['1', '2', '3', '4'])
        meteor_size  = random.randint(0, 29)
        r = self.resources.get_meteor(meteor_type, meteor_color) #리소스를 가져온다.
        meteor = Meteor(pos, d_pos, r[meteor_size]) # Meteor 객체 생성
        self.meteor_sprites.add(meteor) # sprites 추가

 

지금 다 작성한 후,

확인해 보니 회전이 한쪽 방향으로만 움직이는 걸 확인했다.

한번 반대 방향으로 회전하는 운석도 직접 수정해 보기 바란다.

 

 

07. sprites.zip
0.03MB