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, maximum-scale=1.0, user-scalable=no">
<title>GTA VI • 3D Mobile</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r134/three.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body { margin:0; padding:0; overflow:hidden; background:#000; touch-action:none; }
#container { position:relative; width:100vw; height:100vh; }
canvas { display:block; }
/* EPIC TITLE SCREEN WITH BEACH SUNSET */
#titleScreen {
position:absolute; top:0; left:0; width:100%; height:100%;
background: url('https://images.unsplash.com/photo-1507525428034-b723cf961d3e') no-repeat center center;
background-size: cover;
display:flex; flex-direction:column; align-items:center; justify-content:center;
z-index:1000; color:#fff; text-align:center; transition:opacity 1.8s;
}
#titleScreen::before {
content:''; position:absolute; top:0; left:0; width:100%; height:100%;
background: linear-gradient(to bottom, rgba(0,0,0,0.3), rgba(0,20,40,0.75));
z-index:1;
}
.content { position:relative; z-index:2; }
.rockstar { font-size:22px; letter-spacing:12px; color:#ff88cc; margin-bottom:12px;
text-shadow:0 0 20px #ff88cc; animation:rock 1.8s infinite; }
#gtaTitle {
font-family:'Press Start 2P', sans-serif; font-size:118px; font-weight:900;
letter-spacing:14px;
background: linear-gradient(to bottom, #ff1493 0%, #00bfff 45%, #9400d3 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
text-shadow: 0 0 40px #ff00ff,
0 0 80px #00ffff,
0 0 120px #aa00ff;
animation: neonPulse 1.4s infinite alternate;
}
#viceCity { font-size:42px; color:#ffdd44; letter-spacing:10px; margin:20px 0 90px;
text-shadow:0 0 50px #ffdd44; }
#startBtn {
position:relative; z-index:2;
padding:26px 100px; font-size:34px; background:rgba(255,255,255,0.08);
border:8px solid #fff; color:#fff; border-radius:16px;
box-shadow:0 0 70px #ff00ff; font-family:'Press Start 2P';
cursor:pointer; transition:all .2s;
}
#startBtn:active { transform:scale(0.9); box-shadow:0 0 30px #00ffff; }
@keyframes neonPulse { from { filter: brightness(1.1); } to { filter: brightness(1.6); } }
@keyframes rock { 0%,100% { opacity:0.7; } 50% { opacity:1; } }
/* HUD & BUTTONS (unchanged) */
#hud {
position:absolute; top:15px; left:15px; z-index:200; color:#0f0;
font-family:'Press Start 2P'; font-size:17px; text-shadow:2px 2px 8px #000;
}
.btn {
position:absolute; bottom:30px; width:92px; height:92px; border-radius:50%;
display:flex; align-items:center; justify-content:center; font-size:38px;
border:7px solid; background:rgba(0,0,0,0.6); z-index:300;
}
#actionBtn { right:30px; border-color:#0f0; color:#0f0; }
#shootBtn { left:30px; border-color:#f33; color:#f33; }
#missionText {
position:absolute; top:80px; left:50%; transform:translateX(-50%);
background:rgba(0,0,0,0.85); padding:12px 40px; border:5px solid #ff0;
color:#ff0; font-family:'Press Start 2P'; font-size:19px; display:none;
z-index:250; border-radius:10px;
}
#wasted, #missionComplete {
position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);
font-family:'Press Start 2P'; font-size:110px; font-weight:900;
display:none; z-index:400; text-shadow:0 0 60px;
}
#wasted { color:#f00; }
#missionComplete { color:#0f0; font-size:82px; }
</style>
</head>
<body>
<div id="container">
<canvas id="canvas"></canvas>
<div id="titleScreen">
<div class="content">
<div class="rockstar">ROCKSTAR GAMES PRESENTS</div>
<div id="gtaTitle">GTA VI</div>
<div id="viceCity">VICE CITY 2026</div>
<div id="startBtn">TAP ANYWHERE TO START</div>
</div>
</div>
<div id="hud">
<div style="font-size:36px;color:#ff0;margin-bottom:6px;">GTA VI</div>
<div id="money">$1,250</div>
<div id="wanted">WANTED: ★☆☆☆☆</div>
<div id="time">12:00</div>
</div>
<div id="missionText">ROB THE BANK!</div>
<div id="actionBtn" class="btn">🚶</div>
<div id="shootBtn" class="btn">🔫</div>
<div id="wasted">WASTED</div>
<div id="missionComplete">MISSION PASSED!<br><span style="font-size:38px">+ $25,000</span></div>
</div>
<script>
// === GAME CODE (same as before, only title fixed) ===
let scene, camera, renderer;
let player, cars = [], bank;
let currentCar = null;
let bullets = [];
let peds = [];
let policeCars = [];
let money = 1250;
let wantedLevel = 0;
let gameTime = 720;
let gameOver = false;
let missionStage = 0;
let escapeTimer = 0;
let lastShot = 0;
let moveTouch = null;
let movePower = 0, moveAngle = 0;
let shooting = false;
const WORLD = 3200;
function createCar(color) {
const group = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(5.8, 2.4, 2.8), new THREE.MeshPhongMaterial({ color: color, shininess: 80 }));
body.position.y = 1.6;
body.castShadow = true;
group.add(body);
const roof = new THREE.Mesh(new THREE.BoxGeometry(3.6, 1.6, 2.4), new THREE.MeshPhongMaterial({ color: 0x111111, shininess: 30 }));
roof.position.set(0, 2.8, 0);
group.add(roof);
const wheelGeo = new THREE.CylinderGeometry(0.8, 0.8, 0.6, 32);
const wheelMat = new THREE.MeshPhongMaterial({ color: 0x222222 });
[[-2.2,1.2,1.4],[2.2,1.2,1.4],[-2.2,1.2,-1.4],[2.2,1.2,-1.4]].forEach(p => {
const w = new THREE.Mesh(wheelGeo, wheelMat);
w.rotation.z = Math.PI/2;
w.position.set(p[0],p[1],p[2]);
group.add(w);
});
return group;
}
function initGame() {
const canvas = document.getElementById('canvas');
renderer = new THREE.WebGLRenderer({canvas, antialias:true});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
scene = new THREE.Scene();
scene.fog = new THREE.Fog(0x112233, 1200, 3800);
camera = new THREE.PerspectiveCamera(68, window.innerWidth/window.innerHeight, 0.5, 5000);
const hemi = new THREE.HemisphereLight(0xffaa77, 0x334422, 0.9);
scene.add(hemi);
const sun = new THREE.DirectionalLight(0xffdd99, 1.4);
sun.position.set(300, 500, -400);
sun.castShadow = true;
sun.shadow.mapSize.width = 2048;
sun.shadow.mapSize.height = 2048;
scene.add(sun);
const sky = new THREE.Mesh(new THREE.SphereGeometry(3800, 32, 32), new THREE.MeshBasicMaterial({ color: 0x112244, side: THREE.BackSide }));
scene.add(sky);
const ground = new THREE.Mesh(new THREE.PlaneGeometry(WORLD*2, WORLD*2), new THREE.MeshPhongMaterial({ color: 0x113300 }));
ground.rotation.x = -Math.PI/2;
ground.receiveShadow = true;
scene.add(ground);
const roadMat = new THREE.MeshPhongMaterial({ color: 0x222222 });
const roadH = new THREE.Mesh(new THREE.PlaneGeometry(WORLD*2, 320), roadMat);
roadH.rotation.x = -Math.PI/2; roadH.position.y = 0.2; scene.add(roadH);
const roadV = roadH.clone(); roadV.rotation.z = Math.PI/2; roadV.position.x = 0; scene.add(roadV);
const bankMat = new THREE.MeshPhongMaterial({ color: 0x888899 });
bank = new THREE.Mesh(new THREE.BoxGeometry(80, 90, 120), bankMat);
bank.position.set(1200, 45, -1400);
bank.castShadow = true; bank.receiveShadow = true;
scene.add(bank);
function addPalm(x,z) {
const trunk = new THREE.Mesh(new THREE.CylinderGeometry(4,6,50,8), new THREE.MeshPhongMaterial({color:0x664422}));
trunk.position.set(x,25,z); scene.add(trunk);
const leaves = new THREE.Mesh(new THREE.ConeGeometry(22,40,8), new THREE.MeshPhongMaterial({color:0x00aa33}));
leaves.position.set(x,55,z); leaves.rotation.x = Math.PI; scene.add(leaves);
}
addPalm(800,-900); addPalm(-1100,1300); addPalm(1600,1100); addPalm(-1400,-1600);
player = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(1.4, 2.8, 1), new THREE.MeshPhongMaterial({color:0x0066ff}));
body.position.y = 2.2; player.add(body);
const head = new THREE.Mesh(new THREE.SphereGeometry(0.8,32,32), new THREE.MeshPhongMaterial({color:0xffddbb}));
head.position.y = 4.4; player.add(head);
player.position.set(-300, 0, 200);
scene.add(player);
const carColors = [0xff2222, 0x22ff44, 0x4488ff, 0xffff22];
const carPos = [[-400,0,800], [900,0,-600], [-1200,0,400], [600,0,1400]];
carPos.forEach((pos,i) => {
const c = createCar(carColors[i]);
c.position.set(pos[0], 0, pos[1]);
c.userData = { speed: 0 };
scene.add(c);
cars.push(c);
});
for(let i=0; i<18; i++){
const p = new THREE.Mesh(new THREE.BoxGeometry(1.3,2.6,1.1), new THREE.MeshPhongMaterial({color:Math.random()*0xffffff}));
p.position.set((Math.random()-0.5)*WORLD*1.6, 1.3, (Math.random()-0.5)*WORLD*1.6);
p.userData = {vx:(Math.random()-0.5)*1.2, vz:(Math.random()-0.5)*1.2};
scene.add(p);
peds.push(p);
}
setupControls();
animate();
setInterval(updateHUD, 180);
}
function setupControls() {
const cont = document.getElementById('container');
cont.addEventListener('touchstart', e => {
for(let t of e.changedTouches){
const x = t.clientX / window.innerWidth;
if(x < 0.48 && !moveTouch) moveTouch = t.identifier;
else if(x > 0.52) shooting = true;
}
});
cont.addEventListener('touchmove', e => {
e.preventDefault();
for(let t of e.touches){
if(t.identifier === moveTouch){
const cx = window.innerWidth * 0.24;
const cy = window.innerHeight * 0.72;
const dx = (t.clientX - cx) / 130;
const dy = (t.clientY - cy) / 130;
movePower = Math.min(1, Math.hypot(dx,dy));
moveAngle = Math.atan2(dy, dx);
}
}
});
cont.addEventListener('touchend', e => {
for(let t of e.changedTouches){
if(t.identifier === moveTouch){ moveTouch = null; movePower = 0; }
}
shooting = false;
});
document.getElementById('actionBtn').addEventListener('touchstart', () => {
if(gameOver) return;
const pos = currentCar ? currentCar.position : player.position;
if(missionStage === 0 && !currentCar && Math.hypot(pos.x - bank.position.x, pos.z - bank.position.z) < 110){
robBank(); return;
}
for(let c of cars){
if(Math.hypot(pos.x - c.position.x, pos.z - c.position.z) < 22){
if(currentCar) exitCar();
currentCar = c;
player.visible = false;
return;
}
}
if(currentCar) exitCar();
});
document.getElementById('shootBtn').addEventListener('touchstart', () => shooting = true);
document.getElementById('shootBtn').addEventListener('touchend', () => shooting = false);
}
function exitCar() {
if(!currentCar) return;
player.position.copy(currentCar.position);
player.position.x += Math.sin(currentCar.rotation.y) * 12;
player.position.z += Math.cos(currentCar.rotation.y) * 12;
player.visible = true;
currentCar = null;
}
function robBank() {
missionStage = 1;
money += 25000;
wantedLevel = 5;
document.getElementById('missionText').style.display = 'block';
document.getElementById('missionText').textContent = 'GET AWAY FROM THE COPS!';
for(let i=0; i<3; i++){
const pc = createCar(0x0066ff);
pc.position.set(bank.position.x + (i-1)*80, 0, bank.position.z + 200);
pc.rotation.y = Math.PI;
scene.add(pc);
policeCars.push(pc);
}
setTimeout(() => document.getElementById('missionText').style.display = 'none', 3000);
}
function updateMovement(delta) {
const target = currentCar || player;
const speed = currentCar ? 48 : 22;
const dx = Math.sin(moveAngle) * movePower * speed * delta;
const dz = Math.cos(moveAngle) * movePower * speed * delta;
if(currentCar){
currentCar.userData.speed = (currentCar.userData.speed || 0) * 0.88 + movePower * (moveAngle > -0.8 && moveAngle < 0.8 ? 1 : -0.6) * 42 * delta;
currentCar.rotation.y += movePower * 3.2 * (moveAngle > 1 || moveAngle < -1 ? -1 : 1) * delta;
currentCar.position.x += Math.sin(currentCar.rotation.y) * currentCar.userData.speed * delta;
currentCar.position.z += Math.cos(currentCar.rotation.y) * currentCar.userData.speed * delta;
} else {
player.position.x += dx;
player.position.z += dz;
if(movePower > 0.1) player.rotation.y = moveAngle + Math.PI;
}
target.position.x = Math.max(-WORLD*0.9, Math.min(WORLD*0.9, target.position.x));
target.position.z = Math.max(-WORLD*0.9, Math.min(WORLD*0.9, target.position.z));
}
function updateCamera() {
const targetPos = (currentCar || player).position;
const behind = currentCar ? 26 : 16;
const height = currentCar ? 14 : 11;
const offset = new THREE.Vector3(-Math.sin((currentCar || player).rotation.y) * behind, height, -Math.cos((currentCar || player).rotation.y) * behind);
camera.position.lerp(targetPos.clone().add(offset), 0.16);
camera.lookAt(targetPos.x, targetPos.y + (currentCar ? 5 : 4), targetPos.z);
}
function shoot() {
const now = Date.now();
if(now - lastShot < 140) return;
lastShot = now;
const shooter = currentCar || player;
const dir = new THREE.Vector3(Math.sin(shooter.rotation.y + Math.PI), 0.2, Math.cos(shooter.rotation.y + Math.PI));
const bullet = new THREE.Mesh(new THREE.SphereGeometry(0.45, 12, 12), new THREE.MeshBasicMaterial({color:0xffff00}));
bullet.position.copy(shooter.position).add(new THREE.Vector3(0,5,0));
bullet.userData = {vel: dir.multiplyScalar(140), life: 55};
scene.add(bullet);
bullets.push(bullet);
}
function updatePolice(delta) {
const targetPos = (currentCar || player).position;
policeCars.forEach(pc => {
const dx = targetPos.x - pc.position.x;
const dz = targetPos.z - pc.position.z;
const dist = Math.hypot(dx,dz);
if(dist < 20){ gameOver = true; document.getElementById('wasted').style.display = 'block'; }
if(dist > 0){
pc.rotation.y = Math.atan2(dx, dz);
pc.position.x += (dx/dist) * 38 * delta;
pc.position.z += (dz/dist) * 38 * delta;
}
});
}
function animate() {
requestAnimationFrame(animate);
if(gameOver) return;
const delta = 0.017;
gameTime += delta;
updateMovement(delta);
if(shooting) shoot();
for(let i=bullets.length-1; i>=0; i--){
const b = bullets[i];
b.position.addScaledVector(b.userData.vel, delta);
b.userData.life--;
if(b.userData.life <= 0){ scene.remove(b); bullets.splice(i,1); }
}
peds.forEach(p => {
p.position.x += p.userData.vx;
p.position.z += p.userData.vz;
if(Math.random()<0.025){ p.userData.vx = (Math.random()-0.5)*2; p.userData.vz = (Math.random()-0.5)*2; }
});
if(missionStage === 1){
escapeTimer += delta;
updatePolice(delta);
if(escapeTimer > 85){
missionStage = 2;
document.getElementById('missionComplete').style.display = 'block';
setTimeout(() => location.reload(), 4800);
}
}
updateCamera();
renderer.render(scene, camera);
}
function updateHUD() {
if(gameOver) return;
document.getElementById('money').textContent = '$' + money.toLocaleString();
let stars = '★'.repeat(Math.min(5,wantedLevel)) + '☆'.repeat(5-Math.min(5,wantedLevel));
document.getElementById('wanted').innerHTML = `WANTED: ${stars}`;
const h = Math.floor(gameTime/60);
const m = Math.floor(gameTime%60);
document.getElementById('time').textContent = `\( {h}: \){m<10?'0':''}${m}`;
}
// FIXED TITLE SCREEN – TAP ANYWHERE WORKS NOW
window.onload = () => {
const title = document.getElementById('titleScreen');
const startGame = () => {
title.style.opacity = '0';
setTimeout(() => {
title.style.display = 'none';
initGame();
}, 1600);
};
title.addEventListener('touchstart', startGame);
title.addEventListener('click', startGame); // for desktop testing
window.addEventListener('resize', () => {
if(camera && renderer){
camera.aspect = window.5
5
1555KB
2034KB
168.0ms
304.0ms
408.0ms