일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 운석피하기 게임
- Python
- 에르난 디아스
- pygame
- 고양이발 살인사건
- 타이핑좀비
- Gym
- 매트로폴리탄
- 운석 피하기 게임
- 트렌드코리아2025
- 황선엽
- 나는 스물일곱 2등 항해사입니다.
- 시집
- gymnasium
- frozen lake
- 타자연습게임
- 아무도 없는 숲속에서
- 나는 매트로폴리탄 미술관 경비원입니다.
- 타이핑 몬스터
- 쿠바전통음악
- Ai
- 어른의 행복은 조용하다
- comfyui
- 게임개발
- 단어가 품은 세계
- 부에노비스타 소셜클럽
- 인생의 해상도
- 숨결이 바람이 될 때
- Stable diffusion
- openAI
- Today
- Total
스푸79 기록 보관소
운석 피하기 게임 개발(14)- 조준 및 총알 발사 본문
이번 포스트에서는 총알 발사하는 기능을 넣도록 하겠다.
위의 보는 바와 같이 조준선과 미사일 이미지를 새로 그렸다.
조준선은 마우스 커서에 따라 움직이고
왼쪽버튼을 누르면 조준선이 있는 방향을 향해 미사일을 발사한다.
끔찍하지만 이를 위해 우리는 또 수학을 잠깐 알아야한다.
메인캐릭터(비행기)와 조준선의 좌표를 각각 x1, y1, x2, y2라고 할때
마우스 왼쪽버튼을 클릭할 때 미사일이 날아가야하는 각도는 위의 그림과 같다.
위의 미사일 이미지와 메인캐릭터 이미지도 각도만큼 회전을 해줘야한다.
즉, 아래 그림과 같이 이미지를 틀어줘야 자연스러운 모습이 나온다.
그러면 우선 둘 사이에 각도부터 구해보자.
사실 이미 우리는 이 각도를 구하는 걸 한번 해 봤다.
바로 운석의 움직임이다. 운석은 지금 메인 캐릭터를 향해 날아가도록 구현되어 있다.
self.target = (pygame.Vector2(d_pos) - pygame.Vector2(s_pos))
그런데 안타깝게도 운석의 경우에는 생성된 좌표의 값을 기준으로 vector값만 구하도록 처리를 했다.
계속 회전을 하는 운석의 경우는 움직이는 방향만 잡아주면 되서 위와 같이 방향만 잡아주었다.
@staticmethod
def pos_to_angle(x1, y1, x2, y2):
dx = x2 - x1
dy = y2 - y1
angle_radians = math.atan2(dy, dx)
angle_degrees = math.degrees(angle_radians)
# 각도가 음수일 경우 360을 더해줌
if angle_degrees < 0:
angle_degrees += 360
return angle_degrees
수학을 못 했지만 어차피 내가 계산하는게 아니기 때문에 공식만 이해하면 코드를 생성 가능한다.
Util.py 에 pos_to_angle이라는 staticmethod를 하나 생성했다.
두 물체 사이의 x축 거리와 y축 거리를 값을 이용하여 atan2를 이용하면 라디안 값이 나온다.
라디안 값을 다시 각도로 변환해주면 끝.
라디안에 대한 설명은 이 글의 주제를 많이 벗어나는 관계로 간단하게 설명하면
1라디안은 약 57.3도 정도 된다. 조금 더 정밀한 수치값 정도로 이해하고 여기서는 우선 넘어가자.
atan2를 이용해서 라디안 값을 구하고 난 후 각도로 변환하면 음수로 넘어오는 경우가 발생한다.
각도가 음수가 있던가? 의아해하실 분이 있을텐데 그 이유는 아래와 같다.
데카르트 좌표계에서 x축을 기준으로 0도를 잡았다고 하자.
A방향으로 움직이는 각도와 B방향으로 움직이는 각도가 45도로 같다고 했을 때
theta A의 값과 theta B의 값은 각각 무엇일까?
답은 theta A는 45도 theta B는 -45이다.
같은 45도를 이동했지만 방향이 다르기 때문에 이를 thetaB는 음수로 표현한 것이다.
-45는 360를 기준으로 하면 315와 같다. 그래서 위의 pos_to_angle 메서드에
아래와 같이 음수일 경우 360도를 더해주는 로직이 들어간 것이다.
# 각도가 음수일 경우 360을 더해줌
if angle_degrees < 0:
angle_degrees += 360
우리는 이 각도를 0에서 359까지 표현해야한다.
이유는 우리가 생성할 배열에는 음수가 없기 때문이다.
지난 강좌의 소스 폴더를 열어서 player.png를 확인해 보자. 그리고 이번 강좌의 소스 폴더의 player.png와 비교해보자.
좌측이 지난 강좌 이미지고 우측은 이번 강좌 이미지이다.
이미지의 각도를 90도 틀어주었는데 그 이유는 바로 0부터 359까지 이미지를 회전시킨 배열을 만들려고 하기 때문이다.
시작점인 0도는 우측에 보는 이미지와 같이 오른쪽을 향하고 있어야 하기 때문이다.
360은 제외를 하였는데 그 이유는 아시다 시피 360도를 회전하면 0도로 원점으로 돌아오기 때문이다.
그리고 메인캐릭터(비행기)와 같은 방향을 바라봐야하기 때문에 위와 같이 이미지를 오른쪽으로 향하도록 그렸다.
class Missile(pygame.sprite.Sprite):
def __init__(self, pos, dir, image):
super().__init__()
self.image = image
self.rect = self.image.get_rect()
self.rect.center = pos
self.pos = pos
self.dir = dir
self.target = pygame.Vector2(env.ANGLE_TO_VECTORS[self.dir])
self.id = "missile"
self.effect_id = "0"
self.speed = 8
self.mass = 1000
def update(self):
self.move()
def move(self):
self.velocity = self.target * self.speed
self.pos += self.velocity
self.rect.center = self.pos
def handle_collision(self, t_velocity, t_mass):
pass
우선 미사일 class 코드이다. 메인 캐릭터로부터 위치(pos)와 방향(dir) 그리고 이미지를 받을 것이다.
미사일 이미지는 player class 생성 시 로드되고 발사 되는 시점의 이미지 한장만 넘기도록 했다.
(괜히 메모리 사용을 늘릴 필요는 없으니깐...)
class CrossHair(pygame.sprite.Sprite):
def __init__(self, pos):
super().__init__()
self.image = pygame.image.load(env.GAME_IMG_DIR + "crosshair.png").convert_alpha()
self.rect = self.image.get_rect()
self.rect.center = pos
self.id = "crosshair"
def update(self):
x, y = pygame.mouse.get_pos()
self.rect.center = (x, y)
조준선에 해당하는 코드이다. 마우스를 따라만 다니면 되기 때문에 코드는 아주 간단하다.
pygame.mouse.get_pos를 통해 현재 x,y 값을 가져온 후, 이미지의 center 값으로 설정만 해주면 된다.
player_image = pygame.image.load(env.GAME_IMG_DIR + "player.png").convert_alpha()
Util.rotates(player_image, self.player, env.DEFAULT_ANGLE)
missile_image = pygame.image.load(env.GAME_IMG_DIR + "missile.png").convert_alpha()
Util.rotates(missile_image, self.missile, env.DEFAULT_ANGLE)
resource.py에는 위와 같이 player.png와 missile.png를 받은 후 360도 회전해서 이미지를 설정해 주었다.
class Main():
def __init__(self):
pygame.init()
... (중략) ...
pygame.mouse.set_visible(False)
... (중략) ...
self.player_sprites = pygame.sprite.Group()
self.crosshair_sprites = pygame.sprite.Group()
self.crosshair = CrossHair((env.START_POS_X, env.START_POS_Y))
self.player = Player(self, (env.START_POS_X, env.START_POS_Y), \
self.resources.player, \
self.resources.missile)
self.player_sprites.add(self.player)
self.crosshair_sprites.add(self.crosshair)
def play(self):
while self.running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self.player.fired()
main.py 에서는 마우스 커서를 없애기 위해 pygame.mouse.set_visible(False)를 선언했다.
그리고 화면에서 마우스 이벤트를 잡아서 event.button == 1인 경우 player 클래스에서 missile을 생성하도록 처리했다.
이제 거의 운석 피하기 게임이 끝을 보고 있는 것 같다.
다음 포스트에서는 미사일에 맞은 운석이 보석과 아이템을 떨구는 걸 구현해 보겠다.
아이템은 방어막, 폭탄, 에너지를 넣을 생각이다.
전체 소스 코드를 첨부한다.
'게임공작소' 카테고리의 다른 글
운석 피하기 게임 개발(16) - 아이템효과(폭탄, 보석, 에너지 등등) (68) | 2024.10.13 |
---|---|
운석 피하기 게임 개발(15) - 아이템효과(파워업, 방어막) (54) | 2024.10.07 |
운석 피하기 게임 개발(13)- 충돌 시 폭발 효과 만들기 (25) | 2024.09.28 |
운석 피하기 게임 개발(12)- 충돌 시 파편 만들기 (25) | 2024.09.22 |
운석 피하기 게임 개발(11)- 충돌처리(3)(Sweep And Prune알고리즘) (29) | 2024.09.18 |