Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Grand Theft Auto VI - 3D (Mobile Fixed)</title>
<style>
body { margin:0; overflow:hidden; background:#000; font-family:Arial; touch-action:none; }
#title { position:absolute; top:0; left:0; width:100%; height:100%; z-index:200; background-size:cover; image-rendering:pixelated; cursor:pointer; }
#overlay {
position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
color:#fff; font-size:48px; text-shadow:0 0 30px #f0f;
text-align:center; z-index:201; pointer-events:auto;
cursor:pointer; padding:30px 60px; background:rgba(0,0,0,0.4); border-radius:20px;
}
canvas { display:block; z-index:100; }
#hud { position:absolute; bottom:15px; left:15px; color:#fff; font-size:18px; text-shadow:2px 2px #000; z-index:300; pointer-events:none; }
#mission { color:#ff0; font-weight:bold; }
#status { position:absolute; top:30%; left:50%; transform:translateX(-50%); background:rgba(0,0,0,0.85); padding:15px 35px; border-radius:12px; font-size:26px; color:#0f0; display:none; z-index:302; text-align:center; }
/* Mobile controls */
.controls { position:absolute; bottom:20px; z-index:400; pointer-events:none; }
.left { left:20px; }
.right { right:20px; }
.joy { width:130px; height:130px; background:rgba(255,255,255,0.18); border:5px solid rgba(255,255,255,0.5); border-radius:50%; position:relative; pointer-events:auto; }
.knob { width:55px; height:55px; background:#fff; border-radius:50%; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); box-shadow:0 0 20px rgba(0,0,0,0.7); pointer-events:auto; }
.btn { width:78px; height:78px; background:rgba(255,255,255,0.22); border:5px solid #fff; border-radius:50%; margin:8px; color:#fff; font-size:13px; font-weight:bold; display:flex; align-items:center; justify-content:center; text-align:center; pointer-events:auto; text-shadow:0 2px 4px #000; }
</style>
</head>
<body>
<img id="title" src="gta_title.png" alt="GTA VI Title">
<div id="overlay">TAP ANYWHERE<br>TO START</div>
<canvas id="canvas"></canvas>
<div id="hud">
<div id="mission">MISSION: Steal a car</div>
<div>WANTED: <span id="stars"></span></div>
</div>
<div id="status"></div>
<!-- Mobile controls (visible always) -->
<div class="controls left" id="moveContainer"></div>
<div class="controls right" id="rightContainer"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<script>
// ============== THREE.JS GAME ==============
const scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x88ccff, 80, 600);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({canvas:document.getElementById('canvas'), antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
// World setup
const sun = new THREE.DirectionalLight(0xffffff, 1.3); sun.position.set(100,150,100); sun.castShadow = true; scene.add(sun);
scene.add(new THREE.AmbientLight(0x777777));
const ground = new THREE.Mesh(new THREE.PlaneGeometry(1000,1000), new THREE.MeshLambertMaterial({color:0x228833}));
ground.rotation.x = -Math.PI/2; ground.receiveShadow = true; scene.add(ground);
const water = new THREE.Mesh(new THREE.PlaneGeometry(1000,1000), new THREE.MeshLambertMaterial({color:0x0088ff, transparent:true, opacity:0.7}));
water.rotation.x = -Math.PI/2; water.position.y = -0.2; scene.add(water);
const bank = new THREE.Mesh(new THREE.BoxGeometry(40,25,40), new THREE.MeshLambertMaterial({color:0x00aaaa}));
bank.position.set(120,12.5,-80); scene.add(bank);
function makeBuilding(x,z,w,d,h,c) {
const b = new THREE.Mesh(new THREE.BoxGeometry(w,d,h), new THREE.MeshLambertMaterial({color:c}));
b.position.set(x,h/2,z); b.castShadow = b.receiveShadow = true; scene.add(b);
}
makeBuilding(-100,50,60,50,80,0x555555);
makeBuilding(80,140,50,70,60,0x666666);
// Player & game vars
let player = { mesh: new THREE.Mesh(new THREE.CapsuleGeometry(2.5,8,8,16), new THREE.MeshLambertMaterial({color:0x00aaff})), health:100, inCar:false, currentCar:null, speed:0.45, yaw:0 };
player.mesh.position.set(0,6,80); player.mesh.castShadow = true; scene.add(player.mesh);
let cars = [];
function createCar(x,z,color) {
const g = new THREE.Group();
const b = new THREE.Mesh(new THREE.BoxGeometry(14,5,7), new THREE.MeshLambertMaterial({color}));
b.castShadow = true; g.add(b); g.position.set(x,3,z); scene.add(g);
return {group:g, occupied:false};
}
cars.push(createCar(-60,60,0xff2222));
cars.push(createCar(140,-40,0xffaa00));
let peds = [];
for(let i=0;i<15;i++){
const p = new THREE.Mesh(new THREE.CapsuleGeometry(1.8,5,8,16), new THREE.MeshLambertMaterial({color:0xff3333}));
p.position.set(Math.random()*300-150,4,Math.random()*300-150); p.castShadow = true; scene.add(p);
peds.push({mesh:p, vx:(Math.random()-0.5)*0.8, vz:(Math.random()-0.5)*0.8});
}
let police = [];
let missionStage = 0;
let wantedLevel = 0;
let gameStarted = false;
// Title elements
const titleImg = document.getElementById('title');
const overlay = document.getElementById('overlay');
// ============== START GAME FUNCTION ==============
function startGame() {
if(gameStarted) return;
titleImg.style.display = 'none';
overlay.style.display = 'none';
gameStarted = true;
console.log("%cGAME STARTED - Enjoy!", "color:#0f0;font-size:18px");
}
// Make title + overlay tappable/clickable
titleImg.style.pointerEvents = 'auto';
overlay.style.pointerEvents = 'auto';
titleImg.addEventListener('click', startGame, {once:true});
titleImg.addEventListener('touchstart', startGame, {once:true, passive:false});
overlay.addEventListener('click', startGame, {once:true});
overlay.addEventListener('touchstart', startGame, {once:true, passive:false});
// ============== MOBILE CONTROLS ==============
class SimpleJoystick {
constructor(containerId, isLook) {
this.container = document.getElementById(containerId);
this.isLook = isLook;
this.vector = new THREE.Vector2();
const joy = document.createElement('div'); joy.className = 'joy';
const knob = document.createElement('div'); knob.className = 'knob';
joy.appendChild(knob); this.container.appendChild(joy);
this.joy = joy; this.knob = knob; this.maxR = 55;
const start = (e) => { this.active = true; this.update(e); };
const move = (e) => { if(this.active) this.update(e); };
const end = () => { this.active = false; this.reset(); };
joy.addEventListener('touchstart', start, {passive:false});
joy.addEventListener('touchmove', move, {passive:false});
joy.addEventListener('touchend', end);
joy.addEventListener('mousedown', start);
joy.addEventListener('mousemove', move);
joy.addEventListener('mouseup', end);
joy.addEventListener('mouseleave', end);
}
update(e) {
e.preventDefault();
const rect = this.joy.getBoundingClientRect();
const cx = rect.left + rect.width/2;
const cy = rect.top + rect.height/2;
let dx = (e.touches ? e.touches[0].clientX : e.clientX) - cx;
let dy = (e.touches ? e.touches[0].clientY : e.clientY) - cy;
const dist = Math.hypot(dx, dy);
if(dist > this.maxR){ dx = dx/dist * this.maxR; dy = dy/dist * this.maxR; }
this.knob.style.transform = `translate(${dx-27.5}px, ${dy-27.5}px)`;
this.vector.set(dx/this.maxR, this.isLook ? dx/this.maxR : -dy/this.maxR);
}
reset() { this.knob.style.transform = 'translate(0px,0px)'; this.vector.set(0,0); }
}
const moveJoy = new SimpleJoystick('moveContainer', false);
const lookJoy = new SimpleJoystick('rightContainer', true);
// Action buttons
const rightContainer = document.getElementById('rightContainer');
rightContainer.style.display = 'flex'; rightContainer.style.flexDirection = 'column'; rightContainer.style.alignItems = 'flex-end';
const btnRow = document.createElement('div'); btnRow.style.display = 'flex'; btnRow.style.gap = '12px';
function makeBtn(text, action) {
const b = document.createElement('div'); b.className = 'btn'; b.innerHTML = text; b.addEventListener('touchstart', e=>{e.preventDefault(); action();});
btnRow.appendChild(b); return b;
}
makeBtn('PUNCH', ()=>{ if(gameStarted && !player.inCar) punchLogic(); });
makeBtn('STEAL<br>EXIT', ()=>{ if(gameStarted) stealLogic(); });
makeBtn('ROB', ()=>{ if(gameStarted) robLogic(); });
rightContainer.appendChild(btnRow);
// ============== GAME LOGIC FUNCTIONS ==============
function punchLogic() {
for(let p of peds){
if(player.mesh.position.distanceTo(p.mesh.position) < 15){
p.mesh.position.add(new THREE.Vector3().subVectors(p.mesh.position, player.mesh.position).multiplyScalar(5));
showStatus("PUNCHED!");
return;
}
}
}
function stealLogic() {
if(player.inCar){
player.inCar = false; player.currentCar.occupied = false;
showStatus("EXITED CAR");
}else{
for(let c of cars){
if(player.mesh.position.distanceTo(c.group.position) < 20 && !c.occupied){
player.inCar = true; player.currentCar = c; c.occupied = true;
if(missionStage===0){ missionStage=1; updateHUD(); }
showStatus("CAR STOLEN!", "#ff0");
return;
}
}
}
}
function robLogic() {
if(missionStage===1 && player.mesh.position.distanceTo(bank.position) < 35){
missionStage = 2; wantedLevel = 5; updateHUD();
showStatus("BANK ROBBED! RUN!", "#f00");
police = [{group:createCar(140,-120,0x0000ff).group}, {group:createCar(100,-150,0x0000ff).group}];
}
}
function showStatus(txt, col="#0f0") {
const s = document.getElementById('status');
s.textContent = txt; s.style.color = col; s.style.display = 'block';
setTimeout(()=>{ s.style.display='none'; }, 2200);
}
function updateHUD() {
const msgs = ["MISSION: Steal a car", "MISSION: Go to BANK & ROB", "MISSION: ESCAPE COPS!"];
document.getElementById('mission').textContent = msgs[missionStage] || "MISSION COMPLETE!";
document.getElementById('stars').textContent = '★'.repeat(wantedLevel);
}
updateHUD();
function respawnHospital() { player.mesh.position.set(-180,6,180); resetAfterDeath("HOSPITAL"); }
function respawnPolice() { player.mesh.position.set(180,6,-180); resetAfterDeath("POLICE STATION"); }
function resetAfterDeath(place) {
player.health = 100; player.inCar = false; wantedLevel = 0; police = [];
showStatus(`RESPAWNED AT ${place}`, place==="HOSPITAL"?"#0ff":"#f00");
updateHUD();
}
// ============== KEYBOARD (PC) ==============
const keys = {};
window.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);
// ============== GAME LOOP ==============
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
if(!gameStarted) return;
// Movement
let dx = 0, dz = 0;
// Keyboard
if(keys['w'] || keys['arrowup']) dz -= 1;
if(keys['s'] || keys['arrowdown']) dz += 1;
if(keys['a'] || keys['arrowleft']) dx -= 1;
if(keys['d'] || keys['arrowright']) dx += 1;
// Joystick
if(moveJoy.vector.length() > 0.1){
dx += moveJoy.vector.x;
dz += moveJoy.vector.y;
}
if(dx || dz){
const len = Math.hypot(dx, dz) || 1;
dx /= len; dz /= len;
const speed = player.inCar ? 1.3 : player.speed;
const forward = new THREE.Vector3(0,0,-1).applyAxisAngle(new THREE.Vector3(0,1,0), player.yaw);
const right = new THREE.Vector3(1,0,0).applyAxisAngle(new THREE.Vector3(0,1,0), player.yaw);
const dir = forward.multiplyScalar(-dz).add(right.multiplyScalar(dx));
if(player.inCar) player.currentCar.group.position.addScaledVector(dir, speed);
else player.mesh.position.addScaledVector(dir, speed);
}
// Look (joystick)
if(lookJoy.vector.length() > 0.1){
player.yaw -= lookJoy.vector.x * 0.07;
}
// Police chase
if(wantedLevel > 0 && police.length){
for(let p of police){
const target = player.inCar ? player.currentCar.group.position : player.mesh.position;
const dir = target.clone().sub(p.group.position).normalize();
p.group.position.addScaledVector(dir, 1.1);
if(p.group.position.distanceTo(target) < 16) respawnPolice();
}
if(Math.random()<0.05) player.health -= 1;
if(player.health <= 0) respawnHospital();
}
// Camera
const targetPos = player.inCar ? player.currentCar.group.position : player.mesh.position;
const camOffset = new THREE.Vector3(0, 18, 35).applyAxisAngle(new THREE.Vector3(0,1,0), player.yaw);
camera.position.copy(targetPos).add(camOffset);
camera.lookAt(targetPos.x, targetPos.y + 8, targetPos.z);
// Ground clamp
player.mesh.position.y = 6;
if(player.inCar) player.currentCar.group.position.y = 3;
peds.forEach(p => { p.mesh.position.y = 4; p.mesh.position.x += p.vx; p.mesh.position.z += p.vz;
if(Math.random()<0.03){ p.vx = (Math.random()-0.5)*1.2; p.vz = (Math.random()-0.5)*1.2; }
});
}
animate();
// Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// Auto-hide mobile controls on desktop
if(!/Mobi|Android|iPhone|iPad/i.test(navigator.userAgent)){
document.getElementById('moveContainer').style.display = 'none';
document.getElementById('rightContainer').style.display = 'none';
}
console.log("%c✅ FIXED - Tap the big title image or the 'TAP ANYWHERE TO START' box to begin!\nLeft joystick = move | Right joystick = look | Bottom buttons = punch/steal/rob", "color:#0f0; font-size:16px");
</script>
</body>
</html>3
2
138KB
618KB
341.0ms
188.0ms
342.0ms