finalizzazione html e js della fase 1

This commit is contained in:
2026-03-30 13:35:46 +02:00
parent a884ee5dc4
commit 6736d1acd9
5 changed files with 238 additions and 415 deletions
-57
View File
@@ -1,57 +0,0 @@
<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<title>Semplice Island FPS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- SCHERMATA DI CARICAMENTO -->
<div id="loading-screen" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #000; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999;">
<div style="width: 80%; max-width: 600px; background: #222; height: 20px; border-radius: 10px; overflow: hidden; margin-bottom: 10px;">
<div id="loading-bar" style="width: 0%; height: 100%; background: #4CAF50; transition: width 0.3s;"></div>
</div>
<div id="loading-text" style="color: #fff; font-family: monospace; font-size: 14px;">Loading...</div>
</div>
<!-- SCHERMATA INIZIALE -->
<div id="start">
<video autoplay muted loop id="bg-video">
<source src="./video/background.mp4" type="video/mp4">
</video>
<div class="overlay"></div>
<div class="content">
<div class="top-section">
<img src="./img/titolo.png" alt="titolo" class="main-title" />
</div>
<div class="container bottom-section">
<div class="item left">
<div class="controls-box">
<h3>Controlli</h3>
<p><kbd>W</kbd><kbd>A</kbd><kbd>S</kbd><kbd>D</kbd> Movimento</p>
<p><kbd>Mouse</kbd> Camera</p>
</div>
</div>
<div class="item center">
<span class="pulse-text">Clicca per giocare</span>
</div>
<div class="item right"></div>
</div>
</div>
</div>
<!-- INTERFACCIA GRAFICA DEL GIOCO -->
<div id="punti">Rifiuti: <span id="score">0</span></div>
<div id="tempo">Tempo: <span id="minuti">XX</span>:<span id="secondi">XX</span></div>
<div id="crosshair">+</div>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
"three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
}
}
</script>
<script type="module" src="script-test.js"></script>
</body>
</html>
+12 -1
View File
@@ -6,6 +6,15 @@
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
</head> </head>
<body> <body>
<!-- SCHERMATA DI CARICAMENTO -->
<div id="loading-screen" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #000; display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 9999;">
<div style="width: 80%; max-width: 600px; background: #222; height: 20px; border-radius: 10px; overflow: hidden; margin-bottom: 10px;">
<div id="loading-bar" style="width: 0%; height: 100%; background: #4CAF50; transition: width 0.3s;"></div>
</div>
<div id="loading-text" style="color: #fff; font-family: monospace; font-size: 14px;">Loading...</div>
</div>
<!-- SCHERMATA INIZIALE -->
<div id="start"> <div id="start">
<video autoplay muted loop id="bg-video"> <video autoplay muted loop id="bg-video">
<source src="./video/background.mp4" type="video/mp4"> <source src="./video/background.mp4" type="video/mp4">
@@ -30,8 +39,10 @@
</div> </div>
</div> </div>
</div> </div>
<!-- INTERFACCIA GRAFICA DEL GIOCO -->
<div id="punti">Rifiuti: <span id="score">0</span></div> <div id="punti">Rifiuti: <span id="score">0</span></div>
<div id="tempo">Tempo: <span id="time">XX:XX</span></div> <div id="tempo">Tempo: <span id="minuti">XX</span>:<span id="secondi">XX</span></div>
<div id="crosshair">+</div> <div id="crosshair">+</div>
<script type="importmap"> <script type="importmap">
{ {
View File
-331
View File
@@ -1,331 +0,0 @@
import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 1a. INIZIALIZZAZIONE CARICAMENTO ASSETS
const manager = new THREE.LoadingManager();
manager.onStart = function (url, itemsLoaded, itemsTotal) {
document.getElementById("loading-text").innerHTML = `Inizio caricamento: ${url}`;
console.log(`Inizio caricamento: ${url}`);
};
manager.onLoad = function () {
document.getElementById('loading-screen').style.display = 'none';
animate(); // avvia il rendering
};
manager.onProgress = function (url, itemsLoaded, itemsTotal) {
// Calcola la percentuale
const percent = (itemsLoaded / itemsTotal) * 100;
document.getElementById('loading-bar').style.width = percent + '%';
// Aggiorna il testo
document.getElementById("loading-text").innerHTML = `Caricamento: ${itemsLoaded} di ${itemsTotal} - ${url}`;
console.log(`Caricamento: ${itemsLoaded} di ${itemsTotal} - ${url}`);
};
manager.onError = function (url) {
document.getElementById("loading-text").innerHTML = `Errore nel caricamento: ${url}`;
console.log(`Errore nel caricamento: ${url}`);
};
// 1b. SCENA E CAMERA
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x33ccff); // Cielo più azzurro
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
window.addEventListener('resize', function () {
renderer.setSize(window.innerWidth, window.innerHeight)
})
// AGGIUNTA MARE
const oceanGeometry = new THREE.PlaneGeometry(2000, 2000);
const oceanMaterial = new THREE.MeshStandardMaterial({
color: 0x0077be,
transparent: true,
opacity: 0.8
});
const ocean = new THREE.Mesh(oceanGeometry, oceanMaterial);
ocean.rotation.x = -Math.PI / 2;
ocean.position.y = -0.2; // Leggermente sotto l'isola
scene.add(ocean);
// 2. LUCI E AMBIENTE
scene.add(new THREE.AmbientLight(0xffffff, 0.8));
const sunLight = new THREE.DirectionalLight(0xffffff, 1);
sunLight.position.set(10, 20, 10);
scene.add(sunLight);
const islandGroup = new THREE.Group();
const altezzaIsola = 0.5; // Quanto sta il prato sopra il livello del mare (0.0)
const altezzaOcchi = 1.6; // L'altezza standard della telecamera
// --- IL FONDALE (Sabbia scura sul fondo) ---
const floorGeo = new THREE.PlaneGeometry(1000, 1000);
const floorMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a }); // Grigio scuro/Fondale
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -3; // 5 metri sotto il livello del mare
scene.add(floor);
// --- IL PRATO ---
const raggioPrato = 19;
const grassGeo = new THREE.CircleGeometry(raggioPrato, 64);
const grassMat = new THREE.MeshStandardMaterial({ color: 0x4df556 });
const grass = new THREE.Mesh(grassGeo, grassMat);
grass.rotation.x = -Math.PI / 2;
// Posizioniamo il prato all'altezza desiderata
grass.position.y = altezzaIsola;
islandGroup.add(grass);
// --- LA SABBIA (Cilindro Svasato) ---
const altezzaSabbia = 1.5; // Altezza totale del blocco sabbia
const sandGeo = new THREE.CylinderGeometry(raggioPrato, 22 + 10, altezzaSabbia + 3, 64, 1, true);
const sandMat = new THREE.MeshStandardMaterial({ color: 0xedc9af, side: THREE.DoubleSide });
const sand = new THREE.Mesh(sandGeo, sandMat);
// Per far combaciare la cima del cilindro con il prato:
// L'altezza è 1.5, il centro del cilindro deve stare a (AltezzaIsola - metà altezza cilindro)
sand.position.y = altezzaIsola - (altezzaSabbia / 2) - 1.5;
islandGroup.add(sand);
scene.add(islandGroup);
scene.fog = new THREE.Fog(0x33ccff, 20, 100);
// 3. CONTROLLI
window.totalTime;
class PausableTimer {
constructor(callback, delay) {
this.callback = callback;
this.delay = delay; // salvi il delay originale
this.remaining = delay;
this.timerId = null;
this.start = null;
this.loop = null;
this.cont = 0;
window.totalTime = delay / 1000;
}
pause() {
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
// Calcola il tempo passato
this.remaining -= Date.now() - this.start;
}
if (this.loop) {
clearInterval(this.loop);
this.loop = null;
}
console.log("pausa");
}
resume() {
console.log("resume");
if (this.timerId) return; // già in esecuzione
if (this.remaining > 0) {
this.start = Date.now();
this.timerId = setTimeout(() => {
this.pause();
this.callback();
}, this.remaining);
// Se il ciclo di aggiornamento non è già attivo, avvialo
if (!this.loop) {
this.loop = setInterval(() => {
this.cont++;
// Aggiorna il timer visualizzato
document.getElementById("minuti").innerHTML = String(Math.floor((window.totalTime - this.cont) / 60)).padStart(2, '0');
document.getElementById("secondi").innerHTML = String((window.totalTime - this.cont) % 60).padStart(2, '0');
console.log(`${document.getElementById("minuti").innerHTML}:${document.getElementById("secondi").innerHTML}`);
}, 1000);
}
}
}
}
const timer = new PausableTimer(() => {
localStorage.setItem("punteggio", document.getElementById("score").innerHTML.trim());
window.location = "test.html";
}, 60000)
function lock() {
document.getElementById('start').style.display = 'none';
document.getElementById('punti').style.display = 'block';
document.getElementById('tempo').style.display = 'block';
document.getElementById('crosshair').style.display = 'block';
}
function unlock() {
timer.pause();
document.getElementById('start').style.display = 'flex';
document.getElementById('punti').style.display = 'none';
document.getElementById('tempo').style.display = 'none';
document.getElementById('crosshair').style.display = 'none';
}
const controls = new PointerLockControls(camera, document.body);
document.getElementById('start').addEventListener('click', () => {
controls.lock();
lock();
});
controls.addEventListener('lock', () => {
lock();
timer.resume();
});
controls.addEventListener('unlock', () => { unlock(); });
// 4. GESTIONE OGGETTI DI SCENA (20 rifiuti + 6 alberi)
const trashArray = [];
let score = 0;
const loader = new GLTFLoader(manager);
loader.load('models/rifiuto.glb', (gltf) => {
const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO");
mesh.geometry.center(); // Centra l'oggetto per collisioni precise
for (let i = 0; i < 20; i++) {
const clone = mesh.clone();
scene.add(clone);
trashArray.push(clone);
spawn(clone, 0.2);
}
});
loader.load('models/tree.glb', (gltf) => {
const mesh = gltf.scene;
mesh.scale.set(4, 4, 4);
console.log(mesh);
function spawn(obj, radius, minDistance) {
const points = [];
const attempts = 1000; // Numero massimo di tentativi per trovare un punto valido
for (let i = 0; i < attempts; i++) {
let angle = Math.random() * 2 * Math.PI;
let dist = Math.random() * radius;
let x = dist * Math.cos(angle);
let z = dist * Math.sin(angle);
let valid = true;
for (let j = 0; j < points.length; j++) {
const dx = x - points[j].x;
const dz = z - points[j].z;
const distance = Math.sqrt(dx * dx + dz * dz);
if (distance < minDistance) {
valid = false;
break;
}
}
if (valid) {
points.push({ x: x, z: z });
}
}
// Spawna gli alberi nei punti generati
for (let i = 0; i < points.length; i++) {
const clone = obj.clone();
const a = Math.random() * Math.PI * 2;
const r = Math.random() * 18;
const x = Math.cos(a) * r;
const z = Math.sin(a) * r;
// Y deve essere altezzaIsola + un piccolo offset
clone.position.set(points[i].x, altezzaIsola - 0.1, points[i].z);
clone.rotation.y = Math.random() * Math.PI * 2; // Rotazione tra 0 e 360 gradi
scene.add(clone);
}
}
spawn(mesh, 18, 8); // Raggio 18, distanza minima 5
});
console.log(scene)
function spawn(obj) {
const a = Math.random() * Math.PI * 2;
const r = Math.random() * 18;
const x = Math.cos(a) * r;
const z = Math.sin(a) * r;
// Y deve essere altezzaIsola + un piccolo offset
obj.position.set(x, altezzaIsola + 0.1, z);
}
// 5. MOVIMENTO E COLLISIONI
const arrow = false;
const keys = {};
document.onkeydown = (e) => keys[e.code] = true;
document.onkeyup = (e) => keys[e.code] = false;
// Setup Raycaster (fuori dal loop animate)
const raycaster = new THREE.Raycaster();
const downVector = new THREE.Vector3(0, -1, 0);
// --- CONFIGURAZIONE MOVIMENTO ---
let canLeaveIsland = false; // Se false, l'acqua blocca il movimento.
let lastSafePosition = camera.position.clone();
function animate() {
requestAnimationFrame(animate);
if (controls.isLocked) {
// 1. Salva la posizione attuale prima del movimento
const oldPos = camera.position.clone();
// 2. Esegui il movimento WASD
if (arrow) {
if (keys['ArrowUp']) controls.moveForward(0.15);
if (keys['ArrowDown']) controls.moveForward(-0.15);
if (keys['ArrowLeft']) controls.moveRight(-0.15);
if (keys['ArrowRight']) controls.moveRight(0.15);
} else {
if (keys['KeyW']) controls.moveForward(0.15);
if (keys['KeyS']) controls.moveForward(-0.15);
if (keys['KeyA']) controls.moveRight(-0.15);
if (keys['KeyD']) controls.moveRight(0.15);
}
// 3. --- GESTIONE ALTEZZA DINAMICA ---
const rayOrigin = camera.position.clone();
rayOrigin.y = 10; // Spara dall'alto verso il basso
raycaster.set(rayOrigin, downVector);
// Controlliamo Prato e Sabbia
const intersects = raycaster.intersectObjects([grass, sand]);
if (intersects.length > 0) {
// Logica del "Punto più alto":
// Se il raggio colpisce sia prato che sabbia, prendiamo il prato.
let highestPoint = -Infinity;
for (let i = 0; i < intersects.length; i++) {
if (intersects[i].point.y > highestPoint) {
highestPoint = intersects[i].point.y;
}
}
// Applichiamo l'altezza alla camera
camera.position.y = highestPoint + altezzaOcchi;
// --- CONTROLLO BARRIERA ACQUA ---
// Se il punto più alto è sotto il livello del mare (0)
// e non possiamo uscire, blocchiamo il movimento
if (!canLeaveIsland && highestPoint < 0) {
camera.position.x = oldPos.x;
camera.position.z = oldPos.z;
camera.position.y = oldPos.y;
} else {
// Posizione valida, la salviamo
lastSafePosition.copy(camera.position);
camera.position.y = camera.position.y > (-2.25 + altezzaOcchi) ? camera.position.y : (-2.25 + altezzaOcchi);
}
} else {
// --- FUORI DALL'ISOLA (VUOTO) ---
if (!canLeaveIsland) {
camera.position.copy(oldPos);
} else {
camera.position.y = -2.25 + altezzaOcchi; // Volo sull'acqua
}
}
// --- COLLISIONI RIFIUTI ---
trashArray.forEach(t => {
if (camera.position.distanceTo(t.position) < 1.8) {
score++;
document.getElementById('score').innerText = score;
spawn(t);
}
});
}
renderer.render(scene, camera);
}
document.getElementById("minuti").innerHTML = String(Math.floor(window.totalTime / 60)).padStart(2, '0');
document.getElementById("secondi").innerHTML = String(window.totalTime % 60).padStart(2, '0');
+226 -26
View File
@@ -2,9 +2,33 @@ import * as THREE from 'three';
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js'; import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 1. SCENA E CAMERA // 1a. INIZIALIZZAZIONE CARICAMENTO ASSETS
const manager = new THREE.LoadingManager();
manager.onStart = function (url, itemsLoaded, itemsTotal) {
document.getElementById("loading-text").innerHTML = `Inizio caricamento: ${url}`;
console.log(`Inizio caricamento: ${url}`);
};
manager.onLoad = function () {
document.getElementById('loading-screen').style.display = 'none';
animate(); // avvia il rendering
};
manager.onProgress = function (url, itemsLoaded, itemsTotal) {
// Calcola la percentuale
const percent = (itemsLoaded / itemsTotal) * 100;
document.getElementById('loading-bar').style.width = percent + '%';
// Aggiorna il testo
document.getElementById("loading-text").innerHTML = `Caricamento: ${itemsLoaded} di ${itemsTotal} - ${url}`;
console.log(`Caricamento: ${itemsLoaded} di ${itemsTotal} - ${url}`);
};
manager.onError = function (url) {
document.getElementById("loading-text").innerHTML = `Errore nel caricamento: ${url}`;
console.log(`Errore nel caricamento: ${url}`);
};
// 1b. SCENA E CAMERA
const scene = new THREE.Scene(); const scene = new THREE.Scene();
scene.background = new THREE.Color(0x87ceeb); scene.background = new THREE.Color(0x33ccff); // Cielo più azzurro
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer(); const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight); renderer.setSize(window.innerWidth, window.innerHeight);
@@ -12,14 +36,115 @@ document.body.appendChild(renderer.domElement);
window.addEventListener('resize', function () { window.addEventListener('resize', function () {
renderer.setSize(window.innerWidth, window.innerHeight) renderer.setSize(window.innerWidth, window.innerHeight)
}) })
// AGGIUNTA MARE
const oceanGeometry = new THREE.PlaneGeometry(2000, 2000);
const oceanMaterial = new THREE.MeshStandardMaterial({
color: 0x0077be,
transparent: true,
opacity: 0.8
});
const ocean = new THREE.Mesh(oceanGeometry, oceanMaterial);
ocean.rotation.x = -Math.PI / 2;
ocean.position.y = -0.2; // Leggermente sotto l'isola
scene.add(ocean);
// 2. LUCI E AMBIENTE // 2. LUCI E AMBIENTE
scene.add(new THREE.AmbientLight(0xffffff, 1)); scene.add(new THREE.AmbientLight(0xffffff, 0.8));
const island = new THREE.Mesh(new THREE.CircleGeometry(20, 32), new THREE.MeshStandardMaterial({ color: 0x4df556 })); // #4df555 const sunLight = new THREE.DirectionalLight(0xffffff, 1);
island.rotation.x = -Math.PI / 2; sunLight.position.set(10, 20, 10);
scene.add(island); scene.add(sunLight);
const islandGroup = new THREE.Group();
const altezzaIsola = 0.5; // Quanto sta il prato sopra il livello del mare (0.0)
const altezzaOcchi = 1.6; // L'altezza standard della telecamera
// --- IL FONDALE (Sabbia scura sul fondo) ---
const floorGeo = new THREE.PlaneGeometry(1000, 1000);
const floorMat = new THREE.MeshStandardMaterial({ color: 0x1a1a1a }); // Grigio scuro/Fondale
const floor = new THREE.Mesh(floorGeo, floorMat);
floor.rotation.x = -Math.PI / 2;
floor.position.y = -3; // 5 metri sotto il livello del mare
scene.add(floor);
// --- IL PRATO ---
const raggioPrato = 19;
const grassGeo = new THREE.CircleGeometry(raggioPrato, 64);
const grassMat = new THREE.MeshStandardMaterial({ color: 0x4df556 });
const grass = new THREE.Mesh(grassGeo, grassMat);
grass.rotation.x = -Math.PI / 2;
// Posizioniamo il prato all'altezza desiderata
grass.position.y = altezzaIsola;
islandGroup.add(grass);
// --- LA SABBIA (Cilindro Svasato) ---
const altezzaSabbia = 1.5; // Altezza totale del blocco sabbia
const sandGeo = new THREE.CylinderGeometry(raggioPrato, 22 + 10, altezzaSabbia + 3, 64, 1, true);
const sandMat = new THREE.MeshStandardMaterial({ color: 0xedc9af, side: THREE.DoubleSide });
const sand = new THREE.Mesh(sandGeo, sandMat);
// Per far combaciare la cima del cilindro con il prato:
// L'altezza è 1.5, il centro del cilindro deve stare a (AltezzaIsola - metà altezza cilindro)
sand.position.y = altezzaIsola - (altezzaSabbia / 2) - 1.5;
islandGroup.add(sand);
scene.add(islandGroup);
scene.fog = new THREE.Fog(0x33ccff, 20, 100);
// 3. CONTROLLI // 3. CONTROLLI
window.totalTime;
class PausableTimer {
constructor(callback, delay) {
this.callback = callback;
this.delay = delay; // salvi il delay originale
this.remaining = delay;
this.timerId = null;
this.start = null;
this.loop = null;
this.cont = 0;
window.totalTime = delay / 1000;
}
pause() {
if (this.timerId) {
clearTimeout(this.timerId);
this.timerId = null;
// Calcola il tempo passato
this.remaining -= Date.now() - this.start;
}
if (this.loop) {
clearInterval(this.loop);
this.loop = null;
}
console.log("pausa");
}
resume() {
console.log("resume");
if (this.timerId) return; // già in esecuzione
if (this.remaining > 0) {
this.start = Date.now();
this.timerId = setTimeout(() => {
this.pause();
this.callback();
}, this.remaining);
// Se il ciclo di aggiornamento non è già attivo, avvialo
if (!this.loop) {
this.loop = setInterval(() => {
this.cont++;
// Aggiorna il timer visualizzato
document.getElementById("minuti").innerHTML = String(Math.floor((window.totalTime - this.cont) / 60)).padStart(2, '0');
document.getElementById("secondi").innerHTML = String((window.totalTime - this.cont) % 60).padStart(2, '0');
console.log(`${document.getElementById("minuti").innerHTML}:${document.getElementById("secondi").innerHTML}`);
}, 1000);
}
}
}
}
const timer = new PausableTimer(() => {
localStorage.setItem("punteggio", document.getElementById("score").innerHTML.trim());
window.location = "punteggio.html";
}, 60000)
function lock() { function lock() {
document.getElementById('start').style.display = 'none'; document.getElementById('start').style.display = 'none';
document.getElementById('punti').style.display = 'block'; document.getElementById('punti').style.display = 'block';
@@ -27,22 +152,27 @@ function lock() {
document.getElementById('crosshair').style.display = 'block'; document.getElementById('crosshair').style.display = 'block';
} }
function unlock() { function unlock() {
const startDiv = document.getElementById('start'); timer.pause();
startDiv.style.display = 'flex'; // Forza il layout Flexbox document.getElementById('start').style.display = 'flex';
// Nascondi l'HUD
document.getElementById('punti').style.display = 'none'; document.getElementById('punti').style.display = 'none';
document.getElementById('tempo').style.display = 'none'; document.getElementById('tempo').style.display = 'none';
document.getElementById('crosshair').style.display = 'none'; document.getElementById('crosshair').style.display = 'none';
} }
const controls = new PointerLockControls(camera, document.body); const controls = new PointerLockControls(camera, document.body);
document.getElementById('start').addEventListener('click', () => controls.lock()); document.getElementById('start').addEventListener('click', () => {
controls.addEventListener('lock', lock); controls.lock();
controls.addEventListener('unlock', unlock); lock();
});
controls.addEventListener('lock', () => {
lock();
timer.resume();
});
controls.addEventListener('unlock', () => { unlock(); });
// 4. GESTIONE OGGETTI DI SCENA (20 rifiuti + 6 alberi) // 4. GESTIONE OGGETTI DI SCENA (20 rifiuti + 6 alberi)
const trashArray = []; const trashArray = [];
let score = 0; let score = 0;
const loader = new GLTFLoader(); const loader = new GLTFLoader(manager);
loader.load('models/rifiuto.glb', (gltf) => { loader.load('models/rifiuto.glb', (gltf) => {
const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO"); const mesh = gltf.scene.getObjectByName("Trash_Pile_03_GEO");
@@ -86,7 +216,12 @@ loader.load('models/tree.glb', (gltf) => {
// Spawna gli alberi nei punti generati // Spawna gli alberi nei punti generati
for (let i = 0; i < points.length; i++) { for (let i = 0; i < points.length; i++) {
const clone = obj.clone(); const clone = obj.clone();
clone.position.set(points[i].x, -0.3, points[i].z); const a = Math.random() * Math.PI * 2;
const r = Math.random() * 18;
const x = Math.cos(a) * r;
const z = Math.sin(a) * r;
// Y deve essere altezzaIsola + un piccolo offset
clone.position.set(points[i].x, altezzaIsola - 0.1, points[i].z);
clone.rotation.y = Math.random() * Math.PI * 2; // Rotazione tra 0 e 360 gradi clone.rotation.y = Math.random() * Math.PI * 2; // Rotazione tra 0 e 360 gradi
scene.add(clone); scene.add(clone);
} }
@@ -98,34 +233,99 @@ console.log(scene)
function spawn(obj) { function spawn(obj) {
const a = Math.random() * Math.PI * 2; const a = Math.random() * Math.PI * 2;
const r = Math.random() * 18; const r = Math.random() * 18;
obj.position.set(Math.cos(a) * r, 0.1, Math.sin(a) * r); const x = Math.cos(a) * r;
const z = Math.sin(a) * r;
// Y deve essere altezzaIsola + un piccolo offset
obj.position.set(x, altezzaIsola + 0.1, z);
} }
// 5. MOVIMENTO E COLLISIONI // 5. MOVIMENTO E COLLISIONI
const arrow = false;
const keys = {}; const keys = {};
document.onkeydown = (e) => keys[e.code] = true; document.onkeydown = (e) => keys[e.code] = true;
document.onkeyup = (e) => keys[e.code] = false; document.onkeyup = (e) => keys[e.code] = false;
// Setup Raycaster (fuori dal loop animate)
const raycaster = new THREE.Raycaster();
const downVector = new THREE.Vector3(0, -1, 0);
// --- CONFIGURAZIONE MOVIMENTO ---
let canLeaveIsland = false; // Se false, l'acqua blocca il movimento.
let lastSafePosition = camera.position.clone();
function animate() { function animate() {
requestAnimationFrame(animate); requestAnimationFrame(animate);
if (controls.isLocked) {
if (keys['KeyW']) controls.moveForward(0.15);
if (keys['KeyS']) controls.moveForward(-0.15);
if (keys['KeyA']) controls.moveRight(-0.15);
if (keys['KeyD']) controls.moveRight(0.15);
camera.position.y = 1.6;
// Controllo collisioni if (controls.isLocked) {
const pPos = camera.position.clone(); // 1. Salva la posizione attuale prima del movimento
pPos.y -= 1.0; const oldPos = camera.position.clone();
// 2. Esegui il movimento WASD
if (arrow) {
if (keys['ArrowUp']) controls.moveForward(0.15);
if (keys['ArrowDown']) controls.moveForward(-0.15);
if (keys['ArrowLeft']) controls.moveRight(-0.15);
if (keys['ArrowRight']) controls.moveRight(0.15);
} else {
if (keys['KeyW']) controls.moveForward(0.15);
if (keys['KeyS']) controls.moveForward(-0.15);
if (keys['KeyA']) controls.moveRight(-0.15);
if (keys['KeyD']) controls.moveRight(0.15);
}
// 3. --- GESTIONE ALTEZZA DINAMICA ---
const rayOrigin = camera.position.clone();
rayOrigin.y = 10; // Spara dall'alto verso il basso
raycaster.set(rayOrigin, downVector);
// Controlliamo Prato e Sabbia
const intersects = raycaster.intersectObjects([grass, sand]);
if (intersects.length > 0) {
// Logica del "Punto più alto":
// Se il raggio colpisce sia prato che sabbia, prendiamo il prato.
let highestPoint = -Infinity;
for (let i = 0; i < intersects.length; i++) {
if (intersects[i].point.y > highestPoint) {
highestPoint = intersects[i].point.y;
}
}
// Applichiamo l'altezza alla camera
camera.position.y = highestPoint + altezzaOcchi;
// --- CONTROLLO BARRIERA ACQUA ---
// Se il punto più alto è sotto il livello del mare (0)
// e non possiamo uscire, blocchiamo il movimento
if (!canLeaveIsland && highestPoint < 0) {
camera.position.x = oldPos.x;
camera.position.z = oldPos.z;
camera.position.y = oldPos.y;
} else {
// Posizione valida, la salviamo
lastSafePosition.copy(camera.position);
camera.position.y = camera.position.y > (-2.25 + altezzaOcchi) ? camera.position.y : (-2.25 + altezzaOcchi);
}
} else {
// --- FUORI DALL'ISOLA (VUOTO) ---
if (!canLeaveIsland) {
camera.position.copy(oldPos);
} else {
camera.position.y = -2.25 + altezzaOcchi; // Volo sull'acqua
}
}
// --- COLLISIONI RIFIUTI ---
trashArray.forEach(t => { trashArray.forEach(t => {
if (new THREE.Box3().setFromObject(t).expandByScalar(0.3).containsPoint(pPos)) { if (camera.position.distanceTo(t.position) < 1.8) {
score++; score++;
document.getElementById('score').innerText = score; document.getElementById('score').innerText = score;
spawn(t); spawn(t);
} }
}); });
} }
renderer.render(scene, camera); renderer.render(scene, camera);
} }
animate(); document.getElementById("minuti").innerHTML = String(Math.floor(window.totalTime / 60)).padStart(2, '0');
document.getElementById("secondi").innerHTML = String(window.totalTime % 60).padStart(2, '0');