pygame 에 대해 보던 도중 pygame.math.Vector2 클래스에 대해 알게 되어서 게임에서 공의 이동을 vector2 를 이용하도록 변경해 보았습니다.
vector2 함수는 스프라이트등의 물체를 이동시키는데 도움이 되는 클래스 입니다. 예전에 제가 직접 삼각함수를 이용해서 공의 이동을 구현했었는데요. Vector2 클래스를 이용하면 매우 간단히 구현할 수 있습니다.
아직 더 수정해야 할 부분이 있을 것 같지만 시간상 어떻게 될지 모르니 일단 올리고 나중에 수정하는 걸로 하도록 하겠습니다. 그리고 아직 Vector2 클래스의 모든 기능을 다 파악한건 아니라 제가 아는 것만 사용하는 것이고요. 더 쉽게 구현할 수 있는 방법이 있을 수 있는데 알게 되면 그때 수정하던지 새롭게 포스팅 하던지 할 생각입니다.
import pygame
class Ball(pygame.sprite.Sprite):
#img 이미지
def __init__(self,img,x,y):
super().__init__()
self.image = img
self.rect = img.get_rect()
self.rect.center = (x,y)
self.mask = pygame.mask.from_surface(img) #충돌체크용 마스크 생성
self.go = False #공을 움직일 거면 True, 기본값은 당연히 False
self.dt = 2 #공의 이동 거리, 속도
self.vpos = pygame.math.Vector2(x,y) #벡터 좌표의 저장
self.v_vm = pygame.math.Vector2() #이동 할 벡터값. 그냥 변수 초기화
기존과 별로 달라진건 없는데 vpos 와 v_vm 이라는 2개의 변수가 추가 되었습니다.
vpos 는 현재 위치 좌표입니다. rect.center 값이 있는데 따로 보관하는 이유는 vector2 클래스 변수여야 vector2 연산이 가능하기 때문입니다.
v_vm 은 이동하는 벡터 값입니다. vpos 에 v_vm 값을 더하면 공이 이동하게 됩니다. 따라서 여기서 중요한것은 v_vm 값을 구하는 것입니다.
>
아무래도 Vector2 에 대해 간단히 설명해야 할것 같은데요. 예를 들어 Vector2(100,100) 위치에 공이 있다고 할때 이 공을 Vector(300,300) 위치로 이동시키려고 하는 경우 Vector2(100,100) 에 특정 Vector2 값을 더해야 합니다. 이 경우는 이렇게 합니다.
Vector2(100,100) + Vector2(200,200)
뭐...간단한 산수죠. 그런데 Vector2(300,300) 위치까지 1의 이동거리로 계속 이동해야 한다면 어떨까요? 기존에는 삼각함수를 동원한 계산을 해야 했겠지만 Vector2 클래스를 이용하면 간단히 할 수 있습니다.
Vector2(200,200).normalize()
위의 값이 Vector2(100,100) 에서 Vector2(300,300) 으로 1만큼 이동하는 Vector2 값입니다. 위의 값을 Vector2(300,300) 에 도달할때까지 계속 더해 주면 되는 겁니다.
>
#공이 움직일 공간
def boundRect(self,rect):
self.brect = rect
#주어진 각도로 공을 움직임
def start(self, angle):
self.v_vm = pygame.math.Vector2(0,-1).rotate(angle) * self.dt
self.go = True
def start 함수를 보면 조금 바뀌었는데요. 주어진 각도로 1만큼 이동하는 vector2 값 v_vm 을 구하고 있습니다.
Vector2(0,-1)은 위쪽 방향이고 왼쪽으로 회전은 -,오른쪽으로 회전은 + 값입니다.
위의 Vector2 에 대한 설명은 특정 좌표로 1만큼 이동할때의 값을 구하는 거지만, 여기서는 좌표가 아닌 특정 각도로 1만큼 이동할때의 값을 구하고 있는 것 입니다.
이제 공의 좌표에서 v_vm 을 더해주면 정해준 각도로 1씩 공은 이동하게 될 겁니다.
>
#공과 막대의 충돌시 공의 방향을 바꿈
#barx : bar 의 x 좌표
def collideBar(self,barx):
#공이 바의 부딪친 좌표에 따라 공이 이동할 새로운 각도를 계산한다.
bx = self.rect.center[0] - barx
self.v_vm = pygame.math.Vector2(0,-1).rotate(bx) * self.dt
공이 바(bar)에 부딫쳤을때 공을 어느 각도로 보낼것인가를 계산하는 것으로 단순한 산수이니 한번 보세요. 공이 바의 왼쪽에 부딪치면 왼쪽으로 오른쪽으로 부딪치면 오른쪽으로 움직이며 그 위치에 따라 공의 이동 각도가 바뀝니다.
>
def move(self,bs2):
#게임이 시작되지 않았으면 아래 내용을 실행하지 않고 리턴시킴
if not self.go : return
#공을 이동시킴
self.vpos += self.v_vm
#공이 벽에 맞고 튕기는 부분. brect 는 게임이 진행되는 사각형, rect는 공의 사각범위임
if self.rect.left < self.brect.left :
self.vpos.x = self.brect.left + self.rect.width/2
self.v_vm.reflect_ip((1,0))
if self.rect.right > self.brect.right :
self.vpos.x = self.brect.right - self.rect.width/2
self.v_vm.reflect_ip((1,0))
if self.rect.top < self.brect.top :
self.vpos.y = self.brect.top + self.rect.height/2
self.v_vm.reflect_ip((0,1))
if self.rect.bottom > self.brect.bottom:
self.vpos.y = self.brect.bottom - self.rect.height/2
self.v_vm.reflect_ip((0,1))
#공이 벽에 부딪힐때의 소리를 재생함
bs2.play()
#공의 새로운 위치를 입력해줌
self.rect.center = (self.vpos)
공이 벽에 맞고 튕기는 부분이 아주 간략해 졌습니다. 사실 예전 소스가 좀 쓸때 없이 복잡한 것도 있었지만(작동에 문제는 없습니다만....) 뭣 보다 reflect 함수를 이용하기 때문에 별다른 계산을 할 필요가 없기 때문입니다.
reflect 함수는 v_vm 값을 물체가 반사가 되도록 v_vm 값을 새로 계산해 줍니다. 간단히 설명하면 저 함수를 이용하면 공은 반사되어 움직입니다. reflect 함수의 인자는 x 좌표로 공을 반사시킬지(1,0), y 좌표로 반사 시킬지(0,1)만 선택해 주면 됩니다.
pygame 문서를 보면 reflect 관련 함수가 2개가 있는데요. reflect() 와 reflect_ip() 입니다. 2개의 차이는 계산한 값을 return 할 것인가 기존의 값을 변경할 것 인가 입니다.
위에서는 "v_vm.reflect_ip((0,1))" 로 사용했는데요. 만일 reflect() 함수를 쓴다면 "v_vm = v_vm.reflect((0,1))" 로 사용해야 합니다.
마지막 줄엔 스프라이트의 출력을 위해 rect.center 변수에 벡터의 위치값을 넣어 줍니다.
>
pygame.math.Vector2 를 보면 이 외에도 여러 함수가 있습니다만, 제가 아직 그 함수를 다 파악하지 못하고 있습니다.
일단 이 게임에선 이 정도면 사용해도 충분할 것 같기는 합니다만....문서를 봐도 무슨 역활을 하는 함수인지 잘 감이 안와서 열심히 구글 검색을 해보고 직접 테스트 해서 확인해 보고 있습니다.
나중에 알게 되면 따로 포스팅 하겠습니다.
게임의 메인 파일 부분도 Vector2 를 이용하도록 약간 변경되었지만 기존 부분을 reflect 를 쓰도록 변경한 부분이라 사실 변경분은 거의 없어서 따로 설명은 하지 않았습니다.
'프로그래밍 > python' 카테고리의 다른 글
자료형을 파일로 저장하는 방법 (0) | 2020.07.17 |
---|---|
pygame 벽돌깨기 게임 프로젝트 4 - 프레임 설정 (0) | 2019.12.26 |
pygame 에서의 사운드 출력 (0) | 2019.11.04 |
벽돌깨기 게임 프로젝트 4 - 공의 반사각도 변경하기 (2) | 2019.08.05 |
pygame 에서 텍스트 출력 (0) | 2019.07.08 |