我正在努力完成我的網站,這個場景被整合在一起。我希望使用指針鎖控件的用戶不要碰到GLTF模型,因為它看起來不太專業。你會怎么做?我被告知使用DRACO和一個物理對撞機,但不知道如何實現它們。
預先感謝你的幫助。我無法將js和CSS正確地鏈接到HTML,所以這里有一個到成品的鏈接,我也試圖做出響應,但可能是為了下一個問題。:https://exhibition 1-la-disconnect-3-0 . net lify . app/examples/index-on-water . html
import * as THREE from "three";
import { PointerLockControls } from "/examples/jsm/controls/PointerLockControls.js";
// import { PointerLockControls as MobilePointerLockControls } from "/examples/jsm/controls/PointerLockControlsMobile.js";
// import {FBXLoader} from "/examples/jsm/loaders/FBXloader.js";
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// import { KTX2Loader } from 'three/addons/loaders/KTX2Loader.js';
import { Water } from 'three/addons/objects/Water.js';
// import { PositionalAudioHelper } from 'three/addons/helpers/PositionalAudioHelper.js';
let camera, scene, renderer, controls, water;
const objects = [];
let raycaster; //This class is designed to assist with raycasting. Raycasting is used for mouse picking (working out what objects in the 3d space the mouse is over) amongst other things.
let moveForward = false; //movement variables
let moveBackward = false;
let moveLeft = false;
let moveRight = false;
let canJump = false; //movement variables
let prevTime = performance.now(); //time.now
const velocity = new THREE.Vector3(); // velocity is a phenomenon with 2 properties magnitude and direction
const direction = new THREE.Vector3();
// const startButton = document.getElementById( 'startButton' );
// startButton.addEventListener( 'click', init );
init();
animate();
function init() {
// const overlay = document.getElementById( 'overlay' );
// overlay.remove();
// const container = document.getElementById( 'container' );
//camera and position
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
1,
2000
);
camera.position.y = 120;
camera.position.z = -1000;
camera.rotation.x = -Math.PI / 6;
camera.rotation.y = Math.PI; // Rotate camera 180 degrees
// camera.position.z = -1000;
// const reflectionCube = new THREE.CubeTextureLoader()
// .setPath( 'textures/cube/SwedishRoyalCastle/' )
// .load( [ 'px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg' ] );
// Function to increase camera size
function increaseCameraSize() {
const maxCameraSize = 100; // Maximum camera size
if (camera.scale.x < maxCameraSize) {
camera.scale.multiplyScalar(0.9); // Decrease camera size by 10%
updateCameraSizeIndicator();
}
}
// Function to decrease camera size
function decreaseCameraSize() {
const minCameraSize = 0.1; // Minimum camera size
if (camera.scale.x > minCameraSize) {
camera.scale.divideScalar(0.9); // Increase camera size by 10%
updateCameraSizeIndicator();
}
}
// Update camera size indicator on the screen
function updateCameraSizeIndicator() {
const cameraSizeIndicator = document.getElementById("camera-size");
cameraSizeIndicator.textContent = camera.scale.x.toFixed(1);
}
//background and fog
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
const skyLoader = new THREE.CubeTextureLoader();
//sky texture-clouds
const skytexture = skyLoader.load([
'./resources/yellowcloud_ft.jpg',
'./resources/yellowcloud_bk.jpg',
'./resources/yellowcloud_up.jpg',
'./resources/yellowcloud_dn.jpg',
'./resources/yellowcloud_rt.jpg',
'./resources/yellowcloud_lf.jpg',
]);
scene.background = skytexture;
scene.fog = new THREE.Fog(0x8A5000, 0, 300);
//lighting
const ambientLight =new THREE.AmbientLight(0xeeeeff, 0x777788, 0.75)
ambientLight.intensity =0.5;
scene.add(ambientLight)
const light = new THREE.DirectionalLight(0xeeeeff, 0x777788, 0.75);
light.position.set(130, 100, 100);
light.intensity=1;
//shadows
light.castShadow = true;
light.shadow.mapSize.width = 1000;
light.shadow.mapSize.height = 1000;
light.shadow.camera.near = 2;
light.shadow.camera.far = 1500;
light.shadow.camera.near = 2;
light.shadow.camera.far = 1500;
light.shadow.camera.left = 1500;
light.shadow.camera.right = -1500;
light.shadow.camera.top = 1000;
light.shadow.camera.bottom = -1000;
scene.add(light);
//defining pointerlock
controls = new PointerLockControls(camera, document.body);
//html controls info
const blocker = document.getElementById("blocker");
const instructions = document.getElementById("instructions");
//pointerlock system
//lock
instructions.addEventListener("click", function () {
controls.lock();
});
controls.addEventListener("lock", function () {
instructions.style.display = "none";
blocker.style.display = "none";
});
//unlock
controls.addEventListener("unlock", function () {
blocker.style.display = "block";
instructions.style.display = "";
});
//movement controls
scene.add(controls.getObject());
//keydown
const onKeyDown = function (event) {
switch (event.code) {
case "ArrowUp":
case "KeyW":
moveForward = true;
break;
case "ArrowLeft":
case "KeyA":
moveLeft = true;
break;
case "ArrowDown":
case "KeyS":
moveBackward = true;
break;
case "ArrowRight":
case "KeyD":
moveRight = true;
break;
case "Space":
if (canJump === true) velocity.y += 300;
canJump = false;
break;
}
};
//keyup
const onKeyUp = function (event) {
switch (event.code) {
case "ArrowUp":
case "KeyW":
moveForward = false;
break;
case "ArrowLeft":
case "KeyA":
moveLeft = false;
break;
case "ArrowDown":
case "KeyS":
moveBackward = false;
break;
case "ArrowRight":
case "KeyD":
moveRight = false;
break;
}
};
document.addEventListener("touchstart", function(){
console.log("touch")
moveForward = true;
})
document.addEventListener("touchend", function(){
console.log("end")
moveForward = false;
})
document.addEventListener( 'keydown', onKeyDown, false );
document.addEventListener( 'keyup', onKeyUp, false );
// MOBILE TOUCH CONTROLS
// Touch controls
const touchControls = document.getElementById("touch-controls");
const upArrow = document.getElementById("up-arrow");
const leftArrow = document.getElementById("left-arrow");
const downArrow = document.getElementById("down-arrow");
const rightArrow = document.getElementById("right-arrow");
const jumpButton = document.getElementById("jump-button");
// Add touch event listeners to the touch controls
touchControls.addEventListener("touchstart", handleTouchStart, false);
touchControls.addEventListener("touchend", handleTouchEnd, false);
// Touch control variables
let touchX = null;
let touchY = null;
// Function to handle touch start event
function handleTouchStart(event) {
event.preventDefault();
const touches = event.touches;
if (touches && touches.length > 0) {
const touch = touches[0];
touchX = touch.clientX;
touchY = touch.clientY;
handleTouchMove(touch);
}
}
// Function to handle touch end event
function handleTouchEnd(event) {
event.preventDefault();
touchX = null;
touchY = null;
}
// Function to handle touch move event
function handleTouchMove(event) {
event.preventDefault();
if (!touchX || !touchY) {
return;
}
const touch = event.touches[0];
const deltaX = touch.clientX - touchX;
const deltaY = touch.clientY - touchY;
touchX = touch.clientX;
touchY = touch.clientY;
// Determine movement direction based on touch movement
if (deltaY < -10) {
moveForward();
} else if (deltaY > 10) {
moveBackward();
}
if (deltaX < -10) {
moveLeft();
} else if (deltaX > 10) {
moveRight();
}
}
// Function to handle jump button touch start event
function handleJumpButtonTouchStart(event) {
event.preventDefault();
jump();
}
// Add touch event listener to the jump button
jumpButton.addEventListener("touchstart", handleJumpButtonTouchStart, false);
//event listeners
document.addEventListener("keydown", onKeyDown);
document.addEventListener("keyup", onKeyUp);
// Event listener for double click
// Event listener for double click
document.addEventListener("dblclick", onDoubleClick);
// Event listener for key press
document.addEventListener("keypress", onKeyPress);
// Function to handle double click event
function onDoubleClick() {
// Move the camera 10px forward
moveCameraForward(10);
}
// Function to handle key press event
function onKeyPress(event) {
if (event.key === "m") {
// Increase camera size
increaseCameraSize();
} else if (event.key === "n") {
// Decrease camera size
decreaseCameraSize();
}
}
// Function to move the camera forward
function moveCameraForward(distance) {
// Get the current camera position and rotation
const position = camera.position.clone();
const rotation = camera.rotation.clone();
// Calculate the forward vector
const direction = new THREE.Vector3(0, 0, -1);
direction.applyQuaternion(rotation);
// Move the camera forward by the specified distance
position.add(direction.multiplyScalar(distance));
// Teleport the camera horizontally on the floor
teleport(position.x, position.y);
}
// Function to increase camera size
function increaseCameraSize() {
const maxCameraSize = 10; // Maximum camera size
if (camera.scale.x < maxCameraSize) {
camera.scale.multiplyScalar(1.1); // Increase camera size by 10%
}
}
// Function to decrease camera size
function decreaseCameraSize() {
const minCameraSize = 1; // Minimum camera size
if (camera.scale.x > minCameraSize) {
camera.scale.divideScalar(1.1); // Decrease camera size by 10%
}
}
// document.addEventListener("dblclick", onDoubleClick);
// //
raycaster = new THREE.Raycaster(
new THREE.Vector3(),
new THREE.Vector3(0, -1, 0),
0,
10
);
// // floor/plane
// Water
const waterGeometry = new THREE.PlaneGeometry( 10000, 10000);
water = new Water(
waterGeometry,
{
textureWidth: 512,
textureHeight: 1000,
waterNormals: new THREE.TextureLoader().load( 'textures/waternormals.jpg', function ( texture ) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
} ),
sunDirection: new THREE.Vector3(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 3.7,
fog: scene.fog !== undefined
}
);
water.rotation.x = - Math.PI / 2;
scene.add( water );
//GLTF house
const loader = new GLTFLoader();
loader.load( 'models/gltf/Photography-museum.glb', function ( glb ) {
const model = glb.scene;
scene.add(model);
model.position.set(0,-15,-50);
model.scale.set(1,1,1);
model.rotateY(-0.9);
model.traverse(function(node) {
if(node.isMesh)
node.castShadow = true;
node.receiveShadow=true;
});
}, undefined, function ( error ) {
console.error( error );
} );
// renderer size pixel ratio..
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true; //shadows
renderer.shadowMap.type = THREE.PCFSoftShadowMap
document.body.appendChild(renderer.domElement);
;
//resizing listener
window.addEventListener("resize", onWindowResize);
//end of init function
}
// function that does the resize
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
const vrButton = VRButton.createButton(renderer);
renderer.setSize(window.innerWidth, window.innerHeight);
}
//animation and movement diplayed
function animate() {
requestAnimationFrame(animate);
//const time = performance.now() * 0.001;
water.material.uniforms[ 'time' ].value += 1.0 / 60.0;
//time variable so animation is updated on time not device perfomance
const time = performance.now();
//the mouse movement once clicked and pointerlock is enabled
if (controls.isLocked === true) {
raycaster.ray.origin.copy(controls.getObject().position);
raycaster.ray.origin.y -= 10;
const intersections = raycaster.intersectObjects(objects, false);
const onObject = intersections.length > 0;
const delta = (time - prevTime) / 1000;
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
velocity.y -= 7 * 100.0 * delta; // 100.0 = mass
//adding movement to the event listeners mentioned in the init function
direction.z = Number(moveForward) - Number(moveBackward);
direction.x = Number(moveRight) - Number(moveLeft);
direction.normalize(); // this ensures consistent movements in all directions
if (moveForward || moveBackward) velocity.z -= direction.z * 400.0 * delta;
if (moveLeft || moveRight) velocity.x -= direction.x * 400.0 * delta;
//allows movement sideways front and back
controls.moveRight(-velocity.x * delta);
controls.moveForward(-velocity.z * delta);
//jump
controls.getObject().position.y += velocity.y * delta; // new behavior
//stops you from falling through the floor
if (controls.getObject().position.y < 10) {
velocity.y = 0;
controls.getObject().position.y = 10;
canJump = true;
}
}
//previous time from start of function
prevTime = time;
renderer.render(scene, camera);
}
* {
margin: 0;
padding: 0;
}
html,
body {
margin: 0;
background-color: #000;
color: #fff;
font-family: 'Space Mono', monospace;
font-size: 13px;
line-height: 24px;
overscroll-behavior: none;
overflow: hidden;
}
#blocker {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
}
#instructions {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/*
display: -webkit-box;
display: -moz-box;
display: box;
-webkit-box-orient: horizontal;
-moz-box-orient: horizontal;
box-orient: horizontal;
-webkit-box-pack: center;
-moz-box-pack: center;
box-pack: center;
-webkit-box-align: center;
-moz-box-align: center;
box-align: center; */
text-align: center;
font-size: 14px;
cursor: pointer;
}
/* CONTROLS */
span {
font-family: sans-serif;
font-size: 18px;
display: flex;
justify-content: center;
align-items: center;
position: relative;
padding-bottom: 1px;
width: 38px;
height: 35px;
border-radius: 5px;
background-color: #fff;
color: #444;
border-top: 1px solid #ccc;
box-shadow: 0px 1px 0px 2px #ccc;
}
section {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(2, 1fr);
place-items: center;
grid-gap: 1px;
width: calc(46px * 3);
height: calc(46px * 2);
}
section~section {
margin-top: 20px;
}
section>span:nth-child(1) {
grid-column: 2;
grid-row: 1;
}
section>span:nth-child(2) {
grid-column: 1;
grid-row: 2;
}
section>span:nth-child(3) {
grid-column: 2;
grid-row: 2;
}
section>span:nth-child(4) {
grid-column: 3;
grid-row: 2;
}
.size-indicator-right {
position: absolute;
top: 10px;
right: 10px;
color: white;
}
.size-indicator-left{
position: absolute;
top: 10px;
left: 10px;
color: white;
}
@media screen and (max-width: 479px) {
/* smooth scrolling on iOS devices */
body {-webkit-overflow-scrolling: touch;}}
</style>
<!-- Add the necessary meta tags for responsive layout -->
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<!-- Add CSS styles for touch controls -->
<style>
#touch-controls {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
justify-content: center;
align-items: center;
z-index: 999;
}
.touch-button {
width: 60px;
height: 60px;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.7);
margin: 5px;
display: flex;
justify-content: center;
align-items: center;
font-size: 24px;
color: #000;
box-shadow: 0px 2px 6px rgba(0, 0, 0, 0.3);
}
<script>
var isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
if (isMobile) {
document.getElementById("instructions").style.display = "none";
document.getElementById("blocker").style.display = "none";
}
</script>
<!-- Touch controls -->
<!-- Touch controls -->
<!-- <div id="touch-controls" style="display: none;">
<div class="arrow-button" id="up-arrow"></div>
<div class="arrow-button" id="left-arrow"></div>
<div class="arrow-button" id="down-arrow"></div>
<div class="arrow-button" id="right-arrow"></div>
<div id="jump-button"></div>
</div> -->
<div id="touch-controls">
<!-- Add your touch controls HTML here -->
<!-- For example: -->
<button id="up-button">Up</button>
<button id="left-button">Left</button>
<button id="down-button">Down</button>
<button id="right-button">Right</button>
<button id="jump-button">Jump</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script>
// Add your Three.js initialization code here
// Check if the device is a mobile device
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// Show touch controls only on mobile devices
const touchControls = document.getElementById("touch-controls");
if (isMobile) {
touchControls.style.display = "block";
} else {
touchControls.style.display = "none";
}
// Add touch control functionality for mobile devices
if (isMobile) {
const moveForwardButton = document.getElementById("up-button");
const moveLeftButton = document.getElementById("left-button");
const moveBackwardButton = document.getElementById("down-button");
const moveRightButton = document.getElementById("right-button");
const jumpButton = document.getElementById("jump-button");
moveForwardButton.addEventListener("touchstart", function () {
moveForward = true;
});
moveForwardButton.addEventListener("touchend", function () {
moveForward = false;
});
moveLeftButton.addEventListener("touchstart", function () {
moveLeft = true;
});
moveLeftButton.addEventListener("touchend", function () {
moveLeft = false;
});
moveBackwardButton.addEventListener("touchstart", function () {
moveBackward = true;
});
moveBackwardButton.addEventListener("touchend", function () {
moveBackward = false;
});
moveRightButton.addEventListener("touchstart", function () {
moveRight = true;
});
moveRightButton.addEventListener("touchend", function () {
moveRight = false;
});
jumpButton.addEventListener("touchstart", function () {
jump();
});
}
</script>
<div id="blocker">
<div id="instructions">
<p id="playplayplay" style="font-size: 1.5em;border-radius: 100px;padding:20px;margin:20px;border: 1px solid white;width:30%;">Click to play
</p>
<section>
<span>W</span>
<span>A</span>
<span>S</span>
<span>D</span>
</section>
<section>
<span>↑</span>
<span>←</span>
<span>↓</span>
<span>→</span>
</section><br />
Look Around: MOUSE <br />
Jump: SPACEBAR
<span style="margin-top: 20px;">M</span>
<p style="margin-top: 10px;"> Press multiple times to MOVE faster with less fog.</p>
<span style="margin-top: 20px;">N</span> <p style="margin-top: 10px;">Press in increments to come back to NORMAL view.</p>
<p style="margin-top: 20px;">
? Exhibition — La Disconnect 3.0 | Amba Amba Studio | Geneva
</p>
</div>
</div>
<!-- <audio loop id="music" preload="auto" style="display: none">
<source src="sounds/376737_Skullbeatz___Bad_Cat_Maste.ogg" type="audio/ogg">
<source src="sounds/376737_Skullbeatz___Bad_Cat_Maste.mp3" type="audio/mpeg">
</audio>
<div id="overlay">
</div>
<div id="container"></div>
<div id="info">
<button id="startButton">Play</button>
<a target="_blank" rel="noopener noreferrer">three.js</a> webaudio - orientation<br/>
music by <a target="_blank" rel="noopener noreferrer">skullbeatz</a>
</div> -->
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "./jsm/"
}
}
</script>
<!-- Touch controls -->
<!-- Touch controls -->
<!-- <div id="touch-controls" style="display: none;">
<div class="arrow-button" id="up-arrow"></div>
<div class="arrow-button" id="left-arrow"></div>
<div class="arrow-button" id="down-arrow"></div>
<div class="arrow-button" id="right-arrow"></div>
<div id="jump-button"></div>
</div> -->
<!--
<script>
// Check if the device supports touch events
const isTouchDevice = 'ontouchstart' in document.documentElement;
// Initialize the PointerLockControls
const controls = new THREE.PointerLockControls(camera, document.body);
if (isTouchDevice) {
// Enable touch controls
controls.touchEnabled = true;
// Disable pointer lock on touch devices
controls.lock = () => {};
controls.unlock = () => {};
}
// Add event listeners to handle touch events
if (isTouchDevice) {
document.addEventListener('touchstart', () => {
controls.lock();
}, false);
document.addEventListener('touchend', () => {
controls.unlock();
}, false);
}
// Add the controls to the scene
scene.add(controls);
// Check if the device is a mobile device
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
// Show touch controls only on mobile devices
const touchControls = document.getElementById("touch-controls");
if (isMobile) {
touchControls.style.display = "block";
} else {
touchControls.style.display = "none";
}
// Add touch control functionality for mobile devices
if (isMobile) {
// Handle touch events for touch controls
document.getElementById("up-arrow").addEventListener("touchstart", function() {
moveForward = true;
});
document.getElementById("up-arrow").addEventListener("touchend", function() {
moveForward = false;
});
document.getElementById("left-arrow").addEventListener("touchstart", function() {
moveLeft = true;
});
document.getElementById("left-arrow").addEventListener("touchend", function() {
moveLeft = false;
});
document.getElementById("down-arrow").addEventListener("touchstart", function() {
moveBackward = true;
});
document.getElementById("down-arrow").addEventListener("touchend", function() {
moveBackward = false;
});
document.getElementById("right-arrow").addEventListener("touchstart", function() {
moveRight = true;
});
document.getElementById("right-arrow").addEventListener("touchend", function() {
moveRight = false;
});
document.getElementById("jump-button").addEventListener("touchstart", function() {
if (canJump === true) velocity.y += 350;
canJump = false;
});
}
</script> -->