Piiloleikkirunko three.js
Hienoa, että olet innostunut tekemään piiloleikkiä muistuttavan pelin three.js-kirjastolla! Alla on ohjeet three.js:n asentamiseen npm:llä, pelin rungon luomiseen sekä kuvaus pelin toiminnasta. Huomaa, että alla olevat ohjeet ovat yleisiä ja perustuvat three.js-kirjaston toimintaan tiedon katkaisuhetkellä (syyskuu 2021). Mahdolliset päivitykset ja tarkemmat ohjeet kannattaa tarkistaa three.js-dokumentaatiosta.
Three.js:n asentaminen npm:llä
- Asenna Node.js, jos sitä ei ole vielä asennettuna tietokoneellesi. Voit ladata Node.js:n osoitteesta https://nodejs.org/.
- Avaa komentorivi tai terminaali ja siirry hakemistoon, johon haluat asentaa pelin projektin.
- Luo uusi npm-projekti komennolla:
bashnpm init -y
- Asenna three.js ja tarvittavat riippuvuudet komennolla:
bashnpm install three
Pelin runko (HTML ja JS)
HTML-tiedosto (index.html):
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Piiloleikki-peli</title>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<script src="main.js"></script>
</body>
</html>
JavaScript-tiedosto (main.js):
javascript// Tarvittavat muuttujat
let scene, camera, renderer, player, target, timerInterval, targetPosition;
// Pelialueen kartta
const map = [
"##########",
"#........#",
"#........#",
"#...p....#",
"#........#",
"#........#",
"##########",
];
// Alustetaan peli
init();
startGame();
function init() {
// Luodaan kolmiulotteinen näkymä
scene = new THREE.Scene();
// Kamera
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 5, 10);
// Renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Pelaaja (kameraa vastaava näkymä)
player = new THREE.Group();
player.add(camera);
scene.add(player);
// Lattia
const floorGeometry = new THREE.PlaneGeometry(10, 10);
const floorMaterial = new THREE.MeshBasicMaterial({ color: 0x888888, side: THREE.DoubleSide });
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = Math.PI / 2;
scene.add(floor);
// Seinät
for (let i = 0; i < map.length; i++) {
for (let j = 0; j < map[i].length; j++) {
if (map[i][j] === "#") {
const wallGeometry = new THREE.BoxGeometry(1, 2, 1);
const wallMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const wall = new THREE.Mesh(wallGeometry, wallMaterial);
wall.position.set(j - 4.5, 1, i - 3.5);
scene.add(wall);
} else if (map[i][j] === "p") {
// Tallenna pelaajan aloituspaikka
player.position.set(j - 4.5, 0, i - 3.5);
}
}
}
// Kohde
const targetGeometry = new THREE.BoxGeometry(0.5, 0.5, 0.5);
const targetMaterial = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
target = new THREE.Mesh(targetGeometry, targetMaterial);
scene.add(target);
// Aloita renderöinti
animate();
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
function startGame() {
// Laskurin alustus
let count = 0;
// Aseta piilopaikan sijainti (kohde) satunnaisesti
const randomX = Math.floor(Math.random() * map[0].length);
const randomZ = Math.floor(Math.random() * map.length);
targetPosition = new THREE.Vector3(randomX - 4.5, 0.25, randomZ - 3.5);
target.position.copy(targetPosition);
// Näytä laskuri 1-10
timerInterval = setInterval(() => {
count++;
if (count <= 10) {
console.log(count); // Voit vaihtoehtoisesti päivittää laskurin näkymän HTML:ssä
} else {
clearInterval(timerInterval);
console.log("Etsi kohde!");
// Tähän voit lisätä pelaajan liikkumisen ja kohdetta etsivän mekaniikan
}
}, 1000);
}
Tämä on yksinkertainen runko piiloleikkipelille three.js-kirjastolla. Voit lisätä peliin pelaajan liikkumisen ja kohdetta etsivän mekaniikan sekä käyttäjän toimintoihin reagoivan logiikan. Toivottavasti tämä antaa sinulle hyvän lähtökohdan pelin luomiseen!
Tottakai! Alla on esimerkki loadMap-funktiosta, joka ottaa vastaan kartan tekstinä ja muuttaa sen 2-ulotteiseksi taulukoksi. Kartan tekstimuoto toimitetaan funktion argumenttina, ja funktio palauttaa 2-ulotteisen taulukon, jossa jokainen merkki kartasta on sijoitettu oikealle paikalleen.
javascriptfunction loadMap(mapText) {
// Jaa kartan tekstimuoto rivitaulukoksi
const mapRows = mapText.trim().split('\n');
// Luo 2-ulotteinen taulukko karttaa varten
const mapArray = [];
// Käy läpi jokainen rivi ja jaa rivit merkkien taulukoksi
for (let i = 0; i < mapRows.length; i++) {
const row = mapRows[i].trim();
const rowArray = row.split('');
// Lisää rivi taulukkoon
mapArray.push(rowArray);
}
return mapArray;
}
Voit käyttää tätä funktiota lataamaan kartan tiedot peliisi. Funktio ottaa kartan tekstimuotoisen esityksen ja palauttaa 2-ulotteisen taulukon, jossa jokainen merkki on oikeassa paikassaan.
Esimerkki karttatekstistä:
javascriptconst mapText = `
##########
#........#
#........#
#...p....#
#........#
#........#
##########
`;
Voit käyttää funktiota seuraavasti:
javascriptconst mapText = `
##########
#........#
#........#
#...p....#
#........#
#........#
##########
`;
const mapArray = loadMap(mapText);
// Tulostetaan 2-ulotteinen taulukko consoleen tarkistuksen vuoksi
console.log(mapArray);
Tämä tulostaisi konsoliin seuraavan 2-ulotteisen taulukon:
javascript[
[ '#', '#', '#', '#', '#', '#', '#', '#', '#', '#' ],
[ '#', '.', '.', '.', '.', '.', '.', '.', '.', '#' ],
[ '#', '.', '.', '.', '.', '.', '.', '.', '.', '#' ],
[ '#', '.', '.', '.', 'p', '.', '.', '.', '.', '#' ],
[ '#', '.', '.', '.', '.', '.', '.', '.', '.', '#' ],
[ '#', '.', '.', '.', '.', '.', '.', '.', '.', '#' ],
[ '#', '#', '#', '#', '#', '#', '#', '#', '#', '#' ]
]
Kuten näet, kartan merkit on sijoitettu oikeille paikoilleen 2-ulotteiseen taulukkoon. Voit nyt käyttää tätä taulukkoa luodaksesi pelialueen three.js:ssä kuten aiemmin esitetyssä koodissa.
Selvä! Voit lisätä pistemäisen valon (PointLight) 2 metrin korkeuteen kaikkiin kartan ruutuihin, joissa on v-kirjain. Alla on päivitetty koodi, joka sisältää uuden funktion addLights
, joka luo valot kaikkiin v-kirjaimilla merkittyihin ruutuihin.
Huomaa, että oletan tässä esimerkissä, että yksikköyksiköt ovat samat kuin aiemmin eli yksi ruutu on 1 metri x 1 metri.
javascriptfunction addLights(mapArray) {
const lightIntensity = 1.5;
const lightDistance = 5;
// Käy läpi kartan taulukko ja lisää valot v-kirjaimen kohdalle
for (let z = 0; z < mapArray.length; z++) {
for (let x = 0; x < mapArray[z].length; x++) {
if (mapArray[z][x] === 'v') {
const light = new THREE.PointLight(0xffffff, lightIntensity, lightDistance);
light.position.set(x - (mapArray[z].length - 1) / 2, 2, z - (mapArray.length - 1) / 2);
scene.add(light);
}
}
}
}
// Käytä funktiota addLights kartan luomisen jälkeen:
const mapText = `
##########
#........#
#...v....#
#...p....#
#...v....#
#........#
##########
`;
const mapArray = loadMap(mapText);
init();
addLights(mapArray);
startGame();
Tämä koodi luo kartan, jossa on v-kirjaimilla merkittyjä ruutuja, ja lisää pistemäiset valot (PointLight) 2 metrin korkeuteen jokaiseen v-kirjaimen kohdalle. Valon intensiteetti ja etäisyys on määritelty muuttujiin lightIntensity
ja lightDistance
, joita voit säätää tarpeen mukaan. Valot sijoitetaan kartalle oikeisiin koordinaatteihin sen perusteella, missä v-kirjaimet sijaitsevat.
Muista, että tämä on vain yksinkertainen esimerkki. Voit jatkokehittää peliä lisäämällä muita ominaisuuksia ja tehostuksia valaistukseen ja pelimekaniikkaan!
Kyllä, voit käyttää THREE.Raycaster
-luokkaa etsimään sen kohdan, johon käyttäjä klikkaa pelissä. THREE.Raycaster
-luokka luo sädettä, jota voidaan käyttää etsimään kolmiulotteisesta tilasta osumia, esimerkiksi klikkauspisteitä kohdeobjekteihin.
Alla on päivitetty koodi, joka sisältää tapahtumankäsittelijän hiiren klikkauksille. Tässä tapahtumankäsittelijässä käytetään THREE.Raycaster
-luokkaa etsimään klikkauspisteen osumia kohdeobjekteihin, eli kartan ruutuihin. Kun käyttäjä klikkaa pelialuetta, tarkistetaan, osuuko sädetetty piste johonkin kartan ruutuun, ja jos osuu, tarkastetaan, onko kyseinen ruutu kohde (tässä tapauksessa merkitty kuutiolla).
javascript// Alusta muuttuja raycaster ja hiirenpiste
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// Lisää klikkaustapahtumankäsittelijä dokumentille
document.addEventListener('click', onClick, false);
function onClick(event) {
// Muunna hiiren sijainti normalisoiduksi koordinaatiksi (-1 to 1)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
// Aseta raycasterin alkupiste hiirenpisteeseen
raycaster.setFromCamera(mouse, camera);
// Etsi osumia kartan objekteihin
const intersects = raycaster.intersectObjects(scene.children);
// Tarkista osumat
if (intersects.length > 0) {
// Tarkista onko osuma kartan ruuduissa
for (const intersect of intersects) {
const object = intersect.object;
// Jos osuma on kartan ruutu, tarkista onko se kohde (v-kirjain)
if (object instanceof THREE.Mesh) {
const x = Math.round(object.position.x + (mapArray[0].length - 1) / 2);
const z = Math.round(object.position.z + (mapArray.length - 1) / 2);
if (mapArray[z][x] === 'v') {
// Osuma oli kohde, joten pelaaja löysi sen
console.log('Löysit!');
// Voit tässä lisätä haluamasi toiminnallisuuden, kun pelaaja löytää kohteen
// Esim. aloittaa uusi kierros ja arpoa uuden sijainnin kohteelle
}
}
}
}
}
Tämä koodi lisää tapahtumankäsittelijän dokumentille, joka reagoi klikkauksiin. Kun käyttäjä klikkaa pelialuetta, tarkistetaan, osuuko klikkauspiste kartan ruutuihin THREE.Raycaster
-luokan avulla. Jos osuma osuu ruutuun ja kyseinen ruutu on kohde (v-kirjain), niin konsoliin tulostetaan "Löysit!".
Tämä antaa sinulle perusmekaniikan pelaajan klikkauksille ja kohdetta etsimiselle pelissä. Voit tämän pohjalta laajentaa peliäsi ja lisätä muita toiminnallisuuksia pelimekaniikkaan, kuten uuden kierroksen aloittamisen, pisteenlaskun, jne.
Oikein huomattu! Klikkauksen sijainti tulisi ensin suhteuttaa canvas-alueeseen, jotta saamme oikean hiirenpisteen suhteessa peli-ikkunaan. Käytämme renderer.domElement.getClientRect()
-metodia hiiren sijainnin laskemiseen.
Lisäksi päivitän koodia niin, että pelaaja kääntyy klikattuun kohtaan päin asettamalla player.rotation
-ominaisuuden sen suuntaiseksi, missä kohtaan klikkaus osui. Tätä varten käytämme kolmion geometrian keskipistettä ja laskeutua suunnan player.lookAt()
-metodilla.
Alla on päivitetty koodi:
javascript// Lisää klikkaustapahtumankäsittelijä dokumentille
document.addEventListener('click', onClick, false);
function onClick(event) {
// Laske hiiren sijainti suhteessa pelialueeseen
const canvasBounds = renderer.domElement.getBoundingClientRect();
mouse.x = ((event.clientX - canvasBounds.left) / canvasBounds.width) * 2 - 1;
mouse.y = -((event.clientY - canvasBounds.top) / canvasBounds.height) * 2 + 1;
// Aseta raycasterin alkupiste hiirenpisteeseen
raycaster.setFromCamera(mouse, camera);
// Etsi osumia kartan objekteihin
const intersects = raycaster.intersectObjects(scene.children);
// Tarkista osumat
if (intersects.length > 0) {
// Laske suunta pelaajan sijainnosta klikattuun pisteeseen
const targetPosition = intersects[0].point;
player.lookAt(targetPosition);
// Tarkista onko osuma kartan ruuduissa
for (const intersect of intersects) {
const object = intersect.object;
// Jos osuma on kartan ruutu, tarkista onko se kohde (v-kirjain)
if (object instanceof THREE.Mesh) {
const x = Math.round(object.position.x + (mapArray[0].length - 1) / 2);
const z = Math.round(object.position.z + (mapArray.length - 1) / 2);
if (mapArray[z][x] === 'v') {
// Osuma oli kohde, joten pelaaja löysi sen
console.log('Löysit!');
// Voit tässä lisätä haluamasi toiminnallisuuden, kun pelaaja löytää kohteen
// Esim. aloittaa uusi kierros ja arpoa uuden sijainnin kohteelle
}
}
}
}
}
Nyt koodi laskee hiiren sijainnin oikein suhteessa peli-ikkunaan ja kääntää pelaajan suunnan kohti klikattua kohtaa player.lookAt()
-metodilla. Huomaa, että player.lookAt()
kääntää pelaajan y-suunnassa. Jos haluat rajoittaa pelaajan katselua vain vaakasuunnassa, voit nollata pelaajan x- ja z-kierrot ennen lookAt()
-kutsua. Esimerkiksi:
javascriptplayer.rotation.x = 0;
player.rotation.z = 0;
player.lookAt(targetPosition);
Tämän jälkeen pelaaja kääntyy vain pystysuunnassa kohdetta kohti ja ei muuta sijaintiaan. Voit säätää koodia edelleen haluamasi toiminnallisuuden ja pelimekaniikan mukaan.
Kiitos tiedosta! Hyvä, että korjasit koordinaatit suoraan vastaamaan karttataulukon indeksejä ilman ylimääräisiä siirtoja. Huomioin tämän jatkossa vastauksissani.
Jos sinulla on muita kysymyksiä tai tarvitset apua lisää pelisi kehittämisessä, olethan rohkeasti yhteydessä! Onnea pelin kehittämiseen, ja toivottavasti pääset luomaan hauskan ja toimivan piiloleikkipelejä three.js:llä.
Voit laskea pelaajan sijainnin ja kohdeposition (targetPosition) välisen suuntavektorin lisäämällä kohdeposition ja negatiivisen pelaajan sijainnin välisen eron toisiinsa. Sen jälkeen voit normalisoida tämän suuntavektorin ja kertoa sen 0.5:llä saadaksesi uuden sijainnin, joka on 0.5 yksikköä kohdasta targetPosition kohti pelaajan sijaintia.
Alla on esimerkkikoodi, joka laskee halutun sijainnin ja tallentaa sen muuttujaan playerTarget:
javascript// Laske suuntavektori kohti pelaajaa
const directionVector = new THREE.Vector3();
directionVector.subVectors(targetPosition, player.position);
directionVector.normalize();
// Laske uusi sijainti 0.5 yksikköä kohdasta targetPosition kohti pelaajan sijaintia
const distance = 0.5;
const playerTarget = targetPosition.clone().addScaledVector(directionVector, -distance);
Nyt muuttujassa playerTarget on uusi sijainti, joka on 0.5 yksikköä kohdasta targetPosition kohti pelaajan sijaintia. Voit käyttää tätä uutta sijaintia haluamallasi tavalla pelissäsi. Esimerkiksi voit siirtää pelaajaa kohti tätä uutta sijaintia tai käyttää sitä pelaajan kohdesijaintina jossain pelin mekaniikassa.
Selvä! Voit luoda funktion updatePosition()
joka laskee kuluneen ajan edellisen kutsun ja nykyisen kutsun välillä, ja päivittää pelaajan sijainnin (player.position
) ja katselusuunnan (player.lookAt()
) sen perusteella.
Käytän tässä esimerkissä kolmea uutta muuttujaa:
playerMoveSpeed
: pelaajan nopeus, jolla pelaaja liikkuu kohtiplayerMoveTarget
-sijaintia.playerLookSpeed
: pelaajan katselun nopeus, jolla pelaaja kääntyy kohtiplayerLookTarget
-sijaintia.lastTime
: aikaleima edellisestä kutsusta, jota käytetään kuluneen ajan laskemiseen.
Alla on esimerkkikoodi, joka sisältää funktion updatePosition()
:
javascript// Alusta muuttujat kuluneen ajan laskemista varten
let lastTime = 0;
const playerMoveSpeed = 1.5; // Muuta haluamaksesi nopeudeksi
const playerLookSpeed = 0.1; // Muuta haluamaksesi nopeudeksi
function updatePosition() {
// Laske kulunut aika edellisen kutsun ja nykyisen kutsun välillä
const currentTime = performance.now(); // Käytä performance.now() tarkkaan ajanmittaukseen
const deltaTime = (currentTime - lastTime) * 0.001; // Muunna millisekunnit sekunneiksi
lastTime = currentTime;
// Laske suuntavektorit pelaajan liikkeelle ja katselulle
const moveDirection = new THREE.Vector3();
moveDirection.subVectors(playerMoveTarget, player.position);
moveDirection.normalize();
const lookDirection = new THREE.Vector3();
lookDirection.subVectors(playerLookTarget, playerLookTargetNow);
lookDirection.normalize();
// Päivitä pelaajan sijainti kuluneen ajan mukaan
const moveDistance = playerMoveSpeed * deltaTime;
player.position.addScaledVector(moveDirection, moveDistance);
// Päivitä pelaajan katselusuunta kuluneen ajan mukaan
const lookDistance = playerLookSpeed * deltaTime;
if (lookDistance <= 1) {
playerLookTargetNow.addScaledVector(lookDirection, lookDistance);
}
// Käännetään pelaaja katsomaan sijaintiin playerLookTargetNow päin
player.lookAt(playerLookTargetNow);
// Kutsu tätä funktion updatePosition() säännöllisesti esim. requestAnimationFrame-kutsussa
// Esim. requestAnimationFrame(updatePosition);
}
Tämä funktio päivittää pelaajan sijainnin ja katselusuunnan kuluneen ajan mukaan. Pelaaja liikkuu playerMoveSpeed
-nopeudella kohti playerMoveTarget
-sijaintia ja kääntyy katsomaan playerLookTarget
-sijaintia kohti playerLookSpeed
-nopeudella.
Tärkeää on kutsua tätä updatePosition()
-funktiota säännöllisesti pelin päivitysloopissa, esimerkiksi requestAnimationFrame()
-funktion sisällä.
Tämän esimerkkikoodin perusteella voit jatkaa pelaajan liikkumisen ja katselun toiminnallisuuden kehittämistä pelissäsi. Voit esimerkiksi määrittää playerMoveTarget
- ja playerLookTarget
-muuttujat haluamiisi sijainteihin, joiden suuntaan pelaaja liikkuu ja katsoo.
Selvä! Voit päivittää updatePlayerTarget()
-funktiota niin, että se asettaa playerLookTarget
-sijainniksi targetPosition
-sijainnin, mutta enintään 0.2 yksikön y-erolla pelaajan sijaintiin. Lisäksi playerMoveTarget
-sijainniksi asetetaan targetPosition
, mutta samassa y-tasossa kuin pelaajan sijainti. Jos playerMoveTarget
on yli 5 yksikön päässä pelaajasta, asetetaan se enintään 5 yksikön päähän pelaajasta.
Alla on päivitetty updatePlayerTarget()
-funktio:
javascriptfunction updatePlayerTarget() {
// Kopioi targetPosition pelaajan liikesijainniksi (playerMoveTarget)
playerMoveTarget.copy(targetPosition);
playerMoveTarget.y = player.position.y; // Aseta sama y-taso kuin pelaajalla
// Kopioi targetPosition pelaajan katselusijainniksi (playerLookTarget)
playerLookTarget.copy(targetPosition);
// Rajoita y-ero enintään 0.2 yksikköön pelaajan sijaintiin verrattuna
const yDifference = playerLookTarget.y - player.position.y;
if (Math.abs(yDifference) > 0.2) {
playerLookTarget.y = player.position.y + (yDifference > 0 ? 0.2 : -0.2);
}
// Rajoita playerMoveTargetin etäisyys enintään 5 yksikköön pelaajasta
const distanceToMoveTarget = player.position.distanceTo(playerMoveTarget);
if (distanceToMoveTarget > 5) {
const moveDirection = new THREE.Vector3();
moveDirection.subVectors(playerMoveTarget, player.position);
moveDirection.normalize();
playerMoveTarget.copy(player.position).addScaledVector(moveDirection, 5);
}
}
Tämä päivitetty funktio varmistaa, että playerMoveTarget
on sama kuin targetPosition
mutta samassa y-tasossa kuin pelaaja, ja että playerLookTarget
on sama kuin targetPosition
, mutta enintään 0.2 yksikön y-erolla pelaajan sijaintiin. Lisäksi jos playerMoveTarget
on yli 5 yksikön päässä pelaajasta, sitä rajoitetaan enintään 5 yksikön päähän pelaajasta.
Kutsu tätä päivitettyä funktiota tarvittaessa pelissäsi, jotta pelaajan liike- ja katselusijainnit pysyvät halutuissa rajoissa.
Selvä! Voit luoda funktion isWall
, joka tarkistaa, onko annetussa kohdassa (x, z)
seinää mapArray
-taulukossa. Funktion tulisi palauttaa true
, jos annetut koordinaatit ovat seinää tai eivät sisälly taulukkoon (eli ovat taulukon ulkopuolella).
Tässä on esimerkkikoodi, joka sisältää funktion isWall
:
javascriptfunction isWall(x, z) {
// Tarkista ensin, onko koordinaatit taulukon ulkopuolella
if (x < 0 || z < 0 || z >= mapArray.length || x >= mapArray[0].length) {
return true;
}
// Tarkista, onko koordinaatissa seinä ('#')
const tile = mapArray[z][x];
return tile === '#';
}
Tämä funktio tarkistaa ensin, ovatko annetut koordinaatit taulukon ulkopuolella, ja jos ovat, palauttaa true
. Tämä on tärkeää, jotta vältetään virheet, kun yritetään tarkistaa taulukon ulkopuolisia koordinaatteja.
Sitten funktio tarkistaa, onko annetussa koordinaatissa mapArray
-taulukossa merkki #
, joka tarkoittaa seinää. Jos koordinaatissa on merkki #
, funktio palauttaa myös true
.
Käytä tätä funktiota pelissäsi tarvittaessa tarkistamaan, onko annetussa kohdassa seinää tai onko koordinaatit taulukon ulkopuolella. Esimerkiksi voit käyttää sitä pelaajan liikkumisen rajoittamiseen tai tarkistaaksesi, voiko pelaaja liikkua haluttuun suuntaan pelialueella.
Voit käyttää isWall
-funktiota tarkistamaan, onko playerMoveTarget - (dx, 0, dz)
-kohdassa seinää. Ensimmäiseksi sinun on laskettava uudet koordinaatit käyttämällä pelaajan nykyistä playerMoveTarget
-sijaintia ja siitä vähentämällä dx (x-suunnan muutos) ja dz (z-suunnan muutos).
Tässä on esimerkkikoodi, joka käyttää isWall
-funktiota tarkistamaan kohdan playerMoveTarget - (dx, 0, dz)
seinää:
javascriptfunction canMove(dx, dz) {
// Laske uudet koordinaatit pelaajan nykyisen playerMoveTargetin perusteella
const newX = playerMoveTarget.x - dx;
const newZ = playerMoveTarget.z - dz;
// Tarkista, onko uusissa koordinaateissa seinää tai ovatko ne taulukon ulkopuolella
return !isWall(newX, newZ);
}
Tämä canMove
-funktio tarkistaa uusissa koordinaateissa (playerMoveTarget - (dx, 0, dz)
) olevan tilan kutsumalla isWall
-funktiota. Jos isWall
palauttaa true
, se tarkoittaa, että uudessa kohdassa on seinä, ja canMove
palauttaa false
, mikä tarkoittaa, että pelaaja ei voi liikkua kyseiseen suuntaan.
Jos isWall
palauttaa false
, canMove
palauttaa true
, ja tämä tarkoittaa, että pelaaja voi liikkua kyseiseen suuntaan, koska uudessa kohdassa ei ole seinää eikä se ole taulukon ulkopuolella.
Käytä tätä canMove
-funktiota tarvittaessa pelissäsi pelaajan liikkumisen rajoittamiseen, jotta pelaaja voi liikkua vain avoimilla alueilla. Voit kutsua tätä funktiota ennen pelaajan liikuttamista ja tarkistaa, onko liike sallittu vai ei.
Selvä! Voit luoda funktion fixCollision(v, d)
, joka testaa canMove
-funktiolla liikkumista suuntiin (d, 0)
, (-d, 0)
, (0, d)
ja (0, -d)
. Jos canMove
palauttaa false
, eli törmäys tapahtuu, funktio siirtää v
-vektoria d
:n verran kauemmas kyseisellä akselilla, jotta vältetään törmäys.
Tässä on esimerkkikoodi fixCollision
-funktiosta:
javascriptfunction fixCollision(v, d) {
if (!canMove(v.x + d, v.z)) {
// Jos törmäys tapahtuu suunnassa (d, 0), siirrä vektoria d yksikön verran kauemmas kyseisellä akselilla
v.x += d;
}
if (!canMove(v.x - d, v.z)) {
// Jos törmäys tapahtuu suunnassa (-d, 0), siirrä vektoria d yksikön verran kauemmas kyseisellä akselilla
v.x -= d;
}
if (!canMove(v.x, v.z + d)) {
// Jos törmäys tapahtuu suunnassa (0, d), siirrä vektoria d yksikön verran kauemmas kyseisellä akselilla
v.z += d;
}
if (!canMove(v.x, v.z - d)) {
// Jos törmäys tapahtuu suunnassa (0, -d), siirrä vektoria d yksikön verran kauemmas kyseisellä akselilla
v.z -= d;
}
}
Tämä fixCollision
-funktio testaa canMove
-funktiolla liikkumista neljään eri suuntaan (d, 0)
, (-d, 0)
, (0, d)
ja (0, -d)
. Jos törmäys tapahtuu jollain suunnalla, funktio siirtää v
-vektoria d
:n verran kauemmas kyseisellä akselilla, jotta vältetään törmäys.
Voit käyttää tätä fixCollision
-funktiota ennen pelaajan liikuttamista varmistaaksesi, että pelaaja ei törmää seinään tai muihin esteisiin. Kutsu tätä funktiota playerMoveTarget
-vektorin kanssa, ennen kuin päivität pelaajan sijainnin player.position
-vektorin avulla. Voit myös käyttää tätä funktiota muiden vektorien kanssa, kun haluat välttää törmäyksiä pelissäsi.
Selvä! Voit luoda funktion, joka etsii mapArray
-taulukosta kaikki koordinaatit, joissa ei ole seinää, mutta joiden ympärillä on seinää vähintään kahdessa ruudussa neljästä.
Tässä on esimerkkikoodi tällaisen funktion luomiseksi:
javascriptfunction findOpenCoordinatesWithSurroundingWalls() {
const openCoordinates = [];
// Käy läpi kaikki taulukon rivit ja sarakkeet
for (let z = 0; z < mapArray.length; z++) {
for (let x = 0; x < mapArray[0].length; x++) {
// Tarkista, että taulukossa ei ole seinää kyseisessä koordinaatissa
if (mapArray[z][x] !== '#') {
let wallCount = 0;
// Tarkista ympäröivät ruudut (ylä-, ala-, vasen-, oikea-ruudut)
const directions = [
{ dx: 0, dz: 1 },
{ dx: 0, dz: -1 },
{ dx: 1, dz: 0 },
{ dx: -1, dz: 0 },
];
for (const direction of directions) {
const nx = x + direction.dx;
const nz = z + direction.dz;
// Tarkista, että ympäröivässä ruudussa on seinä
if (isWall(nx, nz)) {
wallCount++;
}
}
// Jos ympäröivissä ruuduissa on seinä vähintään 2:sta 4:stä, lisää koordinaatti openCoordinates-taulukkoon
if (wallCount >= 2) {
openCoordinates.push({ x, z });
}
}
}
}
return openCoordinates;
}
Tämä findOpenCoordinatesWithSurroundingWalls
-funktio käy läpi mapArray
-taulukon rivit ja sarakkeet. Se tarkistaa jokaisessa koordinaatissa, että ei ole seinää, ja sitten tarkistaa ympäröivät ruudut (ylä-, ala-, vasen-, oikea-ruudut) ja laskee, kuinka monta niistä on seinää. Jos ympäröivissä ruuduissa on seinä vähintään 2:sta 4:stä, lisätään koordinaatti openCoordinates
-taulukkoon.
Kun funktio on suoritettu, openCoordinates
-taulukossa on kaikki koordinaatit, joissa ei ole seinää, mutta joiden ympärillä on seinää vähintään kahdessa ruudussa neljästä. Voit käyttää näitä koordinaatteja esimerkiksi pelissäsi tietynlaisten pelialueiden tai objektien sijoittamiseen.
Selvä! Voit luoda funktion showText(start, time, text)
, joka näyttää annetun text
-tekstin keskellä ruutua tietyn viiveen jälkeen (start
sekuntia) ja näyttää sen time
sekunnin ajan. Elementin luontiin ja tyylien määrittämiseen käytetään dynaamista HTML-elementtiä, jonka voit lisätä DOM-rakenteeseen.
Tässä on esimerkkikoodi tällaisen funktion luomiseksi:
html<!-- CSS-luokka -->
<style>
.popup-text {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 10px 20px;
border-radius: 5px;
font-size: 20px;
z-index: 9999;
}
</style>
<!-- Tyhjä elementti, johon tekstit luodaan dynaamisesti -->
<div id="popupContainer"></div>
<script>
function showText(start, time, text) {
// Luodaan uusi div-elementti, joka sisältää tekstin
const textElement = document.createElement('div');
textElement.textContent = text;
textElement.classList.add('popup-text');
// Lisätään tekstielementti popupContainer-diviin
const popupContainer = document.getElementById('popupContainer');
popupContainer.appendChild(textElement);
// Asetetaan ajanotto näytölle 'start' sekunnin kuluttua
setTimeout(() => {
// Näytetään tekstielementti
textElement.style.display = 'block';
// Asetetaan ajanotto poistamaan tekstielementti 'time' sekunnin kuluttua
setTimeout(() => {
// Piilotetaan tekstielementti ja poistetaan se popupContainer-divistä
textElement.style.display = 'none';
popupContainer.removeChild(textElement);
}, time * 1000);
}, start * 1000);
}
</script>
Tämä koodi luo showText(start, time, text)
-funktion, joka toimii seuraavasti:
- Funktio luo uuden
<div>
-elementin, joka sisältää annetuntext
-tekstin. - Tekstielementtiä tyylitetään CSS-luokan
popup-text
avulla. - Tekstielementti lisätään
<div>
-elementtiin, jonkaid
onpopupContainer
. Tämä div toimii säiliönä, johon kaikki näytettävät tekstit luodaan dynaamisesti. - Tekstielementti piilotetaan aluksi (
display: none;
). - Asetetaan ajanotto käyttäen
setTimeout
-funktiota, joka näyttää tekstin annetun viiveen jälkeen (start
sekuntia). - Ajanoton sisällä asetetaan toinen ajanotto, joka piilottaa tekstin annetun ajan kuluttua (
time
sekuntia) ja poistaa tekstin<div>
-elementistä.
Voit käyttää tätä showText
-funktiota halutessasi näyttää tekstejä pelissäsi tai muussa verkkosivustossasi. Muista luoda HTML-sivulle myös elementti <div id="popupContainer"></div>
, jotta tekstit voidaan näyttää oikein.
Voit lisätä himmeän yleisvalon (AmbientLight
) sceneen seuraavalla yhdellä rivillä:
javascriptconst ambientLight = new THREE.AmbientLight(0x404040); // Väri voi olla mikä tahansa sopiva RGB-arvo
scene.add(ambientLight);
Tämä luo uuden AmbientLight
-valon, joka antaa tasaisen himmeän valaistuksen koko sceneen. Väriksi on tässä esimerkissä valittu harmaa (0x404040
), mutta voit valita minkä tahansa RGB-värin, joka sopii peliisi tai kohtaukseen. Tämän jälkeen valo lisätään sceneen scene.add(ambientLight);
-rivillä.
Kyllä, mobiililaitteille tulisi huomioida pixel ratio
, jotta näyttöä käsitellään oikein eri laitteilla, joissa voi olla eri pikselimääriä per fyysinen tuuma (DPI - dots per inch). Tämä auttaa varmistamaan, että näytettävät grafiikat ja sisällöt näyttävät yhtä terävinä eri laitteilla.
pixel ratio
voidaan ottaa huomioon käyttämällä window.devicePixelRatio
-ominaisuutta. Tämä ominaisuus palauttaa laitteen fyysisen pikselimäärän suhteen CSS-pikseleihin. Monilla mobiililaitteilla pixel ratio
on yleensä 2, mikä tarkoittaa, että yksi CSS-pikseli vastaa neljää fyysistä pikseliä (2x2 pikseliä).
Yksi rivi koodia ottaaksesi pixel ratio
huomioon Three.js -sovelluksessa on seuraava:
javascriptrenderer.setPixelRatio(window.devicePixelRatio);
Tämä rivi asettaa renderöijän (renderer
) pixel ratio
:n laitteen window.devicePixelRatio
-arvoksi, jolloin Three.js huolehtii sisäisesti skaalaamisesta sopivaan resoluutioon laitteen näytön mukaan. Tämä auttaa parantamaan grafiikan terävyyttä ja yleistä suorituskykyä mobiililaitteilla.
Voit lisätä elementille '.popup-text' minimileveyden ja keskityksen seuraavasti:
html<!-- CSS-luokka -->
<style>
.popup-text {
/* Muut tyylit */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 10px 20px;
border-radius: 5px;
font-size: 20px;
z-index: 9999;
/* Uudet tyylit */
min-width: 5em; /* Minimileveys 5em */
text-align: center; /* Keskitys sisällön teksti */
}
</style>
Lisäsimme CSS-luokkaan '.popup-text' uudet tyylit:
min-width: 5em;
asettaa elementin minimileveyden 5em:ksi. Tämä varmistaa, että elementti ei koskaan kutistu liian pieneksi, vaikka sen sisältämä teksti olisi lyhyt.text-align: center;
asettaa elementin sisällön tekstin keskityksen. Tämä varmistaa, että teksti näytetään keskellä elementtiä vaakasuunnassa.
Nämä tyylit varmistavat, että '.popup-text' -elementti näyttää hyvältä ja helppolukuiselta, vaikka sen sisältämä teksti olisi lyhyt tai pitkä.
Selvä! Voit päivittää fixCollision
-funktiota siten, että se tekee tarkastukset suoraan isWall
-funktiolla ja jos törmäys tapahtuu, korjaus tehdään vastakkaiseen suuntaan kuin missä törmäystä tarkastettiin.
Tässä on päivitetty fixCollision
-funktio:
javascriptfunction fixCollision(v, d) {
// Tarkista törmäys eteenpäin suunnassa (d, 0)
if (isWall(v.x + d, v.z)) {
v.x -= d; // Korjaa törmäys vastakkaiseen suuntaan
}
// Tarkista törmäys taaksepäin suunnassa (-d, 0)
if (isWall(v.x - d, v.z)) {
v.x += d; // Korjaa törmäys vastakkaiseen suuntaan
}
// Tarkista törmäys oikealle suunnassa (0, d)
if (isWall(v.x, v.z + d)) {
v.z -= d; // Korjaa törmäys vastakkaiseen suuntaan
}
// Tarkista törmäys vasemmalle suunnassa (0, -d)
if (isWall(v.x, v.z - d)) {
v.z += d; // Korjaa törmäys vastakkaiseen suuntaan
}
}
Tämä päivitetty fixCollision
-funktio tekee törmäystarkastukset isWall
-funktiolla eri suuntiin (d, 0)
, (-d, 0)
, (0, d)
ja (0, -d)
. Jos törmäys tapahtuu jollain suunnalla, korjaus tehdään vastakkaiseen suuntaan. Tämä varmistaa, että v
-vektori liikkuu mahdollisimman lähelle törmäystä, mutta ei mene sen läpi.
Voit käyttää tätä päivitettyä fixCollision
-funktiota varmistamaan, että v
-vektori ei törmää seinään tai esteisiin pelissäsi tai sovelluksessasi.
Kartan mitat mapW
(leveys) ja mapH
(korkeus) riippuvat mapArray
-taulukon koosta. Voit määrittää mapW
ja mapH
koon mapArray
-taulukon leveydestä ja korkeudesta.
Jos mapArray
on kaksiulotteinen taulukko, voit laskea mapW
-leveyden seuraavasti:
javascriptconst mapW = mapArray[0].length;
Tämä ottaa mapArray
-taulukon ensimmäisen rivin (mapArray[0]
) ja laskee sen leveyden length
-ominaisuudella.
Vastaavasti voit laskea mapH
-korkeuden seuraavasti:
javascriptconst mapH = mapArray.length;
Tämä ottaa mapArray
-taulukon pituuden, eli kuinka monta riviä mapArray
-taulukossa on.
Huomaa, että mapW
ja mapH
ovat tässä tapauksessa vain muuttujia, joihin tallennat mapArray
-taulukon leveyden ja korkeuden. Voit käyttää niitä myöhemmin esimerkiksi kartan piirtämisessä tai liikkumisen rajoittamisessa pelissäsi.
Voit luoda lattian, joka on kartan kokoinen, tammilankun värinen ja tukee valaistusta käyttämällä MeshStandardMaterial
-materiaalia. MeshStandardMaterial
on yksi Three.js:n valmiista materiaaleista, joka tukee valaistusta ja luo realistisen pinnan heijastavine varjoineen.
Tässä on koodi, joka luo lattian MeshStandardMaterial
-materiaalilla:
javascriptconst floorGeometry = new THREE.PlaneGeometry(mapW, mapH);
const floorMaterial = new THREE.MeshStandardMaterial({ color: 0x964B00 }); // Tammilankun värinen (esimerkki)
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.rotation.x = -Math.PI / 2; // Aseta lattia vaakasuoraan tasoon (y=0)
scene.add(floorMesh);
Muokkaa color
-ominaisuutta haluamaksesi tammilankun väriseksi. Esimerkissä käytetään ruskeaa väriä (0x964B00
), mutta voit asettaa minkä tahansa RGB-arvon, joka vastaa tammilankun väriä.
Lisäksi asetetaan lattia vaakasuoraan tasoon floorMesh.rotation.x = -Math.PI / 2;
, jotta se asettuu kartan alapuolelle (y=0).
Tämän jälkeen lattia luodaan ja lisätään sceneen scene.add(floorMesh);
.
Nyt sinulla pitäisi olla kartan kokoinen lattia, joka näyttää tammilankulta ja tukee valaistusta pelissäsi tai sovelluksessasi.
Jotta lisäämäsi valot ja objektit jättäisivät varjoja oikein, sinun on käytettävä MeshStandardMaterial
-materiaalia objekteissa ja otettava huomioon joitakin muita asetuksia valojen osalta.
MeshStandardMaterial
: Varmista, että käytätMeshStandardMaterial
-materiaalia objekteissa, joille haluat näyttää varjot.MeshBasicMaterial
ei tue varjoja, joten sen sijasta käytäMeshStandardMaterial
-materiaalia.
javascriptconst objectMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
const objectMesh = new THREE.Mesh(objectGeometry, objectMaterial);
scene.add(objectMesh);
- Valot: Varmista, että valot ovat oikeassa asemassa ja aseta
castShadow
-ominaisuustrue
valaisimissa, jotka heittävät varjoja. Aseta myösreceiveShadow
-ominaisuustrue
objekteille, jotka vastaanottavat varjot.
javascriptconst directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1);
directionalLight.castShadow = true;
scene.add(directionalLight);
const objectMaterial = new THREE.MeshStandardMaterial({ color: 0xffffff });
const objectMesh = new THREE.Mesh(objectGeometry, objectMaterial);
objectMesh.receiveShadow = true;
scene.add(objectMesh);
- Maaperä: Varmista, että lattia tai maaperä (
floorMesh
kartan alla) asetetaan vastaanottamaan varjoja, jotta objektien varjot heijastuvat siihen oikein.
javascriptconst floorMaterial = new THREE.MeshStandardMaterial({ color: 0x964B00 }); // Tammilankun värinen (esimerkki)
const floorMesh = new THREE.Mesh(floorGeometry, floorMaterial);
floorMesh.rotation.x = -Math.PI / 2; // Aseta lattia vaakasuoraan tasoon (y=0)
floorMesh.receiveShadow = true; // Vastaanota varjot
scene.add(floorMesh);
Kun olet asettanut materiaalit ja varjoihin liittyvät ominaisuudet oikein, valot ja objektit pitäisi jättää varjoja oikein kartan lattialle ja muihin objekteihin.
Kyllä, oikein havaittu! Jotta varjot näkyvät renderöinnissä oikein, sinun täytyy ottaa käyttöön shadowMap
ja asettaa se oikein renderöijässä (renderer
). shadowMap
on Three.js:n ominaisuus, joka mahdollistaa varjojen renderöinnin.
Tässä on lisätty shadowMap
renderöijään:
javascript// Luo renderöijä
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// Lisää shadowMap renderöijään
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // Voit valita varjojen tarkan tyypin tarpeen mukaan
// Muut renderöijän asetukset
// ...
// Lisää valoja ja objekteja, joilla on varjot, kuten aiemmin mainittiin
// ...
Tärkeää on asettaa renderer.shadowMap.enabled = true;
, jotta varjot otetaan käyttöön renderöinnissä. Voit myös valita varjojen tarkemman tyypin asettamalla renderer.shadowMap.type
-ominaisuuden. Tyypiksi voit valita esimerkiksi THREE.PCFSoftShadowMap
, joka tuottaa pehmeitä varjoja, tai THREE.PCFShadowMap
, joka tuottaa kovia varjoja.
Kun olet asettanut shadowMap
oikein, varjot tulisi nyt näkyä renderöinnissä oikein ja objektit heittävät varjoja kartan lattialle ja muihin objekteihin.
Tässä on koodi, joka siirtää target.position
-vektorin matkan targetMoveSpeed * deltaTime
kohteeseen targetNextPosition
päin, jos etäisyys on suurempi kuin 0.1:
javascriptfunction updateTargetPosition(target, targetNextPosition, targetMoveSpeed, deltaTime) {
const distanceToNextPosition = target.position.distanceTo(targetNextPosition);
if (distanceToNextPosition > 0.1) {
const direction = targetNextPosition.clone().sub(target.position).normalize();
const moveDistance = targetMoveSpeed * deltaTime;
const newPosition = target.position.clone().add(direction.multiplyScalar(moveDistance));
target.position.copy(newPosition);
}
}
Tässä updateTargetPosition
-funktiossa:
- Lasketaan etäisyys nykyisen ja seuraavan sijainnin välillä
const distanceToNextPosition = target.position.distanceTo(targetNextPosition);
. - Jos etäisyys on suurempi kuin 0.1, lasketaan suunta seuraavaan sijaintiin
const direction = targetNextPosition.clone().sub(target.position).normalize();
. - Lasketaan liikkumismatka etäisyyden ja ajan (deltaTime) perusteella
const moveDistance = targetMoveSpeed * deltaTime;
. - Luodaan uusi sijainti
newPosition
, joka on nykyisen sijainnin ja liikkumismatkan välinen pisteconst newPosition = target.position.clone().add(direction.multiplyScalar(moveDistance));
. - Päivitetään
target.position
uudella sijainnillatarget.position.copy(newPosition);
.
Tämä koodi liikuttaa target.position
-vektoria matkan targetMoveSpeed * deltaTime
verran kohti targetNextPosition
-sijaintia, kun etäisyys näiden välillä on suurempi kuin 0.1. Muuten se ei tee mitään, jolloin target.position
pysyy paikallaan.
Tässä on createNoiseTexture
-funktio, joka luo 4nn tavun taulukon satunnaisilla luvuilla, lataa sen tekstuuriksi (DataTexture
), ja asettaa wrap-arvot siten, että tekstuuri toistuu. Lisäksi funktio ottaa w0
, h0
, ja rotation
parametrit toistoa ja kiertokulmaa varten:
javascriptfunction createNoiseTexture(n, w0, h0, rotation) {
const size = 4 * n * n;
const data = new Uint8Array(size);
for (let i = 0; i < size; i++) {
data[i] = Math.floor(Math.random() * 256); // Satunnainen arvo välillä 0-255
}
const texture = new THREE.DataTexture(data, n, n, THREE.RGBAFormat);
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(w0, h0);
if (rotation) {
texture.center.set(0.5, 0.5);
texture.rotation = rotation;
}
return texture;
}
Tämä funktio luo n
x n
kokoisen DataTexture
:n, joka täytetään satunnaisilla RGBA-arvoilla. w0
ja h0
ovat toistamisen arvoja, ja rotation
kiertokulmaa varten.
Voit käyttää tätä funktiota luomaan satunnaisen kohinan tekstuurin, joka voidaan asettaa materiaalin tekstuuriksi toistuvaksi ja mahdollisesti kiertää halutulla tavalla. Esimerkiksi voit käyttää tätä kohinaa materiaalissa, joka kuuluu lattiaan tai seinään lisätäksesi visuaalista monimuotoisuutta ja realistisuutta.
Oikea import-rivi Three.js:n lataamiseen riippuu siitä, miten olet asentanut Three.js-kirjaston projektiisi. Jos olet asentanut sen npm-paketin kautta, import-rivi olisi seuraavanlainen:
javascriptimport * as THREE from 'three';
Tämä edellyttää, että olet asentanut Three.js:n npm-paketin projektiisi komennolla:
npm install three
Jos olet ladannut Three.js:n CDN:ltä tai käytät sitä muulla tavalla, import-rivi voi olla erilainen.
Lisäksi olet oikeassa siinä, että koodi tulee määritellä moduuliksi, jotta import-lausetta voi käyttää. Voit tehdä tämän lisäämällä type="module"
attribuutin <script>
-tagiin:
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Three.js Example</title>
</head>
<body>
<!-- Your HTML content here -->
<script type="module">
// Your JavaScript code with import statement here
</script>
</body>
</html>
Tällä tavoin voit käyttää import
-lauseita ja moduulirakennetta Three.js-koodissasi. Muista, että moduulituki vaatii modernin selaimen, joka tukee ECMAScript 6:ta.
Selvä! Tässä on initPlayer
-funktio, joka toteuttaa edellä kuvatut vaiheet:
javascriptfunction initPlayer() {
// Hae koordinaatit
const coordinates = findOpenCoordinatesWithSurroundingWalls();
// Valitse satunnainen lähtöpaikka
const randomIndex = Math.floor(Math.random() * coordinates.length);
const startPosition = coordinates[randomIndex];
// Aseta lähtöpaikka muuttujiin
playerMoveTarget.copy(startPosition);
player.position.copy(startPosition);
target.position.copy(startPosition);
// Valitse koordinaatti, joka on kauimpana pelaajasta
let farthestDistance = 0;
let farthestPosition = new THREE.Vector3();
for (const position of coordinates) {
const distance = player.position.distanceTo(position);
if (distance > farthestDistance) {
farthestDistance = distance;
farthestPosition.copy(position);
}
}
// Aseta kaukaisin koordinaatti muuttujiin
targetNextPosition.copy(farthestPosition);
playerLookTarget.copy(farthestPosition);
playerLookTargetNow.copy(farthestPosition);
}
Tämä initPlayer
-funktio:
- Hakee koordinaatit funktiolla
findOpenCoordinatesWithSurroundingWalls
. - Valitsee satunnaisen lähtöpaikan näistä koordinaateista.
- Asettaa lähtöpaikan muuttujiin
playerMoveTarget
,player.position
jatarget.position
. - Etsii koordinaatin, joka on kauimpana pelaajasta.
- Asettaa kaukaisimman koordinaatin muuttujiin
targetNextPosition
,playerLookTarget
japlayerLookTargetNow
.
Tämän jälkeen initPlayer
-funktio asettaa pelaajan ja kohteen sijainnit sekä asetuksen valmiiksi pelin aloitusta varten.
Selvä! Tässä on relocateTarget
-funktio, joka toteuttaa edellä kuvatut vaiheet:
javascriptfunction relocateTarget() {
const coordinates = findOpenCoordinatesWithSurroundingWalls();
// Poista koordinaatit, jotka ovat pelaajan ja kohteen välissä tai liian lähellä pelaajaa
const filteredCoordinates = coordinates.filter(position => {
const distanceToPlayer = player.position.distanceTo(position);
const distanceToTarget = target.position.distanceTo(position);
if (distanceToPlayer < 5 || distanceToTarget < 5) {
return false;
}
raycaster.set(player.position, position.clone().sub(player.position).normalize());
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
return false;
}
return true;
});
if (filteredCoordinates.length > 0) {
// Valitse satunnainen koordinaatti jäljelle jäävistä
const randomIndex = Math.floor(Math.random() * filteredCoordinates.length);
targetNextPosition.copy(filteredCoordinates[randomIndex]);
}
}
Tämä relocateTarget
-funktio:
- Hakee koordinaatit funktiolla
findOpenCoordinatesWithSurroundingWalls
. - Käy läpi koordinaatit ja poistaa ne, jotka ovat pelaajan ja kohteen välissä tai liian lähellä pelaajaa.
- Valitsee satunnaisen koordinaatin jäljelle jäävistä koordinaateista ja asettaa sen muuttujaan
targetNextPosition
.
Tämän jälkeen relocateTarget
-funktio on valinnut uuden kohdekoordinaatin, johon pelaaja voi liikkua seuraavaksi. Voit kutsua tätä funktiota esimerkiksi silloin, kun pelaaja on löytänyt nykyisen kohteen tai kun uusi kohde on tarpeen muista syistä.
Voit estää käyttäjää valitsemasta sivun tekstiä käyttämällä CSS-ominaisuutta nimeltä user-select
. Tämän ominaisuuden avulla voit hallita, voiko käyttäjä valita sivun tekstin vai ei.
Voit estää käyttäjää valitsemasta tekstiä lisäämällä seuraavan CSS-säännön:
cssbody {
user-select: none;
}
Tämä CSS-sääntö estää käyttäjää valitsemasta tekstiä koko sivulla, koska se vaikuttaa body
-elementtiin ja sen lapsiin.
Jos haluat estää tekstivalinnan vain tietyissä elementeissä, voit soveltaa user-select
-ominaisuutta niihin:
css.no-select {
user-select: none;
}
Sitten voit lisätä luokan .no-select
niihin elementteihin, joiden haluat estää tekstivalinnan:
html<p class="no-select">Tämä teksti ei ole valittavissa.</p>
Tällä tavoin voit estää käyttäjää valitsemasta sivun tekstiä haluamissasi osissa sivua. Huomaa kuitenkin, että tämä ei estä muita tapoja käsitellä tai kopioida tekstiä, kuten kopiointikomentoja näppäimistöltä tai näppäinyhdistelmiä.
Voit käyttää bilineaarista interpolointia saadaksesi värit yksikköneliön kulmiin annetuista väreistä. Bilineaarinen interpolointi on tekniikka, joka luo väriarvon kahden arvon välille interpoloimalla niiden välistä arvoa kahdessa ulottuvuudessa.
Tässä on getColor
-funktio, joka käyttää bilineaarista interpolointia saadakseen värit yksikköneliön kulmiin annetuista väreistä:
javascriptfunction getColor(x, y) {
const colorTL = new THREE.Color("#ff8080"); // Vasen yläkulma
const colorTR = new THREE.Color("#ffff80"); // Oikea yläkulma
const colorBL = new THREE.Color("#80ff80"); // Vasen alakulma
const colorBR = new THREE.Color("#8080ff"); // Oikea alakulma
const colorTop = new THREE.Color().lerpColors(colorTL, colorTR, x); // Yläreunan väri
const colorBottom = new THREE.Color().lerpColors(colorBL, colorBR, x); // Alareunan väri
const colorFinal = new THREE.Color().lerpColors(colorTop, colorBottom, y); // Lopullinen väri
return colorFinal;
}
Tässä getColor
-funktiossa:
- Luomme neljä väriä
colorTL
,colorTR
,colorBL
jacolorBR
, jotka vastaavat yksikköneliön kulmia annetuilla väreillä. - Käytämme
lerpColors
-funktiota, joka on Three.js:n tarjoama värien interpolointifunktio, interpoloidaksemme ylä- ja alareunan värit vaakasuunnassa (x-akseli). - Lopuksi käytämme jälleen
lerpColors
-funktiota interpoloimaan ylä- ja alareunan värit pystysuunnassa (y-akseli) ja saamme näin lopullisen värin.
Kun kutsut getColor(x, y)
, se antaa sinulle halutun väriarvon yksikköneliön kulmissa määritellyistä väreistä, ja voit käyttää tätä väriä esimerkiksi materiaalin värinä Three.js-sovelluksessa.
Voit saada Three.js:n reagoimaan oikein ikkunan koon muuttumiseen seuraavilla toimenpiteillä:
Luo
onWindowResize
-funktio, joka päivittää näkökentän (camera) näkökulman (aspect ratio) ja päivittää renderöijän (renderer) näkymän koon uudestaan.Kuuntele
window
-olion"resize"
-tapahtumaa ja kutsuonWindowResize
-funktiota aina kun ikkunan koko muuttuu.
Alla on esimerkkikoodi, joka näyttää, miten voit toteuttaa nämä toimenpiteet Three.js-sovelluksessa:
javascript// Luo onWindowResize-funktio
function onWindowResize() {
// Päivitä näkökenttä näkökulma
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
// Päivitä renderöijän näkymän koko
renderer.setSize(window.innerWidth, window.innerHeight);
}
// Kuuntele ikkunan resize-tapahtumaa ja kutsu onWindowResize-funktiota
window.addEventListener('resize', onWindowResize, false);
Tämä koodi määrittelee onWindowResize
-funktion, joka päivittää näkökentän näkökulman uudelleen ikkunan koon muuttuessa. Sen jälkeen kuuntelemme window
-olion "resize"
-tapahtumaa ja kutsutaan onWindowResize
-funktiota aina kun ikkunan koko muuttuu.
Tämän avulla Three.js-sovellus reagoi oikein ikkunan koon muutoksiin ja säilyttää näytön oikean suhteen ilman venymistä tai vääristymiä.
Tässä on koodi, joka lataa mallin tiedostosta "target.glb" ja lisää sen ryhmään target
sekä asettaa oikeat materiaalit ja varjosäädöt meshien sisällä:
javascriptconst loader = new THREE.GLTFLoader();
// Luodaan tyhjä THREE.Group, jotta lataus ei estä pelin toimintaa
const target = new THREE.Group();
// Lataa malli tiedostosta
loader.load(
'target.glb',
(gltf) => {
// Lisää ladattu malli ryhmään
gltf.scene.traverse((child) => {
if (child.isMesh) {
// Aseta meshille targetMaterial
child.material = targetMaterial;
// Aseta varjosäädöt
child.castShadow = true;
child.receiveShadow = true;
}
});
// Lisää ladattu malli target-ryhmään
target.add(gltf.scene);
},
undefined,
(error) => {
console.error('Mallin lataaminen epäonnistui:', error);
}
);
Tässä koodissa käytetään THREE.GLTFLoader
-luokkaa lataamaan GLB-mallitiedosto nimeltä "target.glb". Latauksen aikana luodaan tyhjä THREE.Group
nimeltä target
, jotta lataus ei estä pelin toimintaa.
Kun malli on ladattu, käydään läpi sen sisältämät meshit (child.isMesh
) ja asetetaan niille oikea materiaali (targetMaterial
) sekä varjosäädöt (castShadow
ja receiveShadow
) varjojen oikeaa toimintaa varten. Lopuksi ladattu malli lisätään target
-ryhmään.
Näin varmistetaan, että mallin lataus ei estä pelin toimintaa, ja malliin asetetaan oikeat materiaalit ja varjosäädöt varjojen näyttämiseksi oikein pelissä.
Jotta koodi toimisi oikein, tarvitset oikean import-rivin, joka lataa tarvittavat Three.js-komponentit käyttöösi. Koodissa käytetään muun muassa THREE.GLTFLoader
, joten tarvitset ainakin GLTFLoaderin käyttöön.
Oikea import-rivi olisi seuraavanlainen:
javascriptimport * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
Tämä import-rivi lataa kaikki tarvittavat Three.js-komponentit, mukaan lukien THREE.Group
ja GLTFLoader
. Näin voit käyttää näitä komponentteja oikein koodissa ja ladata mallin tiedostosta "target.glb" sekä tehdä muut tarvittavat asetukset.
Selvä! Voit tarkistaa pelaajan etäisyyden kohteesta ja tehdä tarvittavat toimenpiteet updatePosition
-funktion lopussa. Tässä on päivitetty versio updatePosition
-funktiosta, joka sisältää tämän tarkistuksen ja kutsun relocateTarget
-funktiolle sekä showText
-funktiolle:
javascriptfunction updatePosition() {
const deltaTime = clock.getDelta();
// Laske etäisyys pelaajasta kohteeseen
const distanceToTarget = player.position.distanceTo(targetNextPosition);
// Siirrä pelaajaa kohti kohdetta, jos etäisyys on suurempi kuin 0.1
if (distanceToTarget > 0.1) {
player.position.lerp(targetNextPosition, playerMoveSpeed * deltaTime);
}
// Siirrä pelaajan katselupistettä kohti kohdetta
playerLookTargetNow.lerp(targetNextPosition, playerLookSpeed * deltaTime);
player.lookAt(playerLookTargetNow);
// Tarkista, onko pelaajan etäisyys kohteesta alle 1
if (distanceToTarget < 1) {
// Siirrä kohde uuteen paikkaan
relocateTarget();
// Näytä teksti "Etsi taas!" 3 sekunnin ajan
showText(0, 3, "Etsi taas!");
}
// Päivitä kontrollit ja renderöi näkymä
controls.update();
renderer.render(scene, camera);
}
Tässä koodissa updatePosition
-funktiossa:
- Lasketaan etäisyys pelaajasta kohteeseen käyttämällä
player.position.distanceTo(targetNextPosition)
. - Jos etäisyys on alle 1, kutsutaan
relocateTarget
-funktiota siirtääksemme kohde uuteen paikkaan. - Kutsutaan
showText
-funktiota näyttääksemme teksti "Etsi taas!" 3 sekunnin ajan, jos pelaajan etäisyys kohteesta on alle 1.
Näin peli toimii niin, että kun pelaaja saavuttaa kohteen, kohde siirtyy uuteen paikkaan ja näytetään teksti "Etsi taas!" pelaajalle, jonka jälkeen pelaaja voi jatkaa kohteen etsimistä.
ShadowMapin automaattisen päivityksen poistaminen ja sen päivittäminen valitsemissasi tilanteissa vaatii muutoksen renderöijän (renderer) asetuksiin.
Voit poistaa automaattisen shadowMapin päivityksen asettamalla autoUpdate
-ominaisuuden arvoksi false
. Tämän jälkeen voit päivittää shadowMapin manuaalisesti käyttämällä renderer.shadowMap.needsUpdate = true;
aina silloin, kun haluat päivittää varjot, esimerkiksi pelaajan liikkuessa uuteen paikkaan tai kohteen siirtyessä.
Tässä on esimerkki, miten voit tehdä tämän muutoksen:
javascript// Aseta shadowMapin automaattinen päivitys pois päältä
renderer.shadowMap.autoUpdate = false;
// Päivitä shadowMap manuaalisesti haluamissasi tilanteissa
function updateShadowMap() {
renderer.shadowMap.needsUpdate = true;
}
// Kutsu updateShadowMap-funktiota aina kun haluat päivittää varjot, esim. pelaajan liikkuessa tai kohteen siirtyessä
// Esimerkiksi:
function movePlayerToNewPosition(newPosition) {
player.position.copy(newPosition);
updateShadowMap();
}
// Tai kun kutsut relocateTarget-funktiota:
function relocateTarget() {
// Tähän kuuluu koodi relocateTarget-funktion sisältä
updateShadowMap();
}
// Muista myös päivittää varjot aina kun teet muutoksia varjoja aiheuttaviin elementteihin, esim. kun luot uuden valon tai asetat uusia varjojen ominaisuuksia.
Näin voit poistaa automaattisen shadowMapin päivityksen ja päivittää sen manuaalisesti vain silloin, kun se on tarpeen. Tällä tavalla voit säätää ja hallita varjojen päivitystä pelissä valitsemissasi tilanteissa.
Tässä on updatePixelRatio
-funktio, joka toteuttaa kuvaamasi toiminnallisuuden:
javascript// Alusta taulukko tallentamaan viimeisimmät 16 fps:tä
const fpsArray = [];
function updatePixelRatio(deltaTime) {
// Laske fps ajan perusteella
const fps = 1 / deltaTime;
// Lisää uusi fps taulukkoon
fpsArray.push(fps);
// Jos taulukossa on alle 16 lukua, palaa
if (fpsArray.length < 16) {
return;
}
// Hae pienin ja suurin fps taulukosta
const minFPS = Math.min(...fpsArray);
const maxFPS = Math.max(...fpsArray);
// Tyhjennä taulukko
fpsArray.length = 0;
// Jos pienin on yli 55, kutsu increasePixelRatio-funktiota
if (minFPS > 55) {
increasePixelRatio();
}
// Jos suurin on alle 30, kutsu decreasePixelRatio-funktiota
if (maxFPS < 30) {
decreasePixelRatio();
}
}
Tämä updatePixelRatio
-funktio:
- Laskee annetun ajan perusteella fps:n ja tallentaa sen taulukkoon
fpsArray
. - Jos taulukossa on alle 16 lukua, funktio päättyy eikä tee mitään.
- Muussa tapauksessa se hakee pienimmän ja suurimman arvon taulukosta.
- Tyhjentää taulukon valmiiksi seuraavaa laskentakierrosta varten.
- Jos pienin fps on yli 55, kutsuu
increasePixelRatio
-funktiota. - Jos suurin fps on alle 30, kutsuu
decreasePixelRatio
-funktiota.
Tämän avulla voit käyttää updatePixelRatio
-funktiota seuraamaan fps-arvoja ja tekemään tarvittavia muutoksia pikselitiheyteen (pixelRatio
) sen mukaan, onko fps korkea vai matala.
Selvä! Tässä ovat increasePixelRatio
- ja decreasePixelRatio
-funktiot mukaan lukien lipun asetus ja poisto:
javascript// Taulukko pixel ratio -vaihtoehdoista
const pixelRatioOptions = [1 / 16, 1 / 8, 1 / 4, 1 / 3, 1 / 2, 1 / 1.5, 1, 1.5, 2, 3, 4];
// Lipun asetus, jos arvoa joudutaan laskemaan
let decreaseFlag = false;
function increasePixelRatio() {
// Etsi nykyistä pixel ratio -arvoa suurempi vaihtoehto
const currentPixelRatio = renderer.getPixelRatio();
let newPixelRatio = currentPixelRatio;
for (let i = 0; i < pixelRatioOptions.length; i++) {
if (pixelRatioOptions[i] > currentPixelRatio) {
newPixelRatio = pixelRatioOptions[i];
break;
}
}
// Jos löytyi suurempi vaihtoehto ja lippua ei ole asetettu, päivitä pixel ratio
if (newPixelRatio !== currentPixelRatio && !decreaseFlag) {
renderer.setPixelRatio(newPixelRatio);
}
}
function decreasePixelRatio() {
// Etsi nykyistä pixel ratio -arvoa pienempi vaihtoehto
const currentPixelRatio = renderer.getPixelRatio();
let newPixelRatio = currentPixelRatio;
for (let i = pixelRatioOptions.length - 1; i >= 0; i--) {
if (pixelRatioOptions[i] < currentPixelRatio) {
newPixelRatio = pixelRatioOptions[i];
break;
}
}
// Jos löytyi pienempi vaihtoehto, päivitä pixel ratio ja aseta lippu
if (newPixelRatio !== currentPixelRatio) {
renderer.setPixelRatio(newPixelRatio);
decreaseFlag = true;
}
}
// Tyhjennä lippu ikkunan koon muuttuessa
function onWindowResize() {
decreaseFlag = false;
// ... (muut ikkunan koon muutokseen liittyvät päivitykset)
}
Näissä funktioissa etsitään nykyistä pixel ratio -arvoa suurempia tai pienempiä vaihtoehtoja taulukosta pixelRatioOptions
. Jos sopiva vaihtoehto löytyy, pixel ratio päivitetään käyttämällä renderer.setPixelRatio()
. Jos decreaseFlag on asetettu, increasePixelRatio
ei päivitä arvoa, jotta vältytään päivittämästä liian usein. Lippu poistetaan ikkunan koon muuttuessa.
Näin voit valita lähimmän sopivan pixel ratio -arvon nykyiseen arvoon nähden, ja käyttää sitä oikein pelissä fps:n ja suorituskyvyn optimointiin.
Tässä on lyhyt HTML-sivu, jossa on määritelty suomen kieli, otsikko "Piilopeli", tekstilaatikko pelin karttaa varten, pelin aloitukseen nappi, ja linkit lisensseihin:
html<!DOCTYPE html>
<html lang="fi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Piilopeli</title>
</head>
<body>
<h1>Piilopeli</h1>
<textarea id="kartta" rows="40" cols="40"></textarea>
<button id="aloitaPeli">Aloita peli</button>
<script type="module">
import { init } from './main.js';
document.getElementById('aloitaPeli').addEventListener('click', () => {
const karttaTeksti = document.getElementById('kartta').value;
init(karttaTeksti);
});
</script>
<h2>Lisenssit</h2>
<p><a href="LICENSE">Pelin lisenssi</a></p>
<p><a href="LICENSE.threejs">Three.js:n lisenssi</a></p>
</body>
</html>
Tässä HTML-sivussa:
<meta>
-elementti määrittelee merkistökoodauksen ja näkymän asetukset.<title>
-elementti asettaa sivun otsikon.<h1>
-elementti luo otsikon "Piilopeli".<textarea>
-elementti luo tekstilaatikon (40x40) pelin karttaa varten.<button>
-elementti luo aloitusnapin, joka kutsuuinit
-funktiota main.js-moduulista karttatekstin kanssa.<script>
-elementti lataa ja käyttää moduuliamain.js
.- Lisenssit on esitetty omissa kappaleissaan
<h2>
-elementin alla, ja linkit vievät lisenssitiedostoihin.
Huomaa, että tässä oletetaan, että main.js
-moduuli sisältää funktion init
, joka ottaa karttatekstin parametrina. Voit luoda oman main.js
-moduulin, jossa on init
-funktio ja muu tarvittava koodi pelin käynnistämiseksi.
Tässä on CSS-tyylit, jotka keskittävät sivun sisällön ja asettavat textarea
-elementin ja button
-elementin omille riveilleen:
cssbody {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
}
h1 {
text-align: center;
}
textarea {
display: block;
margin: 10px auto;
}
button {
display: block;
margin: 10px auto;
}
Nämä CSS-tyylit:
- Käyttävät
flex
-layoutiabody
-elementille, jotta sivun sisältö keskitetään sekä pysty- että vaakasuunnassa. - Asettaa otsikon
<h1>
-elementin tekstikeskitetyksi. - Muuttaa
textarea
-elementin jabutton
-elementin display-tyyliksiblock
, jotta ne ovat omilla riveillään ja täyttävät rivin leveyden. - Asettaa marginaalit
textarea
-elementille jabutton
-elementille, jotta ne ovat hieman erillään toisistaan ja näyttävät siistiltä. body
-elementille asetetaan myösheight: 100vh;
jotta sisältö keskitetään näytön keskelle myös, kun sivu on pitkä.
Lisää nämä CSS-tyylit sivun <head>
-osioon tai erilliseen .css
-tiedostoon varmistaaksesi, että sivun sisältö on keskitetty ja textarea
ja button
ovat omilla riveillään.
Selvä! Voit lisätä tarkistuksen playerLookTarget
in etäisyydelle pelaajasta updatePosition
-funktioon. Jos playerLookTarget
on yli 2 yksikön päässä pelaajasta, sen y-koordinaatiksi asetetaan pelaajan y-koordinaatti. Tässä koodinpätkä kyseisen tarkistuksen lisäämiseksi:
javascriptfunction updatePosition() {
// ... muu koodi ...
// Tarkista, onko playerLookTarget yli 2 yksikön päässä pelaajasta
const distanceToLookTarget = player.position.distanceTo(playerLookTarget);
if (distanceToLookTarget > 2) {
// Aseta playerLookTargetin y-koordinaatiksi pelaajan y-koordinaatti
playerLookTarget.y = player.position.y;
}
// Päivitä pelaajan katselupistettä kohti kohdetta
playerLookTargetNow.lerp(playerLookTarget, playerLookSpeed * deltaTime);
player.lookAt(playerLookTargetNow);
// ... muu koodi ...
}
Tämä koodinpätkä tarkistaa playerLookTarget
in etäisyyden pelaajasta ja jos se on yli 2 yksikön päässä, asettaa sen y-koordinaatiksi pelaajan y-koordinaatin. Tämän jälkeen päivitetään pelaajan katselupistettä (playerLookTargetNow
) kohti päivitettyä playerLookTarget
ia. Tämä varmistaa, että pelaaja katsoo aina kohteeseen tai sen lähettyville pelin aikana.