美文网首页程序员@IT·互联网
玩“转”Three.js -- Rubik's Cube

玩“转”Three.js -- Rubik's Cube

作者: smart_Alex | 来源:发表于2017-06-17 15:54 被阅读827次

期末临近,各种DDL接踵而来,小编的期末项目之一呢就是用WebGL做一个东西。小编就决定做一个魔方,当然,是可以操作的魔方。现在用WebGL端做3D,基本上都是使用three.js框架。在用three.js写魔方的过程中,在网上看了许多demo与代码,遇到许多坑。小编在这里想先介绍关于写魔方旋转的最佳体验。遇到的坑以后有空再写吧···

需求分析一

  • 整个魔方的旋转(包括绕X,Y,Z轴)

众所周知,一个大魔方是由27个小魔方构成的,每个小魔方就是一个BoxGeometry。

  • 问题:如果对整个魔方进行旋转,我需要去改变每个小魔方的rotation和position参数吗?

对于我们程序员本身来说,肯定是不希望每次旋转都具体去改每个小魔方的参数。我们希望做到的是每次旋转就改一个整体的rotation的值。
以整个魔方的绕Y轴逆时针旋转为例子,我们希望写出来的代码是
parent.rotation[y] += angle;
那么我们试想一下,定义一个父类parent,将所有小魔方作为这个父类的子类。这样转一次是没有问题的。那么绕Y轴转了一次之后,再绕Z轴转有没有问题呢?

事实上是会出问题的。 Object在WebGL中绕Y轴逆方向转ANGLE角后,Object.rotation的值是[0,ANGLE,0]。绕Z轴逆方向转ANGLE角后,我们觉得应该是[0,ANGLE,ANGLE]。但事实上不是这样的,Object.rotation.x的值也会改变。因为在webgl中旋转是由一个4元矩阵确定的。不是由rotation的值直接确定的。所以我们单独改变Object的rotation值是会出很多问题的。

所以,我们得出结论,若将所有小魔方放入一个父类,那么对这个父类做绕不同轴的旋转后将会出现旋转混乱的问题。
这个问题怎么解决呢?很简单。我们需要做五件事

  1. 每次旋转结束之后更新每个小魔方的位置,旋转信息;
  2. 解除与父类的绑定,从场景中移除父类;
  3. 下一次旋转开始前,将父类的rotation值设为[0,0,0]。
  4. 再将小魔方与父类重新绑定
  5. 将父类重新加入到场景中

做这五件事情是为了保证每次父类旋转只改变一个rotation的值,与上次旋转没有任何关系。


需求分析二

  • 单层魔方的旋转

其实将上个需求解决之后,对于单层魔方的旋转问题已经解决了九成;整体魔方是将27个小魔方放入父类,那么单层魔方的旋转就是将需要旋转的9个小魔方放入父类。那么,还有一成是什么?

  • 问题:如何找到需要旋转的9个小魔方?

第一次旋转我可以通过索引来找到该层的九个小魔方,那多次旋转之后呢?举个例子,第一层原来索引是1,2,3,4,5,6,7,8,9,经过多次旋转之后肯定不是这些。那我是需要每次旋转都更新一次索引吗?

当然不能这么做。以世界坐标轴原点为最中心的小魔方轴心为例。最上面那层红色面朝上的9个小魔方有一个共同点,就是position[y]的坐标是一样的。我们是根据cube的position值来选择需要转动的魔方。


魔方

实现

在这里我只贴出来旋转函数的代码,所有代码可以在Github

var centerRotate = function(element,direct,axis){
        //第三步
        parent.rotation.set(0,0,0);
        parent.updateMatrixWorld();
        
        //第四步
        element.forEach(function (e) {
            THREE.SceneUtils.attach(e,scene,parent);
        })

        //第五步
        scene.add(parent);
        var time = 0;                      //旋转次数
        //单次旋转
        function singleTurn(){
            //父类在对应轴上进行旋转
            parent.rotation[axis] += direct*SINGLE_ANGLE;
            parent.updateMatrixWorld();            //更新状态
            renderer.render(scene,camera);      //渲染
            time ++;
            if(time === TOTAL_TIMES){
                parent.updateMatrixWorld();
                
                element.forEach(function (cube) {
                    //对应第一步:更新每个魔方信息
                    cube.updateMatrixWorld();    
                   //第二步:解除绑定
                    THREE.SceneUtils.detach(cube, parent, scene);
                })
                scene.remove(parent);
                clearInterval(timer);
            }
        }

        var timer = setInterval(singleTurn,FRAME_SPEED);
    }

相关文章

  • 玩“转”Three.js -- Rubik's Cube

    期末临近,各种DDL接踵而来,小编的期末项目之一呢就是用WebGL做一个东西。小编就决定做一个魔方,当然,是可以操...

  • Lucky Rubik's cube

    开动脑袋玩转魔方,动手动脑提升脑力。

  • squbs-1. 引导

    原文地址:Unicomplex & Cube Bootstrapping Unicomplex和Cube的引导 s...

  • 渲染物体raycast picking拾取交互(three.js

    Three.js中的webgl_interactive_cube例子展示了picking-拾取物体(使用点击等方式...

  • Rubik's cube

    Rubik's cube 27 Mar 2015 记得是大四的时候, 某天在别人宿舍玩了会魔方, 也不知道是怎么就...

  • Timer Rubik‘s

    A Rubik's Cube timer. if you have any question . please c...

  • 蠎周刊 150: Professors Cube

    原发: 蠎周刊 150: Professors Cube 原文: Pycoder's Weekly (Issue ...

  • 魔方

    Rubik’s Cube 我的魔方开始玩是在初中,小学时那时就看到有人在拧魔方感觉十分好奇,但自己却 根本没有信心...

  • 遥控手臂

    这是我们本次的作业:《遥控手臂》。 单词Word机械手臂mechanical arm魔方rubik's cube自...

  • unity 跟踪角度方法之一

    cube2跟踪cube1的y角度 float y = cube1.transform.eulerAngles.y;...

网友评论

    本文标题:玩“转”Three.js -- Rubik's Cube

    本文链接:https://www.haomeiwen.com/subject/lyovqxtx.html