لعبة الشوتر FPS - الجوال
by FrozenRider22898 lines31.3 KB🛠️ Three.js (3D graphics)
<!DOCTYPE html>
<html lang="ar">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>لعبة الشوتر FPS - الجوال</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
body {
overflow: hidden;
font-family: Arial, sans-serif;
background: #000;
touch-action: none;
user-select: none;
-webkit-user-select: none;
}
#topBar {
position: fixed; top: 15px; left: 15px; right: 15px;
display: flex; justify-content: space-between;
z-index: 10; pointer-events: none;
}
#score, #timer, #health {
color: white; font-size: 16px; font-weight: bold;
text-shadow: 2px 2px 6px black;
background: rgba(0,0,0,0.5);
padding: 8px 12px;
border-radius: 20px;
}
#health { color: #ff5252; }
#lookArea {
position: fixed; top: 0; left: 0;
width: 100%; height: 100%;
z-index: 15;
touch-action: none;
}
#joystick {
position: fixed; bottom: 40px; left: 30px;
width: 130px; height: 130px;
background: rgba(255,255,255,0.15);
border: 2px solid rgba(255,255,255,0.4);
border-radius: 50%;
z-index: 25;
touch-action: none;
}
#stick {
position: absolute;
width: 60px; height: 60px;
background: rgba(255,255,255,0.7);
border: 2px solid white;
border-radius: 50%;
top: 33px; left: 33px;
transition: transform 0.05s ease-out;
box-shadow: 0 4px 10px rgba(0,0,0,0.4);
pointer-events: none;
}
#crosshair {
position: fixed; top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 40px; height: 40px;
z-index: 12;
pointer-events: none;
}
#crosshair .line {
position: absolute;
background: rgba(255, 50, 50, 0.9);
box-shadow: 0 0 4px rgba(0,0,0,0.8);
}
#crosshair .h1 { top: 50%; left: 0; width: 12px; height: 2px; transform: translateY(-50%); }
#crosshair .h2 { top: 50%; right: 0; width: 12px; height: 2px; transform: translateY(-50%); }
#crosshair .v1 { left: 50%; top: 0; width: 2px; height: 12px; transform: translateX(-50%); }
#crosshair .v2 { left: 50%; bottom: 0; width: 2px; height: 12px; transform: translateX(-50%); }
#crosshair .dot { top: 50%; left: 50%; width: 3px; height: 3px; transform: translate(-50%, -50%); border-radius: 50%; }
#fireBtn {
position: fixed; bottom: 50px; right: 40px;
width: 110px; height: 110px;
background: radial-gradient(circle, #ff5252, #b71c1c);
border: 4px solid rgba(255,255,255,0.6);
border-radius: 50%;
color: white;
font-size: 22px;
font-weight: bold;
z-index: 30;
touch-action: none;
box-shadow: 0 6px 20px rgba(255,0,0,0.5), inset 0 0 20px rgba(255,255,255,0.2);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
#fireBtn:active {
transform: scale(0.92);
background: radial-gradient(circle, #ff8a8a, #d32f2f);
}
#reloadBtn {
position: fixed; bottom: 180px; right: 60px;
width: 70px; height: 70px;
background: rgba(50,50,50,0.85);
border: 3px solid rgba(255,255,255,0.5);
border-radius: 50%;
color: white;
font-size: 14px;
font-weight: bold;
z-index: 30;
touch-action: none;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
#reloadBtn:active { transform: scale(0.92); }
#ammo {
position: fixed; bottom: 100px; right: 170px;
color: white; font-size: 20px; font-weight: bold;
text-shadow: 2px 2px 6px black;
background: rgba(0,0,0,0.5);
padding: 6px 14px;
border-radius: 10px;
z-index: 20;
pointer-events: none;
}
#hitFlash {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
background: radial-gradient(circle, transparent 40%, rgba(255,0,0,0.6) 100%);
pointer-events: none;
opacity: 0;
transition: opacity 0.3s;
z-index: 30;
}
#muzzleFlash {
position: fixed; top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 200px; height: 200px;
background: radial-gradient(circle, rgba(255,220,0,0.8), transparent 70%);
pointer-events: none;
opacity: 0;
z-index: 30;
}
#startScreen, #gameOver {
position: fixed; top: 0; left: 0; width: 100%; height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
z-index: 200;
text-align: center;
padding: 20px;
}
#startScreen {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
}
#gameOver {
background: rgba(0,0,0,0.9);
display: none;
}
#startScreen h1 {
font-size: 38px;
margin-bottom: 15px;
color: #ff5252;
text-shadow: 0 0 20px rgba(255,82,82,0.7);
}
#startScreen p {
font-size: 15px;
margin-bottom: 25px;
opacity: 0.9;
line-height: 1.8;
max-width: 420px;
}
#startScreen .controls {
background: rgba(255,255,255,0.1);
padding: 18px;
border-radius: 15px;
margin-bottom: 25px;
text-align: right;
font-size: 14px;
line-height: 1.9;
}
#startBtn, #restartBtn {
padding: 18px 55px;
font-size: 22px;
font-weight: bold;
background: linear-gradient(135deg, #ff5252, #b71c1c);
color: white;
border: none;
border-radius: 50px;
box-shadow: 0 8px 30px rgba(255,82,82,0.6);
cursor: pointer;
touch-action: manipulation;
}
#startBtn:active, #restartBtn:active { transform: scale(0.95); }
#gameOver h1 {
font-size: 44px;
margin-bottom: 20px;
color: #ff5252;
text-shadow: 0 0 30px rgba(255,82,82,0.8);
}
#gameOver p { font-size: 20px; margin-bottom: 12px; }
#gameOver .finalScore {
font-size: 60px;
font-weight: bold;
color: #ffeb3b;
margin: 15px 0 10px;
text-shadow: 3px 3px 15px black;
}
#gameOver .kills {
font-size: 18px;
color: #4CAF50;
margin-bottom: 25px;
}
#hint {
position: fixed;
top: 55%;
left: 50%;
transform: translate(-50%, -50%);
color: rgba(255,255,255,0.85);
font-size: 13px;
text-align: center;
background: rgba(0,0,0,0.6);
padding: 12px 20px;
border-radius: 12px;
z-index: 11;
pointer-events: none;
transition: opacity 1s;
}
</style>
</head>
<body>
<div id="startScreen">
<h1>🔫 لعبة الشوتر FPS</h1>
<div class="controls">
🕹️ <b>العصا اليسرى:</b> للحركة<br>
👆 <b>اسحب في أي مكان:</b> للنظر<br>
🔴 <b>الزر الأحمر:</b> إطلاق النار<br>
🔄 <b>زر RLD:</b> إعادة التلقيم<br>
🎯 <b>الهدف:</b> اقتل أكبر عدد من الأعداء!
</div>
<p>⏱ لديك 60 ثانية — كل عدو يعطيك 10 نقاط</p>
<button id="startBtn">ابدأ اللعب</button>
</div>
<div id="topBar">
<div id="timer">⏱ 60</div>
<div id="score">النقاط: 0</div>
<div id="health">❤️ 100</div>
</div>
<div id="crosshair">
<div class="line h1"></div>
<div class="line h2"></div>
<div class="line v1"></div>
<div class="line v2"></div>
<div class="line dot"></div>
</div>
<div id="hint">اسحب في أي مكان للنظر · اضغط الأحمر لإطلاق النار</div>
<div id="ammo">30 / 30</div>
<div id="lookArea"></div>
<div id="joystick">
<div id="stick"></div>
</div>
<button id="fireBtn">🔥</button>
<button id="reloadBtn">RLD</button>
<div id="hitFlash"></div>
<div id="muzzleFlash"></div>
<div id="gameOver">
<h1>💀 انتهت اللعبة</h1>
<p>نتيجتك النهائية</p>
<div class="finalScore" id="finalScore">0</div>
<div class="kills" id="killsText">الأعداء المقتولون: 0</div>
<button id="restartBtn">العب مرة أخرى</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// ===== الإعداد الأساسي =====
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a1a2e);
scene.fog = new THREE.Fog(0x1a1a2e, 20, 60);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
scene.add(camera);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);
// ===== الإضاءة =====
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 20, 10);
scene.add(light);
scene.add(new THREE.AmbientLight(0x667799, 0.9));
const fillLight = new THREE.PointLight(0x3366ff, 0.5, 50);
fillLight.position.set(0, 5, 0);
scene.add(fillLight);
// ===== الأرضية =====
const floor = new THREE.Mesh(
new THREE.PlaneGeometry(60, 60),
new THREE.MeshStandardMaterial({ color: 0x2c3e50, roughness: 0.9 })
);
floor.rotation.x = -Math.PI / 2;
scene.add(floor);
const grid = new THREE.GridHelper(60, 60, 0x4a6fa5, 0x2c3e50);
grid.position.y = 0.02;
scene.add(grid);
// ===== الجدران =====
const wallMat = new THREE.MeshStandardMaterial({
color: 0x34495e, transparent: true, opacity: 0.5, side: THREE.DoubleSide
});
const wallHeight = 4;
const wallSize = 40;
const wallPositions = [
{ pos: [0, wallHeight/2, -wallSize/2], rot: [0, 0, 0] },
{ pos: [0, wallHeight/2, wallSize/2], rot: [0, 0, 0] },
{ pos: [-wallSize/2, wallHeight/2, 0], rot: [0, Math.PI/2, 0] },
{ pos: [wallSize/2, wallHeight/2, 0], rot: [0, Math.PI/2, 0] }
];
wallPositions.forEach(w => {
const wall = new THREE.Mesh(new THREE.PlaneGeometry(wallSize, wallHeight), wallMat);
wall.position.set(w.pos[0], w.pos[1], w.pos[2]);
wall.rotation.set(w.rot[0], w.rot[1], w.rot[2]);
scene.add(wall);
});
// ===== اللاعب =====
const player = {
position: new THREE.Vector3(0, 1.6, 0),
yaw: 0,
pitch: 0,
health: 100
};
// ===== السلاح =====
const weaponGroup = new THREE.Group();
const weaponBody = new THREE.Mesh(
new THREE.BoxGeometry(0.2, 0.2, 0.7),
new THREE.MeshStandardMaterial({ color: 0x1a1a1a, metalness: 0.8, roughness: 0.3 })
);
weaponBody.position.set(0.35, -0.3, -0.7);
weaponGroup.add(weaponBody);
const weaponBarrel = new THREE.Mesh(
new THREE.CylinderGeometry(0.06, 0.06, 0.5, 12),
new THREE.MeshStandardMaterial({ color: 0x333333, metalness: 0.9, roughness: 0.2 })
);
weaponBarrel.rotation.x = Math.PI / 2;
weaponBarrel.position.set(0.35, -0.3, -1.2);
weaponGroup.add(weaponBarrel);
const weaponGrip = new THREE.Mesh(
new THREE.BoxGeometry(0.15, 0.3, 0.15),
new THREE.MeshStandardMaterial({ color: 0x0a0a0a })
);
weaponGrip.position.set(0.35, -0.5, -0.5);
weaponGroup.add(weaponGrip);
camera.add(weaponGroup);
// ===== الأعداء =====
const enemies = [];
const enemyColors = [0xff1744, 0xff6d00, 0xd500f9, 0x00e5ff];
const MAX_ENEMIES = 8;
const FIELD_LIMIT = 18;
function createEnemy() {
const color = enemyColors[Math.floor(Math.random() * enemyColors.length)];
const enemy = new THREE.Group();
// الجسم - قاعدته على الأرض
const body = new THREE.Mesh(
new THREE.CylinderGeometry(0.4, 0.4, 1.6, 8),
new THREE.MeshStandardMaterial({ color: color, emissive: color, emissiveIntensity: 0.3 })
);
body.position.y = 0.8;
enemy.add(body);
// الرأس
const head = new THREE.Mesh(
new THREE.SphereGeometry(0.35, 12, 12),
new THREE.MeshStandardMaterial({ color: 0xffffff, emissive: color, emissiveIntensity: 0.5 })
);
head.position.y = 1.95;
enemy.add(head);
// العيون
const eyeMat = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const eyeL = new THREE.Mesh(new THREE.SphereGeometry(0.06, 6, 6), eyeMat);
eyeL.position.set(-0.12, 2.0, 0.3);
enemy.add(eyeL);
const eyeR = new THREE.Mesh(new THREE.SphereGeometry(0.06, 6, 6), eyeMat);
eyeR.position.set(0.12, 2.0, 0.3);
enemy.add(eyeR);
// موقع عشوائي بعيد عن اللاعب
let x, z, attempts = 0;
do {
x = (Math.random() - 0.5) * (FIELD_LIMIT * 2 - 4);
z = (Math.random() - 0.5) * (FIELD_LIMIT * 2 - 4);
attempts++;
} while (Math.sqrt((x - player.position.x)**2 + (z - player.position.z)**2) < 8 && attempts < 30);
enemy.position.set(x, 0, z);
enemy.userData = {
color: color,
speed: 0.02 + Math.random() * 0.02,
lastShot: 0,
shootCooldown: 1500 + Math.random() * 1500
};
scene.add(enemy);
enemies.push(enemy);
}
function refillEnemies() {
while (enemies.length < MAX_ENEMIES) {
createEnemy();
}
}
// ===== متغيرات اللعبة =====
let score = 0;
let kills = 0;
let gameActive = false;
let timeLeft = 60;
const moveSpeed = 0.12;
const MAX_AMMO = 30;
let ammo = MAX_AMMO;
let isReloading = false;
// ===== إدخال الحركة =====
const moveInput = { x: 0, y: 0 };
const keys = {};
window.addEventListener('keydown', e => keys[e.key.toLowerCase()] = true);
window.addEventListener('keyup', e => keys[e.key.toLowerCase()] = false);
// ===== عصا التحكم =====
const joystick = document.getElementById('joystick');
const stick = document.getElementById('stick');
let joystickActive = false;
let joystickCenter = { x: 0, y: 0 };
const maxDist = 45;
function getJoystickCenter() {
const rect = joystick.getBoundingClientRect();
return { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 };
}
function handleJoystickStart(e) {
e.preventDefault();
e.stopPropagation();
joystickActive = true;
joystickCenter = getJoystickCenter();
handleJoystickMove(e);
}
function handleJoystickMove(e) {
if (!joystickActive) return;
e.preventDefault();
e.stopPropagation();
const touch = e.touches ? e.touches[0] : e;
let dx = touch.clientX - joystickCenter.x;
let dy = touch.clientY - joystickCenter.y;
const dist = Math.sqrt(dx*dx + dy*dy);
const clampedDist = Math.min(dist, maxDist);
const angle = Math.atan2(dy, dx);
stick.style.transform = `translate(${Math.cos(angle) * clampedDist}px, ${Math.sin(angle) * clampedDist}px)`;
moveInput.x = dx / maxDist;
moveInput.y = dy / maxDist;
const mag = Math.sqrt(moveInput.x**2 + moveInput.y**2);
if (mag > 1) {
moveInput.x /= mag;
moveInput.y /= mag;
}
}
function handleJoystickEnd(e) {
e.preventDefault();
e.stopPropagation();
joystickActive = false;
stick.style.transform = 'translate(0, 0)';
moveInput.x = 0;
moveInput.y = 0;
}
joystick.addEventListener('touchstart', handleJoystickStart, { passive: false });
joystick.addEventListener('touchmove', handleJoystickMove, { passive: false });
joystick.addEventListener('touchend', handleJoystickEnd, { passive: false });
joystick.addEventListener('touchcancel', handleJoystickEnd, { passive: false });
joystick.addEventListener('mousedown', handleJoystickStart);
joystick.addEventListener('mousemove', handleJoystickMove);
joystick.addEventListener('mouseup', handleJoystickEnd);
// ===== منطقة النظر =====
const lookArea = document.getElementById('lookArea');
let lookingActive = false;
let lastLookX = 0, lastLookY = 0;
// ✅✅ حساسية عالية جداً (كانت 0.0025)
const lookSensitivity = 0.008;
function handleLookStart(e) {
if (e.target.closest('#joystick') ||
e.target.closest('#fireBtn') ||
e.target.closest('#reloadBtn')) return;
e.preventDefault();
lookingActive = true;
const touch = e.touches ? e.touches[0] : e;
lastLookX = touch.clientX;
lastLookY = touch.clientY;
const hint = document.getElementById('hint');
if (hint) hint.style.opacity = '0';
}
function handleLookMove(e) {
if (!lookingActive) return;
e.preventDefault();
const touch = e.touches ? e.touches[0] : e;
const dx = touch.clientX - lastLookX;
const dy = touch.clientY - lastLookY;
player.yaw -= dx * lookSensitivity;
player.pitch -= dy * lookSensitivity;
player.pitch = Math.max(-Math.PI/2 + 0.1, Math.min(Math.PI/2 - 0.1, player.pitch));
lastLookX = touch.clientX;
lastLookY = touch.clientY;
}
function handleLookEnd(e) {
e.preventDefault();
lookingActive = false;
}
lookArea.addEventListener('touchstart', handleLookStart, { passive: false });
lookArea.addEventListener('touchmove', handleLookMove, { passive: false });
lookArea.addEventListener('touchend', handleLookEnd, { passive: false });
lookArea.addEventListener('touchcancel', handleLookEnd, { passive: false });
lookArea.addEventListener('mousedown', handleLookStart);
window.addEventListener('mousemove', handleLookMove);
window.addEventListener('mouseup', handleLookEnd);
// ===== إطلاق النار =====
const fireBtn = document.getElementById('fireBtn');
const reloadBtn = document.getElementById('reloadBtn');
const muzzleFlash = document.getElementById('muzzleFlash');
function shoot() {
if (!gameActive || isReloading) return;
if (ammo <= 0) {
reload();
return;
}
ammo--;
updateAmmoDisplay();
muzzleFlash.style.opacity = '1';
setTimeout(() => muzzleFlash.style.opacity = '0', 60);
weaponGroup.position.z = 0.1;
setTimeout(() => weaponGroup.position.z = 0, 60);
const raycaster = new THREE.Raycaster();
const screenCenter = new THREE.Vector2(0, 0);
raycaster.setFromCamera(screenCenter, camera);
let closestEnemy = null;
let closestDist = Infinity;
enemies.forEach(enemy => {
const enemyCenter = new THREE.Vector3(enemy.position.x, 1, enemy.position.z);
const ray = raycaster.ray;
const toEnemy = new THREE.Vector3().subVectors(enemyCenter, ray.origin);
const proj = toEnemy.dot(ray.direction);
if (proj > 0 && proj < 50) {
const closest = ray.origin.clone().add(ray.direction.clone().multiplyScalar(proj));
const distToRay = closest.distanceTo(enemyCenter);
const hitRadius = 0.9 + proj * 0.02;
if (distToRay < hitRadius && proj < closestDist) {
closestDist = proj;
closestEnemy = enemy;
}
}
});
if (closestEnemy) {
killEnemy(closestEnemy);
}
}
function killEnemy(enemy) {
const idx = enemies.indexOf(enemy);
if (idx === -1) return;
enemies.splice(idx, 1);
scene.remove(enemy);
score += 10;
kills++;
updateUI();
setTimeout(() => {
if (gameActive) refillEnemies();
}, 500);
}
function reload() {
if (isReloading || ammo === MAX_AMMO) return;
isReloading = true;
updateAmmoDisplay();
setTimeout(() => {
ammo = MAX_AMMO;
isReloading = false;
updateAmmoDisplay();
}, 1000);
}
function updateAmmoDisplay() {
const el = document.getElementById('ammo');
if (isReloading) {
el.textContent = '... جاري التلقيم';
el.style.color = '#ffeb3b';
} else {
el.textContent = ammo + ' / ' + MAX_AMMO;
el.style.color = ammo <= 5 ? '#ff5252' : 'white';
}
}
fireBtn.addEventListener('pointerdown', (e) => {
e.preventDefault();
e.stopPropagation();
shoot();
});
reloadBtn.addEventListener('pointerdown', (e) => {
e.preventDefault();
e.stopPropagation();
reload();
});
// ===== بدء اللعبة =====
function startGame() {
score = 0;
kills = 0;
timeLeft = 60;
ammo = MAX_AMMO;
isReloading = false;
player.health = 100;
gameActive = true;
enemies.forEach(e => scene.remove(e));
enemies.length = 0;
player.position.set(0, 1.6, 0);
player.yaw = 0;
player.pitch = 0;
refillEnemies();
updateUI();
updateAmmoDisplay();
document.getElementById('startScreen').style.display = 'none';
document.getElementById('gameOver').style.display = 'none';
const hint = document.getElementById('hint');
hint.style.opacity = '1';
setTimeout(() => hint.style.opacity = '0', 4000);
lastTime = performance.now();
animate();
}
function updateUI() {
document.getElementById('score').textContent = 'النقاط: ' + score;
document.getElementById('timer').textContent = '⏱ ' + Math.max(0, Math.ceil(timeLeft));
document.getElementById('health').textContent = '❤️ ' + Math.max(0, player.health);
}
// ===== حلقة اللعبة =====
let lastTime = performance.now();
let bobTime = 0;
function animate() {
if (!gameActive) return;
requestAnimationFrame(animate);
const now = performance.now();
const delta = Math.min((now - lastTime) / 1000, 0.1);
lastTime = now;
// كيبورد للكمبيوتر
if (!joystickActive) {
let x = 0, y = 0;
if (keys['a']) x -= 1;
if (keys['d']) x += 1;
if (keys['w']) y -= 1;
if (keys['s']) y += 1;
moveInput.x = x;
moveInput.y = y;
if (keys['q']) player.yaw += 2 * delta;
if (keys['e']) player.yaw -= 2 * delta;
}
// حركة اللاعب
const forward = new THREE.Vector3(-Math.sin(player.yaw), 0, -Math.cos(player.yaw));
const right = new THREE.Vector3(Math.cos(player.yaw), 0, -Math.sin(player.yaw));
const movement = new THREE.Vector3();
movement.addScaledVector(forward, -moveInput.y);
movement.addScaledVector(right, moveInput.x);
if (movement.length() > 1) movement.normalize();
player.position.x += movement.x * moveSpeed * 60 * delta;
player.position.z += movement.z * moveSpeed * 60 * delta;
player.position.x = Math.max(-FIELD_LIMIT, Math.min(FIELD_LIMIT, player.position.x));
player.position.z = Math.max(-FIELD_LIMIT, Math.min(FIELD_LIMIT, player.position.z));
// اهتزاز خفيف
if (movement.length() > 0.1) {
bobTime += delta * 10;
} else {
bobTime += delta * 3;
}
const bobAmount = movement.length() * 0.05;
const bobY = Math.sin(bobTime) * bobAmount;
// الكاميرا
camera.position.x = player.position.x;
camera.position.y = player.position.y + bobY;
camera.position.z = player.position.z;
const lookDir = new THREE.Vector3(
-Math.sin(player.yaw) * Math.cos(player.pitch),
Math.sin(player.pitch),
-Math.cos(player.yaw) * Math.cos(player.pitch)
);
camera.lookAt(
camera.position.x + lookDir.x,
camera.position.y + lookDir.y,
camera.position.z + lookDir.z
);
// حركة الأعداء - ثابتون على الأرض بدون طفو
enemies.forEach(enemy => {
const ud = enemy.userData;
const dx = player.position.x - enemy.position.x;
const dz = player.position.z - enemy.position.z;
const distToPlayer = Math.sqrt(dx*dx + dz*dz);
// دوران حول Y فقط
enemy.rotation.y = Math.atan2(dx, dz);
// الاقتراب من اللاعب
if (distToPlayer > 3) {
const invDist = 1 / distToPlayer;
enemy.position.x += dx * invDist * ud.speed;
enemy.position.z += dz * invDist * ud.speed;
}
// هجوم اللاعب
if (distToPlayer < 3 && now - ud.lastShot > ud.shootCooldown) {
ud.lastShot = now;
player.health -= 10;
const hitFlash = document.getElementById('hitFlash');
hitFlash.style.opacity = '1';
setTimeout(() => hitFlash.style.opacity = '0', 200);
if (player.health <= 0) {
player.health = 0;
updateUI();
endGame();
return;
}
updateUI();
}
});
if (!gameActive) return;
if (enemies.length < MAX_ENEMIES) {
refillEnemies();
}
timeLeft -= delta;
updateUI();
if (timeLeft <= 0) {
endGame();
return;
}
renderer.render(scene, camera);
}
function endGame() {
gameActive = false;
document.getElementById('finalScore').textContent = score;
document.getElementById('killsText').textContent = 'الأعداء المقتولون: ' + kills;
document.getElementById('gameOver').style.display = 'flex';
}
// ===== أزرار البدء =====
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
startBtn.addEventListener('touchend', (e) => {
e.preventDefault();
startGame();
}, { passive: false });
restartBtn.addEventListener('touchend', (e) => {
e.preventDefault();
startGame();
}, { passive: false });
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
document.addEventListener('touchmove', (e) => {
if (e.target.closest('#joystick') ||
e.target.closest('#lookArea') ||
e.target.closest('#fireBtn') ||
e.target.closest('#reloadBtn')) return;
e.preventDefault();
}, { passive: false });
console.log('✅ اللعبة جاهزة - اضغط ابدأ اللعب');
</script>
</body>
</html>Game Source: لعبة الشوتر FPS - الجوال
Creator: FrozenRider22
Libraries: three
Complexity: complex (898 lines, 31.3 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: fps-frozenrider22-mv2drm5f" to link back to the original. Then publish at arcadelab.ai/publish.