three.js 如何制作魔方


Posted in Javascript onJuly 31, 2020

因为之前的几节讲了一些数学方法,例如Vector3、Matrix4、Euler还有Quaternion的知识。所以这篇郭先生就来说说用three.js怎么制作一个魔方。

three.js 如何制作魔方

制作魔方主要运用坐标变换的知识,制作魔方的方法有很多,建议你先在大脑中构思,然后试着做一做,下面我将一种比较简单的方法。

  1. 制作出魔方各个方块的位置坐标(27个)的数组,然后制作出魔方各个面的材质(6个)
  2. 根据坐标和材质制作魔方的方块,并添加到一个组group
  3. 制作一个标志被选面的几何体(我是用球体),然后隐藏
  4. 使用THREE.Raycaster,给模型绑定事件,并记录选定的一些数据,将标志显示并放到合适的位置
  5. 点击模拟方向键盘,根据之前记录的数据,转动魔方(重点)

以上是主要的步骤,但是对于新同学,需要注意的仍然很多,下面上主要代码,按照方法说

1. 定义的变量

posArr = [
  [100,100,100],[100,100,0],[100,100,-100],[100,0,100],[100,0,0],[100,0,-100],[100,-100,100],[100,-100,0],[100,-100,-100],
  [0,100,100],[0,100,0],[0,100,-100],[0,0,100],[0,0,0],[0,0,-100],[0,-100,100],[0,-100,0],[0,-100,-100],
  [-100,100,100],[-100,100,0],[-100,100,-100],[-100,0,100],[-100,0,0],[-100,0,-100],[-100,-100,100],[-100,-100,0],[-100,-100,-100]
],//方块位置坐标
materials,//材质数组
mouse = new THREE.Vector2(),//通过鼠标点击的位置计算出raycaster所需要的点的位置,以屏幕中心为原点,值的范围为-1到1.
raycaster,//射线对象
group,//存放魔方方块的数组
groupTemp,//魔方转动时临时数组
object3d,//魔方被选择面的标志物对象
currentPos,//魔方被点击小块的位置
currentNor,//魔方被点击小块面的法向量
currentUp,//魔方被点击时,相机up的向量

2. 定义材质数组

initMaterial() {
  var map_red = new THREE.TextureLoader().load('/static/images/color/m_red.jpg', () => this.loadover --);
  var map_orange = new THREE.TextureLoader().load('/static/images/color/m_orange.jpg', () => this.loadover --);
  var map_yellow = new THREE.TextureLoader().load('/static/images/color/m_yellow.jpg', () => this.loadover --);
  var map_blue = new THREE.TextureLoader().load('/static/images/color/m_blue.jpg', () => this.loadover --);
  var map_green = new THREE.TextureLoader().load('/static/images/color/m_green.jpg', () => this.loadover --);
  var map_white = new THREE.TextureLoader().load('/static/images/color/m_white.jpg', () => this.loadover --);
  var map_sprite = new THREE.TextureLoader().load('/static/images/base/direction.png', () => this.loadover --);

  let mater_red = new THREE.MeshBasicMaterial({map: map_red, side: THREE.DoubleSide});
  let mater_orange = new THREE.MeshBasicMaterial({map: map_orange, side: THREE.DoubleSide});
  let mater_yellow = new THREE.MeshBasicMaterial({map: map_yellow, side: THREE.DoubleSide});
  let mater_white = new THREE.MeshBasicMaterial({map: map_white, side: THREE.DoubleSide});
  let mater_blue = new THREE.MeshBasicMaterial({map: map_blue, side: THREE.DoubleSide});
  let mater_green = new THREE.MeshBasicMaterial({map: map_green, side: THREE.DoubleSide});

  materials = [mater_red, mater_orange, mater_yellow,mater_white, mater_blue, mater_green];
}

3. 绘制小方块并绘制标志物(先隐藏)

initsquare() {
  var sphereGeom = new THREE.SphereGeometry(10, 30, 20);
  var sphereMate = new THREE.MeshPhongMaterial({color: 0x333333});
  object3d = new THREE.Mesh(sphereGeom, sphereMate);
  object3d.name = 'object3d';
  object3d.visible = false;
  scene.add(object3d);

  group = new THREE.Group();
  group.name = 'group';

  var geometry = new THREE.BoxGeometry(100, 100, 100);
  var mesh = new THREE.Mesh(geometry, materials);
  posArr.forEach((d,i) => {
    let meshCopy = mesh.clone();
    meshCopy.position.set(d[0], d[1], d[2])
    meshCopy.name = 'box-' + i;
    group.add(meshCopy);
  })

  scene.add(group);

  this.render();
  document.getElementById("loading").style.display = "none";
}

4. 监听模型的点击事件

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('mousedown', () => {
    if (scene.children && scene.getObjectByName('group')) {
      raycaster.setFromCamera(mouse, camera);
      let intersects = raycaster.intersectObjects(scene.getObjectByName('group').children);
      if (intersects[0] && intersects[0].object.name != 'object3d') {
        let index = intersects[0].faceIndex;
        let point = intersects[0].point;
        currentUp = this.computedUp(camera);
        currentNor = this.computedNor(point)
        currentPos = intersects[0].object.position;
        let pos = this.computedPos(point);
        object3d.position.copy(pos);
        object3d.visible = true;
      }
    }
  })
}

5. 监听方向软键盘的点击,根据点击键的不同,生成旋转轴

handleRotate(num) {
  if(!rotateFlag || !currentPos) return ;
  rotateFlag = false;
  groupTemp = new THREE.Group();
  groupTemp.name = 'group-temp';
  let axis;
  let tempMeshArr = [];
  switch (num) {
    case 1:
      axis = currentNor.clone().cross(currentUp);
      break;
    case 2:
      axis = currentNor.clone().cross(currentUp).negate();
      break;
    case 3:
      axis = currentUp.clone().negate();
      break;
    case 4:
      axis = currentUp;
      break;
  }
  let plane = new THREE.Plane().setFromNormalAndCoplanarPoint(axis, currentPos);
  scene.getObjectByName('group').children.forEach(d => Math.abs(plane.distanceToPoint(d.position)) < 1 && tempMeshArr.push(d))
  tempMeshArr.forEach(d => {
    group.remove(d);
    groupTemp.add(d);
  })
  // object3d.visible = false;
  scene.remove(scene.getObjectByName('group-temp'));
  scene.add(groupTemp);
  this.handleTween(axis);
}

6. 加一点tween的补间动画,效果更好哦

handleTween(axis) {
  let start = {angle: 0, axis};
  let end = {angle: Math.PI/2, axis};
  tween = new TWEEN.Tween(start).to(end, 500);
  tween.easing(TWEEN.Easing.Linear.None);
  tween.onUpdate(function () {
    let quaternion = new THREE.Quaternion().setFromAxisAngle(axis, this._object.angle);
    groupTemp.rotation.setFromQuaternion(quaternion);
  });
  tween.onComplete(() => {
    let matrix = this.standerMatrix(groupTemp.matrix);
    groupTemp.children.forEach(d => {
      let mesh = d.clone();
      mesh.applyMatrix4(matrix)
      mesh.position.copy(this.standarPos(mesh.position))
      group.add(mesh);
    })
    scene.remove(groupTemp)
    rotateFlag = true;
  })
  tween.start();
}

郭先生制作魔方的主要思路就是先做出27个方块添加到组A,6个面分别添加不同颜色的贴图(自己p的哦),然后使用raycaster选择点击的面,并确定当时的up方向和法向量方向以备后用,点击上下左右并结合u方向和法向量方向计算出旋转矩阵,根据已有条件计算出是那一排方块改变,并将这9个块添加到组B中,从组A中删除这9个,根据旋转矩阵旋转组B,并且在旋转完之后将组B中的方块添加到组A中,旋转完毕(这里比较难的就是根据上下左右判断旋转轴向量)。

以上就是一种制作魔方的方法,综合了很多矩阵向量四元数欧拉角和平面的知识,希望对新来的同游有些帮助

到此这篇关于three.js 如何制作魔方的文章就介绍到这了,更多相关three.js 制作魔方内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
关于img的href和src取变量及赋值的方法
Apr 28 Javascript
JavaScript简单下拉菜单特效
Sep 13 Javascript
Angularjs实现带查找筛选功能的select下拉框示例代码
Oct 04 Javascript
Bootstrap复选框和单选按钮美化插件(推荐)
Nov 23 Javascript
javascript显示系统当前时间代码
Dec 29 Javascript
详解vue2.0脚手架的webpack 配置文件分析
May 27 Javascript
weebox弹出窗口不居中显示的解决方法
Nov 27 Javascript
JS中的BOM应用
Feb 02 Javascript
微信小程序实现列表下拉刷新上拉加载
Jul 29 Javascript
实例详解vue中的$root和$parent
Apr 29 Javascript
如何在Angular8.0下使用ngx-translate进行国际化配置
Jul 24 Javascript
vue实现禁止浏览器记住密码功能的示例代码
Feb 03 Vue.js
Vue ElementUI实现:限制输入框只能输入正整数的问题
Jul 31 #Javascript
js实现贪吃蛇小游戏(加墙)
Jul 31 #Javascript
vue实现简易图片左右旋转,上一张,下一张组件案例
Jul 31 #Javascript
vue实现给div绑定keyup的enter事件
Jul 31 #Javascript
简单了解JavaScript作用域
Jul 31 #Javascript
基于vue--key值的特殊用处详解
Jul 31 #Javascript
javascript开发实现贪吃蛇游戏
Jul 31 #Javascript
You might like
PHP系统流量分析的程序
2006/10/09 PHP
php桌面中心(二) 数据库写入
2007/03/11 PHP
ThinkPHP3.1新特性之对页面压缩输出的支持
2014/06/19 PHP
PHP文件缓存内容保存格式实例分析
2014/08/20 PHP
使用prototype.js 的时候应该特别注意的几个问题.
2007/04/12 Javascript
ajax 缓存 问题 requestheader
2010/08/01 Javascript
人人网javascript面试题 可以提前实现下
2012/01/05 Javascript
idTabs基于JQuery的根据URL参数选择Tab插件
2012/04/11 Javascript
JS 获取select(多选下拉)中所选值的示例代码
2013/08/02 Javascript
js传参数受特殊字符影响错误的解决方法
2013/10/21 Javascript
jquery表单验证插件(jquery.validate.js)的3种使用方式
2015/03/28 Javascript
js实现图片点击左右轮播
2015/07/08 Javascript
关于JS 预解释的相关理解
2016/06/28 Javascript
Jquery和Js获得元素标签名称的方法总结
2016/10/08 Javascript
jQuery简单实现遍历单选框的方法
2017/03/06 Javascript
基于Two.js实现星球环绕动画效果的示例
2017/11/06 Javascript
基于nodejs的微信JS-SDK简单应用实现
2019/05/21 NodeJs
Python读写Redis数据库操作示例
2014/03/18 Python
python中ConfigParse模块的用法
2014/09/29 Python
Python进行数据提取的方法总结
2016/08/22 Python
Python 保持登录状态进行接口测试的方法示例
2019/08/06 Python
python图形开发GUI库pyqt5的详细使用方法及各控件的属性与方法
2020/02/14 Python
windows、linux下打包Python3程序详细方法
2020/03/17 Python
解决Python3.7.0 SSL低版本导致Pip无法使用问题
2020/09/03 Python
HTML5 History API 实现无刷新跳转
2016/01/11 HTML / CSS
英国空调、除湿机和通风设备排名第一:Air Con Centre
2019/02/25 全球购物
在weblogic中发布ejb需涉及到哪些配置文件
2012/01/17 面试题
DELPHI面试题研发笔试试卷
2015/11/08 面试题
医学专业本科毕业生自我鉴定
2013/12/28 职场文书
什么是就业协议书
2014/04/17 职场文书
某某同志考察材料
2014/05/28 职场文书
群众路线教育党员自我剖析材料
2014/10/06 职场文书
乡镇干部个人整改措施思想汇报
2014/10/10 职场文书
详解SpringBoot异常处理流程及原理
2021/06/21 Java/Android
Win11显卡控制面板打开显卡设置方法
2022/04/20 数码科技
Windows server 2012 NTP时间同步的实现
2022/06/25 Servers