본문 바로가기
개발 일지

파이썬 tkinter 그래픽으로 슈팅 게임 만들기 5

by PrintedLove 2019. 11. 25.

안녕하세요.

이번글에서는 HP바와 점수를 추가하기로 했었는데요.

 

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from tkinter import *
import time
import math
 
window = Tk()
window.title("nemoRPG")   # 게임 이름
window.resizable(0,0)
canvas = Canvas(window, width = 640, height = 640, bg ="white")   # 창 생성
objects, score = set(), 0   # 오브젝트 세트, 점수 선언
 
class Game:   # 게임 클래스
    global objects, score
    def __init__(self):
        self.keys = set()   # 버튼 세트 생성
        self.mx, self.my, self.mPressed = 000   # 마우스 좌표, 클릭 여부
        window.bind("<KeyPress>", self.keyPressHandler)   # 버튼 클릭시 함수호출
        window.bind("<KeyRelease>", self.keyReleaseHandler)   # 버튼 땔시 함수호출
        canvas.bind("<Button-1>", self.mousePress)   # 마우스 클릭시 함수호출
        canvas.bind("<B1-Motion>", self.mousePress)
        canvas.bind("<ButtonRelease-1>", self.mouseRelease)   # 마우스 땔시 함수호출
        canvas.pack()
 
        obj_main = object_main(3103102020"black")   # main 오브젝트 생성
 
        score_view = canvas.create_text(54015, text = "SCORE: " + str(score), font = ("나눔고딕코딩"12), fill = "red")   # 점수 드로우
        canvas.create_rectangle(5542025, fill = "gray82", width =0)   # HP바 바탕 드로우
        hpbar = canvas.create_rectangle(5542025, fill = "springGreen2", width =0)   # HP바 드로우
        hptext = canvas.create_text(20015, text ="HP: (" + str(obj_main.hp) + " / 1000)", font = ("나눔고딕코딩"8))   # HP 숫자 드로우
 
        while(1):  # 메인 루프
            if obj_main in objects:
                for key in self.keys:   # 버튼 체킹
                    if key == ord('A'and obj_main.x_accel > -4: obj_main.x_accel -= 1   # A
                    if key == ord('D'and obj_main.x_accel < 4: obj_main.x_accel += 1   # D
                    if key == ord('W'and obj_main.y_accel > -4: obj_main.y_accel -= 1   # W
                    if key == ord('S'and obj_main.y_accel < 4: obj_main.y_accel += 1   # S
 
                if self.mPressed == 1 and obj_main.coolt == obj_main.cool:   # 마우스 클릭 시
                    obj_attack = object_attack(canvas.coords(obj_main.canvas_id)[0]+7, canvas.coords(obj_main.canvas_id)[1]+766"black"120)    # 공격 오브젝트 생성
                    obj_attack.x_accel, obj_attack.y_accel = self.movePoint(canvas.coords(obj_attack.canvas_id)[0+ 10, canvas.coords(obj_attack.canvas_id)[1+ 10, self.mx, self.my, 25)
                    obj_main.coolt, obj_main.hp = 0 , obj_main.hp - 50
 
                canvas.delete(hpbar); canvas.delete(hptext)   # hp 갱신
                hpbar = canvas.create_rectangle(55420 * obj_main.hp / obj_main.mhp, 25, fill = "springGreen2", width =0)
                hptext = canvas.create_text(20015, text ="HP: (" + str(obj_main.hp) + " / 1000)", font = ("나눔고딕코딩"8))
 
                canvas.itemconfig(score_view, text = "SCORE: " + str(score))   # 점수 갱신
 
            for obj in objects.copy(): obj.step()   # 스텝 함수 호출
 
            window.update()   # 업데이트
            time.sleep(0.01)   # 0.01초 만큼 sleep
                        
    def keyPressHandler(self, event):   # 버튼 세트에 버튼추가
        self.keys.add(event.keycode)
    def keyReleaseHandler(self, event):   # 버튼 세트에 버튼 제거
        if event.keycode in self.keys: self.keys.remove(event.keycode)
 
    def mousePress(self, event):   # 마우스 왼쪽 누를시 좌표 반환, 클릭값 1
        self.mx, self.my, self.mPressed = event.x, event.y, 1
    def mouseRelease(self, event):   # 마우스 왼쪽 땔시 좌표 반환, 클릭값 0
        self.mx, self.my, self.mPressed = event.x, event.y, 0
 
    def movePoint(self, x1, y1, x2, y2, spd):   # 해당 좌표로 이동
        return (x2 - x1) * spd / math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2), (y2 - y1) * spd / math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
 
 
class element:   # 오브젝트 원형
    def __init__(self, x, y, size_x, size_y, color):
        self.x, self.y = x, y   # 생성 위치
        self.size_x, self.size_y = size_x, size_y   # 크기
        self.color = color   # 색
        self.x_accel, self.y_accel = 00   # 가속도
        objects.add(self)   # 오브젝트 세트에 자신 등록
        self.canvas_id = canvas.create_rectangle(x, y, x + self.size_x, y + self.size_y, fill = self.color, width =0)   # 캠버스 추가
 
    def destroy(self):   # 제거 함수
        objects.discard(self)   # 오브젝트 세트에서 자신 제거
        canvas.delete(self.canvas_id)   # 캠버스 제거
        del self
 
    def move(self):   # 움직임 계산(이동, 가속도, 중력) 함수
        x_value, y_value = self.x_accel, self.y_accel
        if x_value != 0 or y_value != 0:   # 좌표 갱신
            if canvas.coords(self.canvas_id)[0+ x_value < 0: x_value, self.x_accel = -canvas.coords(self.canvas_id)[0], -self.x_accel   # 창나감 방지, 튕김효과
            if canvas.coords(self.canvas_id)[1+ y_value < 0: y_value, self.y_accel = -canvas.coords(self.canvas_id)[1], -self.y_accel
            if canvas.coords(self.canvas_id)[2+ x_value > 640: x_value, self.x_accel = 640 - canvas.coords(self.canvas_id)[2], -self.x_accel
            if canvas.coords(self.canvas_id)[3+ y_value > 640: y_value, self.y_accel = 640 - canvas.coords(self.canvas_id)[3], -self.y_accel
            canvas.move(self.canvas_id, x_value,  y_value)   # 수치만큼 이동
            self.mx, self.my = 00   # 이동값 초기화
            self.x_accel, self.y_accel = self.x_accel * 0.98, self.y_accel * 0.98    # 가속도 감소
 
class object_main(element):   # main 오브젝트
    def __init__(self, x, y, size_x, size_y, color):
        super().__init__(x, y, size_x, size_y, color)   # 상속
        self.mhp, self.hp = 10001000   # 체력
        self.cool, self.coolt = 250   # 쿨타임
 
    def step(self):   # 스텝 함수
        global score
        score += 1
        self.move()
        if self.coolt < self.cool: self.coolt += 1  # 쿨타임 감소
        if self.hp < 0: self.destroy()   # HP 0일시 제거
        
 
class object_attack(element):   # attack 오브젝트
    def __init__(self, x, y, size_x, size_y, color, livetime):
        super().__init__(x, y, size_x, size_y, color)   # 상속
        self.livetime = livetime   # 동작 시간
        self.fortime = 0   # 지난 시간
 
    def step(self):   # 스텝 함수
        self.move()
        if self.livetime <= self.fortime: self.destroy()   # 동작 시간 오버 or 멈출시 파괴         
        self.fortime += 1    # 지난 시간 ++
 
Game()   # 게임 실행
cs

 

 

영상을 보시면 HP바와 점수, 그리고 공격속도가 구현되어 있습니다.

 

원리는 이렇습니다.

 

 obj_main의 hp변수를 width 값으로 사각형 canvas를 메인루프가 반복될 때 마다 갱신시켜주는 것입니다.

처음에는 연산 낭비를 방지하기 위해 지역변수를 선언해 hp값이 바뀔때만 갱신되도록 하려 했으나 굳이..? 라는 생각에 집어쳤습니다.

 

 어짜피 왠만한 컴퓨터 성능으론 canvas 갯수가 100개가 넘어가도 프레임 드랍이 발생하지 않기에 괜찮다고 생각했습니다. 물론 규모가 커지면 최적화를 위해 이런저런 장치들을 마련해 두어야 하겠지만요.

 

 점수도 마찬가지로 매 루프마다 갱신됩니다.

이것을 가시적으로 관찰하기 위해 obj_main의 스텝함수에 점수값이 매 루프마다 1씩 증가하도록 설정했습니다.

 

 공격 쿨타임도 구현된 것을 알 수 있습니다. 정확히 25번 루프가 돌면 다음 공격이 가능하게끔 설정했습니다.

또한 공격시마다 테스트를 위해 hp가 50식 감소하도록 설정했습니다.

 

 때문에 21번째 공격과 동시에 obj_main이 제거되면서 더이상 점수가 오르지 않는 모습을 영상에서 확인하실 수 있습니다.

 

다음 글에서는 적 오브젝트를 구현해 보겠습니다.

댓글