Aplicación de Cubo de Rubik 3D
De Wikiprompt, la enciclopedia libre de prompts
Aplicación de Cubo de Rubik 3D Un prompt completo para crear un cubo de Rubik 3D interactivo usando Three.js, con controles, interfaz de usuario y animaciones suaves.
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>3D Rubik's Cube</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #1a1a2e;
overflow: hidden;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#canvas-container {
width: 100vw;
height: 100vh;
}
.hud {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
z-index: 100;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 10px 20px;
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.btn {
padding: 10px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
}
.btn-scramble {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn-scramble:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(102, 126, 234, 0.4);
}
.btn-solve {
background: linear-gradient(135deg, #f093fb, #f5576c);
color: white;
}
.btn-solve:hover {
transform: translateY(-2px);
box-shadow: 0 5px 20px rgba(245, 87, 108, 0.4);
}
.move-counter {
position: fixed;
top: 20px;
right: 20px;
color: white;
font-size: 18px;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 10px 20px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
z-index: 100;
}
.title {
position: fixed;
top: 20px;
left: 20px;
color: white;
font-size: 24px;
font-weight: 700;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
z-index: 100;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
padding: 10px 20px;
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.2);
}
.hint {
position: fixed;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
z-index: 100;
text-align: center;
background: rgba(0, 0, 0, 0.3);
padding: 8px 16px;
border-radius: 6px;
}
@media (max-width: 768px) {
.hud {
flex-direction: column;
bottom: 10px;
padding: 8px;
}
.btn {
padding: 8px 16px;
font-size: 14px;
}
.title {
font-size: 18px;
top: 10px;
left: 10px;
}
.move-counter {
font-size: 14px;
top: 10px;
right: 10px;
}
}
</style>
</head>
<body>
<div id="canvas-container"></div>
<div class="title">🧊 Rubik's Cube</div>
<div class="move-counter">Moves: <span id="moveCount">0</span></div>
<div class="hint">Drag to rotate • Scroll to zoom • R to scramble • S to solve</div>
<div class="hud">
<button class="btn btn-scramble" onclick="scrambleCube()">Scramble</button>
<button class="btn btn-solve" onclick="solveCube()">Solve</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
<script>
// Scene setup
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(5, 5, 5);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
document.getElementById('canvas-container').appendChild(renderer.domElement);
// Controls
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.rotateSpeed = 0.5;
controls.minDistance = 4;
controls.maxDistance = 15;
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(5, 10, 7);
directionalLight.castShadow = true;
scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.3);
directionalLight2.position.set(-5, -3, -5);
scene.add(directionalLight2);
// Cube colors
const COLORS = {
white: 0xffffff,
yellow: 0xffff00,
red: 0xff0000,
orange: 0xff8c00,
blue: 0x0000ff,
green: 0x00ff00
};
// Create cube
const cubeGroup = new THREE.Group();
const cubies = [];
const gap = 0.05;
const size = 0.9;
// Face definitions: [color, axis, position]
const faces = [
{ color: COLORS.white, axis: 'y', position: 1 }, // Top
{ color: COLORS.yellow, axis: 'y', position: -1 }, // Bottom
{ color: COLORS.red, axis: 'x', position: 1 }, // Right
{ color: COLORS.orange, axis: 'x', position: -1 }, // Left
{ color: COLORS.blue, axis: 'z', position: 1 }, // Front
{ color: COLORS.green, axis: 'z', position: -1 } // Back
];
for (let x = -1; x <= 1; x++) {
for (let y = -1; y <= 1; y++) {
for (let z = -1; z <= 1; z++) {
const geometry = new THREE.BoxGeometry(size, size, size);
const materials = [];
// Create materials for each face
for (let i = 0; i < 6; i++) {
const face = faces[i];
let shouldColor = false;
if (face.axis === 'x' && x === face.position) shouldColor = true;
if (face.axis === 'y' && y === face.position) shouldColor = true;
if (face.axis === 'z' && z === face.position) shouldColor = true;
if (shouldColor) {
materials.push(new THREE.MeshStandardMaterial({
color: face.color,
roughness: 0.3,
metalness: 0.1
}));
} else {
materials.push(new THREE.MeshStandardMaterial({
color: 0x1a1a2e,
roughness: 0.8,
metalness: 0.2
}));
}
}
const cubie = new THREE.Mesh(geometry, materials);
cubie.position.set(x * (size + gap), y * (size + gap), z * (size + gap));
cubie.castShadow = true;
cubie.receiveShadow = true;
cubeGroup.add(cubie);
cubies.push(cubie);
}
}
}
scene.add(cubeGroup);
// Add subtle grid floor
const gridHelper = new THREE.GridHelper(10, 20, 0x444466, 0x333355);
gridHelper.position.y = -2.5;
scene.add(gridHelper);
// Animation state
let isAnimating = false;
let moveQueue = [];
let moveCount = 0;
let scrambleMoves = [];
// Move definitions
const MOVES = {
'U': { axis: 'y', layer: 1, angle: -Math.PI / 2 },
'D': { axis: 'y', layer: -1, angle: Math.PI / 2 },
'R': { axis: 'x', layer: 1, angle: -Math.PI / 2 },
'L': { axis: 'x', layer: -1, angle: Math.PI / 2 },
'F': { axis: 'z', layer: 1, angle: -Math.PI / 2 },
'B': { axis: 'z', layer: -1, angle: Math.PI / 2 },
'U\'': { axis: 'y', layer: 1, angle: Math.PI / 2 },
'D\'': { axis: 'y', layer: -1, angle: -Math.PI / 2 },
'R\'': { axis: 'x', layer: 1, angle: Math.PI / 2 },
'L\'': { axis: 'x', layer: -1, angle: -Math.PI / 2 },
'F\'': { axis: 'z', layer: 1, angle: Math.PI / 2 },
'B\'': { axis: 'z', layer: -1, angle: -Math.PI / 2 }
};
function getCubiesInLayer(axis, layer) {
const threshold = 0.1;
return cubies.filter(cubie => {
const pos = cubie.position;
if (axis === 'x') return Math.abs(pos.x - layer * (size + gap)) < threshold;
if (axis === 'y') return Math.abs(pos.y - layer * (size + gap)) < threshold;
if (axis === 'z') return Math.abs(pos.z - layer * (size + gap)) < threshold;
return false;
});
}
function animateMove(move, callback) {
if (isAnimating) {
moveQueue.push({ move, callback });
return;
}
isAnimating = true;
const moveDef = MOVES[move];
if (!moveDef) {
isAnimating = false;
if (callback) callback();
return;
}
const layerCubies = getCubiesInLayer(moveDef.axis, moveDef.layer);
const pivot = new THREE.Group();
scene.add(pivot);
layerCubies.forEach(cubie => {
pivot.attach(cubie);
});
const targetRotation = moveDef.angle;
const duration = 300;
const startTime = Date.now();
function animate() {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = 1 - Math.pow(1 - progress, 3);
if (moveDef.axis === 'x') pivot.rotation.x = targetRotation * easedProgress;
if (moveDef.axis === 'y') pivot.rotation.y = targetRotation * easedProgress;
if (moveDef.axis === 'z') pivot.rotation.z = targetRotation * easedProgress;
if (progress < 1) {
requestAnimationFrame(animate);
} else {
// Snap to exact rotation
if (moveDef.axis === 'x') pivot.rotation.x = targetRotation;
if (moveDef.axis === 'y') pivot.rotation.y = targetRotation;
if (moveDef.axis === 'z') pivot.rotation.z = targetRotation;
// Detach cubies from pivot
layerCubies.forEach(cubie => {
cubeGroup.attach(cubie);
});
scene.remove(pivot);
// Round positions to avoid floating point errors
layerCubies.forEach(cubie => {
cubie.position.x = Math.round(cubie.position.x * 10) / 10;
cubie.position.y = Math.round(cubie.position.y * 10) / 10;
cubie.position.z = Math.round(cubie.position.z * 10) / 10;
});
isAnimating = false;
moveCount++;
document.getElementById('moveCount').textContent = moveCount;
if (callback) callback();
// Process next move in queue
if (moveQueue.length > 0) {
const next = moveQueue.shift();
animateMove(next.move, next.callback);
}
}
}
animate();
}
function scrambleCube() {
if (isAnimating) return;
scrambleMoves = [];
const moves = Object.keys(MOVES);
const numMoves = Math.floor(Math.random() * 6) + 15; // 15-20 moves
for (let i = 0; i < numMoves; i++) {
const move = moves[Math.floor(Math.random() * moves.length)];
scrambleMoves.push(move);
animateMove(move);
}
}
function solveCube() {
if (isAnimating) return;
const reverseMoves = scrambleMoves.slice().reverse().map(move => {
return move.includes("'") ? move.replace("'", "") : move + "'";
});
reverseMoves.forEach(move => {
animateMove(move);
});
scrambleMoves = [];
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
if (e.key.toLowerCase() === 'r') scrambleCube();
if (e.key.toLowerCase() === 's') solveCube();
});
// Window resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Animation loop
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// Initial subtle rotation
cubeGroup.rotation.x = 0.5;
cubeGroup.rotation.y = 0.5;
</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.
Referencias
- Categoría: Prompts de coding
- Fuente: https://x.com/eyishazyer/status/2075850010000764951
Discusión
0 comentarios