|
|
<!DOCTYPE html> |
|
|
<html lang="en"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>3D射击跟枪练习器</title> |
|
|
<script src="https://cdn.tailwindcss.com"></script> |
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
|
|
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/PointerLockControls.js"></script> |
|
|
<style> |
|
|
body { |
|
|
margin: 0; |
|
|
overflow: hidden; |
|
|
font-family: 'Arial', sans-serif; |
|
|
} |
|
|
#container { |
|
|
position: relative; |
|
|
width: 100%; |
|
|
height: 100vh; |
|
|
} |
|
|
#crosshair { |
|
|
position: absolute; |
|
|
top: 50%; |
|
|
left: 50%; |
|
|
transform: translate(-50%, -50%); |
|
|
width: 30px; |
|
|
height: 30px; |
|
|
pointer-events: none; |
|
|
z-index: 100; |
|
|
} |
|
|
#ui { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
padding: 20px; |
|
|
color: white; |
|
|
background-color: rgba(0, 0, 0, 0.5); |
|
|
z-index: 10; |
|
|
} |
|
|
#start-screen { |
|
|
position: absolute; |
|
|
top: 0; |
|
|
left: 0; |
|
|
width: 100%; |
|
|
height: 100%; |
|
|
background-color: rgba(0, 0, 0, 0.8); |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
z-index: 200; |
|
|
color: white; |
|
|
} |
|
|
#instructions { |
|
|
position: absolute; |
|
|
bottom: 20px; |
|
|
left: 20px; |
|
|
color: white; |
|
|
background-color: rgba(0, 0, 0, 0.5); |
|
|
padding: 10px; |
|
|
border-radius: 5px; |
|
|
z-index: 10; |
|
|
} |
|
|
.target-hit { |
|
|
position: absolute; |
|
|
color: red; |
|
|
font-size: 24px; |
|
|
font-weight: bold; |
|
|
animation: fadeOut 1s forwards; |
|
|
} |
|
|
@keyframes fadeOut { |
|
|
to { |
|
|
opacity: 0; |
|
|
transform: translateY(-50px); |
|
|
} |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body> |
|
|
<div id="container"> |
|
|
<div id="crosshair"> |
|
|
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> |
|
|
<circle cx="50" cy="50" r="40" fill="none" stroke="white" stroke-width="2"/> |
|
|
<line x1="50" y1="10" x2="50" y2="30" stroke="white" stroke-width="2"/> |
|
|
<line x1="50" y1="70" x2="50" y2="90" stroke="white" stroke-width="2"/> |
|
|
<line x1="10" y1="50" x2="30" y2="50" stroke="white" stroke-width="2"/> |
|
|
<line x1="70" y1="50" x2="90" y2="50" stroke="white" stroke-width="2"/> |
|
|
</svg> |
|
|
</div> |
|
|
|
|
|
<div id="ui"> |
|
|
<div class="flex justify-between"> |
|
|
<div> |
|
|
<h2 class="text-xl font-bold">3D射击跟枪练习器</h2> |
|
|
<p>提高你的Roblox射击跟枪技巧</p> |
|
|
</div> |
|
|
<div class="text-right"> |
|
|
<p class="text-lg">得分: <span id="score">0</span></p> |
|
|
<p>命中率: <span id="accuracy">0</span>%</p> |
|
|
<p>剩余时间: <span id="time">60</span>秒</p> |
|
|
<p>剩余敌人: <span id="enemies">5</span></p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div id="start-screen"> |
|
|
<h1 class="text-4xl font-bold mb-8">3D射击跟枪练习器</h1> |
|
|
<p class="text-xl mb-8">练习你的跟枪技巧,提高Roblox游戏中的表现</p> |
|
|
<div class="mb-8"> |
|
|
<label for="difficulty" class="block mb-2">选择难度:</label> |
|
|
<select id="difficulty" class="bg-gray-700 text-white p-2 rounded"> |
|
|
<option value="easy">简单</option> |
|
|
<option value="medium" selected>中等</option> |
|
|
<option value="hard">困难</option> |
|
|
<option value="extreme">极限</option> |
|
|
</select> |
|
|
</div> |
|
|
<button id="start-btn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-full text-xl transition duration-300"> |
|
|
开始练习 |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div id="instructions"> |
|
|
<p>WASD: 移动</p> |
|
|
<p>鼠标: 瞄准</p> |
|
|
<p>左键: 射击</p> |
|
|
<p>空格: 跳跃</p> |
|
|
<p>Shift: 冲刺</p> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
let scene, camera, renderer, controls; |
|
|
let enemies = []; |
|
|
let score = 0; |
|
|
let shotsFired = 0; |
|
|
let hits = 0; |
|
|
let gameTime = 60; |
|
|
let gameInterval; |
|
|
let difficulty = 'medium'; |
|
|
let gameStarted = false; |
|
|
let clock = new THREE.Clock(); |
|
|
let player, playerVelocity = new THREE.Vector3(); |
|
|
let playerDirection = new THREE.Vector3(); |
|
|
let moveForward = false; |
|
|
let moveBackward = false; |
|
|
let moveLeft = false; |
|
|
let moveRight = false; |
|
|
let canJump = true; |
|
|
let isSprinting = false; |
|
|
let pointerLockEnabled = false; |
|
|
|
|
|
|
|
|
function init() { |
|
|
|
|
|
scene = new THREE.Scene(); |
|
|
scene.background = new THREE.Color(0x88ccff); |
|
|
scene.fog = new THREE.FogExp2(0x88ccff, 0.002); |
|
|
|
|
|
|
|
|
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); |
|
|
camera.position.y = 1.6; |
|
|
|
|
|
|
|
|
renderer = new THREE.WebGLRenderer({ antialias: true }); |
|
|
renderer.setPixelRatio(window.devicePixelRatio); |
|
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
|
document.getElementById('container').prepend(renderer.domElement); |
|
|
|
|
|
|
|
|
const ambientLight = new THREE.AmbientLight(0x404040); |
|
|
scene.add(ambientLight); |
|
|
|
|
|
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); |
|
|
directionalLight.position.set(1, 1, 1); |
|
|
scene.add(directionalLight); |
|
|
|
|
|
|
|
|
const groundGeometry = new THREE.PlaneGeometry(100, 100); |
|
|
const groundMaterial = new THREE.MeshStandardMaterial({ |
|
|
color: 0x3a5f0b, |
|
|
roughness: 0.8, |
|
|
metalness: 0.2 |
|
|
}); |
|
|
const ground = new THREE.Mesh(groundGeometry, groundMaterial); |
|
|
ground.rotation.x = -Math.PI / 2; |
|
|
ground.receiveShadow = true; |
|
|
scene.add(ground); |
|
|
|
|
|
|
|
|
addObstacles(); |
|
|
|
|
|
|
|
|
createPlayer(); |
|
|
|
|
|
|
|
|
controls = new THREE.PointerLockControls(camera, document.body); |
|
|
|
|
|
|
|
|
window.addEventListener('resize', onWindowResize); |
|
|
document.addEventListener('click', onMouseClick, false); |
|
|
document.addEventListener('keydown', onKeyDown); |
|
|
document.addEventListener('keyup', onKeyUp); |
|
|
|
|
|
|
|
|
document.addEventListener('pointerlockchange', onPointerLockChange, false); |
|
|
document.addEventListener('mozpointerlockchange', onPointerLockChange, false); |
|
|
document.addEventListener('webkitpointerlockchange', onPointerLockChange, false); |
|
|
|
|
|
|
|
|
document.getElementById('start-btn').addEventListener('click', function() { |
|
|
|
|
|
const element = document.body; |
|
|
element.requestPointerLock = element.requestPointerLock || |
|
|
element.mozRequestPointerLock || |
|
|
element.webkitRequestPointerLock; |
|
|
element.requestPointerLock(); |
|
|
}); |
|
|
|
|
|
|
|
|
document.getElementById('difficulty').addEventListener('change', function(e) { |
|
|
difficulty = e.target.value; |
|
|
}); |
|
|
|
|
|
|
|
|
animate(); |
|
|
} |
|
|
|
|
|
|
|
|
function onPointerLockChange() { |
|
|
pointerLockEnabled = (document.pointerLockElement === document.body || |
|
|
document.mozPointerLockElement === document.body || |
|
|
document.webkitPointerLockElement === document.body); |
|
|
|
|
|
if (pointerLockEnabled && !gameStarted) { |
|
|
|
|
|
startGame(); |
|
|
} else if (!pointerLockEnabled && gameStarted) { |
|
|
|
|
|
pauseGame(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function addObstacles() { |
|
|
const boxGeometry = new THREE.BoxGeometry(5, 5, 5); |
|
|
const boxMaterial = new THREE.MeshStandardMaterial({ |
|
|
color: 0x8b4513, |
|
|
roughness: 0.7, |
|
|
metalness: 0.3 |
|
|
}); |
|
|
|
|
|
|
|
|
for (let i = 0; i < 10; i++) { |
|
|
const box = new THREE.Mesh(boxGeometry, boxMaterial); |
|
|
box.position.x = Math.random() * 80 - 40; |
|
|
box.position.z = Math.random() * 80 - 40; |
|
|
box.position.y = 2.5; |
|
|
box.castShadow = true; |
|
|
box.receiveShadow = true; |
|
|
scene.add(box); |
|
|
} |
|
|
|
|
|
|
|
|
const wallGeometry = new THREE.BoxGeometry(1, 3, 20); |
|
|
const wallMaterial = new THREE.MeshStandardMaterial({ color: 0x7f7f7f }); |
|
|
|
|
|
for (let i = 0; i < 5; i++) { |
|
|
const wall = new THREE.Mesh(wallGeometry, wallMaterial); |
|
|
wall.position.x = Math.random() * 60 - 30; |
|
|
wall.position.z = Math.random() * 60 - 30; |
|
|
wall.position.y = 1.5; |
|
|
wall.castShadow = true; |
|
|
wall.receiveShadow = true; |
|
|
scene.add(wall); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function createPlayer() { |
|
|
|
|
|
const geometry = new THREE.BoxGeometry(0.5, 1.8, 0.5); |
|
|
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true }); |
|
|
player = new THREE.Mesh(geometry, material); |
|
|
player.position.y = 1.8; |
|
|
player.visible = false; |
|
|
scene.add(player); |
|
|
} |
|
|
|
|
|
|
|
|
function onWindowResize() { |
|
|
camera.aspect = window.innerWidth / window.innerHeight; |
|
|
camera.updateProjectionMatrix(); |
|
|
renderer.setSize(window.innerWidth, window.innerHeight); |
|
|
} |
|
|
|
|
|
|
|
|
function onMouseClick(event) { |
|
|
if (!gameStarted || !pointerLockEnabled) return; |
|
|
|
|
|
|
|
|
if (event.button === 0) { |
|
|
shotsFired++; |
|
|
updateAccuracy(); |
|
|
|
|
|
|
|
|
const raycaster = new THREE.Raycaster(); |
|
|
raycaster.setFromCamera(new THREE.Vector2(0, 0), camera); |
|
|
|
|
|
|
|
|
const intersects = raycaster.intersectObjects(enemies); |
|
|
|
|
|
if (intersects.length > 0) { |
|
|
const enemy = intersects[0].object; |
|
|
|
|
|
|
|
|
scene.remove(enemy); |
|
|
enemies = enemies.filter(e => e !== enemy); |
|
|
document.getElementById('enemies').textContent = enemies.length; |
|
|
|
|
|
|
|
|
score += 10; |
|
|
document.getElementById('score').textContent = score; |
|
|
|
|
|
|
|
|
hits++; |
|
|
updateAccuracy(); |
|
|
|
|
|
|
|
|
showHitEffect(); |
|
|
|
|
|
|
|
|
if (enemies.length === 0) { |
|
|
endGame(true); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function showHitEffect() { |
|
|
const hitEffect = document.createElement('div'); |
|
|
hitEffect.className = 'target-hit'; |
|
|
hitEffect.textContent = '+10'; |
|
|
hitEffect.style.left = '50%'; |
|
|
hitEffect.style.top = '50%'; |
|
|
document.getElementById('container').appendChild(hitEffect); |
|
|
|
|
|
setTimeout(() => { |
|
|
hitEffect.remove(); |
|
|
}, 1000); |
|
|
} |
|
|
|
|
|
|
|
|
function updateAccuracy() { |
|
|
const accuracy = shotsFired > 0 ? Math.round((hits / shotsFired) * 100) : 0; |
|
|
document.getElementById('accuracy').textContent = accuracy; |
|
|
} |
|
|
|
|
|
|
|
|
function onKeyDown(event) { |
|
|
if (!gameStarted || !pointerLockEnabled) return; |
|
|
|
|
|
switch(event.code) { |
|
|
case 'KeyW': moveForward = true; break; |
|
|
case 'KeyA': moveLeft = true; break; |
|
|
case 'KeyS': moveBackward = true; break; |
|
|
case 'KeyD': moveRight = true; break; |
|
|
case 'Space': |
|
|
if (canJump) { |
|
|
playerVelocity.y += 15; |
|
|
canJump = false; |
|
|
} |
|
|
break; |
|
|
case 'ShiftLeft': |
|
|
isSprinting = true; |
|
|
break; |
|
|
case 'Escape': |
|
|
|
|
|
document.exitPointerLock = document.exitPointerLock || |
|
|
document.mozExitPointerLock || |
|
|
document.webkitExitPointerLock; |
|
|
document.exitPointerLock(); |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function onKeyUp(event) { |
|
|
if (!gameStarted || !pointerLockEnabled) return; |
|
|
|
|
|
switch(event.code) { |
|
|
case 'KeyW': moveForward = false; break; |
|
|
case 'KeyA': moveLeft = false; break; |
|
|
case 'KeyS': moveBackward = false; break; |
|
|
case 'KeyD': moveRight = false; break; |
|
|
case 'ShiftLeft': |
|
|
isSprinting = false; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function createEnemy() { |
|
|
const enemyGeometry = new THREE.BoxGeometry(0.8, 1.8, 0.8); |
|
|
const enemyMaterial = new THREE.MeshStandardMaterial({ |
|
|
color: 0xff0000, |
|
|
roughness: 0.7, |
|
|
metalness: 0.3 |
|
|
}); |
|
|
const enemy = new THREE.Mesh(enemyGeometry, enemyMaterial); |
|
|
|
|
|
|
|
|
let x, z; |
|
|
do { |
|
|
x = (Math.random() * 60) - 30; |
|
|
z = (Math.random() * 60) - 30; |
|
|
} while (Math.sqrt(x*x + z*z) < 10); |
|
|
|
|
|
enemy.position.set(x, 0.9, z); |
|
|
enemy.castShadow = true; |
|
|
enemy.receiveShadow = true; |
|
|
scene.add(enemy); |
|
|
enemies.push(enemy); |
|
|
|
|
|
|
|
|
animateEnemy(enemy); |
|
|
} |
|
|
|
|
|
|
|
|
function animateEnemy(enemy) { |
|
|
let speed = 0.02; |
|
|
let directionChangeTime = 0; |
|
|
let currentDirection = new THREE.Vector3( |
|
|
Math.random() * 2 - 1, |
|
|
0, |
|
|
Math.random() * 2 - 1 |
|
|
).normalize(); |
|
|
|
|
|
|
|
|
switch(difficulty) { |
|
|
case 'easy': speed = 0.015; break; |
|
|
case 'medium': speed = 0.025; break; |
|
|
case 'hard': speed = 0.035; break; |
|
|
case 'extreme': speed = 0.045; break; |
|
|
} |
|
|
|
|
|
function move() { |
|
|
if (!gameStarted) return; |
|
|
|
|
|
|
|
|
directionChangeTime--; |
|
|
if (directionChangeTime <= 0) { |
|
|
|
|
|
if (Math.random() > 0.7) { |
|
|
currentDirection = new THREE.Vector3().subVectors( |
|
|
player.position, |
|
|
enemy.position |
|
|
).normalize(); |
|
|
} else { |
|
|
currentDirection = new THREE.Vector3( |
|
|
Math.random() * 2 - 1, |
|
|
0, |
|
|
Math.random() * 2 - 1 |
|
|
).normalize(); |
|
|
} |
|
|
directionChangeTime = 100 + Math.random() * 100; |
|
|
} |
|
|
|
|
|
|
|
|
enemy.position.x += currentDirection.x * speed; |
|
|
enemy.position.z += currentDirection.z * speed; |
|
|
|
|
|
|
|
|
if (enemy.position.x < -45) enemy.position.x = -45; |
|
|
if (enemy.position.x > 45) enemy.position.x = 45; |
|
|
if (enemy.position.z < -45) enemy.position.z = -45; |
|
|
if (enemy.position.z > 45) enemy.position.z = 45; |
|
|
|
|
|
|
|
|
enemy.lookAt(player.position); |
|
|
|
|
|
requestAnimationFrame(move); |
|
|
} |
|
|
|
|
|
move(); |
|
|
} |
|
|
|
|
|
|
|
|
function startGame() { |
|
|
|
|
|
score = 0; |
|
|
shotsFired = 0; |
|
|
hits = 0; |
|
|
gameTime = 60; |
|
|
gameStarted = true; |
|
|
|
|
|
|
|
|
document.getElementById('score').textContent = score; |
|
|
document.getElementById('accuracy').textContent = '0'; |
|
|
document.getElementById('time').textContent = gameTime; |
|
|
|
|
|
|
|
|
document.getElementById('start-screen').style.display = 'none'; |
|
|
|
|
|
|
|
|
enemies.forEach(enemy => scene.remove(enemy)); |
|
|
enemies = []; |
|
|
|
|
|
|
|
|
const enemyCount = difficulty === 'easy' ? 3 : |
|
|
difficulty === 'medium' ? 5 : |
|
|
difficulty === 'hard' ? 7 : 10; |
|
|
|
|
|
for (let i = 0; i < enemyCount; i++) { |
|
|
createEnemy(); |
|
|
} |
|
|
|
|
|
document.getElementById('enemies').textContent = enemyCount; |
|
|
|
|
|
|
|
|
player.position.set(0, 1.8, 0); |
|
|
controls.getObject().position.set(0, 1.6, 0); |
|
|
controls.getObject().rotation.set(0, 0, 0); |
|
|
|
|
|
|
|
|
gameInterval = setInterval(() => { |
|
|
gameTime--; |
|
|
document.getElementById('time').textContent = gameTime; |
|
|
|
|
|
if (gameTime <= 0) { |
|
|
endGame(false); |
|
|
} |
|
|
}, 1000); |
|
|
} |
|
|
|
|
|
|
|
|
function pauseGame() { |
|
|
|
|
|
const startScreen = document.getElementById('start-screen'); |
|
|
startScreen.style.display = 'flex'; |
|
|
|
|
|
|
|
|
const accuracy = shotsFired > 0 ? Math.round((hits / shotsFired) * 100) : 0; |
|
|
startScreen.innerHTML = ` |
|
|
<h1 class="text-4xl font-bold mb-8">游戏暂停</h1> |
|
|
<div class="text-xl mb-8"> |
|
|
<p>当前得分: ${score}</p> |
|
|
<p>命中率: ${accuracy}%</p> |
|
|
<p>总射击次数: ${shotsFired}</p> |
|
|
<p>命中次数: ${hits}</p> |
|
|
<p>剩余敌人: ${enemies.length}</p> |
|
|
</div> |
|
|
<div class="mb-8"> |
|
|
<label for="difficulty" class="block mb-2">选择难度:</label> |
|
|
<select id="difficulty" class="bg-gray-700 text-white p-2 rounded"> |
|
|
<option value="easy">简单</option> |
|
|
<option value="medium" selected>中等</option> |
|
|
<option value="hard">困难</option> |
|
|
<option value="extreme">极限</option> |
|
|
</select> |
|
|
</div> |
|
|
<button id="start-btn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-full text-xl transition duration-300"> |
|
|
继续游戏 |
|
|
</button> |
|
|
`; |
|
|
|
|
|
|
|
|
document.getElementById('start-btn').addEventListener('click', function() { |
|
|
|
|
|
const element = document.body; |
|
|
element.requestPointerLock = element.requestPointerLock || |
|
|
element.mozRequestPointerLock || |
|
|
element.webkitRequestPointerLock; |
|
|
element.requestPointerLock(); |
|
|
}); |
|
|
|
|
|
document.getElementById('difficulty').addEventListener('change', function(e) { |
|
|
difficulty = e.target.value; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function endGame(win) { |
|
|
gameStarted = false; |
|
|
clearInterval(gameInterval); |
|
|
|
|
|
|
|
|
document.exitPointerLock = document.exitPointerLock || |
|
|
document.mozExitPointerLock || |
|
|
document.webkitExitPointerLock; |
|
|
document.exitPointerLock(); |
|
|
|
|
|
|
|
|
const startScreen = document.getElementById('start-screen'); |
|
|
startScreen.style.display = 'flex'; |
|
|
|
|
|
|
|
|
const accuracy = shotsFired > 0 ? Math.round((hits / shotsFired) * 100) : 0; |
|
|
startScreen.innerHTML = ` |
|
|
<h1 class="text-4xl font-bold mb-8">${win ? '胜利!' : '游戏结束'}</h1> |
|
|
<div class="text-xl mb-8"> |
|
|
<p>最终得分: ${score}</p> |
|
|
<p>命中率: ${accuracy}%</p> |
|
|
<p>总射击次数: ${shotsFired}</p> |
|
|
<p>命中次数: ${hits}</p> |
|
|
<p>剩余敌人: ${enemies.length}</p> |
|
|
</div> |
|
|
<div class="mb-8"> |
|
|
<label for="difficulty" class="block mb-2">选择难度:</label> |
|
|
<select id="difficulty" class="bg-gray-700 text-white p-2 rounded"> |
|
|
<option value="easy">简单</option> |
|
|
<option value="medium" selected>中等</option> |
|
|
<option value="hard">困难</option> |
|
|
<option value="extreme">极限</option> |
|
|
</select> |
|
|
</div> |
|
|
<button id="start-btn" class="bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-6 rounded-full text-xl transition duration-300"> |
|
|
再玩一次 |
|
|
</button> |
|
|
`; |
|
|
|
|
|
|
|
|
document.getElementById('start-btn').addEventListener('click', function() { |
|
|
|
|
|
const element = document.body; |
|
|
element.requestPointerLock = element.requestPointerLock || |
|
|
element.mozRequestPointerLock || |
|
|
element.webkitRequestPointerLock; |
|
|
element.requestPointerLock(); |
|
|
}); |
|
|
|
|
|
document.getElementById('difficulty').addEventListener('change', function(e) { |
|
|
difficulty = e.target.value; |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function animate() { |
|
|
requestAnimationFrame(animate); |
|
|
|
|
|
const delta = clock.getDelta(); |
|
|
|
|
|
if (controls.isLocked && gameStarted) { |
|
|
|
|
|
const speed = isSprinting ? 30 : 15; |
|
|
|
|
|
playerVelocity.x -= playerVelocity.x * 10.0 * delta; |
|
|
playerVelocity.z -= playerVelocity.z * 10.0 * delta; |
|
|
|
|
|
playerDirection.z = Number(moveForward) - Number(moveBackward); |
|
|
playerDirection.x = Number(moveRight) - Number(moveLeft); |
|
|
playerDirection.normalize(); |
|
|
|
|
|
if (moveForward || moveBackward) playerVelocity.z -= playerDirection.z * speed * delta; |
|
|
if (moveLeft || moveRight) playerVelocity.x -= playerDirection.x * speed * delta; |
|
|
|
|
|
|
|
|
playerVelocity.y -= 9.8 * delta; |
|
|
|
|
|
|
|
|
controls.moveRight(-playerVelocity.x * delta); |
|
|
controls.moveForward(-playerVelocity.z * delta); |
|
|
controls.getObject().position.y += playerVelocity.y * delta; |
|
|
|
|
|
|
|
|
if (controls.getObject().position.y < 1.6) { |
|
|
playerVelocity.y = 0; |
|
|
controls.getObject().position.y = 1.6; |
|
|
canJump = true; |
|
|
} |
|
|
|
|
|
|
|
|
const pos = controls.getObject().position; |
|
|
if (pos.x < -45) pos.x = -45; |
|
|
if (pos.x > 45) pos.x = 45; |
|
|
if (pos.z < -45) pos.z = -45; |
|
|
if (pos.z > 45) pos.z = 45; |
|
|
|
|
|
|
|
|
player.position.copy(controls.getObject().position); |
|
|
player.position.y += 0.2; |
|
|
} |
|
|
|
|
|
renderer.render(scene, camera); |
|
|
} |
|
|
|
|
|
|
|
|
init(); |
|
|
</script> |
|
|
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Aprec/test" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> |
|
|
</html> |