Gunship-3D-FPS / index.html
gini1
Update index.html
0d86eb7 verified
raw
history blame
12.6 kB
<!DOCTYPE html>
<html>
<head>
<title>3D Survival Game</title>
<style>
body { margin: 0; }
#info {
position: absolute;
top: 10px;
left: 10px;
color: white;
background: rgba(0,0,0,0.7);
padding: 10px;
font-family: Arial;
font-size: 14px;
z-index: 100;
border-radius: 5px;
}
#weaponAlert {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
background: rgba(255,0,0,0.7);
padding: 20px;
font-family: Arial;
font-size: 24px;
display: none;
z-index: 100;
border-radius: 10px;
}
#safeTimer {
position: absolute;
top: 10px;
right: 10px;
color: white;
background: rgba(0,128,0,0.7);
padding: 10px;
font-family: Arial;
font-size: 18px;
z-index: 100;
border-radius: 5px;
}
</style>
</head>
<body>
<div id="info">
Click to start<br>
WASD - Move<br>
Mouse - Look around<br>
Find the weapon to win!<br>
Avoid the enemies!
</div>
<div id="weaponAlert">Weapon has appeared!</div>
<div id="safeTimer"></div>
<script type="module">
import * as THREE from 'https://unpkg.com/three@0.157.0/build/three.module.js';
import { GLTFLoader } from 'https://unpkg.com/three@0.157.0/examples/jsm/loaders/GLTFLoader.js';
import { PointerLockControls } from 'https://unpkg.com/three@0.157.0/examples/jsm/controls/PointerLockControls.js';
// ๊ธฐ๋ณธ ์„ค์ •
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb);
scene.fog = new THREE.Fog(0x87ceeb, 0, 500);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
// ๊ฒŒ์ž„ ๋ณ€์ˆ˜
const SAFE_TIME = 15; // 15์ดˆ ์•ˆ์ „์‹œ๊ฐ„
let gameStartTime = 0;
let isSafePeriod = true;
let enemies = [];
let weapon = null;
let playerModel = null;
let isGameOver = false;
// ํฌ์ธํ„ฐ ๋ฝ ์ปจํŠธ๋กค
const controls = new PointerLockControls(camera, document.body);
controls.addEventListener('lock', () => {
if (!gameStartTime) {
gameStartTime = Date.now();
setTimeout(spawnWeapon, Math.random() * 10000 + 5000); // 5-15์ดˆ ์‚ฌ์ด์— ๋ฌด๊ธฐ ์Šคํฐ
}
});
// ์ง€ํ˜• ์ƒ์„ฑ
const terrainGeometry = new THREE.PlaneGeometry(500, 500, 100, 100);
const terrainMaterial = new THREE.MeshStandardMaterial({
color: 0x3a8c3a,
roughness: 0.8,
metalness: 0.2
});
// ์ง€ํ˜• ๋†’๋‚ฎ์ด ์„ค์ •
const vertices = terrainGeometry.attributes.position.array;
for (let i = 0; i < vertices.length; i += 3) {
vertices[i + 2] = Math.sin(vertices[i] * 0.05) * Math.cos(vertices[i + 1] * 0.05) * 5;
}
terrainGeometry.attributes.position.needsUpdate = true;
terrainGeometry.computeVertexNormals();
const terrain = new THREE.Mesh(terrainGeometry, terrainMaterial);
terrain.rotation.x = -Math.PI / 2;
terrain.receiveShadow = true;
scene.add(terrain);
// ์กฐ๋ช… ์„ค์ •
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(100, 100, 50);
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 500;
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
scene.add(directionalLight);
// ๋‚˜๋ฌด ์ƒ์„ฑ
function createTrees() {
const treeGeometry = new THREE.CylinderGeometry(0, 4, 20, 8);
const treeMaterial = new THREE.MeshStandardMaterial({ color: 0x0d5c0d });
for (let i = 0; i < 200; i++) {
const tree = new THREE.Mesh(treeGeometry, treeMaterial);
tree.position.set(
(Math.random() - 0.5) * 400,
10,
(Math.random() - 0.5) * 400
);
tree.castShadow = true;
tree.receiveShadow = true;
scene.add(tree);
}
}
// ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ๋กœ๋“œ
const loader = new GLTFLoader();
loader.load('me.glb', (gltf) => {
playerModel = gltf.scene;
playerModel.scale.set(0.5, 0.5, 0.5);
playerModel.traverse((node) => {
if (node.isMesh) {
node.castShadow = true;
node.receiveShadow = true;
}
});
scene.add(playerModel);
});
// ์  ์ƒ์„ฑ
function createEnemies() {
loader.load('enemy.glb', (gltf) => {
const enemyModel = gltf.scene;
for (let i = 0; i < 8; i++) {
const enemy = enemyModel.clone();
enemy.scale.set(0.5, 0.5, 0.5);
// ํ”Œ๋ ˆ์ด์–ด๋กœ๋ถ€ํ„ฐ ๋ฉ€๋ฆฌ ์Šคํฐ
const angle = (i / 8) * Math.PI * 2;
const radius = 100;
enemy.position.set(
Math.cos(angle) * radius,
5,
Math.sin(angle) * radius
);
enemy.traverse((node) => {
if (node.isMesh) {
node.castShadow = true;
node.receiveShadow = true;
}
});
scene.add(enemy);
enemies.push({
model: enemy,
velocity: new THREE.Vector3(),
speed: 0.2 + Math.random() * 0.2
});
}
});
}
// ๋ฌด๊ธฐ ์Šคํฐ
function spawnWeapon() {
const weaponGeometry = new THREE.BoxGeometry(1, 1, 3);
const weaponMaterial = new THREE.MeshStandardMaterial({
color: 0xffd700,
metalness: 0.7,
roughness: 0.3,
emissive: 0xffd700,
emissiveIntensity: 0.5
});
weapon = new THREE.Mesh(weaponGeometry, weaponMaterial);
weapon.position.set(
(Math.random() - 0.5) * 200,
2,
(Math.random() - 0.5) * 200
);
weapon.castShadow = true;
scene.add(weapon);
// ๋ฌด๊ธฐ ์ถœํ˜„ ์•Œ๋ฆผ
const weaponAlert = document.getElementById('weaponAlert');
weaponAlert.style.display = 'block';
setTimeout(() => {
weaponAlert.style.display = 'none';
}, 3000);
}
// ์ด๋™ ์ƒํƒœ
const moveState = {
forward: false,
backward: false,
left: false,
right: false
};
// ํ‚ค๋ณด๋“œ ์ด๋ฒคํŠธ
document.addEventListener('keydown', (event) => {
switch (event.code) {
case 'KeyW': moveState.forward = true; break;
case 'KeyS': moveState.backward = true; break;
case 'KeyA': moveState.left = true; break;
case 'KeyD': moveState.right = true; break;
}
});
document.addEventListener('keyup', (event) => {
switch (event.code) {
case 'KeyW': moveState.forward = false; break;
case 'KeyS': moveState.backward = false; break;
case 'KeyA': moveState.left = false; break;
case 'KeyD': moveState.right = false; break;
}
});
// ๊ฒŒ์ž„ ์˜ค๋ฒ„
function gameOver(won = false) {
if (!isGameOver) {
isGameOver = true;
controls.unlock();
alert(won ? 'You found the weapon and won!' : 'Game Over! You were caught!');
location.reload();
}
}
// ์ดˆ๊ธฐ ์„ค์ •
camera.position.set(0, 5, 0);
createTrees();
createEnemies();
// ๊ฒŒ์ž„ ๋ฃจํ”„
function animate() {
requestAnimationFrame(animate);
if (controls.isLocked && !isGameOver) {
// ์•ˆ์ „ ์‹œ๊ฐ„ ์ฒดํฌ
const elapsedTime = Math.floor((Date.now() - gameStartTime) / 1000);
const safeTimer = document.getElementById('safeTimer');
if (elapsedTime < SAFE_TIME) {
safeTimer.textContent = `Safe Time: ${SAFE_TIME - elapsedTime}s`;
isSafePeriod = true;
} else {
safeTimer.textContent = '';
isSafePeriod = false;
}
// ์ด๋™ ์ฒ˜๋ฆฌ
const speed = 0.5;
if (moveState.forward) controls.moveForward(speed);
if (moveState.backward) controls.moveForward(-speed);
if (moveState.left) controls.moveRight(-speed);
if (moveState.right) controls.moveRight(speed);
// ํ”Œ๋ ˆ์ด์–ด ๋ชจ๋ธ ์—…๋ฐ์ดํŠธ
if (playerModel) {
playerModel.position.copy(camera.position);
playerModel.position.y -= 2;
// ์ด๋™ ๋ฐฉํ–ฅ์— ๋”ฐ๋ฅธ ํšŒ์ „
if (moveState.forward || moveState.backward || moveState.left || moveState.right) {
const direction = new THREE.Vector3();
camera.getWorldDirection(direction);
playerModel.rotation.y = Math.atan2(direction.x, direction.z);
}
}
// ์  ์—…๋ฐ์ดํŠธ
if (!isSafePeriod) {
enemies.forEach(enemy => {
const direction = new THREE.Vector3();
direction.subVectors(camera.position, enemy.model.position);
direction.y = 0;
direction.normalize();
enemy.velocity.add(direction.multiplyScalar(enemy.speed));
enemy.velocity.multiplyScalar(0.98);
enemy.model.position.add(enemy.velocity);
// ์  ํšŒ์ „
enemy.model.lookAt(camera.position);
// ์ถฉ๋Œ ์ฒดํฌ
if (enemy.model.position.distanceTo(camera.position) < 3) {
gameOver(false);
}
});
}
// ๋ฌด๊ธฐ ์ฒดํฌ
if (weapon) {
weapon.rotation.y += 0.02;
if (camera.position.distanceTo(weapon.position) < 3) {
gameOver(true);
}
}
}
renderer.render(scene, camera);
}
// ํ™”๋ฉด ํฌ๊ธฐ ์กฐ์ •
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// ์‹œ์ž‘
document.addEventListener('click', () => {
controls.lock();
});
animate();
</script>
</body>
</html>