Single-File Python Space Shooter Game

Answered using AI by Vibe Coding

Code Question:

напиши игру

Code Solution:

Ниже ‒ полностью-графическая, однофайловая игра «Космический Штурман».
Скопируй код в файл cosmic_shooter.py и запусти python cosmic_shooter.py.
Нужна только стандартная библиотека Python (tkinter встроен во все дистрибутивы).

# cosmic_shooter.py
import math
import random
import tkinter as tk

WIDTH, HEIGHT = 600, 700
PLAYER_SIZE = 40
ENEMY_SIZE   = 30
BULLET_SIZE  = 5
STAR_COUNT   = 80

root = tk.Tk()
root.title("Космический Штурман")
root.resizable(False, False)

canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg="black")
canvas.pack()

# ------------------------ объекты ------------------------
class Player:
    def __init__(self):
        x, y = WIDTH // 2, HEIGHT - 60
        self.id = canvas.create_rectangle(x, y,
                                          x + PLAYER_SIZE, y + PLAYER_SIZE,
                                          fill="cyan", outline="white")
        self.half = PLAYER_SIZE / 2
        self.speed = 6
        self.cooldown = 0

    def move(self, dx, dy):
        coords = canvas.coords(self.id)
        new_x = max(0, min(WIDTH - PLAYER_SIZE, coords[0] + dx * self.speed))
        new_y = max(0, min(HEIGHT - PLAYER_SIZE, coords[1] + dy * self.speed))
        canvas.coords(self.id, new_x, new_y,
                      new_x + PLAYER_SIZE, new_y + PLAYER_SIZE)

    def shoot(self):
        if self.cooldown <= 0:
            x, y = canvas.coords(self.id)[:2]
            bx = x + self.half
            by = y
            bullets.append(Bullet(bx, by))
            self.cooldown = 8


class Bullet:
    def __init__(self, x, y):
        self.id = canvas.create_oval(x - BULLET_SIZE, y - BULLET_SIZE,
                                     x + BULLET_SIZE, y + BULLET_SIZE,
                                     fill="yellow", outline="white")
        self.speed = 12

    def update(self):
        canvas.move(self.id, 0, -self.speed)
        if canvas.coords(self.id)[1] < 0:
            self.destroy()

    def destroy(self):
        canvas.delete(self.id)
        bullets.remove(self)


class Enemy:
    def __init__(self):
        x = random.randint(0, WIDTH - ENEMY_SIZE)
        y = -ENEMY_SIZE
        color = random.choice(["red", "magenta", "orange"])
        self.id = canvas.create_oval(x, y,
                                   x + ENEMY_SIZE, y + ENEMY_SIZE,
                                   fill=color, outline="white")
        self.speed = random.uniform(1.5, 3.5)
        self.hp = 1 if color != "magenta" else 2  # магентовые - боссики

    def update(self):
        canvas.move(self.id, 0, self.speed)
        x, y = canvas.coords(self.id)[:2]
        if y > HEIGHT:
            self.destroy()

    def hit(self):
        self.hp -= 1
        if self.hp <= 0:
            self.destroy()
            return True
        return False

    def destroy(self):
        canvas.delete(self.id)
        if self in enemies:
            enemies.remove(self)


# ------------------------ функции игры ------------------------
def spawn_enemy():
    enemies.append(Enemy())
    root.after(random.randint(700, 2000), spawn_enemy)

def spawn_star():
    x = random.randint(0, WIDTH)
    y = random.randint(-10, HEIGHT)
    s = random.randint(1, 2)
    sid = canvas.create_oval(x, y, x + s, y + s, fill="white", outline="")
    stars.append(sid)
    animate_star(sid)

def animate_star(sid):
    canvas.move(sid, 0, 2)
    x, y = canvas.coords(sid)[:2]
    if y > HEIGHT:
        canvas.coords(sid, random.randint(0, WIDTH), -5)
    root.after(50, lambda: animate_star(sid))

def collision(a, b, size_a, size_b):
    ax, ay = canvas.coords(a)[:2]
    bx, by = canvas.coords(b)[:2]
    return math.hypot(ax - bx, ay - by) < (size_a + size_b) / 2

def game_loop():
    global score
    player.cooldown = max(0, player.cooldown - 1)

    # движение пуль
    for b in bullets[:]:
        b.update()

    # движение врагов
    for e in enemies[:]:
        e.update()

    # столкновения
    for b in bullets[:]:
        for e in enemies[:]:
            if collision(b.id, e.id, BULLET_SIZE * 2, ENEMY_SIZE):
                b.destroy()
                if e.hit():
                    score += 10
                break

    # столкновение врага с игроком
    for e in enemies[:]:
        if collision(player.id, e.id, PLAYER_SIZE, ENEMY_SIZE):
            game_over()
            return

    canvas.itemconfig(score_text, text=f"Счёт: {score}")
    root.after(16, game_loop)

def game_over():
    canvas.create_text(WIDTH // 2, HEIGHT // 2,
                       text="Конец игры!", fill="white",
                       font=("Courier", 36, "bold"))
    canvas.create_text(WIDTH // 2, HEIGHT // 2 + 60,
                       text=f"Финальный счёт: {score}",
                       fill="white", font=("Courier", 18))

# ------------------------ управление ------------------------
keys = set()
def key_press(e):
    keys.add(e.keysym)
def key_release(e):
    keys.discard(e.keysym)
def handle_keys():
    if "Left"  in keys or "a" in keys: player.move(-1, 0)
    if "Right" in keys or "d" in keys: player.move(1, 0)
    if "Up"    in keys or "w" in keys: player.move(0, -1)
    if "Down"  in keys or "s" in keys: player.move(0, 1)
    if "space" in keys: player.shoot()
    root.after(30, handle_keys)

# ------------------------ инициализация ------------------------
player = Player()
bullets, enemies = [], []
stars = []
score = 0
score_text = canvas.create_text(10, 10, anchor="nw",
                                text="Счёт: 0",
                                fill="white", font=("Courier", 14))

root.bind("<KeyPress>", key_press)
root.bind("<KeyRelease>", key_release)
for _ in range(STAR_COUNT):
    spawn_star()
spawn_enemy()
handle_keys()
game_loop()
root.mainloop()

Как играть
← → ↑ ↓ или WASD — летать
Пробел — стрелять
Цель — набрать побольше очков, не позволив врагам коснуться корабля.