본문 바로가기
개발 일지

파이썬 tkinter 그래픽으로 슈팅 게임 만들기 6 (완성)

by PrintedLove 2019. 11. 28.

최종 완성본 소스코드입니다.

 

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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
from tkinter import *
import time
import math
import random
 
window = Tk()
window.title("nemoNemo")   # 게임 이름
window.resizable(0,0)
canvas = Canvas(window, width = 640, height = 640, bg ="black")   # 창 생성
objects, enemyObjects, score = set(), set(), 0   # 오브젝트 세트, 점수 선언
 
class Game:   # 게임 클래스
    def __init__(self):
        global objects, enemyObjects, score
        self.keys = set()   # 버튼 세트 생성
        self.mx, self.my, self.mPressed = 000   # 마우스 좌표, 클릭 여부
        self. runtime, self.spontime = 0300   # 런타임, 스폰타임
        self.hp_before, self.score_before = 00   # 갱신용 지역변수
        self.enemyvalue = [[8"red"], [15"blue"], [10"green"], [25"gold"]]   # 적 정보
        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()
        
        canvas.create_text(32060, text ="네모네모 슈팅 게임", fill = "white", font = ("둥근모꼴"28))   # 설명
        canvas.create_rectangle(100200135235, fill = "gray22", outline = "white"); canvas.create_text(117217, text ="A", fill = "white", font = ("둥근모꼴"16))
        canvas.create_rectangle(143200178235, fill = "gray22", outline = "white"); canvas.create_text(160217, text ="S", fill = "white", font = ("둥근모꼴"16))
        canvas.create_rectangle(186200221235, fill = "gray22", outline = "white"); canvas.create_text(203217, text ="D", fill = "white", font = ("둥근모꼴"16))
        canvas.create_rectangle(143157178192, fill = "gray22", outline = "white"); canvas.create_text(160174, text ="W", fill = "white", font = ("둥근모꼴"16))
        canvas.create_text(160270, text ="< 이동 키 >", fill = "white", font = ("둥근모꼴"12))
        canvas.create_rectangle(450157510235, outline = "white"); canvas.create_line(480187510187, fill = "white"); canvas.create_rectangle(450157480187, fill = "gray22", outline = "white")
        canvas.create_text(480270, text ="< 공격 키 >", fill = "white", font = ("둥근모꼴"12))
        canvas.create_text(300400, text ="   사방에서 몰려오는 네모들을 처치하고 점수를 얻으세요!\n\n   시간이 지날수록 강한 적들이 등장합니다.", fill = "white", font = ("둥근모꼴"12))
        self.textblinker()   # 시작 대기
 
        obj_main = object_main(31631688"white")   # main 오브젝트 생성
 
        score_view = canvas.create_text(54015, text = "SCORE: " + str(score), fill = "white", font = ("둥근모꼴"16))   # 점수 드로우
        canvas.create_rectangle(5542025, fill = "gray22")   # 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(True):  # 메인 루프
            if obj_main in objects:
                for key in self.keys:   # 버튼 체킹
                    if key == ord('A'and obj_main.x_accel > -3: obj_main.x_accel -= 1   # A
                    if key == ord('D'and obj_main.x_accel < 3: obj_main.x_accel += 1   # D
                    if key == ord('W'and obj_main.y_accel > -3: obj_main.y_accel -= 1   # W
                    if key == ord('S'and obj_main.y_accel < 3: 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]+8, canvas.coords(obj_main.canvas_id)[1]+833"white"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+random.randrange(-5,5), self.my+random.randrange(-5,5), 25)
                    obj_main.coolt = 0   # 쿨타임 초기화
 
                if self.hp_before != obj_main.hp:   # hp 갱신
                    canvas.delete(hpbar); canvas.delete(hptext)
                    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 = ("둥근모꼴"12))
                    self.hp_before = obj_main.hp
                if self.score_before != score:   # 점수 갱신
                    canvas.itemconfig(score_view, text = "SCORE: " + str(score))
                    self.score_before = score
 
                self.runtime += 1   # 런타임 증가
                if len(enemyObjects) < 25:   # 적 개체 수 제한
                    if self.runtime % self.spontime == 0:
                        for i in range(4):
                            if self.runtime % (self.spontime * (i + 1** 2== 0: obj_enemy = object_enemy(random.choice([-100740])+random.randrange(-50,50), random.choice([-100740])+random.randrange(-50,50), self.enemyvalue[i][0], self.enemyvalue[i][0], self.enemyvalue[i][1], obj_main, i)   # enemy 오브젝트 스폰
                        self.spontime = max([random.randrange(self.spontime - 2, self.spontime), 50])   # 스폰시간 초기화
                    
                for obj in enemyObjects.copy():
                    degree = math.atan2(canvas.coords(obj_main.canvas_id)[0- canvas.coords(obj.canvas_id)[0], canvas.coords(obj_main.canvas_id)[1- canvas.coords(obj.canvas_id)[1])
                    obj.x_accel, obj.y_accel = -obj.enemy_stat[obj.enemy_type][1* math.cos(degree), 5 * math.sin(degree)   # main 오브젝트 공전
                    if obj.coolt == obj.cool:
                        obj_enemyAttack = object_enemyAttack(canvas.coords(obj.canvas_id)[0]+(obj.size_x-obj.enemy_stat[obj.enemy_type][3])/2, canvas.coords(obj.canvas_id)[1]+(obj.size_y-obj.enemy_stat[obj.enemy_type][3])/2,
                                                            obj.enemy_stat[obj.enemy_type][3], obj.enemy_stat[obj.enemy_type][3], obj.color, 100, obj_main, obj.enemy_stat[obj.enemy_type][5])    # obj_enemyAttack 생성
                        obj_enemyAttack.x_accel, obj_enemyAttack.y_accel = self.movePoint(canvas.coords(obj_enemyAttack.canvas_id)[0]+random.randrange(-5,5), canvas.coords(obj_enemyAttack.canvas_id)[1]+random.randrange(-5,5), canvas.coords(obj_main.canvas_id)[0]+10, canvas.coords(obj_main.canvas_id)[1]+10, obj.enemy_stat[obj.enemy_type][4])
                        obj.coolt = 0
 
            for obj in objects.copy():
                obj.move(); obj.step()
            if not obj_main in objects:
                canvas.delete("all"); break
            window.update(); time.sleep(0.01)   # 0.01초 만큼 sleep
 
        canvas.create_text(320260, text = "Game Over...", fill= "white", font = ("둥근모꼴"38)); canvas.create_text(320370, text = str(score) + " 점", fill = "white", font = ("둥근모꼴"28))
        self.textblinker("exit")   # 종료 대기
        sys.exit(1)
                        
    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)
 
    def textblinker(self, sentance = "start"):   # 대기 텍스트
        menuToggle = True; blinkerText = canvas.create_text(320580, text ="< Please pless spacebar to " + sentance + ". >", fill = "red", font = ("둥근모꼴"12))   # 깜박이 canvas 생성
        while(True):   # 대기
            self.runtime += 1
            for key in self.keys:   # spacebar 누를시 다음으로
                if key == 32:
                    canvas.delete("all"); return
            if self.runtime % 60 == 0:
                if menuToggle == True:
                    canvas.itemconfig(blinkerText, text = ""); menuToggle = False
                else:
                    canvas.itemconfig(blinkerText, text = "< Please pless spacebar to " + sentance + ". >"); menuToggle = True
            window.update(); time.sleep(0.01)
 
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 < 30: y_value, self.y_accel = 30 - 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    # 가속도 감소
 
    def collision(self, obj):   # 충돌 검사
        return True if (canvas.coords(self.canvas_id)[0<= canvas.coords(obj.canvas_id)[2and canvas.coords(self.canvas_id)[2>= canvas.coords(obj.canvas_id)[0and canvas.coords(self.canvas_id)[1<= canvas.coords(obj.canvas_id)[3and canvas.coords(self.canvas_id)[3>= canvas.coords(obj.canvas_id)[1]) else False
 
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 = 50   # 쿨타임
 
    def step(self):   # 스텝 함수
        if self.coolt < self.cool: self.coolt += 1  # 쿨타임 감소
        if self.hp <= 0: self.destroy()   # HP <= 0 일시 제거
 
class object_enemy(element):   # enemy 오브젝트
    def __init__(self, x, y, size_x, size_y, color, obj_main, enemy_type):
        super().__init__(x, y, size_x, size_y, color)   # 상속
        self.enemy_stat = [[10023031015], [50017551150], [1503103158], [250013061233]]   # HP, 속도, 공격속도, 투사체 크기, 투사체 속도, 데미지
        self.enemy_type = enemy_type
        self.mhp = self.enemy_stat[self.enemy_type][0]; self.hp = self.mhp   # 체력
        self.cool, self.coolt = self.enemy_stat[self.enemy_type][2], 0   # 쿨타임
        enemyObjects.add(self)   # enemy 오브젝트 세트에 자신 등록
        self.obj_main = obj_main   # obj_main 오브젝트 받기
 
    def step(self):   # 스텝 함수
        if self.coolt < self.cool: self.coolt += 1  # 쿨타임 감소
        if self.hp <= 0:   # HP <= 0일시 제거
            global score
            self.destroy(); enemyObjects.discard(self); score += self.mhp
 
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, self.fortime = livetime, 0   # 동작 시간
 
    def step(self):   # 스텝 함수
        for obj_s in enemyObjects:
            if self.collision(obj_s):   # 충돌시
                obj_s.hp -= 50; self.destroy(); break
        if self.livetime <= self.fortime: self.destroy()   # 동작 시간 오버시 파괴 
        self.fortime += 1
 
class object_enemyAttack(element):   # enemyAttack 오브젝트
    def __init__(self, x, y, size_x, size_y, color, livetime, obj_main, damage):
        super().__init__(x, y, size_x, size_y, color)   # 상속
        self.livetime, self.fortime = livetime, 0   # 동작 시간
        self.obj_main = obj_main   # obj_main 받기
        self.damage = damage   # 데미지
 
    def step(self):   # 스텝 함수
        if self.obj_main in objects:   # 충돌시
            if self.collision(self.obj_main):
                self.obj_main.hp -= self.damage; self.destroy()
        if self.livetime <= self.fortime: self.destroy()   # 동작 시간 오버시 파괴 
        self.fortime += 1
 
Game()   # 게임 실행
cs

# 2019.12.02 코드 간략화 밑 수정

 

 

제출일 일주일 남기고 넉넉잡아 완성!

더이상의 설명은.. 생략하겠슴다

 

+솔루션 압축 폴더를 제공합니다.

용량 오버로 파일별로 올립니다.

nemoNemo.py
0.01MB
bgm1.wav
7.15MB
bgm2.wav
7.61MB
effect.wav
0.01MB

댓글