Enemigos y disparos

This commit is contained in:
luisgulo 2026-10-04 15:15:48 +02:00
parent 1b272ba47e
commit ac215e0e37
2 changed files with 219 additions and 120 deletions

View File

@ -1,11 +1,11 @@
import sys
import math
import os
import sys
import pygame
# Inicialización de Pygame
pygame.init()
# Configuración de pantalla
# Configuración de Pantalla
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
FPS = 60
@ -16,106 +16,148 @@ TILE_HEIGHT = 32
GRID_SIZE_X = 8
GRID_SIZE_Y = 120
# Colores de apoyo
# Colores de Respaldo / Suelo
COLOR_BG = (15, 15, 25)
COLOR_ROAD = (50, 50, 65)
COLOR_GRID = (30, 30, 45)
def grid_to_iso(gx, gy, gz=0):
"""Convierte coordenadas 3D de rejilla en coordenadas 2D en pantalla."""
"""Convierte coordenadas de la cuadrícula a coordenadas isométricas de pantalla."""
iso_x = (gx - gy) * (TILE_WIDTH // 2)
iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16)
return iso_x, iso_y
# ==============================================================================
# GENERADOR DE SPRITES AUTOMÁTICO (O Carga desde PNG si existen)
# ==============================================================================
def load_or_create_sprite(filename, create_func):
"""Intenta cargar una imagen PNG. Si no existe, la crea dinámicamente."""
try:
image = pygame.image.load(filename).convert_alpha()
return image
except (pygame.error, FileNotFoundError):
return create_func()
def create_placeholder_sprites():
"""Genera sprites vectoriales en memoria si no existen archivos PNG locales."""
sprites = {}
# 1. Jugador (Lasertron)
surf_player = pygame.Surface((64, 64), pygame.SRCALPHA)
pygame.draw.polygon(
surf_player,
(255, 200, 0),
[(32, 8), (56, 24), (32, 56), (8, 24)],
)
pygame.draw.polygon(
surf_player,
(255, 255, 255),
[(32, 8), (56, 24), (32, 56), (8, 24)],
2,
)
pygame.draw.circle(surf_player, (0, 255, 255), (32, 28), 6)
sprites["player"] = surf_player
# 2. VORTEX
surf_vortex = pygame.Surface((64, 64), pygame.SRCALPHA)
pygame.draw.polygon(
surf_vortex,
(220, 50, 50),
[(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
# 3. Bloque empujable
surf_block = pygame.Surface((64, 64), pygame.SRCALPHA)
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)
surf_enemy = pygame.Surface((64, 64), pygame.SRCALPHA)
pygame.draw.polygon(
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
surf_bullet = pygame.Surface((32, 32), pygame.SRCALPHA)
pygame.draw.circle(surf_bullet, (0, 255, 255), (16, 16), 5)
pygame.draw.circle(surf_bullet, (255, 255, 255), (16, 16), 2)
sprites["bullet"] = surf_bullet
return sprites
def create_player_sprite():
surf = pygame.Surface((64, 64), pygame.SRCALPHA)
# Cuerpo principal Lasertron
pygame.draw.ellipse(surf, (255, 200, 0), (16, 20, 32, 28))
pygame.draw.ellipse(surf, (255, 255, 200), (20, 12, 24, 20))
pygame.draw.circle(surf, (0, 200, 255), (32, 20), 6) # Visor
# Cañón de disparo
pygame.draw.rect(surf, (150, 150, 150), (30, 6, 4, 10))
return surf
class SpriteManager:
def __init__(self):
self.placeholders = create_placeholder_sprites()
self.cache = {}
def get_sprite(self, name, filename):
if name in self.cache:
return self.cache[name]
if os.path.exists(filename):
try:
img = pygame.image.load(filename).convert_alpha()
self.cache[name] = img
return img
except Exception as e:
print(
f"Error al cargar {filename}, usando respaldo vectorial: {e}"
)
self.cache[name] = self.placeholders[name]
return self.cache[name]
def create_vortex_sprite():
surf = pygame.Surface((64, 64), pygame.SRCALPHA)
# Base pesada VORTEX
pygame.draw.ellipse(surf, (180, 40, 40), (8, 24, 48, 30))
pygame.draw.ellipse(surf, (220, 60, 60), (14, 16, 36, 22))
pygame.draw.circle(surf, (255, 255, 255), (32, 24), 8)
pygame.draw.circle(surf, (255, 0, 0), (32, 24), 4)
return surf
def create_block_sprite():
surf = pygame.Surface((64, 64), pygame.SRCALPHA)
# Cara superior
pts_top = [(32, 8), (56, 20), (32, 32), (8, 20)]
pygame.draw.polygon(surf, (210, 90, 50), pts_top)
# Cara izquierda
pts_left = [(8, 20), (32, 32), (32, 52), (8, 40)]
pygame.draw.polygon(surf, (150, 60, 30), pts_left)
# Cara derecha
pts_right = [(32, 32), (56, 20), (56, 40), (32, 52)]
pygame.draw.polygon(surf, (180, 75, 40), pts_right)
# Bordes
pygame.draw.polygon(surf, (255, 255, 255), pts_top, 1)
return surf
def create_enemy_sprite():
surf = pygame.Surface((64, 64), pygame.SRCALPHA)
# Dron Alienígena
pygame.draw.ellipse(surf, (50, 220, 100), (12, 16, 40, 32))
pygame.draw.circle(surf, (255, 0, 100), (32, 30), 8)
pygame.draw.line(surf, (50, 220, 100), (20, 48), (12, 58), 3)
pygame.draw.line(surf, (50, 220, 100), (44, 48), (52, 58), 3)
return surf
def create_bullet_sprite():
surf = pygame.Surface((32, 32), pygame.SRCALPHA)
pygame.draw.circle(surf, (0, 255, 255), (16, 16), 6)
pygame.draw.circle(surf, (255, 255, 255), (16, 16), 3)
return surf
# ==============================================================================
# CLASES DEL JUEGO
# ==============================================================================
class Entity:
def __init__(self, gx, gy, sprite, is_solid=True):
def __init__(self, gx, gy, sprite_name, filename, is_solid=True):
self.gx = float(gx)
self.gy = float(gy)
self.gz = 0
self.sprite = sprite
self.sprite_name = sprite_name
self.filename = filename
self.is_solid = is_solid
def draw(self, surface, camera_offset_x, camera_offset_y):
def draw(self, surface, camera_offset_x, camera_offset_y, sprite_mgr):
screen_x, screen_y = grid_to_iso(self.gx, self.gy, self.gz)
draw_x = screen_x + camera_offset_x - (self.sprite.get_width() // 2)
draw_y = screen_y + camera_offset_y - (self.sprite.get_height() // 2)
surface.blit(self.sprite, (draw_x, draw_y))
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_y = screen_y + camera_offset_y - (sprite.get_height() // 2)
surface.blit(sprite, (draw_x, draw_y))
class Bullet(Entity):
def __init__(self, gx, gy, dir_x, dir_y, sprite):
super().__init__(gx, gy, sprite, is_solid=False)
def __init__(self, gx, gy, dir_x, dir_y):
super().__init__(gx, gy, "bullet", "bullet.png", is_solid=False)
self.dir_x = dir_x
self.dir_y = dir_y
self.speed = 0.25
@ -131,7 +173,10 @@ class Bullet(Entity):
for entity in entities:
if entity != self and entity.is_solid:
if abs(entity.gx - self.gx) < 0.5 and abs(entity.gy - self.gy) < 0.5:
if (
abs(entity.gx - self.gx) < 0.5
and abs(entity.gy - self.gy) < 0.5
):
self.alive = False
if isinstance(entity, (PushBlock, Enemy)):
entity.health -= 1
@ -139,8 +184,9 @@ class Bullet(Entity):
class PushBlock(Entity):
def __init__(self, gx, gy, sprite):
super().__init__(gx, gy, sprite, is_solid=True)
def __init__(self, gx, gy):
super().__init__(gx, gy, "block", "block.png", is_solid=True)
self.health = 2
def push(self, dx, dy, entities):
@ -152,7 +198,10 @@ class PushBlock(Entity):
for entity in entities:
if entity != self and entity.is_solid:
if abs(entity.gx - new_gx) < 0.5 and abs(entity.gy - new_gy) < 0.5:
if (
abs(entity.gx - new_gx) < 0.5
and abs(entity.gy - new_gy) < 0.5
):
return False
self.gx = new_gx
@ -161,15 +210,18 @@ class PushBlock(Entity):
class Enemy(Entity):
def __init__(self, gx, gy, sprite):
super().__init__(gx, gy, sprite, is_solid=True)
self.speed = 0.03
def __init__(self, gx, gy):
super().__init__(gx, gy, "enemy", "enemy.png", is_solid=True)
self.speed = 0.04
self.direction = 1
self.health = 1
def update(self, entities):
# Movimiento de patrulla horizontal
target_gx = self.gx + (self.speed * self.direction)
# Rebotar en los bordes de la carretera
if target_gx <= 0.5 or target_gx >= GRID_SIZE_X - 0.5:
self.direction *= -1
target_gx = self.gx + (self.speed * self.direction)
@ -178,9 +230,9 @@ class Enemy(Entity):
class Player(Entity):
def __init__(self, gx, gy, sprite, bullet_sprite):
super().__init__(gx, gy, sprite, is_solid=True)
self.bullet_sprite = bullet_sprite
def __init__(self, gx, gy):
super().__init__(gx, gy, "player", "player.png", is_solid=True)
self.speed = 0.09
self.facing_x = 0.0
self.facing_y = -1.0
@ -209,10 +261,17 @@ class Player(Entity):
blocked = False
for entity in entities:
if entity != self and entity.is_solid:
if abs(entity.gx - target_gx) < 0.5 and abs(entity.gy - target_gy) < 0.5:
if (
abs(entity.gx - target_gx) < 0.5
and abs(entity.gy - target_gy) < 0.5
):
if isinstance(entity, PushBlock):
push_dx = 0.15 if dx > 0 else (-0.15 if dx < 0 else 0)
push_dy = 0.15 if dy > 0 else (-0.15 if dy < 0 else 0)
push_dx = (
0.15 if dx > 0 else (-0.15 if dx < 0 else 0)
)
push_dy = (
0.15 if dy > 0 else (-0.15 if dy < 0 else 0)
)
if not entity.push(push_dx, push_dy, entities):
blocked = True
else:
@ -227,13 +286,16 @@ class Player(Entity):
if keys[pygame.K_SPACE] and self.shoot_cooldown == 0:
spawn_x = self.gx + (self.facing_x * 0.7)
spawn_y = self.gy + (self.facing_y * 0.7)
bullets.append(Bullet(spawn_x, spawn_y, self.facing_x, self.facing_y, self.bullet_sprite))
bullets.append(
Bullet(spawn_x, spawn_y, self.facing_x, self.facing_y)
)
self.shoot_cooldown = 15
class Vortex(Entity):
def __init__(self, gx, gy, sprite):
super().__init__(gx, gy, sprite, is_solid=True)
def __init__(self, gx, gy):
super().__init__(gx, gy, "vortex", "vortex.png", is_solid=True)
self.speed = 0.012
def update(self, entities):
@ -242,7 +304,10 @@ class Vortex(Entity):
blocked = False
for entity in entities:
if entity != self and entity.is_solid:
if abs(entity.gx - self.gx) < 0.6 and abs(entity.gy - target_gy) < 0.6:
if (
abs(entity.gx - self.gx) < 0.6
and abs(entity.gy - target_gy) < 0.6
):
blocked = True
break
@ -251,53 +316,65 @@ class Vortex(Entity):
class Game:
def __init__(self):
self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Highway Encounter - PNG Sprites Version")
pygame.display.set_caption("Highway Encounter - Full Sprites & Enemies")
self.clock = pygame.time.Clock()
self.font = pygame.font.SysFont("monospace", 18, bold=True)
# Carga o Generación de Sprites
self.spr_player = load_or_create_sprite("player.png", create_player_sprite)
self.spr_vortex = load_or_create_sprite("vortex.png", create_vortex_sprite)
self.spr_block = load_or_create_sprite("block.png", create_block_sprite)
self.spr_enemy = load_or_create_sprite("enemy.png", create_enemy_sprite)
self.spr_bullet = load_or_create_sprite("bullet.png", create_bullet_sprite)
self.sprite_mgr = SpriteManager()
self.lives = 4
self.score = 0
self.reset_level()
def reset_level(self):
self.vortex = Vortex(3.5, GRID_SIZE_Y - 5, self.spr_vortex)
self.player = Player(3.5, GRID_SIZE_Y - 8, self.spr_player, self.spr_bullet)
self.vortex = Vortex(3.5, GRID_SIZE_Y - 5)
self.player = Player(3.5, GRID_SIZE_Y - 8)
self.entities = [self.vortex, self.player]
self.bullets = []
# Escuadrón de reserva (vidas)
self.spares = []
for i in range(self.lives - 1):
spare = Entity(3.5, GRID_SIZE_Y - 3 + (i * 1.2), self.spr_player, is_solid=False)
spare = Entity(
3.5,
GRID_SIZE_Y - 3 + (i * 1.2),
"player",
"player.png",
is_solid=False,
)
self.spares.append(spare)
for y in range(10, GRID_SIZE_Y - 15, 6):
self.entities.append(PushBlock(y % GRID_SIZE_X, y, self.spr_block))
if y % 12 == 0:
self.entities.append(Enemy(3.5, y - 2, self.spr_enemy))
# Generar bloques y enemigos alternados a lo largo de la autopista
for y in range(10, GRID_SIZE_Y - 15, 5):
# Agregar bloques
self.entities.append(PushBlock(y % GRID_SIZE_X, y))
# Agregar enemigos cada 10 casillas
if y % 10 == 0:
self.entities.append(Enemy(3.5, y - 2))
def handle_collisions(self):
# Colisión con enemigos
for entity in self.entities:
if isinstance(entity, Enemy):
if abs(entity.gx - self.player.gx) < 0.5 and abs(entity.gy - self.player.gy) < 0.5:
if (
abs(entity.gx - self.player.gx) < 0.5
and abs(entity.gy - self.player.gy) < 0.5
):
self.lives -= 1
if self.lives > 0:
self.reset_level()
return
# Limpiar entidades destruidas (bloques o enemigos con salud <= 0)
for entity in self.entities[:]:
if hasattr(entity, "health") and entity.health <= 0:
self.entities.remove(entity)
self.score += 100
self.score += 150
def draw_hud(self):
zone = int((GRID_SIZE_Y - self.vortex.gy) / 4)
@ -317,8 +394,12 @@ class Game:
if self.lives <= 0:
self.screen.fill((0, 0, 0))
msg = self.font.render("GAME OVER - Press R to Restart", True, (255, 50, 50))
self.screen.blit(msg, (SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2))
msg = self.font.render(
"GAME OVER - Press R to Restart", True, (255, 50, 50)
)
self.screen.blit(
msg, (SCREEN_WIDTH // 2 - 150, SCREEN_HEIGHT // 2)
)
pygame.display.flip()
keys = pygame.key.get_pressed()
@ -328,6 +409,7 @@ class Game:
self.reset_level()
continue
# Actualización
keys = pygame.key.get_pressed()
self.player.update(keys, self.entities, self.bullets)
self.vortex.update(self.entities)
@ -343,32 +425,49 @@ class Game:
self.handle_collisions()
# Desplazamiento de cámara centrado en el jugador
iso_x, iso_y = grid_to_iso(self.player.gx, self.player.gy)
camera_offset_x = SCREEN_WIDTH // 2 - iso_x
camera_offset_y = SCREEN_HEIGHT // 2 - iso_y + 100
# Renderizado
self.screen.fill(COLOR_BG)
# Dibujar mosaico de la carretera
for gy in range(0, GRID_SIZE_Y):
for gx in range(0, GRID_SIZE_X):
sx, sy = grid_to_iso(gx, gy)
draw_x = sx + camera_offset_x
draw_y = sy + camera_offset_y
if -100 < draw_x < SCREEN_WIDTH + 100 and -100 < draw_y < SCREEN_HEIGHT + 100:
if (
-100 < draw_x < SCREEN_WIDTH + 100
and -100 < draw_y < SCREEN_HEIGHT + 100
):
pts = [
(draw_x, draw_y),
(draw_x + TILE_WIDTH // 2, draw_y + TILE_HEIGHT // 2),
(
draw_x + TILE_WIDTH // 2,
draw_y + TILE_HEIGHT // 2,
),
(draw_x, draw_y + TILE_HEIGHT),
(draw_x - TILE_WIDTH // 2, draw_y + TILE_HEIGHT // 2),
(
draw_x - TILE_WIDTH // 2,
draw_y + TILE_HEIGHT // 2,
),
]
pygame.draw.polygon(self.screen, COLOR_ROAD, pts)
pygame.draw.polygon(self.screen, COLOR_GRID, pts, 1)
# Dibujar elementos ordenados por profundidad (Y descendente)
all_drawables = self.entities + self.bullets + self.spares
sorted_entities = sorted(all_drawables, key=lambda e: e.gy, reverse=True)
sorted_entities = sorted(
all_drawables, key=lambda e: e.gy, reverse=True
)
for entity in sorted_entities:
entity.draw(self.screen, camera_offset_x, camera_offset_y)
entity.draw(
self.screen, camera_offset_x, camera_offset_y, self.sprite_mgr
)
self.draw_hud()
pygame.display.flip()

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB