魔方对于大多数人都不陌生,也是个立方体的玩意儿。
这里就简单用three.js实现一下,复杂的还是定位,毕竟是3d的还能乱转。
看代码前还是先来说明:基本框架还是一样----舞台,摄像头和渲染器。
之后用faceMaterial写一个6面颜色不一样的cube,并用27个这样的cube组成魔方的基本样子。
trackballControls是摄像头控制函数,加入可以用鼠标控制其中的摄像头。
监听鼠标按下事件,按下时获取点击的三维坐标,获取在最前端的cube的name。
通过坐标计算旋转方向(这里容易脑壳疼),通过name计算同一平面的其他cube。
通过方向与平面以矩阵旋转平面内9个cube,并把旋转做成动画。
听说好文章结尾都有彩蛋~
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>test3d</title> <style type="text/css"> body{ margin: 0; overflow: hidden; } </style> </head> <body> <div id="webgl"></div> <script type="text/javascript" src="http://test.ganjiacheng.cn/3d/learning-threejs/libs/three.js"></script> <script type="text/javascript" src="http://test.ganjiacheng.cn/3d/learning-threejs/libs/TrackballControls.js"></script> <script type="text/javascript"> function init(){ var scene=new THREE.Scene(); var camera=new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000); camera.position.set(-20,20,20); camera.lookAt(scene.position); var renderer=new THREE.WebGLRenderer(); renderer.setClearColor(0xdadada); renderer.setSize(window.innerWidth,window.innerHeight); renderer.shadowMapEnabled=true; var axes=new THREE.AxisHelper(2); scene.add(axes); var group=new THREE.Mesh(); var mats=[]; mats.push(new THREE.MeshBasicMaterial({color:0x009e60}));//g mats.push(new THREE.MeshBasicMaterial({color:0x0051ba}));//b mats.push(new THREE.MeshBasicMaterial({color:0xffd500}));//y mats.push(new THREE.MeshBasicMaterial({color:0xff5800}));//j mats.push(new THREE.MeshBasicMaterial({color:0xC41E3A}));//r mats.push(new THREE.MeshBasicMaterial({color:0xffffff}));//w var faceMaterial=new THREE.MeshFaceMaterial(mats); for(var x=0;x<3;x++){ for(var y=0;y<3;y++){ for(var z=0;z<3;z++){ var cubeGeom=new THREE.BoxGeometry(2.9,2.9,2.9); var cube=new THREE.Mesh(cubeGeom,faceMaterial); cube.position.set(x*3-3,y*3-3,z*3-3); cube.name=z+3*y+9*x; group.add(cube); } } } scene.add(group); var trackballControls=new THREE.TrackballControls(camera); trackballControls.rotateSpeed=1.0; trackballControls.zoomSpeed=1.0; trackballControls.panSpeed=1.0; document.addEventListener('mousedown',onMouseDown,false); var clock=new THREE.Clock(); document.getElementById("webgl").appendChild(renderer.domElement); renderer.render(scene,camera); var test=new THREE.MeshBasicMaterial({color:0x000000}); var startMove=-1; var moveList=[]; var rotateDirection; var DirectionLR=true; var j=0; function onMouseDown(event){ if(startMove!=-1){ return; } var vector=new THREE.Vector3((event.clientX/window.innerWidth)*2-1,-(event.clientY/window.innerHeight)*2+1,0.5); vector=vector.unproject(camera); var raycaster=new THREE.Raycaster(camera.position,vector.sub(camera.position).normalize()); var intersects=raycaster.intersectObjects(group.children); if(intersects.length>0){ j=0; moveList=[]; startMove=intersects[0].object.name; var y=scene.children[1].children[startMove].position.y; getRotateDirection(intersects[0].point.x,intersects[0].point.y,intersects[0].point.z); if(rotateDirection==1){ for(var i=0;i<27;i++){ if(xround(scene.children[1].children[i].position.y,2)==xround(y,2)){ moveList.push(i); } } }else if(rotateDirection==2){ for(var i=0;i<27;i++){ if(xround(scene.children[1].children[i].position.x,2)==xround(scene.children[1].children[startMove].position.x,2)){ moveList.push(i); } } }else if(rotateDirection=3){ for(var i=0;i<27;i++){ if(xround(scene.children[1].children[i].position.z,2)==xround(scene.children[1].children[startMove].position.z,2)){ moveList.push(i); } } } } } function reset(){ startMove=-1; } function rotationMF(moveList){ var rotationV=DirectionLR?Math.PI/100:-Math.PI/100; if(rotateDirection==1){ if(j<50){ for(var i in moveList){ var rotation = new THREE.Matrix4().makeRotationY(rotationV); scene.children[1].children[moveList[i]].applyMatrix(rotation); } j++; }else{ reset() } }else if(rotateDirection==2){ if(j<50){ for(var i in moveList){ var rotation = new THREE.Matrix4().makeRotationX(rotationV); scene.children[1].children[moveList[i]].applyMatrix(rotation); } j++; }else{ reset() } }else if(rotateDirection==3){ if(j<50){ for(var i in moveList){ var rotation = new THREE.Matrix4().makeRotationZ(rotationV); scene.children[1].children[moveList[i]].applyMatrix(rotation); } j++; }else{ reset() } } } function xround(x, num){ return Math.round(x * Math.pow(10, num)) / Math.pow(10, num); } function getRotateDirection(x,y,z){ function dealxyz(axis){ for(var i=0;i<3;i++){ if(xround(axis[i],2)==-4.45 || xround(axis[i],2)==4.45){ var fl=xround(axis.splice(i,1),2)==-4.45; axis[0]=axis[0]>1.5?axis[0]-3:axis[0]; axis[0]=axis[0]<-1.5?axis[0]+3:axis[0]; axis[1]=axis[1]>1.5?axis[1]-3:axis[1]; axis[1]=axis[1]<-1.5?axis[1]+3:axis[1]; var judge; if(i==0 && Math.abs(axis[0])<Math.abs(axis[1])){ rotateDirection=1; judge=fl?(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]<0):(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]>0); }else if(i==0 && Math.abs(axis[0])>Math.abs(axis[1])){ rotateDirection=3; judge=!fl?(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]<0):(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]>0); }else if(i==1 && Math.abs(axis[0])<Math.abs(axis[1])){ rotateDirection=2; judge=!fl?(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]<0):(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]>0); }else if(i==1 && Math.abs(axis[0])>Math.abs(axis[1])){ rotateDirection=3; judge=fl?(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]<0):(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]>0); }else if(i==2 && Math.abs(axis[0])>Math.abs(axis[1])){ rotateDirection=1; judge=fl?(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]>0):(Math.abs(axis[1])<Math.abs(axis[0]) && axis[0]<0); console.log(judge); }else if(i==2 && Math.abs(axis[0])<Math.abs(axis[1])){ rotateDirection=2; judge=!fl?(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]>0):(Math.abs(axis[0])<Math.abs(axis[1]) && axis[1]<0); } return judge; } } } DirectionLR=!dealxyz([x,y,z]); } function renderScene(){ var delta=clock.getDelta(); if(startMove!=-1){rotationMF(moveList);} trackballControls.update(delta); requestAnimationFrame(renderScene); renderer.render(scene,camera); } renderScene(); } window.onload=init; </script> </body> </html>
这里不预览啦,主要注明的一点就是看起来比写起来真是两码事,
一开始纠结在rotation的旋转会连带转自己的坐标轴。后来慢慢发现他转的是他的children子元素,并可以创造矩阵来旋转。
本来想的很好做一个沿y轴转四个面,然后推广到x,z,只要写一套就行。现实还是安心的做完了6个面以及每个面里9个小块的分析。
本来还想着怎么能写的系统一点,可以轻松调InOut,这样就可以做多元的魔方,说不定还能研究个魔方的随机打乱和复原,好吧继续想着吧。
还有点感悟就是three.js文档虽然齐全不过问的问题确实不多,有点难搜到相似问题。搜到的时候讲的都是欧拉角,旋转矩阵,四元数这种画风。。。
three.js完结篇,,
才怪
版权声明:本文为原创文章,转载请注明出处和作者,不得用于商业用途,请遵守
CC BY-NC-SA 4.0协议。
赞赏一下