import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; /** * GeometryDashJava.java * Una versión simple y completa de un juego tipo "Geometry Dash" en Java Swing. * - Un solo archivo, compilable con javac * - Juego lateral con scroll, saltos, obstáculos, colisiones y puntuación * - Controles: espacio para saltar / click para acción / R para reiniciar * * Compilar: * javac GeometryDashJava.java * Ejecutar: * java GeometryDashJava * * Nota: este archivo no requiere assets externos; todo se dibuja con primitivas 2D. */ public class GeometryDashJava extends JPanel implements ActionListener, KeyListener, MouseListener { // Tamaño de la ventana public static final int WIDTH = 1000; public static final int HEIGHT = 600; // Temporizador del juego (~60FPS) private final Timer timer; // Jugador private final Player player; // Obstáculos y scroll private final List obstacles = new ArrayList<>(); private final Random rnd = new Random(); private int worldScroll = 0; // offset global private int spawnCooldown = 0; // Estado private boolean running = false; private boolean gameOver = false; private int score = 0; private int bestScore = 0; // UI / debug private Font hudFont = new Font("Arial", Font.BOLD, 20); public GeometryDashJava() { setPreferredSize(new Dimension(WIDTH, HEIGHT)); setBackground(new Color(8, 8, 12)); setFocusable(true); addKeyListener(this); addMouseListener(this); player = new Player(120, HEIGHT - 120, 48, 48); timer = new Timer(1000 / 60, this); timer.start(); } // Inicio del juego / reinicio public void startGame() { obstacles.clear(); worldScroll = 0; spawnCooldown = 0; score = 0; gameOver = false; running = true; player.reset(); } public void endGame() { running = false; gameOver = true; bestScore = Math.max(bestScore, score); } // Lógica principal se ejecuta cada frame @Override public void actionPerformed(ActionEvent e) { if (running) { // scroll speed increases slowly with score int baseSpeed = 6 + (score / 100); worldScroll += baseSpeed; // spawn obstacles spawnCooldown -= 1; if (spawnCooldown <= 0) { spawnObstacle(); spawnCooldown = 60 + rnd.nextInt(Math.max(40, 220 - score / 5)); } // update obstacles for (Iterator it = obstacles.iterator(); it.hasNext(); ) { Obstacle o = it.next(); o.x -= baseSpeed; if (o.x + o.width < 0) it.remove(); } // player physics player.update(); // collisions for (Obstacle o : obstacles) { if (o.collidesWith(player)) { endGame(); break; } } // scoring: increase when worldScroll passes multiples of 100 if (worldScroll % 6 == 0) score += 1; } repaint(); } // Generador de obstáculos (plataformas y picos) private void spawnObstacle() { int type = rnd.nextInt(3); int gap = 200 + rnd.nextInt(220); int baseX = WIDTH + gap; if (type == 0) { // un simple bloque en el suelo (tamaño variable) int w = 40 + rnd.nextInt(80); int h = 40 + rnd.nextInt(80); int y = HEIGHT - 80 - (h - 40); obstacles.add(new Obstacle(baseX, y, w, h, Obstacle.Type.BLOCK)); } else if (type == 1) { // un conjunto de picos (pinchos) que el jugador debe saltar int cnt = 1 + rnd.nextInt(3); int spikeWidth = 32; for (int i = 0; i < cnt; i++) { int x = baseX + i * spikeWidth; int w = spikeWidth; int h = 48 + rnd.nextInt(32); int y = HEIGHT - 80 - h; obstacles.add(new Obstacle(x, y, w, h, Obstacle.Type.SPIKE)); } } else { // plataforma elevada (puede requerir doble salto si lo implementas) int w = 80 + rnd.nextInt(120); int h = 24; int y = HEIGHT - 200 - rnd.nextInt(120); obstacles.add(new Obstacle(baseX, y, w, h, Obstacle.Type.PLATFORM)); } } // Dibujo @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2 = (Graphics2D) g.create(); // suavizado g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // ground g2.setColor(new Color(18, 18, 28)); g2.fillRect(0, HEIGHT - 80, WIDTH, 80); // grid parallax - simple lineas para dar sensación de movimiento g2.setColor(new Color(16, 20, 30)); for (int x = - (worldScroll % 80); x < WIDTH; x += 80) { g2.fillRect(x, HEIGHT - 90, 2, 90); } // draw obstacles for (Obstacle o : obstacles) { o.draw(g2); } // draw player player.draw(g2); // HUD g2.setColor(Color.WHITE); g2.setFont(hudFont); g2.drawString("Puntos: " + score, 18, 28); g2.drawString("Mejor: " + bestScore, 18, 52); if (!running) { String msg = gameOver ? "Juego terminado — Pulsa R para reiniciar" : "Pulsa espacio para empezar"; g2.setFont(new Font("Arial", Font.BOLD, 28)); int sw = g2.getFontMetrics().stringWidth(msg); g2.setColor(new Color(0, 0, 0, 160)); g2.fillRect(WIDTH / 2 - sw / 2 - 16, HEIGHT / 2 - 34, sw + 32, 68); g2.setColor(Color.WHITE); g2.drawString(msg, WIDTH / 2 - sw / 2, HEIGHT / 2 + 8); } g2.dispose(); } // Input handlers @Override public void keyTyped(KeyEvent e) {} @Override public void keyPressed(KeyEvent e) { int kc = e.getKeyCode(); if (kc == KeyEvent.VK_SPACE) { if (!running) startGame(); if (!gameOver) player.jump(); } else if (kc == KeyEvent.VK_R) { startGame(); } } @Override public void keyReleased(KeyEvent e) {} @Override public void mouseClicked(MouseEvent e) { if (!running) startGame(); if (!gameOver) player.jump(); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) {} @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} // ----- CLASES INTERNAS ----- static class Player { double x, y; int width, height; double vy = 0; // velocidad vertical final double gravity = 1600.0 / 60.0; // px por seg^2 aproximado por frame final double jumpPower = -26.0; // impulso boolean onGround = true; public Player(int x, int y, int w, int h) { this.x = x; this.y = y; this.width = w; this.height = h; } public void reset() { this.x = 120; this.y = HEIGHT - 120; this.vy = 0; this.onGround = true; } public void update() { // aplicar gravedad vy += gravity * (1.0/60.0); y += vy; // colisión simple con suelo double floor = HEIGHT - 80 - height; // superficie de colisión if (y >= floor) { y = floor; vy = 0; onGround = true; } else { onGround = false; } } public void jump() { if (onGround) { vy = jumpPower; onGround = false; } } public Rectangle getBounds() { return new Rectangle((int)x, (int)y, width, height); } public void draw(Graphics2D g2) { // cuerpo principal con estilo geométrico g2.setColor(new Color(120, 230, 255)); g2.fillRoundRect((int)x, (int)y, width, height, 8, 8); // borde g2.setColor(new Color(10, 90, 120)); g2.setStroke(new BasicStroke(2)); g2.drawRoundRect((int)x, (int)y, width, height, 8, 8); // ojo g2.setColor(new Color(8, 20, 40)); g2.fillOval((int)x + width - 18, (int)y + 12, 10, 10); } } static class Obstacle { enum Type { BLOCK, SPIKE, PLATFORM } int x, y, width, height; Type type; public Obstacle(int x, int y, int w, int h, Type t) { this.x = x; this.y = y; this.width = w; this.height = h; this.type = t; } public boolean collidesWith(Player p) { Rectangle r1 = new Rectangle(x, y, width, height); Rectangle r2 = p.getBounds(); // para picos, ajustamos la forma como triángulo aproximado if (type == Type.SPIKE) { // simple bounding-box check first if (!r1.intersects(r2)) return false; // aproximación: si la parte superior del jugador toca la base inclinada return r1.intersects(r2); } return r1.intersects(r2); } public void draw(Graphics2D g2) { if (type == Type.SPIKE) { // dibujar picos como triángulos g2.setColor(new Color(200, 80, 80)); int cols = width / 16; for (int i = 0; i < cols; i++) { int px = x + i * 16; int[] xs = {px, px + 8, px + 16}; int[] ys = {y + height, y, y + height}; g2.fillPolygon(xs, ys, 3); } } else if (type == Type.PLATFORM) { g2.setColor(new Color(255, 200, 110)); g2.fillRoundRect(x, y, width, height, 6, 6); g2.setColor(new Color(180, 140, 60)); g2.setStroke(new BasicStroke(2)); g2.drawRoundRect(x, y, width, height, 6, 6); } else { g2.setColor(new Color(170, 120, 210)); g2.fillRect(x, y, width, height); g2.setColor(new Color(110, 60, 150)); g2.setStroke(new BasicStroke(2)); g2.drawRect(x, y, width, height); } } } // Lanzador de la ventana public static void main(String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame("Geometry Dash - Java Swing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); GeometryDashJava panel = new GeometryDashJava(); frame.setContentPane(panel); frame.pack(); frame.setResizable(false); frame.setLocationRelativeTo(null); frame.setVisible(true); }); } }