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()
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")
if name == "main":
raiz = tk.Tk()
juego = DevoradorEstelar(raiz)
raiz.mainloop()
Additional information
No response