如何使用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 相关文章推荐
扩展Jquery插件处理mouseover时内部有子元素时发生样式闪烁
Dec 08 Javascript
jQuery 快速结束当前正在执行的动画
Nov 20 Javascript
jQuery中ajax的post()方法用法实例
Dec 26 Javascript
js实现带圆角的两级导航菜单效果代码
Aug 24 Javascript
jQuery表单验证插件解析(推荐)
Jul 21 Javascript
JS对象深度克隆实例分析
Mar 16 Javascript
JS实现碰撞检测的方法分析
Jan 19 Javascript
在vue项目中,使用axios跨域处理
Mar 07 Javascript
解决使用layui对select append元素无效或者未及时更新的问题
Sep 18 Javascript
layui监听下拉选框选中值变化的方法(包含监听普通下拉选框)
Sep 24 Javascript
vue v-for出来的列表,点击某个li使得当前被点击的li字体变红操作
Jul 17 Javascript
vue实现前端列表多条件筛选
Oct 26 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/09/30 PHP
PHP GD库相关图像生成和处理函数小结
2016/09/30 PHP
在 Laravel 中动态隐藏 API 字段的方法
2019/10/25 PHP
PHP实现递归的三种方法
2020/07/04 PHP
phpStorm2020 注册码
2020/09/17 PHP
js去除重复字符串两种实现方法
2013/01/09 Javascript
浅析jQuery对select操作小结(遍历option,操作option)
2013/07/04 Javascript
jquery用get实现ajax在ie里面刷新不进入后台解决方法
2013/08/12 Javascript
JavaScript中的toLocaleDateString()方法使用简介
2015/06/12 Javascript
nodejs利用ajax实现网页无刷新上传图片实例代码
2017/06/06 NodeJs
以BootStrap Tab为例写一个前端组件
2017/07/25 Javascript
浅谈Vue数据绑定的原理
2018/01/08 Javascript
使用Vue.js和Element-UI做一个简单登录页面的实例
2018/02/23 Javascript
微信小程序实现折线图的示例代码
2019/06/07 Javascript
NodeJS http模块用法示例【创建web服务器/客户端】
2019/11/05 NodeJs
vue 实现websocket发送消息并实时接收消息
2019/12/09 Javascript
[01:14:34]DOTA2上海特级锦标赛C组资格赛#2 LGD VS Newbee第一局
2016/02/28 DOTA
Python中模拟enum枚举类型的5种方法分享
2014/11/22 Python
Python外星人入侵游戏编程完整版
2020/03/30 Python
Python构建XML树结构的方法示例
2017/06/30 Python
利用Python代码实现数据可视化的5种方法详解
2018/03/25 Python
python版本的仿windows计划任务工具
2018/04/30 Python
Python实现的本地文件搜索功能示例【测试可用】
2018/05/30 Python
python实现ID3决策树算法
2018/08/29 Python
python之Flask实现简单登录功能的示例代码
2018/12/24 Python
python正则表达式匹配不包含某几个字符的字符串方法
2019/07/23 Python
Python人工智能之路 jieba gensim 最好别分家之最简单的相似度实现
2019/08/13 Python
Python利用for循环打印星号三角形的案例
2020/04/12 Python
Python通过zookeeper实现分布式服务代码解析
2020/07/22 Python
python 使用openpyxl读取excel数据
2021/02/18 Python
html5的画布canvas——画出弧线、旋转的图形实例代码+效果图
2013/06/09 HTML / CSS
光信息科学与技术专业职业生涯规划
2014/03/13 职场文书
2015人事行政工作总结范文
2015/05/21 职场文书
2016大学军训通讯稿
2015/11/25 职场文书
2016年第16个全民国防教育日宣传活动总结
2016/04/05 职场文书
Javascript中Microtask和Macrotask鲜为人知的知识点
2022/04/02 Javascript