일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
- 황선엽
- 즐거운 어른
- 인생의 해상도
- 게임개발
- 타자연습게임
- 제이슨 슈라이어
- 나는 매트로폴리탄 미술관 경비원입니다.
- 매트로폴리탄
- openAI
- 에릭 바론
- 나는 스물일곱 2등 항해사입니다.
- 운석피하기 게임
- 숨결이 바람이 될 때
- frozen lake
- gymnasium
- Gym
- 타이핑좀비
- 타이핑 몬스터
- Python
- 마담 프루스트
- Ai
- 단어가 품은 세계
- Stable diffusion
- 야매요리사
- 이와타씨에게 묻다
- 아무도 없는 숲속에서
- comfyui
- pygame
- hal연구소
- 시집
- Today
- Total
스푸79 기록 보관소
운석 피하기 게임 개발(7)- 운석 처리(2) 본문
우선 지난 포스트에서 말한 기존 운석 처리 코드의 문제점부터 수정해보겠다.
0.5초 간격으로 계속해서 Meteor 클래스는 새로운 객체로 생성이 된다.
그런데 0.5초 간격으로 생성된 Meteor 객체는 언제 소멸이 될까?
현재 소멸 처리를 해 주는 부분이 없다.
물론, 화면에서 사라지지만
화면에서 안 보일 뿐 해당 객체는 계속 화면 밖에서 동작을 하면서 메모리를 야금야금 쓰고 있을 것이다.
운석 피하기 게임이 1분 정도 진행되는거라면 괜찮겠지만
만약 한시간 이상 계속 플레이가 된다면 어떻게 될까?
시간이 지날 수록 사용되는 메모리는 계속 늘어날 것이고
프로그램 속도는 점점 느려질 것이다.
그러다 결국에는 out of memory가 되서 화면이 멈추거나 강제 종료가 될 것이다.
이것을 일명 memroy leak 이라고 한다.
기존 Meteor 클래스에 객체가 화면을 벗어나면
메모리를 해제하도록 코드를 아래와 같이 추가해주자.
그리고 해당 메서드를 update함수에 넣어주어서 움직임이 처리될때마다 확인하도록 한다.
def dispose(self):
# 화면 밖으로 벗어나게 되면 객체를 삭제, memory leak 방지
if self.rect.x + self.rect.width < 0:
self.kill()
elif self.rect.x + self.rect.width >= env.WIDTH:
self.kill()
elif self.rect.y + self.rect.height < 0:
self.kill()
elif self.rect.y + self.rect.height >= env.HEIGHT:
self.kill()
이번 포스트에서는 지난 포스트에서 얘기한 부자연스러운 운석을 조금 더 자연스럽게 처리할 생각이다.
추가적으로 고정된 사이즈의 운석만 생성되는 부분도 수정할 것이다.
util.py 라는 새로운 코드를 만들자. 그리고 해당 코드를 아래와 같이 작성한다.
import random
import pygame
class Util:
@staticmethod
def is_probability(percent):
return random.random() < percent
@staticmethod
def rotates(image, sprites, angle_inc):
angle = 0
for _ in range(360):
sprites.append(pygame.transform.rotozoom(image, angle, 1))
angle += angle_inc
@staticmethod
def resize_image(image, resize_px):
width = image.get_width()
height = image.get_height()
aspect_ratio = width / height
target_width = width - resize_px
target_height = int(target_width / aspect_ratio)
return pygame.transform.scale(image, (target_width, target_height))
Util 클래스에는 staticmethod를 총 3개 생성했다.
1) is_probability : 확률 처리하는 메서드를 만들었다. 기존 코드는 random.randint(1, 25) == 1 이런 식으로 처리를 했다.
하지만 확률통계적으로 저렇게 처리를 하면, 계산상으로는 25%일지 모르지만 확률분포상으로는 25%라고 말하기가 어렵다. 그래서 확률을 조금 더 가깝게 처리하기 위해 코드를 변경했다.
기존코드
if random.randint(1, env.METEOR_DIR_CHANE) == 1: #25% 확률 일직선 처리
if dir == env.TOP:
d_pos = (pos[0], env.HEIGHT)
... (중략) ...
변경된코드
if Util.is_probability(env.METEOR_DIR_CHANE): # env.METEOR_DIR_CHANCE = 0.25
if dir == env.TOP:
d_pos = (pos[0], env.HEIGHT)
... (중략) ...
2) rotates : 이미지의 회전처리하는 코드이다. pygame을 좀 아는 분들은 왜 pygame.transform.rotates를 안 쓰고 rotozoom을 썼는지 의아해 하실 분도 계실 것이다. rotozoom을 이용하면 rotates보다 이미지 깨짐이 덜하다.
3) resize_image : 이미지의 사이지를 변경하는 코드인데, pygame 자체의 이미지 resize는 정사각형인 경우에는 괜찮은 편이나 내가 그린 운석 asset은 직사각형이라 비율 대비 다시 폭과 너비를 계산하도록 코드를 작성했다.
이 3개의 코드의 역할은 이렇다.
운석이미지를 360도 회전시키년서 각도별로 이미지를 새로 생성할 것이다.
그리고 운석이미지의 현재 크기에서 30 pixel 까지 줄여서 다시 만들 것이다.
class Meteor(pygame.sprite.Sprite):
def __init__(self, s_pos, d_pos):
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()
이것이 기존에 작성한 Meteor 클래스 코드이다.
이 코드의 문제점은
객체가 생성될 때마다 pygame.image.load를 통해 물리 경로에 있는 이미지를 불러온다는 점이다.
이것도 운석 수가 많지 않은 경우에는 큰 문제가 되지 않을 것이다.
아니면 컴퓨터 성능이 압도적으로 좋은 경우에도 영향이 없을 것이다.
문제는 360도 회전을 시킨 이미지와 사이즈까지 줄인 이미지 처리를 매 클래스가 객체로 생성될때마다
처리가 된다면 그건 또 다른 문제일 것이다.
생각해 보자.
운석용 이미지는 총 8장, 거기에 1 pixel 부터 30 pixel까지 사이즈를 줄이고 해당 이미지를 360도 회전 시킨다면
총 86,400 장의 처리가 객체 생성시 마다 동작이 되어야한다.
컴퓨터 성능이 아무리 뛰어나도 절대 이건 효율적이지가 않다.
class Resources:
def __init__(self):
self.meteorA1 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorB1 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorA2 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorB2 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorA3 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorB3 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorA4 = [[] for _ in range(env.METEOR_SIZE)]
self.meteorB4 = [[] for _ in range(env.METEOR_SIZE)]
self.render()
def render(self):
a1_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_01_A.png").convert_alpha()
b1_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_01_B.png").convert_alpha()
a2_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_02_A.png").convert_alpha()
b2_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_02_B.png").convert_alpha()
a3_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_03_A.png").convert_alpha()
b3_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_03_B.png").convert_alpha()
a4_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_04_A.png").convert_alpha()
b4_image = pygame.image.load(env.GAME_IMG_DIR + "meteor_04_B.png").convert_alpha()
for i in range(env.METEOR_SIZE):
Util.rotates(Util.resize_image(a1_image, i + 1), self.meteorA1[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(b1_image, i + 1), self.meteorB1[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(a2_image, i + 1), self.meteorA2[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(b2_image, i + 1), self.meteorB2[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(a3_image, i + 1), self.meteorA3[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(b3_image, i + 1), self.meteorB3[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(a4_image, i + 1), self.meteorA4[i] , env.DEFAULT_ANGLE)
Util.rotates(Util.resize_image(b4_image, i + 1), self.meteorB4[i] , env.DEFAULT_ANGLE)
Resources.py 를 새로 만들었다.
이렇게 별도로 리소스를 생성하고 Meteor 객체 생성 시점에 Resource에서 사용할 이미지를 받아오도록 처리하면
위에서 말한 비효율적인 처리 부분이 해결이 된다.
우선 1번 운석 처리와 각 이미지 사이즈는 1, 10, 20, 30으로 줄이고 360도 회전처리하도록 간단하게 코드를 작성했다.
첨부된 파일의 resources_test.py를 실행하면 초기화된 두 운석 이미지가 회전하는 걸 확인할 수가 있다.
회전 속도가 느리다고 생각이 되면 이미지 index를 현재 1씩 증가하도록 처리했는데
이 값을 늘리면 빠른 속도로 회전하는 운석을 확인할 수가 있다.
포스트 내용이 좀 길어졌다. 다음 포스트에서는 resource를 초기화하는
코드를 정리하고 실제 Meteor클래스에서 해당 resource를 사용할 계획이다.
오늘 강의에 사용한 코드 전체를 첨부한다.
다음 포스트가 올라오기 전에 본인이 직접 현재 코드를 수정해서 한번 Meteor 클래스에 적용해 보는것도 괜찮을 것이다.
이런 거는 ChatGPT가 해주지 않는다.
개발하는 과정에서 아직 인간의 영역은 많이 남아있다.
'게임공작소' 카테고리의 다른 글
운석 피하기 게임 개발(9)- 충돌 처리(1) (8) | 2024.09.05 |
---|---|
운석 피하기 게임 개발(8)- 운석 처리(3) (0) | 2024.08.31 |
운석 피하기 게임 개발(6)- 운석 처리(1) (1) | 2024.08.26 |
운석 피하기 게임 개발(5)- 배경 넣기(feat. ChatGPT) (1) | 2024.08.25 |
운석 피하기 게임 개발(4)- 전역변수 설정 (0) | 2024.08.23 |