ad208三国

 找回密码
 加入
搜索
热搜: 活动 交友 discuz
查看: 582|回复: 2

three.js 制作一个三维的推箱子游戏

[复制链接]
发表于 2023-6-28 13:36:35 | 显示全部楼层 |阅读模式

pc2g,电脑好游戏 以下文字转载自https://www.mrguo.link/article?id=29
今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏。在线案例请点击three.js推箱子

要制作一个推箱子游戏,正常要有以下4个步骤
  • 定义一些数组,要有开始箱子数组、结束箱子数组、地面数组还有墙面数组,有这四个数组就可以组成一个关卡。
  • 根据数组初始化地面墙面箱子和目标地点标志物。
  • 使用FirstPersonControls控制器,控制相机移动,根据地面箱子和墙面算出可移动区域。
  • 根据相机正对箱子时,用鼠标点击箱子,控制箱子移动,并做成功性校验。
下面我们上代码分析代码
1. 定义数组这四个数组分别是墙的数组、地面的数组、箱子初始位置数组和目标数组。
copywallArr = [[0, 0], [1, 0], [2, 0], [3, 0], [3, 1], [4, 1], [4, 2], [4, 3], [5, 3], [5, 4], [5, 5], [5, 6], [4, 6], [3, 6], [2, 6], [1, 6], [0, 6], [0, 5], [0, 4], [0, 3], [0, 2], [0, 1]]scopeArr = [[1, 1], [2, 1], [1, 2], [2, 2], [3, 2], [1, 3], [2, 3], [1, 4], [4, 4], [1, 5], [2, 5], [3, 5], [4, 5]];boxArr = [[3, 3], [2, 4], [3, 4]];targetArr = [[2, 2], [1, 3], [2, 3]]; jswallArr = [[0, 0], [1, 0], [2, 0], [3, 0], [3, 1], [4, 1], [4, 2], [4, 3], [5, 3], [5, 4], [5, 5], [5, 6], [4, 6], [3, 6], [2, 6], [1, 6], [0, 6], [0, 5], [0, 4], [0, 3], [0, 2], [0, 1]]scopeArr = [[1, 1], [2, 1], [1, 2], [2, 2], [3, 2], [1, 3], [2, 3], [1, 4], [4, 4], [1, 5], [2, 5], [3, 5], [4, 5]];boxArr = [[3, 3], [2, 4], [3, 4]];targetArr = [[2, 2], [1, 3], [2, 3]]; 版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接: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[0] * 40 - 20, 20, d[1] * 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[0] * 40 - 20, 20, d[1] * 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
 楼主| 发表于 2023-6-28 13:37:51 | 显示全部楼层
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[0] * 40 - 20;
        wall.position.y = 20;
        wall.position.z = d[1] * 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[0] * 40 - 20;
        wall.position.y = 20;
        wall.position.z = d[1] * 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[0];
        targetArr.forEach(d => {
            hongqi.position.set(d[0] * 40 - 20, -50, d[1] * 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[0];
        targetArr.forEach(d => {
            hongqi.position.set(d[0] * 40 - 20, -50, d[1] * 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[0] && intersects[0].object.name == 'box') {
                this.computeMove(intersects[0].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[0] && intersects[0].object.name == 'box') {
                this.computeMove(intersects[0].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[0] == dd[0] && d[1] == dd[1]
        })
        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[0] == dd[0] && d[1] == dd[1]
        })
        if(!res) {
            boo = false;
        }
    })
    if(boo) {
        setTimeout(() => {
            alert('恭喜你赢了!')
        },100)
    }
}
版权声明:本文为郭志强的原创文章,转载请附上原文出处链接及本声明。原文链接:https://www.mrguo.link

由于当时做这个小案例时还是菜鸟,所以很少用一些three.js的辅助方法,见笑了。

Archiver|手机版|小黑屋|ad208三国

GMT+8, 2024-4-29 03:34

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表