美文网首页程序员@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

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