Flujo Gravitacional: Juego de Evasión de Vuelo Basado en Física
De Wikiprompt, la enciclopedia libre de prompts
Flujo Gravitacional: Juego de Evasión de Vuelo Basado en Física Un prompt detallado para generar un juego HTML de un solo archivo con jugabilidad basada en física, obstáculos Bezier procedimentales, síntesis de Web Audio y una estética pastel zen.
Contenido del PromptGuardar
🌐
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gravity Flux</title>
<style>
body {
margin: 0;
overflow: hidden;
background: #1a1a2e;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
canvas {
display: block;
margin: 0 auto;
background: #1a1a2e;
}
#ui {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: #e0e0e0;
font-size: 18px;
text-align: center;
pointer-events: none;
text-shadow: 0 0 10px rgba(0,0,0,0.5);
}
#score {
font-size: 24px;
font-weight: bold;
color: #f0e6d3;
}
#instructions {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
color: #b0a8a0;
font-size: 14px;
text-align: center;
pointer-events: none;
opacity: 0.7;
}
</style>
</head>
<body>
<div id="ui">
<div>Gravity Flux</div>
<div id="score">0</div>
</div>
<div id="instructions">Click or tap to start - Navigate through the gates</div>
<canvas id="gameCanvas"></canvas>
<script>
// Audio context and synthesis
let audioCtx = null;
let pulseInterval = null;
let isPlaying = false;
function initAudio() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
}
function playPulse() {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(80 + Math.random() * 40, audioCtx.currentTime);
gain.gain.setValueAtTime(0.08, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.3);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.3);
}
function playSweep() {
if (!audioCtx) return;
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(200, audioCtx.currentTime);
osc.frequency.exponentialRampToValueAtTime(600, audioCtx.currentTime + 0.2);
gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + 0.3);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + 0.3);
}
function startPulse() {
if (pulseInterval) clearInterval(pulseInterval);
pulseInterval = setInterval(() => {
if (isPlaying) playPulse();
}, 800);
}
// Canvas setup
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
let W, H;
function resizeCanvas() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
}
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// Game state
let score = 0;
let gameOver = false;
let gameStarted = false;
// Player (soft-body sphere)
const player = {
x: W / 2,
y: H / 2,
radius: 20,
vx: 0,
vy: 0,
targetRadius: 20,
currentRadius: 20,
inflation: 0,
// Soft body deformation
deformX: 0,
deformY: 0,
deformSpeed: 0.02
};
// Gates
let gates = [];
const GATE_WIDTH = 120;
const GATE_HEIGHT_MIN = 80;
const GATE_HEIGHT_MAX = 180;
const GATE_SPEED = 1.2;
const GATE_SPAWN_INTERVAL = 120; // frames
let frameCount = 0;
// Gate class with Bezier curves
class Gate {
constructor(x, y, height, direction) {
this.x = x;
this.y = y;
this.height = height;
this.direction = direction; // 1 for down, -1 for up
this.passed = false;
this.time = Math.random() * Math.PI * 2;
this.morphSpeed = 0.02 + Math.random() * 0.02;
this.amplitude = 15 + Math.random() * 20;
this.phase = Math.random() * Math.PI * 2;
}
update() {
this.x -= GATE_SPEED;
this.time += this.morphSpeed;
// Organic morphing using sine waves
const morph = Math.sin(this.time) * this.amplitude;
this.currentHeight = this.height + morph;
// Ensure minimum height
if (this.currentHeight < 40) this.currentHeight = 40;
}
draw(ctx) {
const halfH = this.currentHeight / 2;
const centerY = this.y;
const topY = centerY - halfH;
const bottomY = centerY + halfH;
const leftX = this.x - GATE_WIDTH / 2;
const rightX = this.x + GATE_WIDTH / 2;
// Draw organic Bezier curves for the gate boundaries
ctx.save();
ctx.shadowColor = 'rgba(200, 180, 160, 0.3)';
ctx.shadowBlur = 20;
ctx.strokeStyle = '#d4c5b0';
ctx.lineWidth = 3;
ctx.globalAlpha = 0.8;
// Top curve (upper boundary)
ctx.beginPath();
ctx.moveTo(leftX, topY);
const cp1x = leftX + GATE_WIDTH * 0.3;
const cp1y = topY - Math.sin(this.time * 0.5 + this.phase) * 10;
const cp2x = rightX - GATE_WIDTH * 0.3;
const cp2y = topY + Math.cos(this.time * 0.7 + this.phase) * 10;
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, rightX, topY);
ctx.stroke();
// Bottom curve (lower boundary)
ctx.beginPath();
ctx.moveTo(leftX, bottomY);
const cp3x = leftX + GATE_WIDTH * 0.3;
const cp3y = bottomY + Math.sin(this.time * 0.6 + this.phase + 1) * 10;
const cp4x = rightX - GATE_WIDTH * 0.3;
const cp4y = bottomY - Math.cos(this.time * 0.8 + this.phase + 1) * 10;
ctx.bezierCurveTo(cp3x, cp3y, cp4x, cp4y, rightX, bottomY);
ctx.stroke();
// Side connectors (organic)
ctx.beginPath();
ctx.moveTo(leftX, topY);
ctx.lineTo(leftX, bottomY);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(rightX, topY);
ctx.lineTo(rightX, bottomY);
ctx.stroke();
ctx.restore();
// Store gate bounds for collision (simplified as rectangle with morph)
this.topBound = topY;
this.bottomBound = bottomY;
this.leftBound = leftX;
this.rightBound = rightX;
}
isOffScreen() {
return this.x + GATE_WIDTH / 2 < 0;
}
checkCollision(px, py, pr) {
// Check if player is within gate horizontal bounds
if (px + pr > this.leftBound && px - pr < this.rightBound) {
// Check vertical bounds
if (py - pr < this.topBound || py + pr > this.bottomBound) {
return true; // collision with gate boundary
}
}
return false;
}
checkPassed(px, pr) {
if (!this.passed && px - pr > this.rightBound) {
this.passed = true;
return true;
}
return false;
}
}
// Initialize game
function initGame() {
score = 0;
gameOver = false;
gameStarted = true;
gates = [];
frameCount = 0;
player.x = W / 2;
player.y = H / 2;
player.vx = 0;
player.vy = 0;
player.currentRadius = 20;
player.targetRadius = 20;
player.inflation = 0;
document.getElementById('score').textContent = '0';
// Start audio pulse
if (audioCtx) {
isPlaying = true;
startPulse();
}
}
// Spawn a new gate
function spawnGate() {
const height = GATE_HEIGHT_MIN + Math.random() * (GATE_HEIGHT_MAX - GATE_HEIGHT_MIN);
const y = 100 + Math.random() * (H - 200);
const direction = Math.random() > 0.5 ? 1 : -1;
gates.push(new Gate(W + 50, y, height, direction));
}
// Update player physics (soft body)
function updatePlayer() {
// Gravity (gentle)
player.vy += 0.05;
// Damping
player.vx *= 0.98;
player.vy *= 0.98;
// Update position
player.x += player.vx;
player.y += player.vy;
// Soft body inflation/deflation based on vertical speed
const speed = Math.abs(player.vy);
player.targetRadius = 20 + speed * 0.5;
if (player.targetRadius > 35) player.targetRadius = 35;
if (player.targetRadius < 15) player.targetRadius = 15;
// Smooth radius transition
player.currentRadius += (player.targetRadius - player.currentRadius) * 0.1;
// Deformation based on movement
player.deformX = Math.sin(Date.now() * 0.003) * 2;
player.deformY = Math.cos(Date.now() * 0.002) * 2;
// Keep player on screen (with soft boundaries)
if (player.y - player.currentRadius < 10) {
player.y = player.currentRadius + 10;
player.vy = 0;
}
if (player.y + player.currentRadius > H - 10) {
player.y = H - player.currentRadius - 10;
player.vy = 0;
}
if (player.x - player.currentRadius < 10) {
player.x = player.currentRadius + 10;
player.vx = 0;
}
if (player.x + player.currentRadius > W - 10) {
player.x = W - player.currentRadius - 10;
player.vx = 0;
}
}
// Draw player as soft-body sphere
function drawPlayer() {
ctx.save();
ctx.shadowColor = 'rgba(200, 180, 160, 0.4)';
ctx.shadowBlur = 30;
// Draw soft body with deformation
const r = player.currentRadius;
const deformX = player.deformX;
const deformY = player.deformY;
// Create gradient for soft look
const gradient = ctx.createRadialGradient(
player.x - r * 0.3, player.y - r * 0.3, r * 0.1,
player.x, player.y, r
);
gradient.addColorStop(0, '#f0e6d3');
gradient.addColorStop(0.5, '#d4c5b0');
gradient.addColorStop(1, '#b0a090');
ctx.beginPath();
// Draw deformed circle using bezier curves
const points = 12;
const angleStep = (Math.PI * 2) / points;
for (let i = 0; i <= points; i++) {
const angle = i * angleStep;
const rad = r + Math.sin(angle * 2 + Date.now() * 0.002) * 3 + Math.cos(angle * 3 + Date.now() * 0.001) * 2;
const x = player.x + Math.cos(angle) * rad + deformX;
const y = player.y + Math.sin(angle) * rad + deformY;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.fillStyle = gradient;
ctx.fill();
ctx.strokeStyle = '#c0b0a0';
ctx.lineWidth = 1.5;
ctx.stroke();
ctx.restore();
}
// Draw background elements (ambient)
function drawBackground() {
ctx.save();
ctx.globalAlpha = 0.1;
ctx.fillStyle = '#d4c5b0';
// Draw subtle floating particles
for (let i = 0; i < 20; i++) {
const x = (Math.sin(Date.now() * 0.0005 + i * 100) * 0.5 + 0.5) * W;
const y = (Math.cos(Date.now() * 0.0003 + i * 50) * 0.5 + 0.5) * H;
const size = 2 + Math.sin(Date.now() * 0.001 + i) * 1;
ctx.beginPath();
ctx.arc(x, y, size, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
// Game loop
function gameLoop() {
if (!gameStarted) {
// Draw start screen
ctx.clearRect(0, 0, W, H);
drawBackground();
ctx.fillStyle = '#f0e6d3';
ctx.font = '48px Segoe UI, sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Gravity Flux', W/2, H/2 - 40);
ctx.font = '20px Segoe UI, sans-serif';
ctx.fillText('Click or tap to start', W/2, H/2 + 20);
requestAnimationFrame(gameLoop);
return;
}
if (gameOver) {
// Draw game over screen
ctx.clearRect(0, 0, W, H);
drawBackground();
ctx.fillStyle = '#f0e6d3';
ctx.font = '48px Segoe UI, sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Game Over', W/2, H/2 - 40);
ctx.font = '24px Segoe UI, sans-serif';
ctx.fillText('Score: ' + score, W/2, H/2 + 10);
ctx.font = '18px Segoe UI, sans-serif';
ctx.fillText('Click to restart', W/2, H/2 + 50);
requestAnimationFrame(gameLoop);
return;
}
// Update
frameCount++;
if (frameCount % GATE_SPAWN_INTERVAL === 0) {
spawnGate();
}
// Update gates
for (let i = gates.length - 1; i >= 0; i--) {
gates[i].update();
if (gates[i].isOffScreen()) {
gates.splice(i, 1);
}
}
// Update player
updatePlayer();
// Check collisions and scoring
for (let i = 0; i < gates.length; i++) {
const gate = gates[i];
// Collision check
if (gate.checkCollision(player.x, player.y, player.currentRadius)) {
gameOver = true;
isPlaying = false;
if (pulseInterval) clearInterval(pulseInterval);
break;
}
// Score check
if (gate.checkPassed(player.x, player.currentRadius)) {
score++;
document.getElementById('score').textContent = score;
playSweep();
}
}
// Draw everything
ctx.clearRect(0, 0, W, H);
drawBackground();
// Draw gates
for (const gate of gates) {
gate.draw(ctx);
}
// Draw player
drawPlayer();
// Draw ambient glow
ctx.save();
ctx.shadowColor = 'rgba(200, 180, 160, 0.1)';
ctx.shadowBlur = 50;
ctx.fillStyle = 'transparent';
ctx.fillRect(0, 0, W, H);
ctx.restore();
requestAnimationFrame(gameLoop);
}
// Input handling
canvas.addEventListener('click', (e) => {
initAudio();
if (!gameStarted || gameOver) {
initGame();
return;
}
// Apply impulse to player (upward)
player.vy = -3;
player.vx += (e.clientX - player.x) * 0.02;
});
canvas.addEventListener('touchstart', (e) => {
e.preventDefault();
initAudio();
const touch = e.touches[0];
if (!gameStarted || gameOver) {
initGame();
return;
}
player.vy = -3;
player.vx += (touch.clientX - player.x) * 0.02;
});
// Keyboard support
document.addEventListener('keydown', (e) => {
if (e.key === ' ' || e.key === 'ArrowUp') {
e.preventDefault();
initAudio();
if (!gameStarted || gameOver) {
initGame();
return;
}
player.vy = -3;
}
if (e.key === 'ArrowLeft') {
player.vx = -2;
}
if (e.key === 'ArrowRight') {
player.vx = 2;
}
});
// Start game loop
gameLoop();
</script>
</body>
</html>
Iniciá sesión para ver el prompt completo
Continuar con:
Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad
Uso
Este prompt está diseñado para usarse con coding. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.
Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.
Discusión
0 comentarios