Meta Description" name="description" />

Share this result

Previews are deleted daily. Get a permanent share link sent to your inbox:
Script
<!doctype html> <html lang="tr"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover" /> <title>Mobil Voxel Demo (Three.js)</title> <style> html,body { height:100%; margin:0; background:#000; -webkit-user-select:none; user-select:none; touch-action:none; } #game { position:fixed; inset:0; display:block; } /* Joystick */ .joystick { position:fixed; left:16px; bottom:16px; width:120px; height:120px; border-radius:50%; background: rgba(255,255,255,0.05); display:flex; align-items:center; justify-content:center; touch-action:none; z-index:20; } .thumb { width:50px;height:50px;border-radius:50%; background: rgba(255,255,255,0.14); transform: translate(0,0); transition: transform 0s; } /* Right controls */ .right-controls { position:fixed; right:12px; bottom:16px; display:flex; flex-direction:column; gap:12px; z-index:20; align-items:center; touch-action:none; } .btn { width:72px; height:72px; border-radius:14px; display:flex; align-items:center; justify-content:center; background: rgba(255,255,255,0.06); color: #fff; font-weight:700; -webkit-tap-highlight-color: transparent; user-select:none; } .btn:active { background: rgba(255,255,255,0.12); } /* top-right overlay for camera hint */ .hint { position:fixed; right:12px; top:12px; z-index:20; color:#fff; font-size:13px; background: rgba(0,0,0,0.3); padding:8px 10px; border-radius:8px; } /* small FPS/Help at top-left */ .status { position:fixed; left:12px; top:12px; z-index:20; color:#fff; font-size:13px; background: rgba(0,0,0,0.3); padding:6px 10px; border-radius:8px; } /* Ensure canvas covers the viewport */ canvas { width:100%; height:100%; display:block; } </style> </head> <body> <canvas id="game"></canvas> <!-- Joystick --> <div class="joystick" id="joystick" aria-hidden="true"> <div class="thumb" id="thumb"></div> </div> <!-- Right side buttons --> <div class="right-controls" id="rightControls" aria-hidden="true"> <div class="btn" id="jumpBtn">Zıpla</div> <div class="btn" id="placeBtn">Koy/Kır</div> </div> <div class="hint">Sağda sürükle: kamera</div> <div class="status" id="status">Hazır</div> <script src="https://unpkg.com/three@0.152.2/build/three.min.js"></script> <script> /* Mobil Voxel Demo - Sol alt: joystick (touch) - Sağ swipe: kamera döndürme - Sağ butonlar: zıplama, blok koy/kır (kısa bas: koy, uzun bas: kır) - Tek dosyada çalışır */ // ---------------------- Basic Three.js setup ---------------------- const canvas = document.getElementById('game'); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); renderer.setSize(window.innerWidth, window.innerHeight, false); renderer.outputEncoding = THREE.sRGBEncoding; const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); // sky const camera = new THREE.PerspectiveCamera(70, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.set(0, 4, 8); camera.lookAt(0,2,0); // Lights const ambient = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambient); const dir = new THREE.DirectionalLight(0xffffff, 0.7); dir.position.set(5,10,2); scene.add(dir); // Resize handling function onResize(){ renderer.setSize(window.innerWidth, window.innerHeight, false); camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); } window.addEventListener('resize', onResize); // ---------------------- Simple voxel world ---------------------- const blockSize = 1; const blocks = new Map(); // key: "x,y,z" -> mesh // Materials (simple) const mat = new THREE.MeshStandardMaterial({ color:0x8B5A2B }); // dirt const grassMat = new THREE.MeshStandardMaterial({ color:0x3cb043 }); // Create a small flat world (16x1x16 with a few random blocks) const worldRadius = 8; for(let x=-8;x<8;x++){ for(let z=-8;z<8;z++){ addBlock(x,0,z, Math.random()<0.02 ? 2 : 1); // sparse taller blocks occasionally // add a few blocks above as scenery } } for(let i=0;i<40;i++){ const rx = Math.floor((Math.random()*16)-8); const rz = Math.floor((Math.random()*16)-8); const ry = 1 + Math.floor(Math.random()*3); addBlock(rx,ry,rz,1); } function key(x,y,z){ return `${x},${y},${z}`; } function addBlock(x,y,z,type=1){ const k = key(x,y,z); if(blocks.has(k)) return; const geom = new THREE.BoxGeometry(blockSize,blockSize,blockSize); const m = new THREE.Mesh(geom, type===2 ? grassMat : mat); m.position.set(x*blockSize, y*blockSize + blockSize/2, z*blockSize); scene.add(m); blocks.set(k, {mesh:m, type}); } function removeBlock(x,y,z){ const k = key(x,y,z); const o = blocks.get(k); if(!o) return false; scene.remove(o.mesh); o.mesh.geometry.dispose(); // material reused so not disposing blocks.delete(k); return true; } // ---------------------- Player physics & control state ---------------------- const player = { pos: new THREE.Vector3(0,2,0), vel: new THREE.Vector3(0,0,0), speed: 3.0, // m/s onGround: false, height: 1.6 }; const GRAVITY = -20; const JUMP_SPEED = 7; let lastTime = performance.now()/1000; // Camera rotation state let yaw = 0; // horizontal let pitch = 0; // vertical const pitchLimit = Math.PI/2 - 0.1; // ---------------------- Joystick (left) ---------------------- const joystick = document.getElementById('joystick'); const thumb = document.getElementById('thumb'); let joystickActive = false; let joyId = null; let joyStart = { x:0, y:0 }; let joyVec = { x:0, y:0 }; // -1..1 function resetThumb(){ thumb.style.transform = `translate(0px,0px)`; } joystick.addEventListener('touchstart', e=>{ e.preventDefault(); const t = e.changedTouches[0]; joystickActive = true; joyId = t.identifier; joyStart.x = t.clientX; joyStart.y = t.clientY; }); window.addEventListener('touchmove', e=>{ if(!joystickActive) return; for(const t of e.changedTouches){ if(t.identifier!==joyId) continue; const dx = t.clientX - joyStart.x; const dy = t.clientY - joyStart.y; const max = 40; // pixels radius for full speed const nx = Math.max(-1, Math.min(1, dx/max)); const ny = Math.max(-1, Math.min(1, dy/max)); // Update thumb position (invert y because screen coords) thumb.style.transform = `translate(${nx*max}px, ${ny*max}px)`; joyVec.x = nx; joyVec.y = ny; } }, {passive:false}); window.addEventListener('touchend', e=>{ if(!joystickActive) return; for(const t of e.changedTouches){ if(t.identifier!==joyId) continue; joystickActive=false; joyId=null; joyVec.x = 0; joyVec.y = 0; resetThumb(); } }); // ---------------------- Right-side camera swipe ---------------------- let rightTouchId = null; let rightTouchStart = null; function isOnRightSide(x){ return x > window.innerWidth * 0.45; // right half-ish } window.addEventListener('touchstart', e=>{ for(const t of e.changedTouches){ if(isOnRightSide(t.clientX) && rightTouchId===null){ rightTouchId = t.identifier; rightTouchStart = { x: t.clientX, y: t.clientY }; } } }, {passive:true}); window.addEventListener('touchmove', e=>{ for(const t of e.changedTouches){ if(t.identifier!==rightTouchId) continue; const dx = t.clientX - rightTouchStart.x; const dy = t.clientY - rightTouchStart.y; rightTouchStart.x = t.clientX; rightTouchStart.y = t.clientY; // sensitivity tuned const sensX = 0.005; const sensY = 0.005; yaw -= dx * sensX; pitch -= dy * sensY; pitch = Math.max(-pitchLimit, Math.min(pitchLimit, pitch)); } }, {passive:true}); window.addEventListener('touchend', e=>{ for(const t of e.changedTouches){ if(t.identifier===rightTouchId){ rightTouchId = null; rightTouchStart = null; } } }, {passive:true}); // ---------------------- Buttons: Jump and Place/Break ---------------------- const jumpBtn = document.getElementById('jumpBtn'); const placeBtn = document.getElementById('placeBtn'); const status = document.getElementById('status'); jumpBtn.addEventListener('touchstart', e=>{ e.preventDefault(); if(player.onGround){ player.vel.y = JUMP_SPEED; player.onGround = false; } }, {passive:false}); // Place/Break: short tap = place block in front, long press > 500ms = break targeted block let placeTouchTimer = null; let placeTouchId = null; placeBtn.addEventListener('touchstart', e=>{ e.preventDefault(); const t = e.changedTouches[0]; placeTouchId = t.identifier; placeTouchTimer = setTimeout(()=>{ // long-press -> break placeTouchTimer = null; doBreak(); }, 500); }, {passive:false}); placeBtn.addEventListener('touchend', e=>{ e.preventDefault(); for(const t of e.changedTouches){ if(t.identifier!==placeTouchId) continue; if(placeTouchTimer){ clearTimeout(placeTouchTimer); placeTouchTimer = null; doPlace(); } placeTouchId = null; } }, {passive:false}); // ---------------------- Raycast helpers for place/break ---------------------- const raycaster = new THREE.Raycaster(); const tmpVec = new THREE.Vector3(); function worldCoordFromVec3(v){ // convert position to grid coordinate (integer block coords) const x = Math.round(v.x / blockSize); const y = Math.round((v.y - blockSize/2) / blockSize); const z = Math.round(v.z / blockSize); return {x,y,z}; } function getTargetBlock(){ // cast a ray from camera forward to detect closest block within 6m raycaster.set(camera.position, camera.getWorldDirection(tmpVec)); const intersects = raycaster.intersectObjects(Array.from(blocks.values()).map(b=>b.mesh), true); if(intersects.length>0){ const p = intersects[0].point; return worldCoordFromVec3(p); } return null; } function doPlace(){ // find a spot slightly in front of camera and place a block at integer position offset const dir = camera.getWorldDirection(tmpVec).clone(); const placePos = camera.position.clone().add(dir.multiplyScalar(2.2)); // ~2m ahead const coords = { x: Math.round(placePos.x / blockSize), y: Math.round((placePos.y - blockSize/2) / blockSize), z: Math.round(placePos.z / blockSize) }; // simple check: don't place inside player const dx = coords.x*blockSize - player.pos.x; const dz = coords.z*blockSize - player.pos.z; if(Math.abs(dx) < 0.9 && Math.abs(dz) < 0.9 && coords.y <= Math.round(player.pos.y)) { status.textContent = 'İçine blok koyma!'; setTimeout(()=> status.textContent='Hazır',800); return; } addBlock(coords.x, coords.y, coords.z, 1); status.textContent = `Koyuldu: ${coords.x},${coords.y},${coords.z}`; setTimeout(()=> status.textContent='Hazır',700); } function doBreak(){ const t = getTargetBlock(); if(!t){ status.textContent='Hedef bulunamadı'; setTimeout(()=> status.textContent='Hazır',700); return; } const ok = removeBlock(t.x, t.y, t.z); if(ok){ status.textContent = `Kırıldı: ${t.x},${t.y},${t.z}`; setTimeout(()=> status.textContent='Hazır',700); } else { status.textContent='Kırılamadı'; setTimeout(()=> status.textContent='Hazır',700); } } // ---------------------- Game loop ---------------------- function step(dt){ // Move according to joystick - relative to camera yaw const forward = new THREE.Vector3(Math.sin(yaw), 0, Math.cos(yaw)); // forward vector const right = new THREE.Vector3(Math.sin(yaw - Math.PI/2), 0, Math.cos(yaw - Math.PI/2)); const moveVec = new THREE.Vector3(); moveVec.addScaledVector(forward, -joyVec.y); // joystick y maps to forward/back moveVec.addScaledVector(right, joyVec.x); // joystick x maps to right/left if(moveVec.lengthSq() > 0.0001){ moveVec.normalize(); moveVec.multiplyScalar(player.speed); } // apply to horizontal velocity (simple) player.vel.x = moveVec.x; player.vel.z = moveVec.z; // gravity player.vel.y += GRAVITY * dt; // integrate player.pos.addScaledVector(player.vel, dt); // simple ground collision (ground at y = 0 blocks exist there) // Check block under player's feet const footX = Math.round(player.pos.x / blockSize); const footZ = Math.round(player.pos.z / blockSize); // find highest block at that x,z let highest = -Infinity; for(const k of blocks.keys()){ const [bx,by,bz] = k.split(',').map(Number); if(bx===footX && bz===footZ){ highest = Math.max(highest, by); } } const groundY = (highest === -Infinity) ? -1 : highest; // -1 means no block const groundWorldY = (groundY+1) * blockSize; // top of top block? but simpler use (groundY+1)*blockSize? // We'll assume player's feet should be at block top + small offset const minY = (groundY === -Infinity) ? 0 : (groundY+1)*blockSize + 0.0001; if(player.pos.y <= player.height/2 + minY){ player.pos.y = player.height/2 + minY; player.vel.y = 0; player.onGround = true; } else { player.onGround = false; } // update camera to follow player const camOffset = new THREE.Vector3(0, player.height-0.2, 0); const camPos = player.pos.clone().add(camOffset); // adjust camera behind player based on yaw/pitch const distance = 6; const cx = Math.cos(pitch) * Math.sin(yaw); const cz = Math.cos(pitch) * Math.cos(yaw); const cy = Math.sin(pitch); const behind = new THREE.Vector3(-cx, -cy, -cz).multiplyScalar(distance); camera.position.copy(camPos.clone().add(behind)); // look at player's head camera.lookAt(player.pos.x, player.pos.y + player.height - 0.2, player.pos.z); } // minimal render loop function animate(){ requestAnimationFrame(animate); const now = performance.now()/1000; let dt = Math.min(0.05, now - lastTime); lastTime = now; step(dt); renderer.render(scene, camera); } animate(); // ---------------------- Small helpers: tap to teleport / debug (optional) ---------------------- /* Double tap to reset view (for convenience) */ let lastTap = 0; window.addEventListener('touchend', (e)=>{ const t = performance.now(); if(t - lastTap < 300){ // double tap -> teleport forward (debug) const dir = camera.getWorldDirection(new THREE.Vector3()); player.pos.add(dir.multiplyScalar(2)); status.textContent='İleri atla (debug)'; setTimeout(()=>status.textContent='Hazır',700); } lastTap = t; }, {passive:true}); // Cleanup when page hidden (save battery) document.addEventListener('visibilitychange', () => { if(document.hidden){ // pause rendering? here we just lower pixel ratio renderer.setPixelRatio(1); } else { renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); } }); </script> </body> </html><!doctype html> <html lang="tr"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,viewport-fit=cover" /> <title>Mobil Voxel Demo (Three.js)</title> <style> html,body { height:100%; margin:0; background:#000; -webkit-user-select:none; user-select:none; touch-action:none; } #game { position:fixed; inset:0; display:block; } /* Joystick */ .joystick { position:fixed; left:16px; bottom:16px; width:120px; height:120px; border-radius:50%; background: rgba(255,255,255,0.05); display:flex; align-items:center; justify-content:center; touch-action:none; z-index:20; } .thumb { width:50px;height:50px;border-radius:50%; background: rgba(255,255,255,0.14); transform: translate(0,0); transition: transform 0s; } /* Right controls */ .right-controls { position:fixed; right:12px; bottom:16px; display:flex; flex-direction:column; gap:12px; z-index:20; align-items:center; touch-action:none; } .btn { width:72px; height:72px; border-radius:14px; display:flex; align-items:center; justify-content:center; background: rgba(255,255,255,0.06); color: #fff; font-weight:700; -webkit-tap-highlight-color: transparent; user-select:none; } .btn:active { background: rgba(255,255,255,0.12); } /* top-right overlay for camera hint */ .hint { position:fixed; right:12px; top:12px; z-index:20; color:#fff; font-size:13px; background: rgba(0,0,0,0.3); padding:8px 10px; border-radius:8px; } /* small FPS/Help at top-left */ .status { position:fixed; left:12px; top:12px; z-index:20; color:#fff; font-size:13px; background: rgba(0,0,0,0.3); padding:6px 10px; border-radius:8px; } /* Ensure canvas covers the viewport */ canvas { width:100%; height:100%; display:block; } </style> </head> <body> <canvas id="game"></canvas> <!-- Joystick --> <div class="joystick" id="joystick" aria-hidden="true"> <div class="thumb" id="thumb"></div> </div> <!-- Right side buttons --> <div class="right-controls" id="rightControls" aria-hidden="true"> <div class="btn" id="jumpBtn">Zıpla</div> <div class="btn" id="placeBtn">Koy/Kır</div> </div> <div class="hint">Sağda sürükle: kamera</div> <div class="status" id="status">Hazır</div> <script src="https://unpkg.com/three@0.152.2/build/three.min.js"></script> <script> /* Mobil Voxel Demo - Sol alt: joystick (touch) - Sağ swipe: kamera döndürme - Sağ butonlar: zıplama, blok koy/kır (kısa bas: koy, uzun bas: kır) - Tek dosyada çalışır */ // ---------------------- Basic Three.js setup ---------------------- const canvas = document.getElementById('game'); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); renderer.setSize(window.innerWidth, window.innerHeight, false); renderer.outputEncoding = THREE.sRGBEncoding; const scene = new THREE.Scene(); scene.background = new THREE.Color(0x87ceeb); // sky const camera = new THREE.PerspectiveCamera(70, window.innerWidth/window.innerHeight, 0.1, 1000); camera.position.set(0, 4, 8); camera.lookAt(0,2,0); // Lights const ambient = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambient); const dir = new THREE.DirectionalLight(0xffffff, 0.7); dir.position.set(5,10,2); scene.add(dir); // Resize handling function onResize(){ renderer.setSize(window.innerWidth, window.innerHeight, false); camera.aspect = window.innerWidth/window.innerHeight; camera.updateProjectionMatrix(); } window.addEventListener('resize', onResize); // ---------------------- Simple voxel world ---------------------- const blockSize = 1; const blocks = new Map(); // key: "x,y,z" -> mesh // Materials (simple) const mat = new THREE.MeshStandardMaterial({ color:0x8B5A2B }); // dirt const grassMat = new THREE.MeshStandardMaterial({ color:0x3cb043 }); // Create a small flat world (16x1x16 with a few random blocks) const worldRadius = 8; for(let x=-8;x<8;x++){ for(let z=-8;z<8;z++){ addBlock(x,0,z, Math.random()<0.02 ? 2 : 1); // sparse taller blocks occasionally // add a few blocks above as scenery } } for(let i=0;i<40;i++){ const rx = Math.floor((Math.random()*16)-8); const rz = Math.floor((Math.random()*16)-8); const ry = 1 + Math.floor(Math.random()*3); addBlock(rx,ry,rz,1); } function key(x,y,z){ return `${x},${y},${z}`; } function addBlock(x,y,z,type=1){ const k = key(x,y,z); if(blocks.has(k)) return; const geom = new THREE.BoxGeometry(blockSize,blockSize,blockSize); const m = new THREE.Mesh(geom, type===2 ? grassMat : mat); m.position.set(x*blockSize, y*blockSize + blockSize/2, z*blockSize); scene.add(m); blocks.set(k, {mesh:m, type}); } function removeBlock(x,y,z){ const k = key(x,y,z); const o = blocks.get(k); if(!o) return false; scene.remove(o.mesh); o.mesh.geometry.dispose(); // material reused so not disposing blocks.delete(k); return true; } // ---------------------- Player physics & control state ---------------------- const player = { pos: new THREE.Vector3(0,2,0), vel: new THREE.Vector3(0,0,0), speed: 3.0, // m/s onGround: false, height: 1.6 }; const GRAVITY = -20; const JUMP_SPEED = 7; let lastTime = performance.now()/1000; // Camera rotation state let yaw = 0; // horizontal let pitch = 0; // vertical const pitchLimit = Math.PI/2 - 0.1; // ---------------------- Joystick (left) ---------------------- const joystick = document.getElementById('joystick'); const thumb = document.getElementById('thumb'); let joystickActive = false; let joyId = null; let joyStart = { x:0, y:0 }; let joyVec = { x:0, y:0 }; // -1..1 function resetThumb(){ thumb.style.transform = `translate(0px,0px)`; } joystick.addEventListener('touchstart', e=>{ e.preventDefault(); const t = e.changedTouches[0]; joystickActive = true; joyId = t.identifier; joyStart.x = t.clientX; joyStart.y = t.clientY; }); window.addEventListener('touchmove', e=>{ if(!joystickActive) return; for(const t of e.changedTouches){ if(t.identifier!==joyId) continue; const dx = t.clientX - joyStart.x; const dy = t.clientY - joyStart.y; const max = 40; // pixels radius for full speed const nx = Math.max(-1, Math.min(1, dx/max)); const ny = Math.max(-1, Math.min(1, dy/max)); // Update thumb position (invert y because screen coords) thumb.style.transform = `translate(${nx*max}px, ${ny*max}px)`; joyVec.x = nx; joyVec.y = ny; } }, {passive:false}); window.addEventListener('touchend', e=>{ if(!joystickActive) return; for(const t of e.changedTouches){ if(t.identifier!==joyId) continue; joystickActive=false; joyId=null; joyVec.x = 0; joyVec.y = 0; resetThumb(); } }); // ---------------------- Right-side camera swipe ---------------------- let rightTouchId = null; let rightTouchStart = null; function isOnRightSide(x){ return x > window.innerWidth * 0.45; // right half-ish } window.addEventListener('touchstart', e=>{ for(const t of e.changedTouches){ if(isOnRightSide(t.clientX) && rightTouchId===null){ rightTouchId = t.identifier; rightTouchStart = { x: t.clientX, y: t.clientY }; } } }, {passive:true}); window.addEventListener('touchmove', e=>{ for(const t of e.changedTouches){ if(t.identifier!==rightTouchId) continue; const dx = t.clientX - rightTouchStart.x; const dy = t.clientY - rightTouchStart.y; rightTouchStart.x = t.clientX; rightTouchStart.y = t.clientY; // sensitivity tuned const sensX = 0.005; const sensY = 0.005; yaw -= dx * sensX; pitch -= dy * sensY; pitch = Math.max(-pitchLimit, Math.min(pitchLimit, pitch)); } }, {passive:true}); window.addEventListener('touchend', e=>{ for(const t of e.changedTouches){ if(t.identifier===rightTouchId){ rightTouchId = null; rightTouchStart = null; } } }, {passive:true}); // ---------------------- Buttons: Jump and Place/Break ---------------------- const jumpBtn = document.getElementById('jumpBtn'); const placeBtn = document.getElementById('placeBtn'); const status = document.getElementById('status'); jumpBtn.addEventListener('touchstart', e=>{ e.preventDefault(); if(player.onGround){ player.vel.y = JUMP_SPEED; player.onGround = false; } }, {passive:false}); // Place/Break: short tap = place block in front, long press > 500ms = break targeted block let placeTouchTimer = null; let placeTouchId = null; placeBtn.addEventListener('touchstart', e=>{ e.preventDefault(); const t = e.changedTouches[0]; placeTouchId = t.identifier; placeTouchTimer = setTimeout(()=>{ // long-press -> break placeTouchTimer = null; doBreak(); }, 500); }, {passive:false}); placeBtn.addEventListener('touchend', e=>{ e.preventDefault(); for(const t of e.changedTouches){ if(t.identifier!==placeTouchId) continue; if(placeTouchTimer){ clearTimeout(placeTouchTimer); placeTouchTimer = null; doPlace(); } placeTouchId = null; } }, {passive:false}); // ---------------------- Raycast helpers for place/break ---------------------- const raycaster = new THREE.Raycaster(); const tmpVec = new THREE.Vector3(); function worldCoordFromVec3(v){ // convert position to grid coordinate (integer block coords) const x = Math.round(v.x / blockSize); const y = Math.round((v.y - blockSize/2) / blockSize); const z = Math.round(v.z / blockSize); return {x,y,z}; } function getTargetBlock(){ // cast a ray from camera forward to detect closest block within 6m raycaster.set(camera.position, camera.getWorldDirection(tmpVec)); const intersects = raycaster.intersectObjects(Array.from(blocks.values()).map(b=>b.mesh), true); if(intersects.length>0){ const p = intersects[0].point; return worldCoordFromVec3(p); } return null; } function doPlace(){ // find a spot slightly in front of camera and place a block at integer position offset const dir = camera.getWorldDirection(tmpVec).clone(); const placePos = camera.position.clone().add(dir.multiplyScalar(2.2)); // ~2m ahead const coords = { x: Math.round(placePos.x / blockSize), y: Math.round((placePos.y - blockSize/2) / blockSize), z: Math.round(placePos.z / blockSize) }; // simple check: don't place inside player const dx = coords.x*blockSize - player.pos.x; const dz = coords.z*blockSize - player.pos.z; if(Math.abs(dx) < 0.9 && Math.abs(dz) < 0.9 && coords.y <= Math.round(player.pos.y)) { status.textContent = 'İçine blok koyma!'; setTimeout(()=> status.textContent='Hazır',800); return; } addBlock(coords.x, coords.y, coords.z, 1); status.textContent = `Koyuldu: ${coords.x},${coords.y},${coords.z}`; setTimeout(()=> status.textContent='Hazır',700); } function doBreak(){ const t = getTargetBlock(); if(!t){ status.textContent='Hedef bulunamadı'; setTimeout(()=> status.textContent='Hazır',700); return; } const ok = removeBlock(t.x, t.y, t.z); if(ok){ status.textContent = `Kırıldı: ${t.x},${t.y},${t.z}`; setTimeout(()=> status.textContent='Hazır',700); } else { status.textContent='Kırılamadı'; setTimeout(()=> status.textContent='Hazır',700); } } // ---------------------- Game loop ---------------------- function step(dt){ // Move according to joystick - relative to camera yaw const forward = new THREE.Vector3(Math.sin(yaw), 0, Math.cos(yaw)); // forward vector const right = new THREE.Vector3(Math.sin(yaw - Math.PI/2), 0, Math.cos(yaw - Math.PI/2)); const moveVec = new THREE.Vector3(); moveVec.addScaledVector(forward, -joyVec.y); // joystick y maps to forward/back moveVec.addScaledVector(right, joyVec.x); // joystick x maps to right/left if(moveVec.lengthSq() > 0.0001){ moveVec.normalize(); moveVec.multiplyScalar(player.speed); } // apply to horizontal velocity (simple) player.vel.x = moveVec.x; player.vel.z = moveVec.z; // gravity player.vel.y += GRAVITY * dt; // integrate player.pos.addScaledVector(player.vel, dt); // simple ground collision (ground at y = 0 blocks exist there) // Check block under player's feet const footX = Math.round(player.pos.x / blockSize); const footZ = Math.round(player.pos.z / blockSize); // find highest block at that x,z let highest = -Infinity; for(const k of blocks.keys()){ const [bx,by,bz] = k.split(',').map(Number); if(bx===footX && bz===footZ){ highest = Math.max(highest, by); } } const groundY = (highest === -Infinity) ? -1 : highest; // -1 means no block const groundWorldY = (groundY+1) * blockSize; // top of top block? but simpler use (groundY+1)*blockSize? // We'll assume player's feet should be at block top + small offset const minY = (groundY === -Infinity) ? 0 : (groundY+1)*blockSize + 0.0001; if(player.pos.y <= player.height/2 + minY){ player.pos.y = player.height/2 + minY; player.vel.y = 0; player.onGround = true; } else { player.onGround = false; } // update camera to follow player const camOffset = new THREE.Vector3(0, player.height-0.2, 0); const camPos = player.pos.clone().add(camOffset); // adjust camera behind player based on yaw/pitch const distance = 6; const cx = Math.cos(pitch) * Math.sin(yaw); const cz = Math.cos(pitch) * Math.cos(yaw); const cy = Math.sin(pitch); const behind = new THREE.Vector3(-cx, -cy, -cz).multiplyScalar(distance); camera.position.copy(camPos.clone().add(behind)); // look at player's head camera.lookAt(player.pos.x, player.pos.y + player.height - 0.2, player.pos.z); } // minimal render loop function animate(){ requestAnimationFrame(animate); const now = performance.now()/1000; let dt = Math.min(0.05, now - lastTime); lastTime = now; step(dt); renderer.render(scene, camera); } animate(); // ---------------------- Small helpers: tap to teleport / debug (optional) ---------------------- /* Double tap to reset view (for convenience) */ let lastTap = 0; window.addEventListener('touchend', (e)=>{ const t = performance.now(); if(t - lastTap < 300){ // double tap -> teleport forward (debug) const dir = camera.getWorldDirection(new THREE.Vector3()); player.pos.add(dir.multiplyScalar(2)); status.textContent='İleri atla (debug)'; setTimeout(()=>status.textContent='Hazır',700); } lastTap = t; }, {passive:true}); // Cleanup when page hidden (save battery) document.addEventListener('visibilitychange', () => { if(document.hidden){ // pause rendering? here we just lower pixel ratio renderer.setPixelRatio(1); } else { renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2)); } }); </script> </body> </html>
Landing Page
This ad does not have a landing page available
Network Timeline
Performance Summary

2

Requests

2

Domains

185KB

Transfer Size

649KB

Content Size

4,501.0ms

Dom Content Loaded

108.0ms

First Paint

4,501.0ms

Load Time
Domain Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...
Timings (ms)
Loading...
Total Time
Loading...
Content Breakdown
Transfer Size (bytes)
Loading...
Content Size (bytes)
Loading...
Header Size (bytes)
Loading...
Requests
Loading...