반응형

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 를 쓰도록 변경한 부분이라 사실 변경분은 거의 없어서 따로 설명은 하지 않았습니다.

 

+ Recent posts