Nuestro conocimiento compartido. Nuestro tesoro compartido. Wikipedia.
ShareCode
Permalink: http://www.treeweb.es/u/974/ 01/02/2011

ShareCode

1 <!DOCTYPE html>2 <html lang="es">3 <head>4  <meta charset="UTF-8">5  <title>Juego de Plataformas: Pelota Elástica</title>6  <style>7  body {8  margin: 0;9  padding: 0;10  background-color: #222;11  display: flex;12  justify-content: center;13  align-items: center;14  height: 100vh;15  color: white;16  font-family: 'Arial', sans-serif;17  overflow: hidden;18  }19  #gameContainer {20  position: relative;21  box-shadow: 0 0 20px rgba(0,0,0,0.5);22  }23  canvas {24  background: linear-gradient(to bottom, #87CEEB, #E0F7FA);25  border: 4px solid #444;26  border-radius: 4px;27  }28  #ui {29  position: absolute;30  top: 10px;31  left: 10px;32  font-size: 20px;33  font-weight: bold;34  color: #333;35  text-shadow: 1px 1px 0px white;36  }37  #gameOverScreen {38  position: absolute;39  top: 0; left: 0; width: 100%; height: 100%;40  background: rgba(0,0,0,0.85);41  display: none;42  flex-direction: column;43  justify-content: center;44  align-items: center;45  color: white;46  }47  button {48  padding: 10px 20px;49  font-size: 18px;50  cursor: pointer;51  background: #ff4757;52  border: none;53  color: white;54  border-radius: 5px;55  margin-top: 20px;56  }57  button:hover { background: #ff6b81; }58  </style>59 </head>60 <body>61 62 <div id="gameContainer">63  <div id="ui">Vidas: <span id="livesDisplay">3</span></div>64  <canvas id="gameCanvas" width="800" height="450"></canvas>65  66  <div id="gameOverScreen">67  <h1 id="statusText">GAME OVER</h1>68  <button onclick="resetGame(true)">Jugar de nuevo</button>69  </div>70 </div>71 72 <script>73  const canvas = document.getElementById('gameCanvas');74  const ctx = canvas.getContext('2d');75  const livesDisplay = document.getElementById('livesDisplay');76  const gameOverScreen = document.getElementById('gameOverScreen');77  const statusText = document.getElementById('statusText');78 79  // --- CONFIGURACIÓN DEL JUEGO ---80  const GRAVITY = 0.6;81  const FRICTION = 0.8;82  const SPEED = 1;83  const JUMP_FORCE = -14;84  85  // Estado del juego86  let gameRunning = true;87  let keys = { ArrowRight: false, ArrowLeft: false, ArrowUp: false };88 89  // --- EL JUGADOR (LA PELOTA) ---90  const player = {91  x: 100,92  y: 100,93  radius: 15,94  color: '#ff4757',95  vx: 0,96  vy: 0,97  isGrounded: false,98  lives: 3,99  // Propiedades de deformación (Squash & Stretch)100  scaleX: 1,101  scaleY: 1,102  103  update: function() {104  // Movimiento Horizontal105  if (keys.ArrowRight) this.vx += SPEED;106  if (keys.ArrowLeft) this.vx -= SPEED;107  108  this.vx *= FRICTION;109  this.x += this.vx;110 111  // Colisión Horizontal112  checkCollisions(this, 'x');113 114  // Movimiento Vertical115  this.vy += GRAVITY;116  this.y += this.vy;117  this.isGrounded = false; // Asumimos que cae hasta que se demuestre lo contrario118 119  // Colisión Vertical120  checkCollisions(this, 'y');121 122  // Salto123  if (keys.ArrowUp && this.isGrounded) {124  this.vy = JUMP_FORCE;125  this.isGrounded = false;126  // Efecto elástico al saltar (se estira verticalmente)127  this.scaleX = 0.7;128  this.scaleY = 1.3;129  }130 131  // Muerte por caída132  if (this.y > canvas.height + 50) {133  playerDie();134  }135 136  // --- Lógica de Deformación Visual ---137  // Recuperar forma original poco a poco138  this.scaleX += (1 - this.scaleX) * 0.1;139  this.scaleY += (1 - this.scaleY) * 0.1;140 141  // Deformación basada en velocidad (Estirarse al caer o subir rápido)142  if (!this.isGrounded) {143  // Cuanto más rápido vaya verticalmente, más se estira en Y144  const stretch = Math.abs(this.vy) * 0.03; 145  this.scaleY = 1 + stretch;146  this.scaleX = 1 - (stretch * 0.5); // Conservar volumen147  }148  },149 150  draw: function() {151  ctx.save();152  // Trasladar al centro de la pelota para escalar desde el centro153  ctx.translate(this.x, this.y);154  ctx.scale(this.scaleX, this.scaleY);155  156  // Dibujar la pelota157  ctx.beginPath();158  ctx.arc(0, 0, this.radius, 0, Math.PI * 2);159  ctx.fillStyle = this.color;160  ctx.fill();161  162  // Brillo (para que parezca 3D)163  ctx.beginPath();164  ctx.arc(-5, -5, 5, 0, Math.PI * 2);165  ctx.fillStyle = 'rgba(255,255,255,0.5)';166  ctx.fill();167  168  ctx.restore();169  }170  };171 172  // --- PLATAFORMAS ---173  // x, y, ancho, alto, tipo (static, moving), props extra174  let platforms = [];175 176  function initLevel() {177  platforms = [178  // Suelo inicial179  { x: 0, y: 350, w: 300, h: 40, type: 'static' },180  181  // Primer salto182  { x: 380, y: 280, w: 100, h: 20, type: 'static' },183  184  // Plataforma móvil horizontal185  { x: 550, y: 200, w: 120, h: 20, type: 'moving', dx: 2, minX: 550, maxX: 750 },186  187  // Plataforma alta188  { x: 850, y: 150, w: 100, h: 20, type: 'static' },189 190  // Suelo final lejos (hay un agujero grande antes)191  { x: 1050, y: 350, w: 400, h: 40, type: 'static' }192  ];193  }194 195  // --- CÁMARA ---196  // La cámara sigue al jugador suavemente197  let cameraX = 0;198 199  // --- FÍSICA Y COLISIONES ---200  function checkCollisions(p, axis) {201  for (let plat of platforms) {202  // Verificar solapamiento básico AABB (Axis-Aligned Bounding Box)203  // Consideramos la pelota como un cuadrado para las colisiones físicas204  if (p.x + p.radius > plat.x && 205  p.x - p.radius < plat.x + plat.w &&206  p.y + p.radius > plat.y && 207  p.y - p.radius < plat.y + plat.h) {208 209  if (axis === 'x') {210  // Chocar lateralmente211  if (p.vx > 0) p.x = plat.x - p.radius;212  if (p.vx < 0) p.x = plat.x + plat.w + p.radius;213  p.vx = 0;214  } else {215  // Chocar verticalmente216  if (p.vy > 0) { // Cayendo217  p.y = plat.y - p.radius;218  p.isGrounded = true;219  220  // Efecto de aplastamiento al aterrizar221  if (p.vy > 2) { // Solo si cae con fuerza222  p.scaleX = 1.4;223  p.scaleY = 0.6;224  }225  226  // Si es plataforma móvil, mover al jugador con ella227  if (plat.type === 'moving') {228  p.x += plat.dx;229  }230  } else if (p.vy < 0) { // Saltando y golpeando cabeza231  p.y = plat.y + plat.h + p.radius;232  }233  p.vy = 0;234  }235  }236  }237  }238 239  function updatePlatforms() {240  for (let plat of platforms) {241  if (plat.type === 'moving') {242  plat.x += plat.dx;243  // Rebotar entre límites244  if (plat.x > plat.maxX || plat.x < plat.minX) {245  plat.dx *= -1;246  }247  }248  }249  }250 251  function drawPlatforms() {252  for (let plat of platforms) {253  ctx.fillStyle = plat.type === 'moving' ? '#FFD700' : '#654321';254  // Bordes verdes si es suelo estático255  if(plat.type === 'static') ctx.strokeStyle = '#2E8B57';256  else ctx.strokeStyle = '#DAA520';257  258  ctx.lineWidth = 3;259  ctx.fillRect(plat.x, plat.y, plat.w, plat.h);260  ctx.strokeRect(plat.x, plat.y, plat.w, plat.h);261  }262  }263 264  // --- GESTIÓN DEL JUEGO ---265  function playerDie() {266  player.lives--;267  livesDisplay.innerText = player.lives;268  269  if (player.lives > 0) {270  // Respawn271  player.x = 100;272  player.y = 100;273  player.vx = 0;274  player.vy = 0;275  cameraX = 0; // Resetear cámara276  } else {277  gameRunning = false;278  statusText.innerText = "¡TE HAS QUEDADO SIN VIDAS!";279  gameOverScreen.style.display = 'flex';280  }281  }282 283  function resetGame(fullReset = false) {284  if (fullReset) {285  player.lives = 3;286  livesDisplay.innerText = 3;287  }288  player.x = 100;289  player.y = 100;290  player.vx = 0;291  player.vy = 0;292  cameraX = 0;293  gameRunning = true;294  gameOverScreen.style.display = 'none';295  initLevel();296  loop();297  }298 299  // --- BUCLE PRINCIPAL ---300  function loop() {301  if (!gameRunning) return;302 303  ctx.clearRect(0, 0, canvas.width, canvas.height);304 305  // Actualizar lógica306  player.update();307  updatePlatforms();308 309  // Actualizar cámara (seguimiento suave)310  // Mantiene al jugador en el tercio izquierdo de la pantalla311  let targetCamX = player.x - 200;312  // Limitar cámara para que no vaya hacia atrás del inicio313  if (targetCamX < 0) targetCamX = 0;314  cameraX += (targetCamX - cameraX) * 0.1;315 316  // Dibujar mundo relativo a la cámara317  ctx.save();318  ctx.translate(-cameraX, 0);319 320  drawPlatforms();321  player.draw();322 323  // Condición de victoria simple (llegar al final)324  if (player.x > 1400) {325  gameRunning = false;326  statusText.innerText = "¡HAS GANADO!";327  gameOverScreen.style.display = 'flex';328  }329 330  ctx.restore();331 332  requestAnimationFrame(loop);333  }334 335  // --- CONTROLES ---336  window.addEventListener('keydown', e => {337  if(e.code === 'ArrowRight') keys.ArrowRight = true;338  if(e.code === 'ArrowLeft') keys.ArrowLeft = true;339  if(e.code === 'ArrowUp') keys.ArrowUp = true;340  });341 342  window.addEventListener('keyup', e => {343  if(e.code === 'ArrowRight') keys.ArrowRight = false;344  if(e.code === 'ArrowLeft') keys.ArrowLeft = false;345  if(e.code === 'ArrowUp') keys.ArrowUp = false;346  });347 348  // Iniciar349  initLevel();350  loop();351 352 </script>353 </body>354 </html>
Enlace
El enlace para compartir es: