import math import random import tkinter as tk
WIDTH, HEIGHT = 1100, 700 SKY_TOP = "#06162f" SKY_BOTTOM = "#194f78"
class Target: def __init__(self, canvas, level): self.canvas = canvas self.r = random.randint(22, 36) self.x = WIDTH + self.r self.y = random.randint(100, HEIGHT - 180) self.speed = random.uniform(2.0, 3.3) + level * 0.22 self.direction = random.choice([-1, 1]) if self.direction == 1: self.x = -self.r
tag = f"target_{id(self)}" self.tag = tag self.parts = [ canvas.create_oval(self.x-self.r, self.y-self.r, self.x+self.r, self.y+self.r, fill="#ffca3a", outline="#fff4b8", width=3, tags=tag), canvas.create_oval(self.x-10, self.y-10, self.x+10, self.y+10, fill="#ef476f", outline="white", width=2, tags=tag), canvas.create_line(self.x-self.r-12, self.y, self.x+self.r+12, self.y, fill="#8ecae6", width=6, tags=tag), ]
def move(self): dx = self.speed * self.direction self.x += dx self.canvas.move(self.tag, dx, 0) return -80 <= self.x <= WIDTH + 80
def contains(self, x, y): return math.hypot(x - self.x, y - self.y) <= self.r + 8
def destroy(self): self.canvas.delete(self.tag)
class ArcadeTargetSimulator: def __init__(self, root): self.root = root self.root.title("가상 미사일 조준 시뮬레이터 — Arcade Mode") self.root.resizable(False, False)
self.canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, highlightthickness=0, cursor="none") self.canvas.pack() self.draw_background()
self.score = 0 self.ammo = 6 self.max_ammo = 6 self.level = 1 self.missed = 0 self.running = True self.targets = [] self.crosshair = []
self.hud = self.canvas.create_text( 25, 25, anchor="nw", fill="white", font=("Arial", 18, "bold"), text="") self.help_text = self.canvas.create_text( WIDTH - 25, 28, anchor="ne", fill="#d8f3ff", font=("Arial", 13), text="마우스: 조준·발사 R: 재장전 Space: 일시정지") self.status = self.canvas.create_text( WIDTH / 2, HEIGHT - 42, fill="#ffed9a", font=("Arial", 16, "bold"), text="움직이는 표적의 중앙을 맞혀 보세요!")
self.canvas.bind("<Motion>", self.aim) self.canvas.bind("<Button-1>", self.fire) self.root.bind("r", self.reload) self.root.bind("R", self.reload) self.root.bind("<space>", self.toggle_pause)
self.update_hud() self.spawn_target() self.game_loop()
def draw_background(self): for y in range(HEIGHT): ratio = y / HEIGHT a = tuple(int(SKY_TOP[i:i+2], 16) for i in (1, 3, 5)) b = tuple(int(SKY_BOTTOM[i:i+2], 16) for i in (1, 3, 5)) color = "#%02x%02x%02x" % tuple( int(a[j] * (1-ratio) + b[j] * ratio) for j in range(3)) self.canvas.create_line(0, y, WIDTH, y, fill=color)
self.canvas.create_oval(820, 80, 950, 210, fill="#f8f1c8", outline="") self.canvas.create_polygon(0, 570, 180, 390, 360, 570, fill="#173c55", outline="") self.canvas.create_polygon(230, 570, 490, 340, 760, 570, fill="#1b455d", outline="") self.canvas.create_polygon(590, 570, 850, 410, 1100, 570, fill="#15394f", outline="") self.canvas.create_rectangle(0, 570, WIDTH, HEIGHT, fill="#07141f", outline="") for x in range(0, WIDTH, 55): h = random.randint(25, 90) self.canvas.create_rectangle(x, 570-h, x+38, 570, fill="#0b2230", outline="") self.canvas.create_line(0, 570, WIDTH, 570, fill="#6ad5ff", width=2)
def aim(self, event): for item in self.crosshair: self.canvas.delete(item) x, y = event.x, event.y color = "#6effb7" if self.running else "#a7b0ba" self.crosshair = [ self.canvas.create_oval(x-20, y-20, x+20, y+20, outline=color, width=2), self.canvas.create_line(x-30, y, x-8, y, fill=color, width=2), self.canvas.create_line(x+8, y, x+30, y, fill=color, width=2), self.canvas.create_line(x, y-30, x, y-8, fill=color, width=2), self.canvas.create_line(x, y+8, x, y+30, fill=color, width=2), ]
def fire(self, event): if not self.running: return if self.ammo <= 0: self.show_status("탄약이 없습니다 — R 키로 재장전", "#ff8a8a") return self.ammo -= 1 self.flash(event.x, event.y)
hit = None for target in reversed(self.targets): if target.contains(event.x, event.y): hit = target break
if hit: distance = math.hypot(event.x-hit.x, event.y-hit.y) points = max(10, int(100 - distance * 2)) self.score += points hit.destroy() self.targets.remove(hit) self.show_status(f"명중! +{points}점", "#7dffb2") self.explosion(event.x, event.y) new_level = self.score // 500 + 1 if new_level > self.level: self.level = new_level self.show_status(f"LEVEL {self.level} — 표적 속도 상승!", "#fff176") else: self.show_status("빗나갔습니다", "#ff9e9e") self.update_hud()
def flash(self, x, y): line = self.canvas.create_line(WIDTH/2, HEIGHT-5, x, y, fill="#effcff", width=3) self.root.after(70, lambda: self.canvas.delete(line))
def explosion(self, x, y): circles = [] for radius, color in ((12, "white"), (22, "#ffd166"), (34, "#ef476f")): circles.append(self.canvas.create_oval(x-radius, y-radius, x+radius, y+radius, outline=color, width=4)) self.root.after(160, lambda: [self.canvas.delete(c) for c in circles])
def reload(self, _event=None): if self.running: self.ammo = self.max_ammo self.update_hud() self.show_status("재장전 완료", "#8eeaff")
def toggle_pause(self, _event=None): self.running = not self.running self.show_status("게임 재개" if self.running else "일시정지", "#8eeaff" if self.running else "#dddddd")
def show_status(self, text, color): self.canvas.itemconfigure(self.status, text=text, fill=color)
def update_hud(self): ammo_bar = "●" * self.ammo + "○" * (self.max_ammo - self.ammo) self.canvas.itemconfigure( self.hud, text=f"SCORE {self.score:05d} LEVEL {self.level} AMMO {ammo_bar} MISS {self.missed}")
def spawn_target(self): if self.running and len(self.targets) < min(2 + self.level, 7): self.targets.append(Target(self.canvas, self.level)) delay = max(500, 1350 - self.level * 90) self.root.after(delay, self.spawn_target)
def game_loop(self): if self.running: for target in self.targets[:]: if not target.move(): target.destroy() self.targets.remove(target) self.missed += 1 self.update_hud() self.show_status("표적이 지나갔습니다", "#ffb36b") self.root.after(16, self.game_loop)
if __name__ == "__main__": app_root = tk.Tk() ArcadeTargetSimulator(app_root) app_root.mainloop()
|