Aplicativo de Cubo Mágico 3D

De Wikiprompt, a enciclopédia livre de prompts

Eyisha Zyer
Contribuído porEyisha ZyerXFonte

11 de jul. de 2026

Aplicativo de Cubo Mágico 3D Um prompt abrangente para criar um Cubo Mágico 3D interativo usando Three.js, com controles, interface do usuário e animações suaves.

Conteúdo do PromptSalvar

🌐
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Rubik's Cube 3D - Interactive</title> <style> body { margin: 0; overflow: hidden; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #111; color: white; } #info { position: absolute; top: 20px; left: 20px; background: rgba(20, 20, 20, 0.8); backdrop-filter: blur(8px); padding: 12px 24px; border-radius: 30px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 20px rgba(0,0,0,0.5); font-size: 14px; letter-spacing: 1px; z-index: 10; pointer-events: none; } #move-counter { position: absolute; top: 20px; right: 20px; background: rgba(20, 20, 20, 0.8); backdrop-filter: blur(8px); padding: 12px 24px; border-radius: 30px; border: 1px solid rgba(255,255,255,0.2); box-shadow: 0 4px 20px rgba(0,0,0,0.5); font-size: 16px; font-weight: 600; z-index: 10; pointer-events: none; color: #ffaa00; } .btn-container { position: absolute; bottom: 30px; left: 0; right: 0; display: flex; justify-content: space-between; padding: 0 30px; z-index: 20; pointer-events: none; } .btn { pointer-events: auto; background: rgba(30, 30, 40, 0.85); backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.25); color: white; padding: 14px 32px; border-radius: 50px; font-size: 18px; font-weight: 600; letter-spacing: 1px; cursor: pointer; transition: all 0.2s ease; box-shadow: 0 8px 30px rgba(0,0,0,0.6); text-transform: uppercase; border: 1px solid rgba(255,255,255,0.2); } .btn:hover { background: rgba(50, 50, 70, 0.9); transform: scale(1.05); border-color: rgba(255,255,255,0.5); } .btn:active { transform: scale(0.95); } #scramble-btn { background: rgba(255, 100, 80, 0.2); border-color: rgba(255, 120, 100, 0.5); } #scramble-btn:hover { background: rgba(255, 100, 80, 0.4); } #solve-btn { background: rgba(80, 200, 120, 0.2); border-color: rgba(100, 255, 150, 0.5); } #solve-btn:hover { background: rgba(80, 200, 120, 0.4); } #hint { position: absolute; bottom: 100px; left: 50%; transform: translateX(-50%); background: rgba(0,0,0,0.5); backdrop-filter: blur(4px); padding: 8px 20px; border-radius: 20px; font-size: 14px; color: #ccc; border: 1px solid rgba(255,255,255,0.1); z-index: 5; white-space: nowrap; } @media (max-width: 600px) { .btn { padding: 10px 20px; font-size: 14px; } #hint { display: none; } } </style> </head> <body> <div id="info">🧊 RUBIK'S CUBE 3D</div> <div id="move-counter">Moves: 0</div> <div class="btn-container"> <button id="scramble-btn" class="btn">🔀 Scramble</button> <button id="solve-btn" class="btn">✨ Solve</button> </div> <div id="hint">Drag to rotate • Scroll to zoom • R = scramble • S = solve</div> <!-- Import Three.js and OrbitControls from CDN --> <script type="importmap"> { "imports": { "three": "https://unpkg.com/three@0.128.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.128.0/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; // --- Setup Scene, Camera, Renderer --- const scene = new THREE.Scene(); scene.background = new THREE.Color(0x111122); // Add subtle fog for depth scene.fog = new THREE.Fog(0x111122, 10, 30); 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 }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; renderer.setPixelRatio(window.devicePixelRatio); document.body.appendChild(renderer.domElement); // --- Controls --- const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.dampingFactor = 0.05; controls.autoRotate = false; controls.enableZoom = true; controls.rotateSpeed = 1.0; controls.zoomSpeed = 1.2; controls.target.set(0, 0, 0); controls.maxPolarAngle = Math.PI / 2; // --- Lighting --- // Ambient light for base illumination const ambientLight = new THREE.AmbientLight(0x404060); scene.add(ambientLight); // Main directional light with shadows const dirLight = new THREE.DirectionalLight(0xffffff, 1); dirLight.position.set(3, 5, 2); dirLight.castShadow = true; dirLight.receiveShadow = true; scene.add(dirLight); // Fill light from opposite side const fillLight = new THREE.DirectionalLight(0x4466ff, 0.3); fillLight.position.set(-3, 1, -2); scene.add(fillLight); // Back light for rim const backLight = new THREE.DirectionalLight(0xffaa44, 0.2); backLight.position.set(0, 2, -5); scene.add(backLight); // Soft point light for overall glow const pointLight = new THREE.PointLight(0xffffff, 0.5, 10); pointLight.position.set(1, 3, 2); scene.add(pointLight); // --- Create the Rubik's Cube --- const cubeGroup = new THREE.Group(); scene.add(cubeGroup); // Colors for faces: standard Rubik's colors const colors = { white: 0xffffff, yellow: 0xffdd00, red: 0xff3333, orange: 0xff8800, blue: 0x2266ff, green: 0x33cc33 }; // Size of each cubie const size = 0.95; const gap = 0.05; const totalSize = size + gap; // Materials for each face (order: right, left, up, down, front, back) // We'll create 6 materials per cubie, but only visible on the outer faces. // For simplicity, we'll create cubies with 6 colored materials, but set the inner faces to black/dark. // Actually, to make it look clean, we'll use a single material with an array for each face. // But for performance and simplicity, we'll create cubies with 6 materials and set the inner ones to dark. // However, a more elegant approach: use a BoxGeometry with 6 materials, and for inner faces use a dark color. // But since we want the classic look, we'll assign colors only to the outer faces. // We'll create 26 cubies (skip center? no, include all 27 for simplicity, but center is hidden anyway) // Actually, we need 26 visible cubies (center is hidden). We'll create all 27 and hide the center. const cubies = []; const cubeSize = 3; // Helper to create a cubie with proper face colors function createCubie(x, y, z) { // Determine which faces are exposed (on the outer layer) const isOuterX = (x === 1 || x === -1); const isOuterY = (y === 1 || y === -1); const isOuterZ = (z === 1 || z === -1); // Materials: order in Three.js BoxGeometry: +x, -x, +y, -y, +z, -z const materials = [ new THREE.MeshStandardMaterial({ color: isOuterX && x === 1 ? colors.red : 0x222222, roughness: 0.3, metalness: 0.1 }), // +x new THREE.MeshStandardMaterial({ color: isOuterX && x === -1 ? colors.orange : 0x222222, roughness: 0.3, metalness: 0.1 }), // -x new THREE.MeshStandardMaterial({ color: isOuterY && y === 1 ? colors.white : 0x222222, roughness: 0.3, metalness: 0.1 }), // +y new THREE.MeshStandardMaterial({ color: isOuterY && y === -1 ? colors.yellow : 0x222222, roughness: 0.3, metalness: 0.1 }), // -y new THREE.MeshStandardMaterial({ color: isOuterZ && z === 1 ? colors.green : 0x222222, roughness: 0.3, metalness: 0.1 }), // +z new THREE.MeshStandardMaterial({ color: isOuterZ && z === -1 ? colors.blue : 0x222222, roughness: 0.3, metalness: 0.1 }) // -z ]; const geometry = new THREE.BoxGeometry(size, size, size); const cubie = new THREE.Mesh(geometry, materials); // Position cubie.position.set(x * totalSize, y * totalSize, z * totalSize); // Enable shadows cubie.castShadow = true; cubie.receiveShadow = true; // Store original position for potential reset cubie.userData = { x, y, z }; return cubie; } // Create all cubies (including center, but we'll hide it) for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { for (let z = -1; z <= 1; z++) { // Skip the very center (0,0,0) - it's invisible anyway if (x === 0 && y === 0 && z === 0) continue; const cubie = createCubie(x, y, z); cubeGroup.add(cubie); cubies.push(cubie); } } } // Add a subtle wireframe or edge lines? Not necessary, but we can add a slight emissive glow. // --- Animation State --- let moveQueue = []; // Queue of moves to execute let isAnimating = false; let moveCount = 0; const moveCounterElement = document.getElementById('move-counter'); // Define move types: axis, layer, angle (90 deg) // We'll implement standard moves: U, D, L, R, F, B (and their inverses) // For simplicity, we'll just do random moves on axes with layer -1, 0, 1 // Function to update move counter function updateMoveCounter() { moveCounterElement.textContent = `Moves: ${moveCount}`; } // Function to get the cubies in a specific layer function getCubiesInLayer(axis, layer) { const result = []; const threshold = 0.1; cubies.forEach(cubie => { const pos = cubie.position; if (axis === 'x') { if (Math.abs(pos.x - layer * totalSize) < threshold) result.push(cubie); } else if (axis === 'y') { if (Math.abs(pos.y - layer * totalSize) < threshold) result.push(cubie); } else if (axis === 'z') { if (Math.abs(pos.z - layer * totalSize) < threshold) result.push(cubie); } }); return result; } // Function to rotate a layer around an axis function rotateLayer(axis, layer, angle, callback) { const cubiesInLayer = getCubiesInLayer(axis, layer); if (cubiesInLayer.length === 0) { if (callback) callback(); return; } // Create a pivot group const pivot = new THREE.Group(); scene.add(pivot); // Attach cubies to pivot cubiesInLayer.forEach(cubie => { pivot.attach(cubie); }); // Determine rotation axis let rotAxis; if (axis === 'x') rotAxis = new THREE.Vector3(1, 0, 0); else if (axis === 'y') rotAxis = new THREE.Vector3(0, 1, 0); else if (axis === 'z') rotAxis = new THREE.Vector3(0, 0, 1); // Animate rotation const duration = 300; // ms const startTime = performance.now(); const startRotation = pivot.rotation.clone(); const targetRotation = startRotation.clone(); // Add angle to the specific axis if (axis === 'x') targetRotation.x += angle; else if (axis === 'y') targetRotation.y += angle; else if (axis === 'z') targetRotation.z += angle; function animateRotation(time) { const elapsed = time - startTime; const progress = Math.min(elapsed / duration, 1); // Ease in-out const eased = progress < 0.5 ? 2 * progress * progress : 1 - Math.pow(-2 * progress + 2, 2) / 2; // Interpolate rotation pivot.rotation.x = startRotation.x + (targetRotation.x - startRotation.x) * eased; pivot.rotation.y = startRotation.y + (targetRotation.y - startRotation.y) * eased; pivot.rotation.z = startRotation.z + (targetRotation.z - startRotation.z) * eased; if (progress < 1) { requestAnimationFrame(animateRotation); } else { // Finalize: detach cubies from pivot and add back to cubeGroup cubiesInLayer.forEach(cubie => { cubeGroup.attach(cubie); }); scene.remove(pivot); // Snap rotations to nearest 90 deg to avoid drift cubiesInLayer.forEach(cubie => { cubie.rotation.x = Math.round(cubie.rotation.x / (Math.PI/2)) * (Math.PI/2); cubie.rotation.y = Math.round(cubie.rotation.y / (Math.PI/2)) * (Math.PI/2); cubie.rotation.z = Math.round(cubie.rotation.z / (Math.PI/2)) * (Math.PI/2); }); if (callback) callback(); } } requestAnimationFrame(animateRotation); } // Function to perform a random move function performRandomMove(callback) { const axes = ['x', 'y', 'z']; const layers = [-1, 0, 1]; const angles = [Math.PI/2, -Math.PI/2]; const axis = axes[Math.floor(Math.random() * axes.length)]; const layer = layers[Math.floor(Math.random() * layers.length)]; const angle = angles[Math.floor(Math.random() * angles.length)]; moveCount++; updateMoveCounter(); rotateLayer(axis, layer, angle, callback); } // Scramble function function scramble() { if (isAnimating) return; isAnimating = true; const numMoves = 15 + Math.floor(Math.random() * 6); // 15-20 let movesDone = 0; function doNextMove() { if (movesDone >= numMoves) { isAnimating = false; return; } movesDone++; performRandomMove(() => { // Small delay between moves for visual clarity setTimeout(doNextMove, 50); }); } doNextMove(); } // Solve function: reverse the scramble moves // Since we don't track the exact moves, we'll just do a reverse scramble (random moves) // But to actually solve, we need to reverse the exact moves. For simplicity, we'll just do a "reverse" scramble. // However, the requirement says "reverses the scramble moves automatically". // To do this properly, we need to store the moves. Let's implement that. // We'll store the moves performed during scramble. let moveHistory = []; // Override performRandomMove to record moves function performRandomMoveRecorded(callback) { const axes = ['x', 'y', 'z']; const layers = [-1, 0, 1]; const angles = [Math.PI/2, -Math.PI/2]; const axis = axes[Math.floor(Math.random() * axes.length)]; const layer = layers[Math.floor(Math.random() * layers.length)]; const angle = angles[Math.floor(Math.random() * angles.length)]; moveHistory.push({ axis, layer, angle }); moveCount++; updateMoveCounter(); rotateLayer(axis, layer, angle, callback); } // Scramble using recorded moves function scrambleRecorded() { if (isAnimating) return; isAnimating = true; moveHistory = []; const numMoves = 15 + Math.floor(Math.random() * 6); let movesDone = 0; function doNextMove() { if (movesDone >= numMoves) { isAnimating = false; return; } movesDone++; performRandomMoveRecorded(() => { setTimeout(doNextMove, 50); }); } doNextMove(); } // Solve function: reverse the moves function solve() { if (isAnimating) return; if (moveHistory.length === 0) { // If no history, just do nothing or maybe scramble? We'll just return. return; } isAnimating = true; // Reverse the moves const reverseMoves = moveHistory.slice().reverse().map(m => ({ axis: m.axis, layer: m.layer, angle: -m.angle })); // Clear history after solving moveHistory = []; let movesDone = 0; const totalMoves = reverseMoves.length; function doNextMove() { if (movesDone >= totalMoves) { isAnimating = false; moveCount = 0; updateMoveCounter(); return; } const move = reverseMoves[movesDone]; movesDone++; moveCount++; updateMoveCounter(); rotateLayer(move.axis, move.layer, move.angle, () => { setTimeout(doNextMove, 50); }); } doNextMove(); } // --- UI Event Listeners --- document.getElementById('scramble-btn').addEventListener('click', () => { if (!isAnimating) { // Reset move count moveCount = 0; updateMoveCounter(); scrambleRecorded(); } }); document.getElementById('solve-btn').addEventListener('click', () => { if (!isAnimating) { solve(); } }); // Keyboard shortcuts window.addEventListener('keydown', (e) => { if (e.key === 'r' || e.key === 'R') { if (!isAnimating) { moveCount = 0; updateMoveCounter(); scrambleRecorded(); } } if (e.key === 's' || e.key === 'S') { if (!isAnimating) { solve(); } } }); // --- Animation Loop --- function animate() { requestAnimationFrame(animate); // Update controls controls.update(); // Render renderer.render(scene, camera); } animate(); // --- Resize Handler --- window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }); // --- Initial move counter --- updateMoveCounter(); // --- Add a subtle starfield or background effect? Not necessary, but we can add some particles. // Optional: Add a few floating particles for ambiance const particleGeo = new THREE.BufferGeometry(); const particleCount = 500; const posArray = new Float32Array(particleCount * 3); for (let i = 0; i < particleCount * 3; i += 3) { posArray[i] = (Math.random() - 0.5) * 40; posArray[i+1] = (Math.random() - 0.5) * 40; posArray[i+2] = (Math.random() - 0.5) * 40; } particleGeo.setAttribute('position', new THREE.BufferAttribute(posArray, 3)); const particleMat = new THREE.PointsMaterial({ color: 0x88aaff, size: 0.05, transparent: true, opacity: 0.6 }); const particles = new THREE.Points(particleGeo, particleMat); scene.add(particles); // Add a subtle ground reflection? Not needed. // Add a small glow around the cube? Could use a point light. const glowLight = new THREE.PointLight(0x4466ff, 0.3, 8); glowLight.position.set(0, 0, 0); scene.add(glowLight); console.log('Rubik\'s Cube ready!'); </script> </body> </html>

Entre para ver o prompt completo

Continuar com:

Ao entrar, você concorda com nossos Termos de uso e Política de privacidade

Uso

Este prompt foi projetado para uso com coding. Copie o conteúdo acima e cole na sua ferramenta de IA preferida.

Para melhores resultados, personalize os marcadores (indicados por colchetes ou maiúsculas) com seus requisitos específicos.

Referências

Categorias:coding| twitter| 3d-cube| three-js

Discussão

0 comentários