Kota Merdeka - Mirip GTA SA
by CrystalLegend30222 lines8.9 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kota Merdeka - Mirip GTA SA</title>
<style>
body { margin: 0; overflow: hidden; font-family: Arial; }
#info {
position: absolute; top: 10px; left: 10px;
color: white; background: rgba(0,0,0,0.6);
padding: 10px; border-radius: 5px; z-index: 100;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.158.0/examples/js/controls/PointerLockControls.js"></script>
</head>
<body>
<div id="info">
🎮 Kontrol:<br>
WASD = Gerak | Mouse = Lihat Sekitar<br>
Spasi = Lompat/Gas | Shift = Lari<br>
E = Masuk/Keluar Mobil | Klik layar untuk mulai
</div>
<script>
let scene, camera, renderer, controls;
let player, car, isInCar = false;
let moveForward = false, moveBackward = false, moveLeft = false, moveRight = false;
let canJump = true;
const playerSpeed = 0.12;
const runSpeed = 0.22;
const carSpeed = 0.15;
const carTurnSpeed = 0.03;
// Inisialisasi dasar
function init() {
// Scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0x87CEEB);
scene.fog = new THREE.Fog(0x87CEEB, 30, 150);
// Kamera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 200);
camera.position.set(0, 5, -10);
// Renderer
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Kontrol kamera
controls = new THREE.PointerLockControls(camera, document.body);
document.addEventListener('click', () => controls.lock());
controls.addEventListener('lock', () => document.getElementById('info').style.display = 'none');
controls.addEventListener('unlock', () => document.getElementById('info').style.display = 'block');
// Pencahayaan
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(20, 50, 20);
light.castShadow = true;
scene.add(light);
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
// Tanah
const groundGeo = new THREE.PlaneGeometry(200, 200);
const groundMat = new THREE.MeshStandardMaterial({ color: 0x3a7d44, roughness: 0.8 });
const ground = new THREE.Mesh(groundGeo, groundMat);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// Bangunan kota
createBuildings();
// Karakter pemain
createPlayer();
// Kendaraan
createCar();
// Deteksi tombol
document.addEventListener('keydown', onKeyDown);
document.addEventListener('keyup', onKeyUp);
window.addEventListener('resize', onWindowResize);
animate();
}
// Buat bangunan sederhana
function createBuildings() {
const colors = [0x8b4513, 0x2c3e50, 0x95a5a6, 0xe74c3c];
for (let x = -80; x <= 80; x += 20) {
for (let z = -80; z <= 80; z += 20) {
if (Math.abs(x) < 10 && Math.abs(z) < 10) continue;
const height = Math.random() * 8 + 4;
const buildingGeo = new THREE.BoxGeometry(8, height, 8);
const buildingMat = new THREE.MeshStandardMaterial({
color: colors[Math.floor(Math.random() * colors.length)]
});
const building = new THREE.Mesh(buildingGeo, buildingMat);
building.position.set(x, height/2, z);
building.castShadow = true;
building.receiveShadow = true;
scene.add(building);
}
}
}
// Buat karakter
function createPlayer() {
const playerGeo = new THREE.CapsuleGeometry(0.5, 1, 4, 8);
const playerMat = new THREE.MeshStandardMaterial({ color: 0x2196F3 });
player = new THREE.Mesh(playerGeo, playerMat);
player.position.set(0, 1, 0);
player.castShadow = true;
scene.add(player);
}
// Buat mobil
function createCar() {
const bodyGeo = new THREE.BoxGeometry(2, 1, 4);
const bodyMat = new THREE.MeshStandardMaterial({ color: 0xe63946 });
const body = new THREE.Mesh(bodyGeo, bodyMat);
body.position.y = 0.6;
const wheelGeo = new THREE.CylinderGeometry(0.4, 0.4, 0.3, 16);
const wheelMat = new THREE.MeshStandardMaterial({ color: 0x222222 });
const wheel1 = new THREE.Mesh(wheelGeo, wheelMat);
wheel1.rotation.z = Math.PI / 2;
wheel1.position.set(-1, 0.3, 1.5);
const wheel2 = wheel1.clone(); wheel2.position.x = 1;
const wheel3 = wheel1.clone(); wheel3.position.z = -1.5;
const wheel4 = wheel2.clone(); wheel4.position.z = -1.5;
car = new THREE.Group();
car.add(body, wheel1, wheel2, wheel3, wheel4);
car.position.set(8, 0, 0);
car.castShadow = true;
scene.add(car);
}
// Input tombol
function onKeyDown(e) {
switch(e.code) {
case 'KeyW': moveForward = true; break;
case 'KeyS': moveBackward = true; break;
case 'KeyA': moveLeft = true; break;
case 'KeyD': moveRight = true; break;
case 'Space': if(!isInCar && canJump) { player.position.y += 1.2; canJump = false; setTimeout(() => canJump = true, 800); } break;
case 'ShiftLeft': case 'ShiftRight': player.isRunning = true; break;
case 'KeyE': enterExitCar(); break;
}
}
function onKeyUp(e) {
switch(e.code) {
case 'KeyW': moveForward = false; break;
case 'KeyS': moveBackward = false; break;
case 'KeyA': moveLeft = false; break;
case 'KeyD': moveRight = false; break;
case 'ShiftLeft': case 'ShiftRight': player.isRunning = false; break;
}
}
// Masuk/keluar mobil
function enterExitCar() {
const jarak = player.position.distanceTo(car.position);
if(!isInCar && jarak < 3) {
isInCar = true;
player.visible = false;
camera.position.set(0, 2, -5);
camera.parent = car;
} else if(isInCar) {
isInCar = false;
player.visible = true;
player.position.copy(car.position).add(new THREE.Vector3(-3, 0, 0));
camera.parent = null;
camera.position.set(player.position.x, player.position.y + 2, player.position.z - 5);
}
}
// Gerakan
function updateMovement() {
const speed = player.isRunning ? runSpeed : playerSpeed;
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
direction.y = 0;
direction.normalize();
if(!isInCar) {
if(moveForward) player.position.addScaledVector(direction, speed);
if(moveBackward) player.position.addScaledVector(direction, -speed * 0.7);
if(moveLeft) player.position.addScaledVector(direction.cross(new THREE.Vector3(0,1,0)), -speed * 0.7);
if(moveRight) player.position.addScaledVector(direction.cross(new THREE.Vector3(0,1,0)), speed * 0.7);
if(player.position.y > 1) player.position.y -= 0.05;
camera.position.set(player.position.x, player.position.y + 2, player.position.z - 5);
} else {
if(moveForward) car.translateZ(-carSpeed);
if(moveBackward) car.translateZ(carSpeed * 0.6);
if(moveLeft) car.rotation.y += carTurnSpeed;
if(moveRight) car.rotation.y -= carTurnSpeed;
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
updateMovement();
renderer.render(scene, camera);
}
init();
</script>
</body>
</html>Game Source: Kota Merdeka - Mirip GTA SA
Creator: CrystalLegend30
Libraries: three
Complexity: complex (222 lines, 8.9 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: kota-merdeka-mirip-gta-sa-crystallegend30" to link back to the original. Then publish at arcadelab.ai/publish.