Mini game “alimentar al animal”— Three.js
Lo que haremos el día de hoy será hacer un minigame del curso “create with code” de unity, pero este lo haremos en threeJS
Primero lo que haremos será iniciar creando un cubo
import * as THREE from "../threejs/build/three.module.js";
let elThreejs = document.getElementById("threejs");
let camera,scene,renderer;
init();
function init() {
// Scene
scene = new THREE.Scene();
// Camera
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.z = 5;
camera.position.y = 1;
// render
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.shadowMap.enabled = true;
renderer.outputEncoding = THREE.sRGBEncoding;
elThreejs.appendChild(renderer.domElement);
addBox();
animate()
}
function animate(){
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
function addBox(){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshBasicMaterial({color: 'pink'});
let cube = new THREE.Mesh(geometry, material);
scene.add(cube);
}
Luego pondremos la cámara de manera que tengamos una vista de arriba a abajo.
camera.position.y = 20;
// rotate camera to see the scene from the top
camera.rotation.x = -Math.PI / 2;
Luego de eso agregamos un plano.
function addPlane(){
let geometry = new THREE.BoxGeometry(50, 0, 50);
let material = new THREE.MeshBasicMaterial({color: 'gray'});
let plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, -10);
scene.add(plane);
}
Luego de ello, moveremos nuestro player para la derecha y la izquierda con los teclas “W” y “S”
function addKeysListener(){
window.addEventListener('keydown', function(event){
keyboard[event.keyCode] = true;
} , false);
window.addEventListener('keyup', function(event){
keyboard[event.keyCode] = false;
} , false);
}
function movePlayer(){
// left letter A
if(keyboard[65]) cube.position.x -= 0.25;
// right letter D
if(keyboard[68]) cube.position.x += 0.25;
}
Luego limitaremos el movimiento a solo 40 metros
function movePlayer(){
// left letter A
if(keyboard[65] && cube.position.x > -20) cube.position.x -= 0.25;
// right letter D
if(keyboard[68] && cube.position.x < 20) cube.position.x += 0.25;
}
Ahora lanzaremos los proyectiles
function init() {
addProjectile();
}
function animate(){
updateProjectiles();
}
async function addProjectile(){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshBasicMaterial({color: 'green'});
projectileMesh = new THREE.Mesh(geometry, material);
}
function updateProjectiles(){
projectileMeshes.forEach((projectile, index) => {
projectile.position.z -= 0.5;
});
}
function addKeysListener(){
window.addEventListener("keyup", (event) => {
// space bar
if (event.keyCode == 32) {
let projectileMeshClone = projectileMesh.clone();
projectileMeshClone.position.x = playerMesh.position.x;
projectileMeshClone.position.y = playerMesh.position.y;
projectileMeshClone.position.z = playerMesh.position.z;
scene.add(projectileMeshClone);
projectileMeshes.push(projectileMeshClone);
}
});
}
Luego de ello destruiremos los objetos que estén fuera de la pantalla
function updateProjectiles(){
projectileMeshes.forEach((projectile, index) => {
projectile.position.z -= 0.5;
if(projectile.position.z < -20){
scene.remove(projectile);
projectileMeshes.splice(index, 1);
}
});
}
Luego agregamos el proyectil qué ira en posición contraria.
let animalMeshes = [];
let animalMesh;
function addAnimal(posX = 0){
let geometry = new THREE.BoxGeometry(1,1,1);
let material = new THREE.MeshBasicMaterial({color: 'yellow'});
animalMesh = new THREE.Mesh(geometry, material);
//set position
animalMesh.position.x = posX;
animalMesh.position.y = 0;
animalMesh.position.z = -20;
animalMeshes.push(animalMesh);
scene.add(animalMesh);
}
function spawnAnimals(){
// random number between -20 and 20
let randomX = Math.floor(Math.random() * 20) - 10;
addAnimal(randomX);
setInterval(() => {
randomX = Math.floor(Math.random() * 20) - 10;
addAnimal(randomX);
}, 2000);
}
function updateAnimals(){
animalMeshes.forEach((animal, index) => {
animal.position.z += 0.15;
if(animal.position.z > 0){
scene.remove(animal);
animalMeshes.splice(index, 1);
}
});
}
Ahora eliminamos el proyectil y los animales cuando colisionen, pasa eso comparamos las posiciones en X y en Z, para entender mejor las colisiones recomiendo leer este artículo primero.
animalMeshes.forEach((animal, indexa) => {
projectileMeshes.forEach((projectile, indexb) => {
if( animal.position.x >= projectile.position.x - 1 &&
animal.position.x <= projectile.position.x + 1 &&
animal.position.z >= projectile.position.z - 1 &&
animal.position.z <= projectile.position.z + 1){
scene.remove(animal);
animalMeshes.splice(indexa, 1);
scene.remove(projectile);
projectileMeshes.splice(indexb, 1);
}
});
});
Ahora agregamos los modelos 3D
import { GLTFLoader } from '../threejs/examples/jsm/loaders/GLTFLoader.js';
async function addPlayer(){
const gltfLoader = new GLTFLoader().setPath( 'src/assets/' );
const playerGLTF = await gltfLoader.loadAsync( 'player.glb' );
playerMesh = playerGLTF.scene.children[0];
// playerMesh.position.set(0, 0, 0); // default
scene.add(playerMesh);
}
function addPlane(){
const texture = new THREE.TextureLoader().load( "src/assets/Texture_Grass.png" );
let geometry = new THREE.BoxGeometry(50, 0, 50);
let material = new THREE.MeshBasicMaterial({map: texture});
let plane = new THREE.Mesh(geometry, material);
plane.position.set(0, 0, -10);
scene.add(plane);
}
async function addProjectile(){
const gltfLoader = new GLTFLoader().setPath( 'src/assets/' );
const projectileGLTF = await gltfLoader.loadAsync( 'pizza.glb' );
projectileMesh = projectileGLTF.scene;
projectileMesh.scale.set(2, 2, 2);
}
async function loadAnimal(){
const gltfLoader = new GLTFLoader().setPath('src/assets/');
const animalGLTF = await gltfLoader.loadAsync( 'moose.glb' );
animalMesh = animalGLTF.scene;
}
Ahora agregamos animacion de los animales.
let mixers = [];
function addAnimal(posX){
let model1 = SkeletonUtils.clone(animalGLTF.scene);
let animations = {};
animalGLTF.animations.forEach( animation => {
animations[animation.name] = animation;
});
let actualAnimation = "MooseAnimation";
const mixer1 = new THREE.AnimationMixer(model1);
mixer1.clipAction(animations[actualAnimation]).play();
model1.position.x = posX;
model1.position.y = 0;
model1.position.z = -30;
animalMeshes.push(model1);
scene.add(model1);
mixers.push(mixer1);
}
function animate(){
const dt = clock.getDelta();
for ( const mixer of mixers ) mixer.update( dt );
Genial ya lo tenemos.