From 7dd9fac0c841c9a773dae85e408a9da4c275bc0f Mon Sep 17 00:00:00 2001 From: luisgulo Date: Sun, 4 Oct 2026 15:08:51 +0200 Subject: [PATCH] Comprobamos movimiento en vista isometrica --- README.md | 4 +- highway_vector.py | 239 ++++++++++++++++++++++++++++++++++ img-versiones/version_0.1.png | Bin 0 -> 14415 bytes 3 files changed, 241 insertions(+), 2 deletions(-) create mode 100644 highway_vector.py create mode 100644 img-versiones/version_0.1.png diff --git a/README.md b/README.md index e88f538..589b05f 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ El Vortex avanza automáticamente de forma constante hacia arriba. Si encuentra 1. Clona este repositorio: ```bash - git clone https://gitea.soloconlinux.org.es/luisgulo/Hightway_Vector.git - cd highway-vector + git clone https://gitea.soloconlinux.org.es/luisgulo/Highway_Vector.git + cd Highway_Vector ``` 2. Crea y activa un entorno virtual para las librerias de Python: ```bash diff --git a/highway_vector.py b/highway_vector.py new file mode 100644 index 0000000..1096105 --- /dev/null +++ b/highway_vector.py @@ -0,0 +1,239 @@ +import sys +import pygame + +# Inicialización de Pygame +pygame.init() + +# Constantes de Pantalla +SCREEN_WIDTH = 800 +SCREEN_HEIGHT = 600 +FPS = 60 + +# Configuración de Cuadrícula e Isometría +TILE_WIDTH = 64 +TILE_HEIGHT = 32 +GRID_SIZE_X = 8 +GRID_SIZE_Y = 100 # Largo de la autopista + +# Colores (Paleta Retro Spectrum/Amstrad) +COLOR_BG = (10, 10, 15) +COLOR_GRID = (40, 40, 60) +COLOR_ROAD = (60, 60, 80) +COLOR_VORTEX = (0, 255, 200) +COLOR_PLAYER = (255, 200, 0) +COLOR_BLOCK = (200, 50, 50) +COLOR_WALL = (100, 100, 120) + + +def grid_to_iso(gx, gy, gz=0): + """Convierte coordenadas de cuadrícula 3D (x, y, z) a coordenadas de pantalla 2D.""" + iso_x = (gx - gy) * (TILE_WIDTH // 2) + iso_y = (gx + gy) * (TILE_HEIGHT // 2) - (gz * 16) + return iso_x, iso_y + + +class Entity: + + def __init__(self, gx, gy, color, is_solid=True): + self.gx = float(gx) + self.gy = float(gy) + self.gz = 0 + self.color = color + self.is_solid = is_solid + self.radius = 0.4 + + def draw(self, surface, camera_offset_x, camera_offset_y): + screen_x, screen_y = grid_to_iso(self.gx, self.gy, self.gz) + draw_x = screen_x + camera_offset_x + draw_y = screen_y + camera_offset_y + + # Dibujar un bloque o personaje estilo isométrico + points = [ + (draw_x, draw_y - 20), + (draw_x + TILE_WIDTH // 2, draw_y - 20 + TILE_HEIGHT // 2), + (draw_x, draw_y - 20 + TILE_HEIGHT), + (draw_x - TILE_WIDTH // 2, draw_y - 20 + TILE_HEIGHT // 2), + ] + pygame.draw.polygon(surface, self.color, points) + pygame.draw.polygon( + surface, (255, 255, 255), points, 2 + ) # Borde brillante + + +class PushBlock(Entity): + + def __init__(self, gx, gy): + super().__init__(gx, gy, COLOR_BLOCK, is_solid=True) + + def push(self, dx, dy, entities): + new_gx = self.gx + dx + new_gy = self.gy + dy + + # Comprobar límites de la carretera + if not (0 <= new_gx < GRID_SIZE_X and 0 <= new_gy < GRID_SIZE_Y): + return False + + # Comprobar colisión con otras entidades + for entity in entities: + if entity != self and entity.is_solid: + if ( + abs(entity.gx - new_gx) < 0.6 + and abs(entity.gy - new_gy) < 0.6 + ): + return False + + self.gx = new_gx + self.gy = new_gy + return True + + +class Player(Entity): + + def __init__(self, gx, gy): + super().__init__(gx, gy, COLOR_PLAYER, is_solid=True) + self.speed = 0.08 + + def update(self, keys, entities): + dx, dy = 0, 0 + if keys[pygame.K_LEFT] or keys[pygame.K_a]: + dx -= self.speed + if keys[pygame.K_RIGHT] or keys[pygame.K_d]: + dx += self.speed + if keys[pygame.K_UP] or keys[pygame.K_w]: + dy -= self.speed + if keys[pygame.K_DOWN] or keys[pygame.K_s]: + dy += self.speed + + if dx != 0 or dy != 0: + target_gx = self.gx + dx + target_gy = self.gy + dy + + # Limites horizontales + if not (0 <= target_gx < GRID_SIZE_X): + dx = 0 + if not (0 <= target_gy < GRID_SIZE_Y): + dy = 0 + + # Intento de movimiento y colisión con bloques + for entity in entities: + if entity != self and entity.is_solid: + if ( + abs(entity.gx - (self.gx + dx)) < 0.6 + and abs(entity.gy - (self.gy + dy)) < 0.6 + ): + # Intentar empujar si es un PushBlock + if isinstance(entity, PushBlock): + push_dx = 0.1 if dx > 0 else (-0.1 if dx < 0 else 0) + push_dy = 0.1 if dy > 0 else (-0.1 if dy < 0 else 0) + if not entity.push(push_dx, push_dy, entities): + return + else: + return # Bloqueado + + self.gx += dx + self.gy += dy + + +class Vortex(Entity): + + def __init__(self, gx, gy): + super().__init__(gx, gy, COLOR_VORTEX, is_solid=True) + self.speed = 0.015 # Avance constante automático + + def update(self, entities): + target_gy = self.gy - self.speed + + # Verificar si hay algún obstáculo al avanzar + blocked = False + for entity in entities: + if entity != self and entity.is_solid: + if ( + abs(entity.gx - self.gx) < 0.7 + and abs(entity.gy - target_gy) < 0.7 + ): + blocked = True + break + + if not blocked and target_gy >= 0: + self.gy = target_gy + + +class Game: + + def __init__(self): + self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) + pygame.display.set_caption("Highway Encounter Python Remake") + self.clock = pygame.time.Clock() + + self.vortex = Vortex(3.5, 95) + self.player = Player(3.5, 92) + + self.entities = [self.vortex, self.player] + + # Generar bloques de prueba a lo largo de la autopista + for y in range(10, 90, 5): + self.entities.append(PushBlock(y % GRID_SIZE_X, y)) + self.entities.append(PushBlock((y + 3) % GRID_SIZE_X, y + 2)) + + def run(self): + running = True + while running: + self.clock.tick(FPS) + + # 1. Procesar Eventos + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + + # 2. Actualizar Estado + keys = pygame.key.get_pressed() + self.player.update(keys, self.entities) + self.vortex.update(self.entities) + + # 3. Calcular desplazamiento de cámara (centrada en el VORTEX o 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 + + # 4. Renderizado + self.screen.fill(COLOR_BG) + + # Dibujar suelo (Autopista) + 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 + + # Solo dibujar si está visible en pantalla + 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, draw_y + TILE_HEIGHT), + ( + 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) + + # Ordenar entidades por coordenada 'gy' descendente para renderizar correctamente la profundidad + sorted_entities = sorted( + self.entities, key=lambda e: e.gy, reverse=True + ) + for entity in sorted_entities: + entity.draw(self.screen, camera_offset_x, camera_offset_y) + + pygame.display.flip() + + pygame.quit() + sys.exit() + + +if __name__ == "__main__": + Game().run() diff --git a/img-versiones/version_0.1.png b/img-versiones/version_0.1.png new file mode 100644 index 0000000000000000000000000000000000000000..171eba25380bf0091cb5e36329a84b7ca1bbda2c GIT binary patch literal 14415 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 WZN!a@@V^A&_UzcZJ#VXP