Você pode criar um simulador de carro 3D simples c...

Criado em: 29 de maio de 2025

Respondido usando O3 por Chat01

Pergunta

Você pode criar um simulador de carro 3D simples com Three.js em um único arquivo HTML? Por favor, adicione nuvens, montanhas, uma estrada, algumas árvores e um trem circulando. E também as teclas de seta e WASD para controlar o carro. Faça a câmera acompanhar o carrinho em terceira pessoa. Coloque moedas para coletar. E coloque uma iA para jogar contra. Quem coletar 10 moedas primeiro ganha. Quando uma moeda for coletada, outra deve spawnar para repor. Quando houver colisão o carro deve explodir e retornar para o ponto inicial. Informe no centro da tela quando a iA coletar uma moeda. A tecla Espaço deve iniciar e reiniciar o jogo.

Pensamento

Designing a 3D car simulator

The user’s asking for a 3D car simulator with Three.js in a single HTML file. I need to include elements like clouds, mountains, a road, trees, and a circulating train. The car should be controlled with arrow keys and WASD, with third-person camera tracking. I’ll add collectible coins and an AI opponent that races for them. If a coin is collected, it spawns again. The game should reset the car if there’s a collision. I'll use a spacebar to start/restart the game.

Thought for 13 seconds

Resposta

Abaixo segue um arquivo HTML único que já carrega o Three.js via CDN e reúne todos os elementos que você pediu.
Basta salvar como index.html, abrir no navegador moderno e pressionar Espaço para começar / reiniciar:

html
<!DOCTYPE html> <html lang="pt-br"> <head> <meta charset="utf-8" /> <title>Mini Car 3D – Three.js</title> <style> html,body{margin:0;height:100%;overflow:hidden;background:#87ceeb;font-family:sans-serif} #ui{ position:fixed;top:0;left:0;right:0;text-align:center; color:#fff;font-size:22px;text-shadow:1px 1px 3px #000;pointer-events:none } #centerMsg{font-size:28px;margin-top:8px;color:#ffe400} canvas{display:block} </style> </head> <body> <div id="ui"> <div id="score">Jogador 0 | IA 0</div> <div id="centerMsg"></div> <div style="font-size:14px;opacity:.7;margin-top:4px"> Setas / WASD = dirigir · Espaço = iniciar / reiniciar </div> </div> <script src="https://cdn.jsdelivr.net/npm/[email protected]/build/three.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/PointerLockControls.js"></script> <script> /* ----- variáveis globais ----- */ let scene, camera, renderer, clock, keys = {}; let player, ai, coins = [], train; let playerScore = 0, aiScore = 0, playing = false; const COINS_TO_WIN = 10; const uiScore = document.getElementById('score'); const centerMsg = document.getElementById('centerMsg'); /* ---------- setup básico ---------- */ init(); animate(); function init(){ scene = new THREE.Scene(); scene.fog = new THREE.Fog(0x87ceeb, 60, 400); camera = new THREE.PerspectiveCamera(60, innerWidth/innerHeight, 0.1, 1000); renderer = new THREE.WebGLRenderer({antialias:true}); renderer.setSize(innerWidth, innerHeight); document.body.appendChild(renderer.domElement); clock = new THREE.Clock(); /* luz */ const hemi = new THREE.HemisphereLight(0xffffff, 0x444444, 1.0); scene.add(hemi); const dir = new THREE.DirectionalLight(0xffffff, .8); dir.position.set(-30,60,-30); scene.add(dir); buildGround(); buildMountains(); buildTrees(); buildClouds(); buildRoad(); buildTrain(); buildCars(); spawnCoins(15); window.addEventListener('resize', () => { camera.aspect = innerWidth/innerHeight; camera.updateProjectionMatrix(); renderer.setSize(innerWidth, innerHeight); }); /* controls */ document.addEventListener('keydown', (e)=>{keys[e.code]=true; if(e.code==='Space') resetGame(); }); document.addEventListener('keyup', (e)=>{keys[e.code]=false;}); } /* ---------- construção do mundo ---------- */ function buildGround(){ const geo = new THREE.PlaneGeometry(1000,1000,1,1); const mat = new THREE.MeshLambertMaterial({color:0x4db14d}); const ground = new THREE.Mesh(geo,mat); ground.rotation.x = -Math.PI/2; scene.add(ground); } function buildMountains(){ const mtMat = new THREE.MeshLambertMaterial({color:0x888888}); for(let i=0;i<20;i++){ const h = THREE.MathUtils.randFloat(30,60); const geo = new THREE.ConeGeometry(h*0.7,h,6); const mesh = new THREE.Mesh(geo,mtMat); const r = 200 + Math.random()*200; const angle = Math.random()*Math.PI*2; mesh.position.set(Math.cos(angle)*r,h/2,Math.sin(angle)*r); mesh.rotation.y = Math.random()*Math.PI; scene.add(mesh); } } function buildTrees(){ const trunkMat = new THREE.MeshLambertMaterial({color:0x8b5a2b}); const leafMat = new THREE.MeshLambertMaterial({color:0x1f7a28}); for(let i=0;i<50;i++){ const group = new THREE.Group(); const trunk = new THREE.Mesh(new THREE.CylinderGeometry(.7,.7,6,6),trunkMat); trunk.position.y = 3; const leaves = new THREE.Mesh(new THREE.ConeGeometry(2.5,6,8),leafMat); leaves.position.y = 8; group.add(trunk,leaves); const r = 140 + Math.random()*160; const angle = Math.random()*Math.PI*2; group.position.set(Math.cos(angle)*r,0,Math.sin(angle)*r); scene.add(group); } } function buildClouds(){ const cloudMat = new THREE.MeshLambertMaterial({color:0xffffff}); for(let i=0;i<25;i++){ const parts = new THREE.Group(); const base = new THREE.Mesh(new THREE.SphereGeometry(4,8,6),cloudMat); parts.add(base); for(let j=0;j<2;j++){ const puff = new THREE.Mesh(new THREE.SphereGeometry(3,8,6),cloudMat); puff.position.set(THREE.MathUtils.randFloatSpread(5),THREE.MathUtils.randFloatSpread(2),THREE.MathUtils.randFloatSpread(2)); parts.add(puff); } parts.position.set(THREE.MathUtils.randFloatSpread(400), 60+Math.random()*40, THREE.MathUtils.randFloatSpread(400)); scene.add(parts); } } function buildRoad(){ const roadMat = new THREE.MeshLambertMaterial({color:0x333333}); const road = new THREE.Mesh(new THREE.PlaneGeometry(600,12),roadMat); road.rotation.x = -Math.PI/2; scene.add(road); } function buildTrain(){ const railMat = new THREE.MeshLambertMaterial({color:0x554433}); const track = new THREE.TorusGeometry(80,0.4,8,100); scene.add(new THREE.Mesh(track,railMat)); train = new THREE.Group(); const carGeo = new THREE.BoxGeometry(6,4,4); const carMat = new THREE.MeshLambertMaterial({color:0xff0000}); for(let i=0;i<3;i++){ const wagon = new THREE.Mesh(carGeo,carMat); wagon.position.x = i*7; train.add(wagon); } train.position.y = 2; scene.add(train); } /* ---------- carros ---------- */ function buildCars(){ const carGeo = new THREE.BoxGeometry(4,2,2.5); const carMatP = new THREE.MeshLambertMaterial({color:0x2196f3}); const carMatAI= new THREE.MeshLambertMaterial({color:0xffc107}); player = new THREE.Mesh(carGeo,carMatP); ai = new THREE.Mesh(carGeo,carMatAI); resetCar(player,true); resetCar(ai,false); scene.add(player,ai); } function resetCar(car,isPlayer){ car.position.set(isPlayer? -20:20 ,1.1,0); car.rotation.set(0,isPlayer?0:Math.PI,0); car.userData.vel = new THREE.Vector3(); car.visible = true; } /* ---------- moedas ---------- */ function spawnCoins(n){ const coinGeo = new THREE.CylinderGeometry(1,1,0.4,16); const coinMat = new THREE.MeshLambertMaterial({color:0xffd700,emissive:0x604000}); for(let i=0;i<n;i++){ const coin = new THREE.Mesh(coinGeo,coinMat); coin.rotation.x = Math.PI/2; repositionCoin(coin); scene.add(coin); coins.push(coin); } } function repositionCoin(coin){ coin.position.set(THREE.MathUtils.randFloat(-280,280),1.5,THREE.MathUtils.randFloat(-40,40)); } /* ---------- lógica principal ---------- */ function resetGame(){ playerScore = aiScore = 0; updateScore(); resetCar(player,true); resetCar(ai,false); coins.forEach(repositionCoin); centerMsg.textContent = ''; playing = true; } function updateScore(){ uiScore.textContent = `Jogador ${playerScore} | IA ${aiScore}`; } function animate(){ requestAnimationFrame(animate); const dt = clock.getDelta(); if(playing){ updatePlayer(dt); updateAI(dt); updateTrain(dt); checkCoinCollisions(); checkCarCollision(); if(playerScore>=COINS_TO_WIN || aiScore>=COINS_TO_WIN){ playing = false; centerMsg.textContent = playerScore>aiScore? 'Você venceu!':'A IA venceu!'; } } /* câmera em terceira pessoa */ const camOffset = new THREE.Vector3(0,6,-10).applyQuaternion(player.quaternion); camera.position.copy(player.position).add(camOffset); camera.lookAt(player.position); renderer.render(scene,camera); } /* ---------------- updates ---------------- */ function updatePlayer(dt){ const acc = 25, maxSpd = 30, turn = 2.2; const vel = player.userData.vel; /* aceleração frontal/traseira */ const forward = (keys['KeyW']||keys['ArrowUp']) ? 1 : (keys['KeyS']||keys['ArrowDown']) ? -1 : 0; if(forward!==0){ vel.z = THREE.MathUtils.clamp(vel.z - forward*acc*dt, -maxSpd, maxSpd); }else{ vel.z *= 0.94; // arrasto } /* rotação */ if(keys['KeyA']||keys['ArrowLeft']) player.rotation.y += turn*dt; if(keys['KeyD']||keys['ArrowRight']) player.rotation.y -= turn*dt; /* aplicar movimento */ const dir = new THREE.Vector3(0,0,vel.z*dt).applyEuler(player.rotation); player.position.add(dir); } function updateAI(dt){ /* estratégia simples: sempre dirigir para a moeda mais próxima */ let target = null, bestDist = 1e9; coins.forEach(c=>{ const d = c.position.distanceTo(ai.position); if(d<bestDist){bestDist=d;target=c;} }); if(!target) return; const desiredDir = target.position.clone().sub(ai.position).normalize(); const forward = new THREE.Vector3(0,0,-1).applyEuler(ai.rotation); const turn = forward.clone().cross(desiredDir).y; ai.rotation.y -= THREE.MathUtils.clamp(turn,-1,1)*1.8*dt; ai.userData.vel = ai.userData.vel || new THREE.Vector3(); ai.userData.vel.z = -20; // velocidade constante const move = new THREE.Vector3(0,0,ai.userData.vel.z*dt).applyEuler(ai.rotation); ai.position.add(move); } function updateTrain(dt){ train.userData.t = (train.userData.t||0) + dt*0.25; const ang = train.userData.t*Math.PI*2; train.position.set(Math.cos(ang)*80,2,Math.sin(ang)*80); train.rotation.y = -ang; } function checkCoinCollisions(){ coins.forEach(c=>{ if(c.position.distanceTo(player.position)<3){ playerScore++; updateScore(); repositionCoin(c); }else if(c.position.distanceTo(ai.position)<3){ aiScore++; updateScore(); centerMsg.textContent = `IA coletou uma moeda!`; setTimeout(()=>{centerMsg.textContent='';},800); repositionCoin(c); } }); } function checkCarCollision(){ if(player.position.distanceTo(ai.position)<3){ explode(player); explode(ai); setTimeout(()=>{ resetCar(player,true); resetCar(ai,false); },1500); } } function explode(car){ car.visible=false; const boom = new THREE.Mesh(new THREE.SphereGeometry(5,8,6),new THREE.MeshBasicMaterial({color:0xff3300})); boom.position.copy(car.position); scene.add(boom); setTimeout(()=>scene.remove(boom),800); } </script> </body> </html>

Como funciona

  • Setas ou WASD movimentam o carrinho azul do jogador.
  • A câmera segue em terceira pessoa (logo atrás e acima).
  • A IA (amarelo) corre automaticamente atrás da moeda mais próxima.
  • Ao tocar em uma moeda, ela é recolocada noutro ponto na estrada; ganha quem chegar a 10.
  • Colisão entre carros gera uma “explosão” simples e reinicia a posição de ambos.
  • Espaço inicia ou reinicia a partida.

O código é propositalmente enxuto para caber em um único arquivo, mas serve como base: você pode trocar geometrias por modelos GLTF, adicionar partículas de explosão, melhorar inteligência da IA, HUD, sons etc. Divirta-se! 🎮

Compartilhar esta Q&A