Загрузка данных
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GTA Browser Pro - Living City</title>
<style>
body { margin: 0; overflow: hidden; font-family: 'Arial Black', sans-serif; background: #87CEEB; }
#hud {
position: absolute; top: 20px; right: 20px; text-align: right;
color: #2ecc71; font-size: 32px; font-weight: 900; text-shadow: 3px 3px #000;
}
#wanted { color: #f1c40f; font-size: 45px; letter-spacing: 2px; }
#speed-ui { color: white; font-size: 22px; background: rgba(0,0,0,0.5); padding: 8px 15px; border-radius: 10px; margin-top: 10px; }
.controls {
position: absolute; bottom: 20px; left: 20px; color: #fff; font-size: 13px;
background: rgba(0,0,0,0.7); padding: 15px; border-radius: 10px; pointer-events: none; border: 2px solid #3498db;
}
#crosshair {
position: absolute; top: 50%; left: 50%; width: 8px; height: 8px;
background: rgba(255, 255, 255, 0.8); border: 2px solid #ff0000; border-radius: 50%; transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div id="crosshair"></div>
<div id="hud">
<div id="money">$00000000</div>
<div id="wanted"></div>
<div id="speed-ui">0 KM/H</div>
</div>
<div class="controls">
<b style="color: #3498db; font-size: 16px;">ГТА ОНЛАЙН (BROWSER)</b><br><br>
<b>WASD</b> - Движение | <b>SPACE</b> - РУЧНИК/ДРИФТ<br>
<b>F</b> - ВХОД/ВЫХОД | <b>ЛКМ</b> - ОГОНЬ<br>
<b>МЫШКА</b> - ОБЗОР 360° | <b>R</b> - РЕСТАРТ
</div>
<script type="importmap">
{ "imports": { "three": "https://unpkg.com/three@0.160.0/build/three.module.js" } }
</script>
<script type="module">
import * as THREE from 'three';
let scene, camera, renderer, clock;
let player, car, npcs = [], police = [];
let isInsideCar = false;
let keys = {};
let stats = { money: 0, wanted: 0 };
const buildings = [];
const bullets = [];
let cameraAngleX = 0;
let cameraAngleY = 0.3;
init();
animate();
function init() {
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.Fog(0x87CEEB, 50, 400);
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 2000);
clock = new THREE.Clock();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// МАКСИМАЛЬНЫЙ СВЕТ
const ambient = new THREE.AmbientLight(0xffffff, 1.2);
scene.add(ambient);
const sun = new THREE.DirectionalLight(0xffffff, 1.8);
sun.position.set(150, 300, 100);
sun.castShadow = true;
scene.add(sun);
createCity();
// СОЗДАНИЕ ИГРОКА (УЛУЧШЕННЫЙ)
player = createHuman(0x3498db);
scene.add(player);
// МАШИНА
car = createCar();
car.position.set(10, 0, 15);
scene.add(car);
car.userData = { speed: 0, maxSpeed: 2.2, accel: 0.05, angle: 0 };
// СПАВН ПЕШЕХОДОВ
for(let i = 0; i < 50; i++) {
spawnNPC();
}
document.addEventListener('mousemove', (e) => {
if (document.pointerLockElement === document.body) {
cameraAngleX -= e.movementX * 0.005;
cameraAngleY = Math.max(0.1, Math.min(1.2, cameraAngleY + e.movementY * 0.005));
}
});
document.addEventListener('mousedown', () => {
document.body.requestPointerLock();
shoot();
});
window.addEventListener('keydown', e => {
keys[e.code] = true;
if(e.code === 'KeyF') toggleCar();
if(e.code === 'KeyR') location.reload();
});
window.addEventListener('keyup', e => keys[e.code] = false);
}
function createHuman(shirtColor) {
const group = new THREE.Group();
// Тело
const torso = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.8, 0.3), new THREE.MeshStandardMaterial({color: shirtColor}));
torso.position.y = 1.0;
// Голова
const head = new THREE.Mesh(new THREE.BoxGeometry(0.35, 0.35, 0.35), new THREE.MeshStandardMaterial({color: 0xffdbac}));
head.position.y = 1.6;
// Ноги
const legGeo = new THREE.BoxGeometry(0.25, 0.6, 0.25);
const legMat = new THREE.MeshStandardMaterial({color: 0x111111});
const legL = new THREE.Mesh(legGeo, legMat);
legL.position.set(-0.15, 0.3, 0);
const legR = new THREE.Mesh(legGeo, legMat);
legR.position.set(0.15, 0.3, 0);
// Руки
const armGeo = new THREE.BoxGeometry(0.2, 0.7, 0.2);
const armL = new THREE.Mesh(armGeo, new THREE.MeshStandardMaterial({color: 0xffdbac}));
armL.position.set(-0.45, 1.0, 0);
const armR = new THREE.Mesh(armGeo, new THREE.MeshStandardMaterial({color: 0xffdbac}));
armR.position.set(0.45, 1.0, 0);
group.add(torso, head, legL, legR, armL, armR);
return group;
}
function createCar() {
const g = new THREE.Group();
const body = new THREE.Mesh(new THREE.BoxGeometry(2.2, 0.7, 4.5), new THREE.MeshStandardMaterial({color: 0xc0392b}));
body.position.y = 0.6;
const roof = new THREE.Mesh(new THREE.BoxGeometry(1.8, 0.6, 2.2), new THREE.MeshStandardMaterial({color: 0x222222}));
roof.position.set(0, 1.2, -0.2);
// Колеса
const wGeo = new THREE.CylinderGeometry(0.45, 0.45, 0.4, 16);
const wMat = new THREE.MeshStandardMaterial({color: 0x111111});
[[-1.1, 0.4, 1.5], [1.1, 0.4, 1.5], [-1.1, 0.4, -1.5], [1.1, 0.4, -1.5]].forEach(p => {
const w = new THREE.Mesh(wGeo, wMat);
w.rotation.z = Math.PI/2;
w.position.set(...p);
g.add(w);
});
g.add(body, roof);
return g;
}
function createCity() {
const ground = new THREE.Mesh(new THREE.PlaneGeometry(3000, 3000), new THREE.MeshStandardMaterial({color: 0x444444}));
ground.rotation.x = -Math.PI/2;
scene.add(ground);
for(let i = -12; i < 12; i++) {
for(let j = -12; j < 12; j++) {
if(Math.abs(i) < 2 && Math.abs(j) < 2) continue;
const h = 10 + Math.random() * 80;
const b = new THREE.Mesh(
new THREE.BoxGeometry(20, h, 20),
new THREE.MeshStandardMaterial({color: new THREE.Color().setHSL(Math.random(), 0.2, 0.5)})
);
b.position.set(i * 55, h/2, j * 55);
scene.add(b);
buildings.push(b);
}
}
}
function spawnNPC() {
const npc = createHuman(Math.random() * 0xffffff);
npc.position.set((Math.random()-0.5)*400, 0, (Math.random()-0.5)*400);
npc.userData = {
targetAngle: Math.random() * Math.PI * 2,
timer: Math.random() * 100
};
scene.add(npc);
npcs.push(npc);
}
function toggleCar() {
const d = player.position.distanceTo(car.position);
if(!isInsideCar && d < 6) {
isInsideCar = true;
player.visible = false;
} else if(isInsideCar) {
isInsideCar = false;
player.visible = true;
player.position.copy(car.position).add(new THREE.Vector3(3, 0, 0));
}
}
function shoot() {
const b = new THREE.Mesh(new THREE.SphereGeometry(0.2), new THREE.MeshBasicMaterial({color: 0xffff00}));
const dir = new THREE.Vector3();
camera.getWorldDirection(dir);
b.position.copy(isInsideCar ? car.position : player.position).add(new THREE.Vector3(0, 1.5, 0));
bullets.push({ mesh: b, dir: dir, life: 100 });
scene.add(b);
// Проверка попадания в NPC
npcs.forEach((npc, idx) => {
if(b.position.distanceTo(npc.position) < 3) {
stats.money += 100;
stats.wanted = Math.min(stats.wanted + 1, 5);
}
});
}
function checkWall(pos, radius) {
const pBox = new THREE.Box3().setFromCenterAndSize(pos, new THREE.Vector3(radius, 2, radius));
for(let b of buildings) {
if(pos.distanceTo(b.position) < 25) {
const bBox = new THREE.Box3().setFromObject(b);
if(bBox.intersectsBox(pBox)) return true;
}
}
return false;
}
function handlePhysics() {
if(isInsideCar) {
const isDrifting = keys['Space'];
const turnSpeed = isDrifting ? 0.09 : 0.05;
const friction = isDrifting ? 0.95 : 0.985;
if(keys['KeyW']) car.userData.speed = Math.min(car.userData.speed + car.userData.accel, car.userData.maxSpeed);
if(keys['KeyS']) car.userData.speed = Math.max(car.userData.speed - car.userData.accel, -0.7);
car.userData.speed *= friction;
if(Math.abs(car.userData.speed) > 0.1) {
if(keys['KeyA']) car.rotation.y += turnSpeed;
if(keys['KeyD']) car.rotation.y -= turnSpeed;
}
const nextPos = car.position.clone();
nextPos.x += Math.sin(car.rotation.y) * car.userData.speed;
nextPos.z += Math.cos(car.rotation.y) * car.userData.speed;
if(!checkWall(nextPos, 3.5)) {
car.position.copy(nextPos);
} else {
car.userData.speed *= -0.4;
}
player.position.copy(car.position);
// Давим пешеходов
npcs.forEach(npc => {
if(npc.position.distanceTo(car.position) < 3) {
npc.position.y = -5; // "Убираем"
stats.money += 50;
stats.wanted = Math.min(stats.wanted + 1, 5);
}
});
} else {
const nextPos = player.position.clone();
player.rotation.y = cameraAngleX;
const moveSpeed = 0.35;
if(keys['KeyW']) {
nextPos.x += Math.sin(player.rotation.y) * moveSpeed;
nextPos.z += Math.cos(player.rotation.y) * moveSpeed;
}
if(keys['KeyS']) {
nextPos.x -= Math.sin(player.rotation.y) * moveSpeed;
nextPos.z -= Math.cos(player.rotation.y) * moveSpeed;
}
if(!checkWall(nextPos, 1.2)) player.position.copy(nextPos);
}
// Логика NPC
npcs.forEach(npc => {
npc.userData.timer--;
if(npc.userData.timer <= 0) {
npc.userData.targetAngle = Math.random() * Math.PI * 2;
npc.userData.timer = 100 + Math.random() * 200;
}
npc.rotation.y = THREE.MathUtils.lerp(npc.rotation.y, npc.userData.targetAngle, 0.05);
const nextNpcPos = npc.position.clone();
nextNpcPos.x += Math.sin(npc.rotation.y) * 0.05;
nextNpcPos.z += Math.cos(npc.rotation.y) * 0.05;
if(!checkWall(nextNpcPos, 1)) npc.position.copy(nextNpcPos);
});
}
function animate() {
requestAnimationFrame(animate);
handlePhysics();
bullets.forEach((b, i) => {
b.mesh.position.addScaledVector(b.dir, 2.5);
b.life--;
if(b.life <= 0) {
scene.remove(b.mesh);
bullets.splice(i, 1);
}
});
const target = isInsideCar ? car : player;
const dist = isInsideCar ? 18 : 10;
camera.position.x = target.position.x - Math.sin(cameraAngleX) * dist;
camera.position.z = target.position.z - Math.cos(cameraAngleX) * dist;
camera.position.y = target.position.y + cameraAngleY * dist;
camera.lookAt(target.position.clone().add(new THREE.Vector3(0, 1.5, 0)));
document.getElementById('money').innerText = "$" + stats.money.toString().padStart(8, '0');
document.getElementById('wanted').innerText = "★".repeat(stats.wanted);
document.getElementById('speed-ui').innerText = isInsideCar ? Math.round(Math.abs(car.userData.speed) * 160) + " KM/H" : "ON FOOT";
renderer.render(scene, camera);
}
</script>
</body>
</html>