three.js 制作一个三维的推箱子游戏
以下文字转载自https://www.mrguo.link/article?id=29今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏。在线案例请点击three.js推箱子
https://www.mrguo.link:4433/images/1595988443360.jpg
要制作一个推箱子游戏,正常要有以下4个步骤
[*]定义一些数组,要有开始箱子数组、结束箱子数组、地面数组还有墙面数组,有这四个数组就可以组成一个关卡。
[*]根据数组初始化地面墙面箱子和目标地点标志物。
[*]使用FirstPersonControls控制器,控制相机移动,根据地面箱子和墙面算出可移动区域。
[*]根据相机正对箱子时,用鼠标点击箱子,控制箱子移动,并做成功性校验。
下面我们上代码分析代码
1. 定义数组这四个数组分别是墙的数组、地面的数组、箱子初始位置数组和目标数组。
copywallArr = [, , , , , , , , , , , , , , , , , , , , , ]scopeArr = [, , , , , , , , , , , , ];boxArr = [, , ];targetArr = [, , ]; jswallArr = [, , , , , , , , , , , , , , , , , , , , , ]scopeArr = [, , , , , , , , , , , , ];boxArr = [, , ];targetArr = [, , ]; 版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link2. 根据箱子初始位置数组初始化箱子copyinitBox() { var textureBox = new THREE.TextureLoader().load("/static/images/base/crate.png"); if (boxGroup) { scene.remove(boxGroup) } boxGroup = new THREE.Group(); boxGroup.name = 'box_group' boxArr.forEach(d => { var boxGeom = new THREE.BoxGeometry(40, 40, 40); var boxMate = []; boxGeom.faces.forEach(d => boxMate.push(new THREE.MeshBasicMaterial({ map: textureBox }))) var boxMesh = new THREE.Mesh(boxGeom, boxMate); boxMesh.position.set(d * 40 - 20, 20, d * 40 - 20); boxMesh.name = 'box'; boxGroup.add(boxMesh); }) scene.add(boxGroup); //判断是否赢得比赛 this.isWinner(boxArr, targetArr)}jsinitBox() { var textureBox = new THREE.TextureLoader().load("/static/images/base/crate.png"); if (boxGroup) { scene.remove(boxGroup) } boxGroup = new THREE.Group(); boxGroup.name = 'box_group' boxArr.forEach(d => { var boxGeom = new THREE.BoxGeometry(40, 40, 40); var boxMate = []; boxGeom.faces.forEach(d => boxMate.push(new THREE.MeshBasicMaterial({ map: textureBox }))) var boxMesh = new THREE.Mesh(boxGeom, boxMate); boxMesh.position.set(d * 40 - 20, 20, d * 40 - 20); boxMesh.name = 'box'; boxGroup.add(boxMesh); }) scene.add(boxGroup); //判断是否赢得比赛 this.isWinner(boxArr, targetArr)}版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link3. 根据地面数组初始化地面copyinitGround() { var textureGround = new THREE.TextureLoader().load("/static/images/wall/plaster.jpg", () => {this.loaded_num --}); var textureGroundNormal = new THREE.TextureLoader().load("/static/images/wall/plaster-normal.jpg", () => {this.loaded_num --}); var textureGroundSpecular = new THREE.TextureLoader().load("/static/images/wall/plaster-diffuse.jpg", () => {this.loaded_num --}); textureGround.wrapS = textureGround.wrapT = THREE.RepeatWrapping; textureGround.repeat.set(50, 50); textureGroundNormal.wrapS = textureGroundNormal.wrapT = THREE.RepeatWrapping; textureGroundNormal.repeat.set(50, 50); var materialGround = new THREE.MeshPhongMaterial({ map: textureGround }) materialGround.normalMap = textureGroundNormal; materialGround.specularMap = textureGroundSpecular; var ground = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000, 1, 1), materialGround); ground.rotation.x = - Math.PI / 2; scene.add(ground);}js initGround() {
var textureGround = new THREE.TextureLoader().load("/static/images/wall/plaster.jpg", () => {this.loaded_num --});
var textureGroundNormal = new THREE.TextureLoader().load("/static/images/wall/plaster-normal.jpg", () => {this.loaded_num --});
var textureGroundSpecular = new THREE.TextureLoader().load("/static/images/wall/plaster-diffuse.jpg", () => {this.loaded_num --});
textureGround.wrapS = textureGround.wrapT = THREE.RepeatWrapping;
textureGround.repeat.set(50, 50);
textureGroundNormal.wrapS = textureGroundNormal.wrapT = THREE.RepeatWrapping;
textureGroundNormal.repeat.set(50, 50);
var materialGround = new THREE.MeshPhongMaterial({
map: textureGround
})
materialGround.normalMap = textureGroundNormal;
materialGround.specularMap = textureGroundSpecular;
var ground = new THREE.Mesh(new THREE.PlaneGeometry(1000, 1000, 1, 1), materialGround);
ground.rotation.x = - Math.PI / 2;
scene.add(ground);
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link
4. 根据墙数组初始化地面
copyinitWall() {
var normal = new THREE.TextureLoader().load("/static/images/wall/stone.jpg", () => {this.loaded_num --});
var bump = new THREE.TextureLoader().load("/static/images/wall/stone-bump.jpg", () => {this.loaded_num --});
wallArr.forEach(d => {
var wallBox = new THREE.BoxGeometry(40, 40, 40);
var material = new THREE.MeshPhongMaterial({
map: normal,
bumpMap: bump,
bumpScale: 1
})
var wall = new THREE.Mesh(wallBox, material);
wall.position.x = d * 40 - 20;
wall.position.y = 20;
wall.position.z = d * 40 - 20;
scene.add(wall);
})
}
js
initWall() {
var normal = new THREE.TextureLoader().load("/static/images/wall/stone.jpg", () => {this.loaded_num --});
var bump = new THREE.TextureLoader().load("/static/images/wall/stone-bump.jpg", () => {this.loaded_num --});
wallArr.forEach(d => {
var wallBox = new THREE.BoxGeometry(40, 40, 40);
var material = new THREE.MeshPhongMaterial({
map: normal,
bumpMap: bump,
bumpScale: 1
})
var wall = new THREE.Mesh(wallBox, material);
wall.position.x = d * 40 - 20;
wall.position.y = 20;
wall.position.z = d * 40 - 20;
scene.add(wall);
})
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link
5. 根据目标数组初始化目标物
copyinitTarget() {
let objLoader = new OBJLoader();
objLoader.setPath("/static/images/texture/hongqi/");
objLoader.load('hongqi.obj', (object) => {
this.loaded_num --;
let hongqi = object.children;
targetArr.forEach(d => {
hongqi.position.set(d * 40 - 20, -50, d * 40 - 20)
hongqi.scale.set(0.12, 0.12, 0.12)
hongqi.material = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide });
scene.add(hongqi.clone())
})
})
}
js
initTarget() {
let objLoader = new OBJLoader();
objLoader.setPath("/static/images/texture/hongqi/");
objLoader.load('hongqi.obj', (object) => {
this.loaded_num --;
let hongqi = object.children;
targetArr.forEach(d => {
hongqi.position.set(d * 40 - 20, -50, d * 40 - 20)
hongqi.scale.set(0.12, 0.12, 0.12)
hongqi.material = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide });
scene.add(hongqi.clone())
})
})
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link
6. 监听箱子的点击事件
每次点击的时候执行computeMove方法,判断如果是否可移动。
copyinitEventListener() {
raycaster = new THREE.Raycaster();
document.addEventListener('mousemove', function (event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}, false)
document.addEventListener('click', () => {
if (scene.children && scene.getObjectByName('box')) {
raycaster.setFromCamera(mouse, camera);
let intersects = raycaster.intersectObjects(scene.getObjectByName('box_group').children);
if (intersects && intersects.object.name == 'box') {
this.computeMove(intersects.object, camera.position);
}
}
})
}
js
initEventListener() {
raycaster = new THREE.Raycaster();
document.addEventListener('mousemove', function (event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
}, false)
document.addEventListener('click', () => {
if (scene.children && scene.getObjectByName('box')) {
raycaster.setFromCamera(mouse, camera);
let intersects = raycaster.intersectObjects(scene.getObjectByName('box_group').children);
if (intersects && intersects.object.name == 'box') {
this.computeMove(intersects.object, camera.position);
}
}
})
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link
7. 监听游戏成功
如果成功了,那么简单的弹出提示。
copyisWinner(arr1, arr2) {
let boo = true; //true为赢
arr1.forEach(d => {
let res = arr2.some(dd => {
return d == dd && d == dd
})
if(!res) {
boo = false;
}
})
if(boo) {
setTimeout(() => {
alert('恭喜你赢了!')
},100)
}
}
js
isWinner(arr1, arr2) {
let boo = true; //true为赢
arr1.forEach(d => {
let res = arr2.some(dd => {
return d == dd && d == dd
})
if(!res) {
boo = false;
}
})
if(boo) {
setTimeout(() => {
alert('恭喜你赢了!')
},100)
}
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link
由于当时做这个小案例时还是菜鸟,所以很少用一些three.js的辅助方法,见笑了。
页:
[1]