Mejora de player y vortex -sin enemigos-
This commit is contained in:
parent
11fdd51efb
commit
e80a88cb13
@ -16,94 +16,124 @@ TILE_HEIGHT = 32
|
|||||||
GRID_SIZE_X = 8
|
GRID_SIZE_X = 8
|
||||||
GRID_SIZE_Y = 120
|
GRID_SIZE_Y = 120
|
||||||
|
|
||||||
# Colores de Respaldo / Suelo
|
# Colores del Entorno
|
||||||
COLOR_BG = (15, 15, 25)
|
COLOR_BG = (15, 15, 25)
|
||||||
COLOR_ROAD = (50, 50, 65)
|
COLOR_ROAD = (50, 50, 65)
|
||||||
COLOR_GRID = (30, 30, 45)
|
COLOR_GRID = (30, 30, 45)
|
||||||
|
|
||||||
|
# Paleta de Colores de Entidades
|
||||||
|
COLOR_YELLOW = (255, 215, 0)
|
||||||
|
COLOR_WHITE = (240, 240, 240)
|
||||||
|
COLOR_RED = (220, 40, 40)
|
||||||
|
COLOR_BROWN = (139, 69, 19)
|
||||||
|
COLOR_ORANGE = (255, 140, 0)
|
||||||
|
|
||||||
|
|
||||||
def grid_to_iso(gx, gy, gz=0):
|
def grid_to_iso(gx, gy, gz=0):
|
||||||
"""Convierte coordenadas de la cuadrícula a coordenadas isométricas de pantalla."""
|
"""Convierte coordenadas 3D de rejilla a coordenadas 2D de pantalla."""
|
||||||
iso_x = (gx - gy) * (TILE_WIDTH // 2)
|
iso_x = (gx - gy) * (TILE_WIDTH // 2)
|
||||||
iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16)
|
iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16)
|
||||||
return iso_x, iso_y
|
return iso_x, iso_y
|
||||||
|
|
||||||
|
|
||||||
|
def draw_isometric_prism(surface, color, width=40, height=35):
|
||||||
|
"""Genera una superficie isométrica con forma de prisma o cubo."""
|
||||||
|
surf = pygame.Surface((64, 64), pygame.SRCALPHA)
|
||||||
|
cx, cy = 32, 32
|
||||||
|
|
||||||
|
w2 = width // 2
|
||||||
|
h2 = width // 4
|
||||||
|
|
||||||
|
# Cara Superior
|
||||||
|
top_pts = [
|
||||||
|
(cx, cy - height),
|
||||||
|
(cx + w2, cy - height + h2),
|
||||||
|
(cx, cy - height + 2 * h2),
|
||||||
|
(cx - w2, cy - height + h2),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Cara Izquierda
|
||||||
|
left_pts = [
|
||||||
|
(cx - w2, cy - height + h2),
|
||||||
|
(cx, cy - height + 2 * h2),
|
||||||
|
(cx, cy + 2 * h2),
|
||||||
|
(cx - w2, cy + h2),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Cara Derecha
|
||||||
|
right_pts = [
|
||||||
|
(cx, cy - height + 2 * h2),
|
||||||
|
(cx + w2, cy - height + h2),
|
||||||
|
(cx + w2, cy + h2),
|
||||||
|
(cx, cy + 2 * h2),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Sombra/Sombreado para volumen
|
||||||
|
c_top = color
|
||||||
|
c_left = tuple(max(0, c - 40) for c in color)
|
||||||
|
c_right = tuple(max(0, c - 70) for c in color)
|
||||||
|
|
||||||
|
pygame.draw.polygon(surf, c_top, top_pts)
|
||||||
|
pygame.draw.polygon(surf, c_left, left_pts)
|
||||||
|
pygame.draw.polygon(surf, c_right, right_pts)
|
||||||
|
|
||||||
|
# Bordes para definición
|
||||||
|
pygame.draw.polygon(surf, (20, 20, 20), top_pts, 1)
|
||||||
|
pygame.draw.polygon(surf, (20, 20, 20), left_pts, 1)
|
||||||
|
pygame.draw.polygon(surf, (20, 20, 20), right_pts, 1)
|
||||||
|
|
||||||
|
return surf
|
||||||
|
|
||||||
|
|
||||||
def create_placeholder_sprites():
|
def create_placeholder_sprites():
|
||||||
"""Genera sprites vectoriales en memoria si no existen archivos PNG locales."""
|
"""Crea las formas geométricas vectoriales solicitadas."""
|
||||||
sprites = {}
|
sprites = {}
|
||||||
|
|
||||||
# 1. Jugador (Lasertron)
|
# 1. Jugador: Pirámide Amarilla
|
||||||
surf_player = pygame.Surface((64, 64), pygame.SRCALPHA)
|
surf_player = pygame.Surface((64, 64), pygame.SRCALPHA)
|
||||||
|
base_pts = [(32, 48), (52, 38), (32, 28), (12, 38)]
|
||||||
|
apex = (32, 8)
|
||||||
|
|
||||||
|
# Caras de la pirámide
|
||||||
pygame.draw.polygon(
|
pygame.draw.polygon(
|
||||||
surf_player,
|
surf_player, (200, 160, 0), [base_pts[3], base_pts[0], apex]
|
||||||
(255, 200, 0),
|
) # Izquierda
|
||||||
[(32, 8), (56, 24), (32, 56), (8, 24)],
|
|
||||||
)
|
|
||||||
pygame.draw.polygon(
|
pygame.draw.polygon(
|
||||||
surf_player,
|
surf_player, (255, 215, 0), [base_pts[0], base_pts[1], apex]
|
||||||
(255, 255, 255),
|
) # Derecha
|
||||||
[(32, 8), (56, 24), (32, 56), (8, 24)],
|
pygame.draw.polygon(surf_player, (255, 235, 100), base_pts) # Base
|
||||||
2,
|
pygame.draw.line(surf_player, (50, 50, 0), apex, base_pts[0], 2)
|
||||||
)
|
|
||||||
pygame.draw.circle(surf_player, (0, 255, 255), (32, 28), 6)
|
|
||||||
sprites["player"] = surf_player
|
sprites["player"] = surf_player
|
||||||
|
|
||||||
# 2. VORTEX
|
# 2. VORTEX: Cubo amarillo con "bola" encima
|
||||||
surf_vortex = pygame.Surface((64, 64), pygame.SRCALPHA)
|
surf_vortex = draw_isometric_prism(None, COLOR_YELLOW, width=38, height=25)
|
||||||
pygame.draw.polygon(
|
# Bola superior
|
||||||
surf_vortex,
|
pygame.draw.circle(surf_vortex, (255, 255, 200), (32, 12), 8)
|
||||||
(220, 50, 50),
|
pygame.draw.circle(surf_vortex, (0, 0, 0), (32, 12), 8, 1)
|
||||||
[(32, 4), (60, 20), (52, 52), (12, 52)],
|
|
||||||
)
|
|
||||||
pygame.draw.polygon(
|
|
||||||
surf_vortex,
|
|
||||||
(255, 255, 255),
|
|
||||||
[(32, 4), (60, 20), (52, 52), (12, 52)],
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
pygame.draw.circle(surf_vortex, (255, 255, 0), (32, 28), 10)
|
|
||||||
sprites["vortex"] = surf_vortex
|
sprites["vortex"] = surf_vortex
|
||||||
|
|
||||||
# 3. Bloque empujable
|
# 3. Bloque Empujable: Cubo Blanco
|
||||||
surf_block = pygame.Surface((64, 64), pygame.SRCALPHA)
|
sprites["block"] = draw_isometric_prism(None, COLOR_WHITE, width=40, height=25)
|
||||||
pygame.draw.polygon(
|
|
||||||
surf_block,
|
|
||||||
(180, 70, 40),
|
|
||||||
[(32, 12), (58, 26), (32, 40), (6, 26)],
|
|
||||||
)
|
|
||||||
pygame.draw.polygon(
|
|
||||||
surf_block,
|
|
||||||
(120, 40, 20),
|
|
||||||
[(6, 26), (32, 40), (32, 58), (6, 44)],
|
|
||||||
)
|
|
||||||
pygame.draw.polygon(
|
|
||||||
surf_block,
|
|
||||||
(150, 50, 30),
|
|
||||||
[(58, 26), (32, 40), (32, 58), (58, 44)],
|
|
||||||
)
|
|
||||||
sprites["block"] = surf_block
|
|
||||||
|
|
||||||
# 4. Enemigo Alienígena (Drone)
|
# 4. Enemigo Indestructible: Prisma Blanco
|
||||||
surf_enemy = pygame.Surface((64, 64), pygame.SRCALPHA)
|
sprites["enemy_indestructible"] = draw_isometric_prism(
|
||||||
pygame.draw.polygon(
|
None, COLOR_WHITE, width=32, height=35
|
||||||
surf_enemy,
|
|
||||||
(50, 200, 100),
|
|
||||||
[(32, 10), (54, 28), (32, 46), (10, 28)],
|
|
||||||
)
|
)
|
||||||
pygame.draw.polygon(
|
|
||||||
surf_enemy,
|
|
||||||
(255, 255, 255),
|
|
||||||
[(32, 10), (54, 28), (32, 46), (10, 28)],
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
pygame.draw.circle(surf_enemy, (255, 0, 0), (32, 28), 5) # Ojo rojo
|
|
||||||
sprites["enemy"] = surf_enemy
|
|
||||||
|
|
||||||
# 5. Bala
|
# 5. Enemigos Destructibles (Prismas por estado de vida)
|
||||||
|
sprites["enemy_red"] = draw_isometric_prism(
|
||||||
|
None, COLOR_RED, width=32, height=35
|
||||||
|
)
|
||||||
|
sprites["enemy_brown"] = draw_isometric_prism(
|
||||||
|
None, COLOR_BROWN, width=32, height=35
|
||||||
|
)
|
||||||
|
sprites["enemy_orange"] = draw_isometric_prism(
|
||||||
|
None, COLOR_ORANGE, width=32, height=35
|
||||||
|
)
|
||||||
|
|
||||||
|
# 6. Proyectil
|
||||||
surf_bullet = pygame.Surface((32, 32), pygame.SRCALPHA)
|
surf_bullet = pygame.Surface((32, 32), pygame.SRCALPHA)
|
||||||
pygame.draw.circle(surf_bullet, (0, 255, 255), (16, 16), 5)
|
pygame.draw.circle(surf_bullet, (0, 255, 255), (16, 16), 4)
|
||||||
pygame.draw.circle(surf_bullet, (255, 255, 255), (16, 16), 2)
|
|
||||||
sprites["bullet"] = surf_bullet
|
sprites["bullet"] = surf_bullet
|
||||||
|
|
||||||
return sprites
|
return sprites
|
||||||
@ -115,19 +145,17 @@ class SpriteManager:
|
|||||||
self.placeholders = create_placeholder_sprites()
|
self.placeholders = create_placeholder_sprites()
|
||||||
self.cache = {}
|
self.cache = {}
|
||||||
|
|
||||||
def get_sprite(self, name, filename):
|
def get_sprite(self, name, filename=None):
|
||||||
if name in self.cache:
|
if name in self.cache:
|
||||||
return self.cache[name]
|
return self.cache[name]
|
||||||
|
|
||||||
if os.path.exists(filename):
|
if filename and os.path.exists(filename):
|
||||||
try:
|
try:
|
||||||
img = pygame.image.load(filename).convert_alpha()
|
img = pygame.image.load(filename).convert_alpha()
|
||||||
self.cache[name] = img
|
self.cache[name] = img
|
||||||
return img
|
return img
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(
|
print(f"Error cargando {filename}: {e}")
|
||||||
f"Error al cargar {filename}, usando respaldo vectorial: {e}"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.cache[name] = self.placeholders[name]
|
self.cache[name] = self.placeholders[name]
|
||||||
return self.cache[name]
|
return self.cache[name]
|
||||||
@ -135,7 +163,7 @@ class SpriteManager:
|
|||||||
|
|
||||||
class Entity:
|
class Entity:
|
||||||
|
|
||||||
def __init__(self, gx, gy, sprite_name, filename, is_solid=True):
|
def __init__(self, gx, gy, sprite_name, filename=None, is_solid=True):
|
||||||
self.gx = float(gx)
|
self.gx = float(gx)
|
||||||
self.gy = float(gy)
|
self.gy = float(gy)
|
||||||
self.gz = 0
|
self.gz = 0
|
||||||
@ -147,7 +175,6 @@ class Entity:
|
|||||||
screen_x, screen_y = grid_to_iso(self.gx, self.gy, self.gz)
|
screen_x, screen_y = grid_to_iso(self.gx, self.gy, self.gz)
|
||||||
sprite = sprite_mgr.get_sprite(self.sprite_name, self.filename)
|
sprite = sprite_mgr.get_sprite(self.sprite_name, self.filename)
|
||||||
|
|
||||||
# Centrar la imagen con respecto a la casilla isométrica
|
|
||||||
draw_x = screen_x + camera_offset_x - (sprite.get_width() // 2)
|
draw_x = screen_x + camera_offset_x - (sprite.get_width() // 2)
|
||||||
draw_y = screen_y + camera_offset_y - (sprite.get_height() // 2)
|
draw_y = screen_y + camera_offset_y - (sprite.get_height() // 2)
|
||||||
|
|
||||||
@ -178,8 +205,10 @@ class Bullet(Entity):
|
|||||||
and abs(entity.gy - self.gy) < 0.5
|
and abs(entity.gy - self.gy) < 0.5
|
||||||
):
|
):
|
||||||
self.alive = False
|
self.alive = False
|
||||||
if isinstance(entity, (PushBlock, Enemy)):
|
if isinstance(entity, PushBlock):
|
||||||
entity.health -= 1
|
entity.health -= 1
|
||||||
|
elif isinstance(entity, Enemy) and entity.destructible:
|
||||||
|
entity.take_damage()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
@ -211,39 +240,43 @@ class PushBlock(Entity):
|
|||||||
|
|
||||||
class Enemy(Entity):
|
class Enemy(Entity):
|
||||||
|
|
||||||
def __init__(self, gx, gy):
|
def __init__(self, gx, gy, destructible=True):
|
||||||
super().__init__(gx, gy, "enemy", "enemy.png", is_solid=True)
|
sprite = "enemy_red" if destructible else "enemy_indestructible"
|
||||||
|
super().__init__(gx, gy, sprite, "enemy.png", is_solid=True)
|
||||||
self.speed = 0.04
|
self.speed = 0.04
|
||||||
self.direction = 1
|
self.direction = 1
|
||||||
self.health = 1
|
self.destructible = destructible
|
||||||
|
self.health = 3 if destructible else 999
|
||||||
|
|
||||||
|
def take_damage(self):
|
||||||
|
if not self.destructible:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.health -= 1
|
||||||
|
if self.health == 2:
|
||||||
|
self.sprite_name = "enemy_brown"
|
||||||
|
elif self.health == 1:
|
||||||
|
self.sprite_name = "enemy_orange"
|
||||||
|
|
||||||
def update(self, entities):
|
def update(self, entities):
|
||||||
# Calcular la posición objetiva horizontal
|
|
||||||
target_gx = self.gx + (self.speed * self.direction)
|
target_gx = self.gx + (self.speed * self.direction)
|
||||||
|
|
||||||
# 1. Rebotar si llega a los límites físicos de la carretera
|
# Rebotar en los bordes de la carretera
|
||||||
if target_gx <= 0.5 or target_gx >= GRID_SIZE_X - 0.5:
|
if target_gx <= 0.5 or target_gx >= GRID_SIZE_X - 0.5:
|
||||||
self.direction *= -1
|
self.direction *= -1
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. Comprobar colisión con bloques u otras entidades sólidas
|
# Comprobar bloqueos con bloques u otras entidades sólidas
|
||||||
blocked = False
|
|
||||||
for entity in entities:
|
for entity in entities:
|
||||||
if entity != self and entity.is_solid:
|
if entity != self and entity.is_solid:
|
||||||
# Comprobar la distancia con el bloque en la rejilla
|
|
||||||
if (
|
if (
|
||||||
abs(entity.gx - target_gx) < 0.6
|
abs(entity.gx - target_gx) < 0.6
|
||||||
and abs(entity.gy - self.gy) < 0.6
|
and abs(entity.gy - self.gy) < 0.6
|
||||||
):
|
):
|
||||||
blocked = True
|
self.direction *= -1
|
||||||
break
|
return
|
||||||
|
|
||||||
if blocked:
|
self.gx = target_gx
|
||||||
# Si se topa con un bloque empujable u otro obstáculo, invierte su rumbo
|
|
||||||
self.direction *= -1
|
|
||||||
else:
|
|
||||||
# Si el camino está despejado, avanza
|
|
||||||
self.gx = target_gx
|
|
||||||
|
|
||||||
|
|
||||||
class Player(Entity):
|
class Player(Entity):
|
||||||
@ -336,7 +369,7 @@ class Game:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
||||||
pygame.display.set_caption("Highway Encounter - Full Sprites & Enemies")
|
pygame.display.set_caption("Highway Encounter - Custom Enemies & Shapes")
|
||||||
self.clock = pygame.time.Clock()
|
self.clock = pygame.time.Clock()
|
||||||
self.font = pygame.font.SysFont("monospace", 18, bold=True)
|
self.font = pygame.font.SysFont("monospace", 18, bold=True)
|
||||||
self.sprite_mgr = SpriteManager()
|
self.sprite_mgr = SpriteManager()
|
||||||
@ -353,7 +386,7 @@ class Game:
|
|||||||
self.entities = [self.vortex, self.player]
|
self.entities = [self.vortex, self.player]
|
||||||
self.bullets = []
|
self.bullets = []
|
||||||
|
|
||||||
# Escuadrón de reserva (vidas)
|
# Escuadrón de reserva
|
||||||
self.spares = []
|
self.spares = []
|
||||||
for i in range(self.lives - 1):
|
for i in range(self.lives - 1):
|
||||||
spare = Entity(
|
spare = Entity(
|
||||||
@ -365,17 +398,17 @@ class Game:
|
|||||||
)
|
)
|
||||||
self.spares.append(spare)
|
self.spares.append(spare)
|
||||||
|
|
||||||
# Generar bloques y enemigos alternados a lo largo de la autopista
|
# Generar bloques y ambos tipos de enemigos a lo largo del mapa
|
||||||
for y in range(10, GRID_SIZE_Y - 15, 5):
|
for y in range(10, GRID_SIZE_Y - 15, 6):
|
||||||
# Agregar bloques
|
|
||||||
self.entities.append(PushBlock(y % GRID_SIZE_X, y))
|
self.entities.append(PushBlock(y % GRID_SIZE_X, y))
|
||||||
|
|
||||||
# Agregar enemigos cada 10 casillas
|
# Alternar entre enemigos destructibles e indestructibles
|
||||||
if y % 10 == 0:
|
if y % 12 == 0:
|
||||||
self.entities.append(Enemy(3.5, y - 2))
|
destructible = (y % 24) == 0
|
||||||
|
self.entities.append(Enemy(3.5, y - 2, destructible=destructible))
|
||||||
|
|
||||||
def handle_collisions(self):
|
def handle_collisions(self):
|
||||||
# Colisión con enemigos
|
# Muerte por contacto con enemigo
|
||||||
for entity in self.entities:
|
for entity in self.entities:
|
||||||
if isinstance(entity, Enemy):
|
if isinstance(entity, Enemy):
|
||||||
if (
|
if (
|
||||||
@ -387,11 +420,11 @@ class Game:
|
|||||||
self.reset_level()
|
self.reset_level()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Limpiar entidades destruidas (bloques o enemigos con salud <= 0)
|
# Limpiar bloques destruidos y enemigos muertos
|
||||||
for entity in self.entities[:]:
|
for entity in self.entities[:]:
|
||||||
if hasattr(entity, "health") and entity.health <= 0:
|
if hasattr(entity, "health") and entity.health <= 0:
|
||||||
self.entities.remove(entity)
|
self.entities.remove(entity)
|
||||||
self.score += 150
|
self.score += 200
|
||||||
|
|
||||||
def draw_hud(self):
|
def draw_hud(self):
|
||||||
zone = int((GRID_SIZE_Y - self.vortex.gy) / 4)
|
zone = int((GRID_SIZE_Y - self.vortex.gy) / 4)
|
||||||
@ -426,7 +459,6 @@ class Game:
|
|||||||
self.reset_level()
|
self.reset_level()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Actualización
|
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
self.player.update(keys, self.entities, self.bullets)
|
self.player.update(keys, self.entities, self.bullets)
|
||||||
self.vortex.update(self.entities)
|
self.vortex.update(self.entities)
|
||||||
@ -447,10 +479,9 @@ class Game:
|
|||||||
camera_offset_x = SCREEN_WIDTH // 2 - iso_x
|
camera_offset_x = SCREEN_WIDTH // 2 - iso_x
|
||||||
camera_offset_y = SCREEN_HEIGHT // 2 - iso_y + 100
|
camera_offset_y = SCREEN_HEIGHT // 2 - iso_y + 100
|
||||||
|
|
||||||
# Renderizado
|
|
||||||
self.screen.fill(COLOR_BG)
|
self.screen.fill(COLOR_BG)
|
||||||
|
|
||||||
# Dibujar mosaico de la carretera
|
# Dibujar suelo
|
||||||
for gy in range(0, GRID_SIZE_Y):
|
for gy in range(0, GRID_SIZE_Y):
|
||||||
for gx in range(0, GRID_SIZE_X):
|
for gx in range(0, GRID_SIZE_X):
|
||||||
sx, sy = grid_to_iso(gx, gy)
|
sx, sy = grid_to_iso(gx, gy)
|
||||||
@ -476,7 +507,7 @@ class Game:
|
|||||||
pygame.draw.polygon(self.screen, COLOR_ROAD, pts)
|
pygame.draw.polygon(self.screen, COLOR_ROAD, pts)
|
||||||
pygame.draw.polygon(self.screen, COLOR_GRID, pts, 1)
|
pygame.draw.polygon(self.screen, COLOR_GRID, pts, 1)
|
||||||
|
|
||||||
# Dibujar elementos ordenados por profundidad (Y descendente)
|
# Orden de renderizado por profundidad
|
||||||
all_drawables = self.entities + self.bullets + self.spares
|
all_drawables = self.entities + self.bullets + self.spares
|
||||||
sorted_entities = sorted(
|
sorted_entities = sorted(
|
||||||
all_drawables, key=lambda e: e.gy, reverse=True
|
all_drawables, key=lambda e: e.gy, reverse=True
|
||||||
|
|||||||
BIN
img-versiones/version_2.0.png
Normal file
BIN
img-versiones/version_2.0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Loading…
x
Reference in New Issue
Block a user