본문 바로가기
관/핫한 파이썬 공부

파이썬의 여러 모듈 공부하기 04.

by 4차원 박스 2019. 11. 2.

gui 의 3가지 구성요소
1. 화면구성 : 위젯/컴포넌트
2. 배치 : 레이아웃 매니저
3. 이벤트 처리 : 이벤트핸들링

 

메뉴만들기, 이벤트 처리하기.

현대는 좋은 리소스들을 찾아서 좋게 활용하는 능력이 
매우 중요하다.




메뉴위젯 사용하기.

상위메뉴에는 이벤트 불가함
하위 메뉴에 이벤트 추가가능.

이벤트처리 하는 방식
mainloop에서 이벤트를 기다리면서 반복 루프를 실행한다.

ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
위젯 . bind(이벤트 지정자 , 이벤트 처리 함수)    ㅣ
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
의 구성을 가진다.


위젯의 이벤트 지정자와 일치하는 이벤트가
발생하면 주어진 이벤트함수가(콜백 함수) 이벤트 객체와함께 호출함

버튼 키 엔터 이벤트 등이 있다


callback함수 :  정해진 함수를 뜻한다.
즉  어느경우에는 무조건 이러한 함수를 실행하도록 정해놓은것. 
ex)__init__ 함수 또한 콜백함수로 볼수있다. 
주로 이벤트에서 많이 쓰인는것이 콜백 함수이다.


이벤트 처리함수와 콜백함수
전자는 tk인터에서 이벤트를 처리하는 용도로 만든 함수
콜백함수는 이미정해진 (이벤트용) 함수이다.
컴퓨터 전원키고 끄는것이 콜백함수에 속한다.

이벤트 관련함수는 다음 을 참고한다.

 

이벤트 01.

 

 

이벤트 02.

이벤트 객체는 event.~ 의 방식으로 사용됨

이때 이벤트 처리함수의 변수에는 (event)가 온다
L.config(text=msg) 를 해야 라벨이 달라지는지 파악


from tkinter import*

w=Tk()

def ma(event):
    print(event.x, event.y, "이것이 클릭한 마우스 좌표임")


l=Label(w,text='hello')
l.bind("",ma)
l.pack()
w.mainloop()

이건 클릭할때 이벤트를 라벨에 붙임.

#키 이벤트임
w=Tk()

l=Label(w,text='mouse & key 이벤트', width=30, height=20)
l.pack()

def key(event):
    if event.char==event.keysym:
        msg='일반키임  %r' % event.char
    else:
        msg='특수키들 %r' % event.keysym
    l.config(text=msg)

def mouse(event):
    msg='마우스 이벤트 %s' % event.widget
    l.config(text=msg)

l.bind_all('',key)
l.bind_all('',mouse)
w.mainloop()


이번엔 어느 갤러그 게임의 코드를 분석해본다

(oop의 개념의 코드가 작성된 것 이므로 간단하게 어떤식으로 작동 하는지만 주석으로

알아 본다.)

 

 


게임분석 은 캐릭터부터 하기 돌아가는 순서부터 하기



#갤러그 게임코드
from tkinter import *
import time #랜덤을 사용할 예정


# 게임의 스프라이트를 나타내는 클래스로 공통적으로 사용되는 변수와 메소드를 가지고 있다.
class Sprite :
    # 생성자
    def __init__(self, image, x, y): #Sprite 클래스에서 가장 먼저 실행될 함수이다.
        self.img = image # 스프라이트가 가지고 있는 이미지
        self.x = x # 현재 위치의 x좌표
        self.y = y # 현재 위치의 y좌표
        self.dx = 0 # 단위시간에 움직이는 x방향 거리
        self.dy = 0      # 단위시간에 움직이는 y방향 거리
                
    # 스프라이트의 가로 길이 반환
    def getWidth(self) :
        return self.img.width()

    # 스프라이트의 세로 길이 반환
    def  getHeight(self) :
        return self.img.height()

    # 스프라이트를 화면에 그리기
    def draw(self, g) :
        g.create_image(self.x, self.y, anchor=NW, image=self.img) #앵커로 좌측 상단에 위치 지정함

    # 스프라이트를 움직이는 메소드
    def move(self) :
        self.x += self.dx   #기존 위치인 셀프x 에 셀프d를 더해준다
        self.y += self.dy   #기존 위치인 셀프y 에 셀프d를 더해준다

    # dx를 설정하는 설정자 메소드 
    def  setDx(self, dx) :
        self.dx = dx

    # dy를 설정하는 설정자 메소드 
    def  setDy(self, dy) :
        self.dy = dy

    # dx를 반환하는 접근자 메소드 
    def  getDx(self) :
        return self.dx

    # dy를 반환하는 접근자 메소드 
    def  getDy(self) :
        return self.dy

    # x를 반환하는 접근자 메소드 
    def  getX(self) :
        return self.x

    # y를 반환하는 접근자 메소드 
    def  getY(self) :
        return self.y

    # 다른 스프라이트와의 충돌 여부를 계산한다. 충돌이면 true를 반환한다.
    # 3장의 Lab 문제를 참조한다. 
    def  checkCollision(self, other) :
        p1x = self.x
        p1y = self.y
        p2x = self.x+self.getWidth() #최종으로 현 위치에서 이미지의 가로 길이만큼 더한 위치
        p2y = self.y+self.getHeight() #최중으로 현 위치에서 이미지의 세로 길이만큼 더한 위치
        p3x = other.x #아마도 우주선의 위치가 아닌가
        p3y = other.y
        p4x = other.x+other.getWidth()
        p4y = other.y+other.getHeight()

        overlapped = not( p4x < p2x or
            p3x > p2x or
            p2y < p3y or
            p1y > p4y)
        return overlapped

    # 충돌을 처리한다. 현재는 아무 일도 하지 않는다. 나중에 사용될 클래스에서 불리면 사용될 예정임. 
    def  handleCollision(self, other) :
        pass






    
# 우리의 우주선을 나타내는 클래스
class StarShipSprite(Sprite):
    def __init__(self, game, image, x, y): #스타십 클래스에서 가장먼저 실행되는 함수인데
        super().__init__(image, x, y)       #스프라이트 라는 클래스를 상속받아 쓴다 여기서 스트라이프를 살펴보거나 아래를 슬쩍본다.
        self.game = game           #game 이라는 변수를 만든듯      
        self.dx = 0
        self.dy = 0

    # 우주선을 움직인다. 만약 윈도우의 경계를 넘으려고 하면 움직이지 않는다.  
    def move(self):
        if ((self.dx < 0)  and (self.x < 10)) :   #움직이는 변화가 왼쪽이고  좌표의 위치가 10보다 작을때는 움직이지 않게
            return
        if ((self.dx > 0) and (self.x > 870)) :   #움직이는 변화가 우측이고  좌표의 위치가 900보다 클때는 움직이지 않게
            return
        super().move()   #스프라이트의 무브함수를 사용하는듯 스프라이트의 내용으로 움직인다
        self.dx = 0   #움직이는 값을 초기화 해주는 역할인듯 dx가 누적되면 순식간에 사라질듯하니까 이렇게 해둔거 같다.

    # 충돌을 처리한다. 외계인 우주선과 충돌하면 게임이 종료된다. 
    def handleCollision(self, other) :
        if  type(other) is AlienSprite :   #위의 스트라이프 클래스에서의 함수
            self.game.endGame()






# 외계인 우주선을 나타내는 클래스
class AlienSprite(Sprite):
    def __init__(self, game, image, x, y):
        super().__init__(image, x, y)
        self.game = game
        self.dx = -10 # x 방향으로만 움직인다. 

    # 외계인 우주선을 움직이는 메소드
    
    # 윈도우에 경계에 도달하면 한칸 아래로 이동한다. 
    def move(self):
        if (((self.dx < 0) and (self.x < 10)) or ((self.dx > 0) and (self.x > 850))) : #외계인움직임을 나타내는 함수
            self.dx = -self.dx
            self.y += 50
            if (self.y > 600) :
                game.endGame()
        super().move()






        
# 포탄을 나타내는 클래스
class ShotSprite(Sprite):
    def __init__(self, game, image, x, y):
        super().__init__(image, x, y)
        self.game = game
        self.dy = -45  #총알 날아가는 속도 (50보다 커버리면 외계인의 사진에 닿지 않게된다.)

    # 화면을 벗어나면 객체를 리스트에서 삭제한다. 
    def move(self):
        super().move()
        if (self.y < -100) :
            self.game.removeSprite(self)

    # 충돌을 처리한다. 포탄과 외계인 우주선 객체를 모두 리스트에서 삭제한다. 
    def handleCollision(self, other) :
        if  type(other) is AlienSprite :
            self.game.removeSprite(self)
            self.game.removeSprite(other)

     





            
# 게임을 나타내는 클래스
class GalagaGame():  #가아아아장 먼저 불려지는 클래스 이다!

    # 왼쪽 화살표 키 이벤트를 처리하는 함수
    def keyLeft(self, event) :
        self.starship.setDx(-10)
        return

    # 오른쪽 화살표 키 이벤트를 처리하는 함수
    def keyRight(self, event) :
        self.starship.setDx(+10)
        return

    # 스페이스 키 이벤트를 처리하는 함수
    def keySpace(self, event) :  #스페이스를 누르면 총이 나가는 이벤트를 갤러그게임의 함수인데 이 함수는 총알클래스의 인스턴스를 포함함
        self.fire()
        return

    # 게임에 필요한 스프라이트를 생성하는 메소드
    def  initSprites(self) :    #두번째 단계로 실행되는 함수이다
        self.starship = StarShipSprite(self, self.shipImage, 450, 850)  #스타십 클래스의 인스턴스를 셀프로 지정한듯함 여기서 스타십의 클래스를 봐야할듯
        self.sprites.append(self.starship)  #스프라이트에 스타쉽의 값을 넣어준다
        for y in range(0, 1): #외계인의 행의 수
            for x in range(0, 20): #외계인의 열의 수
                alien = AlienSprite(self, self.alienImage, 100 + (x * 50), (50) + y * 30) #인스턴스를 만들어서 에일리언 클래스를 사용하는데 움직이는 값을 나타내는듯하
                self.sprites.append(alien)

    # 생성자 메소드
    def __init__(self, master): #제일 먼저 사용되는 클래스의 함수이다.
        self.master = master
        self.sprites = []  #여기에 배열 리스트로 저장이 된다.
        self.canvas = Canvas(master, width=900, height=900)          #캔버스를 새로 만들어서 그 크기를 정해준다(실행되는 게임창과는 별도이다).
        self.canvas.pack()
        self.shotImage = PhotoImage(file="fire.png")            #총알 이미지 불러오고
        self.shipImage = PhotoImage(file="starship.png")        #우주선 이미지 불러오고
        self.alienImage = PhotoImage(file="alien.png")          #적 이미지 불러오고
        self.running = True
        self.initSprites()                                  #initsprites 가 실행된다 (이건 갤러그 게임안의 함수이다).
        master.bind("",  self.keyLeft)
        master.bind("", self.keyRight)
        master.bind("", self.keySpace)
        
    # 게임을 시작하는 메소드 
    def startGame(self) :
        self.sprites.clear()
        initSprites()

    # 게임을 종료하는 메소드 
    def endGame(self) :
        self.running = False
        pass
    
    # 스프라이트를 리시트에서 삭제한다. 
    def removeSprite(self, sprite) :
        if( sprite in self.sprites):
            self.sprites.remove(sprite)
            del sprite

    # 포탄을 발사하는 메소드 
    def fire(self) :
        shot = ShotSprite(self, self.shotImage, self.starship.getX() + 10, self.starship.getY() - 30) #총알관련 클래스를 본다.
        self.sprites.append(shot)

    # 화면을 그리는 메소드 
    def paint(self, g) :
        self.canvas.delete(ALL)
        self.canvas.create_rectangle(0, 0, 900, 900, fill="blue") #게임 화면이 이것이다.
        for sprite in self.sprites:
            sprite.draw(self.canvas)

    # 게임 루프
    def  gameLoop(self) :
        for  sprite in self.sprites:
            sprite.move()

        # 스프라이트 리스트 안의 객체끼리의 충돌을 검사한다. 
        for  me in self.sprites: 
            for  other in self.sprites :
                if me != other:
                    if (me.checkCollision(other)) :
                        me.handleCollision(other)
                        other.handleCollision(me)
        self.paint(self.canvas)
        if( self.running ):
         self.master.after(10, self.gameLoop)  



root = Tk() #윈도우 만들거
g = GalagaGame(root) #g 라는 인스턴스는 갤러그게임 이라는 클래스를 보게 만든다 이것이 윈도우에 붙여지는거라고 본다
root.after(10, g.gameLoop()) #갤러그 게임 클래스의 게임루프라는 함수가 10초 뒤에 실행된다. 게임루프 함수도 본다
root.mainloop() # 이벤트가 처리될때 까지 실행되게 한다.

댓글