如何使用three.js 制作一个三维的推箱子游戏


Posted in Javascript onJuly 29, 2020

今天郭先生发现大家更喜欢看我发的three.js小作品,今天我就发一个3d版本推箱子的游戏,其实webGL有很多框架,three.js并不合适做游戏引擎,但是可以尝试一些小游戏。在线案例请点击

要制作一个推箱子游戏,正常要有以下4个步骤

  1. 定义一些数组,要有开始箱子数组、结束箱子数组、地面数组还有墙面数组,有这四个数组就可以组成一个关卡。
  2. 根据数组初始化地面墙面箱子和目标地点标志物。
  3. 使用FirstPersonControls控制器,控制相机移动,根据地面箱子和墙面算出可移动区域。
  4. 根据相机正对箱子时,用鼠标点击箱子,控制箱子移动,并做成功性校验。

下面我们上代码分析代码

1. 定义数组

这四个数组分别是墙的数组、地面的数组、箱子初始位置数组和目标数组。

wallArr = [[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]];

2. 根据箱子初始位置数组初始化箱子

initBox() {
  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)
}

3. 根据地面数组初始化地面

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);
}

4. 根据墙数组初始化地面

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);
  })
}

5. 根据目标数组初始化目标物

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())
    })
  })
}

6. 监听箱子的点击事件

每次点击的时候执行computeMove方法,判断如果是否可移动。

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);
      }
    }
  })
}

7. 监听游戏成功

如果成功了,那么简单的弹出提示。

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)
  }
}

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

以上就是如何使用three.js 制作一个三维的推箱子游戏的详细内容,更多关于three.js 制作推箱子游戏的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
JavaScript中的作用域链和闭包
Jun 30 Javascript
jQuery 关于伪类选择符的使用说明
Apr 24 Javascript
jQuery中DOM树操作之复制元素的方法
Jan 23 Javascript
IE8 内存泄露(内存一直增长 )的原因及解决办法
Apr 06 Javascript
BootStrap Validator对于隐藏域验证和程序赋值即时验证的问题浅析
Dec 01 Javascript
基于jQuery实现的查看全文功能【实用】
Dec 11 Javascript
微信小程序教程系列之设置标题栏和导航栏(7)
Jun 29 Javascript
原生JS实现前端本地文件上传
Sep 08 Javascript
详解Jest结合Vue-test-utils使用的初步实践
Jun 27 Javascript
微信小程序使用GoEasy实现websocket实时通讯
May 19 Javascript
javascript解析json格式的数据方法详解
Aug 07 Javascript
vue 虚拟DOM的原理
Oct 03 Javascript
Vue实现input宽度随文字长度自适应操作
Jul 29 #Javascript
vue 判断元素内容是否超过宽度的方式
Jul 29 #Javascript
在vue中实现给每个页面顶部设置title
Jul 29 #Javascript
vue实现移动端项目多行文本溢出省略
Jul 29 #Javascript
ElementUI 修改默认样式的几种办法(小结)
Jul 29 #Javascript
Element中Slider滑块的具体使用
Jul 29 #Javascript
vue 实现超长文本截取,悬浮框提示
Jul 29 #Javascript
You might like
php缓存技术详细总结
2013/08/07 PHP
PHPExcel读取EXCEL中的图片并保存到本地的方法
2015/02/14 PHP
php简单实现发送带附件的邮件
2015/06/10 PHP
php语言中使用json的技巧及json的实现代码详解
2015/10/27 PHP
在Mac OS上搭建Nginx+PHP+MySQL开发环境的教程
2015/12/21 PHP
WordPress中重置文章循环的rewind_posts()函数讲解
2016/01/11 PHP
Yii 2.0在Grid中格式化时间方法示例
2017/06/06 PHP
js操作iframe的一些方法介绍
2013/06/25 Javascript
jquery限定文本框只能输入数字即整数和小数
2013/11/29 Javascript
用jQuery与JSONP轻松解决跨域访问的问题
2014/02/04 Javascript
详解JavaScript中shift()方法的使用
2015/06/09 Javascript
jQuery实现标题有打字效果的焦点图代码
2015/11/16 Javascript
js滑动提示效果代码分享
2016/03/10 Javascript
JavaScript函数节流概念与用法实例详解
2016/06/20 Javascript
微信小程序 LOL 英雄介绍开发实例
2016/09/30 Javascript
bootstrap为水平排列的表单和内联表单设置可选的图标
2017/02/15 Javascript
浅谈vue实现数据监听的函数 Object.defineProperty
2017/06/08 Javascript
一个基于react的图片裁剪组件示例
2018/04/18 Javascript
Angularjs 根据一个select的值去设置另一个select的值方法
2018/08/13 Javascript
vue+elementUI实现表单和图片上传及验证功能示例
2019/05/14 Javascript
Vue Element UI + OSS实现上传文件功能
2019/07/31 Javascript
js实现随机点名器精简版
2020/06/29 Javascript
使用Python读取二进制文件的实例讲解
2018/07/09 Python
Python实现自定义读写分离代码实例
2019/11/16 Python
Python3 读取Word文件方式
2020/02/13 Python
如何在sublime编辑器中安装python
2020/05/20 Python
使用HTML5 Canvas API中的clip()方法裁剪区域图像
2016/03/25 HTML / CSS
为您的家、后院、车库等在线购物:Spreetail
2019/06/17 全球购物
英国拖鞋购买网站:Bedroom Athletics
2020/02/28 全球购物
sealed修饰符是干什么的
2012/10/23 面试题
自荐信怎么写呢?
2013/12/09 职场文书
2014年党小组工作总结
2014/12/20 职场文书
2016年会开场白台词
2015/06/01 职场文书
Python 机器学习工具包SKlearn的安装与使用
2021/05/14 Python
一文搞清楚MySQL count(*)、count(1)、count(col)区别
2022/03/03 MySQL
Python if else条件语句形式详解
2022/03/24 Python