Skip to content

Juego interactivo de Python #14971

Description

@Dannyen131911

What would you like to share?

import tkinter as tk
import math
import random

------------------ CONFIGURACIÓN ------------------

ANCHO = 800
ALTO = 700
VELOCIDAD_MS = 20

RADIO_INICIAL = 14
RADIO_META = 95
VELOCIDAD_BASE = 4.5
NUM_OBJETOS = 34
ESPACIADO_REJILLA = 45

class DevoradorEstelar:
def init(self, raiz):
self.raiz = raiz
self.raiz.title("Devorador Estelar 🕳️ - Python")

    self.canvas = tk.Canvas(raiz, width=ANCHO, height=ALTO, bg="#02010a", highlightthickness=0)
    self.canvas.pack()

    self.teclas = set()
    self.raiz.bind("<KeyPress>", lambda e: self.teclas.add(e.keysym))
    self.raiz.bind("<KeyRelease>", lambda e: self.teclas.discard(e.keysym))
    self.raiz.bind("<r>", lambda e: self.reiniciar())
    self.raiz.bind("<R>", lambda e: self.reiniciar())

    # Rejilla de puntos para el efecto de lente gravitacional
    self.rejilla = []
    for gx in range(0, ANCHO + ESPACIADO_REJILLA, ESPACIADO_REJILLA):
        for gy in range(0, ALTO + ESPACIADO_REJILLA, ESPACIADO_REJILLA):
            self.rejilla.append((gx, gy))

    self.reiniciar()

# ---------------- ESTADO ----------------
def reiniciar(self):
    self.jugador_x, self.jugador_y = ANCHO / 2, ALTO / 2
    self.jugador_radio = RADIO_INICIAL
    self.puntuacion = 0
    self.particulas = []
    self.temblor = 0
    self.tiempo = 0
    self.juego_terminado = False
    self.resultado = None

    self.objetos = []
    for _ in range(NUM_OBJETOS):
        self.objetos.append(self.crear_objeto())

    self.dibujar()
    self.bucle_juego()

def crear_objeto(self):
    borde = random.choice(["arriba", "abajo", "izq", "der"])
    if borde == "arriba":
        x, y = random.uniform(0, ANCHO), -20
    elif borde == "abajo":
        x, y = random.uniform(0, ANCHO), ALTO + 20
    elif borde == "izq":
        x, y = -20, random.uniform(0, ALTO)
    else:
        x, y = ANCHO + 20, random.uniform(0, ALTO)

    # La mayoría son pequeños, pocos son grandes y peligrosos
    radio = random.choices([6, 10, 16, 26, 38], weights=[40, 30, 18, 8, 4])[0]
    angulo = random.uniform(0, 360)
    velocidad = random.uniform(0.6, 1.6)
    return {
        "x": x, "y": y, "radio": radio,
        "vx": velocidad * math.cos(math.radians(angulo)),
        "vy": velocidad * math.sin(math.radians(angulo)),
    }

def generar_particulas(self, x, y, color, cantidad=14):
    for _ in range(cantidad):
        ang = random.uniform(0, 360)
        vel = random.uniform(1.5, 5)
        self.particulas.append({
            "x": x, "y": y,
            "vx": vel * math.cos(math.radians(ang)),
            "vy": vel * math.sin(math.radians(ang)),
            "vida": 16, "color": color
        })

# ---------------- LÓGICA ----------------
def actualizar(self):
    self.tiempo += 1

    dx = dy = 0
    if "Left" in self.teclas or "a" in self.teclas:
        dx = -1
    if "Right" in self.teclas or "d" in self.teclas:
        dx = 1
    if "Up" in self.teclas or "w" in self.teclas:
        dy = -1
    if "Down" in self.teclas or "s" in self.teclas:
        dy = 1

    # Cuanto más grande el agujero negro, más lento se mueve
    velocidad_actual = VELOCIDAD_BASE * (18 / (18 + self.jugador_radio))
    if dx or dy:
        norma = math.hypot(dx, dy)
        self.jugador_x += dx / norma * velocidad_actual
        self.jugador_y += dy / norma * velocidad_actual

    self.jugador_x = max(0, min(ANCHO, self.jugador_x))
    self.jugador_y = max(0, min(ALTO, self.jugador_y))

    radio_influencia = self.jugador_radio * 7

    for obj in self.objetos[:]:
        dxo = self.jugador_x - obj["x"]
        dyo = self.jugador_y - obj["y"]
        dist = math.hypot(dxo, dyo) or 1

        # Atracción gravitacional hacia el jugador si está en su radio de influencia
        if dist < radio_influencia:
            fuerza = (radio_influencia - dist) / radio_influencia * 0.6
            obj["vx"] += dxo / dist * fuerza
            obj["vy"] += dyo / dist * fuerza
            # leve componente tangencial para que "orbite" en espiral
            obj["vx"] += -dyo / dist * fuerza * 0.4
            obj["vy"] += dxo / dist * fuerza * 0.4

        # fricción suave para que no acelere sin límite
        obj["vx"] *= 0.985
        obj["vy"] *= 0.985

        obj["x"] += obj["vx"]
        obj["y"] += obj["vy"]

        # Colisión con el jugador
        if dist < self.jugador_radio:
            if obj["radio"] < self.jugador_radio * 0.85:
                # Absorbido: crece el jugador
                masa_nueva = self.jugador_radio ** 2 + obj["radio"] ** 2 * 0.5
                self.jugador_radio = math.sqrt(masa_nueva)
                self.puntuacion += int(obj["radio"])
                self.generar_particulas(obj["x"], obj["y"], "#b388ff", 10)
                self.objetos.remove(obj)
                self.objetos.append(self.crear_objeto())
            else:
                # Objeto demasiado grande: el jugador se encoge de golpe
                self.jugador_radio *= 0.6
                self.temblor = 14
                self.generar_particulas(obj["x"], obj["y"], "#ff5252", 20)
                self.objetos.remove(obj)
                self.objetos.append(self.crear_objeto())
                if self.jugador_radio < 8:
                    self.juego_terminado = True
                    self.resultado = "perdio"
            continue

        # Reciclar objetos que se alejan demasiado
        if obj["x"] < -60 or obj["x"] > ANCHO + 60 or obj["y"] < -60 or obj["y"] > ALTO + 60:
            self.objetos.remove(obj)
            self.objetos.append(self.crear_objeto())

    for p in self.particulas[:]:
        p["x"] += p["vx"]
        p["y"] += p["vy"]
        p["vida"] -= 1
        if p["vida"] <= 0:
            self.particulas.remove(p)

    if self.temblor > 0:
        self.temblor -= 1

    if self.jugador_radio >= RADIO_META:
        self.juego_terminado = True
        self.resultado = "gano"

# ---------------- DIBUJO ----------------
def dibujar(self):
    self.canvas.delete("all")

    offset_x = random.uniform(-1, 1) * self.temblor
    offset_y = random.uniform(-1, 1) * self.temblor

    # --- Rejilla con efecto de lente gravitacional ---
    for (gx, gy) in self.rejilla:
        dx = gx - self.jugador_x
        dy = gy - self.jugador_y
        dist = math.hypot(dx, dy) or 1
        fuerza = min(28, (self.jugador_radio * 55) / dist)
        desplazado_x = gx - dx / dist * fuerza + offset_x
        desplazado_y = gy - dy / dist * fuerza + offset_y
        self.canvas.create_oval(desplazado_x - 1, desplazado_y - 1,
                                 desplazado_x + 1, desplazado_y + 1,
                                 fill="#3a2a6a", outline="")

    # Objetos (estrellas/planetas)
    for obj in self.objetos:
        if obj["radio"] < self.jugador_radio * 0.85:
            color, borde = "#81d4fa", "#e1f5fe"
        else:
            color, borde = "#ff8a65", "#ffccbc"
        x, y, r = obj["x"] + offset_x, obj["y"] + offset_y, obj["radio"]
        self.canvas.create_oval(x - r, y - r, x + r, y + r, fill=color, outline=borde, width=1)

    # Partículas
    for p in self.particulas:
        self.canvas.create_oval(p["x"] + offset_x - 2, p["y"] + offset_y - 2,
                                 p["x"] + offset_x + 2, p["y"] + offset_y + 2,
                                 fill=p["color"], outline="")

    # --- Agujero negro del jugador (disco de acreción giratorio) ---
    px, py, pr = self.jugador_x + offset_x, self.jugador_y + offset_y, self.jugador_radio
    for i in range(3):
        ang = self.tiempo * 4 + i * 120
        rx = pr * 1.6
        ry = pr * 0.7
        self.canvas.create_oval(px - rx, py - ry, px + rx, py + ry,
                                 outline="#ff9100", width=2)
    self.canvas.create_oval(px - pr, py - pr, px + pr, py + pr, fill="#000000", outline="#7c4dff", width=2)

    # --- HUD ---
    self.canvas.create_text(110, 25, text=f"Puntos: {self.puntuacion}",
                             fill="white", font=("Courier", 14, "bold"))
    self.canvas.create_text(ANCHO - 130, 25, text=f"Tamaño: {int(self.jugador_radio)}/{RADIO_META}",
                             fill="#b388ff", font=("Courier", 14, "bold"))

    self.canvas.create_text(ANCHO / 2, ALTO - 15,
                             text="←↑↓→ muévete | absorbe astros AZULES (más chicos), evita los NARANJAS (más grandes)",
                             fill="#aaaaaa", font=("Courier", 10))

    if self.juego_terminado:
        gano = self.resultado == "gano"
        color = "#b388ff" if gano else "#ff5252"
        mensaje = "¡TE VOLVISTE SUPERMASIVO! 🌌" if gano else "¡FUISTE DESTRUIDO!"
        self.canvas.create_rectangle(ANCHO / 2 - 200, ALTO / 2 - 60, ANCHO / 2 + 200, ALTO / 2 + 60,
                                      fill="black", outline=color, width=3)
        self.canvas.create_text(ANCHO / 2, ALTO / 2 - 20, text=mensaje,
                                 fill=color, font=("Courier", 16, "bold"))
        self.canvas.create_text(ANCHO / 2, ALTO / 2 + 8, text=f"Puntuación final: {self.puntuacion}",
                                 fill="white", font=("Courier", 12))
        self.canvas.create_text(ANCHO / 2, ALTO / 2 + 32, text="Presiona R para reintentar",
                                 fill="white", font=("Courier", 11))

def bucle_juego(self):
    if not self.juego_terminado:
        self.actualizar()
        self.dibujar()
        self.raiz.after(VELOCIDAD_MS, self.bucle_juego)
    else:
        self.dibujar()

if name == "main":
raiz = tk.Tk()
juego = DevoradorEstelar(raiz)
raiz.mainloop()

Additional information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    awaiting triageAwaiting triage from a maintainer

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions