魔方面部旋转动画以奇怪的方式旋转

问题描述 投票:0回答:2

很难解释我的程序出了什么问题,所以我制作了一个视频来直观地向您展示 https://youtu.be/j8yrB2BQUrI

如视频所示,涉及 y 轴和 z 轴旋转的旋转无法正常工作。我添加了下面的代码,我知道我添加了很多代码,非常抱歉。

rotateX/Y/Z 函数只是更新每个立方体的旋转结构,就是这样,所以我没有添加该代码。对于这个写得不好的问题,我很抱歉,我只是尝试了我能想到的一切,但无法让它工作

`

        private async void L(int n)         //This is a working function
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateX(6 *DEGREE);
                cube[1].rotateX(6 * DEGREE);        //rotates the left face of the cube 90 degrees
                cube[2].rotateX(6 * DEGREE);
                cube[9].rotateX(6 * DEGREE);
                cube[10].rotateX(6 * DEGREE);
                cube[11].rotateX(6 * DEGREE);
                cube[17].rotateX(6 * DEGREE);
                cube[18].rotateX(6 * DEGREE);
                cube[19].rotateX(6 * DEGREE);
                Invalidate(rec);
                await Task.Delay(1);
            }
            cube[0].rotateX(-90 * DEGREE);      // here it quickly rotates the cube back to how it was originally
            cube[1].rotateX(-90 * DEGREE);
            cube[2].rotateX(-90 * DEGREE);
            cube[9].rotateX(-90 * DEGREE);
            cube[10].rotateX(-90 * DEGREE);
            cube[11].rotateX(-90 * DEGREE);
            cube[17].rotateX(-90 * DEGREE);
            cube[18].rotateX(-90 * DEGREE);
            cube[19].rotateX(-90 * DEGREE);
            CubeFaces = Rotations.L(1, CubeFaces);      // this changes the colours of the left face and then updates the display
            Invalidate(rec);                            // to make it seem as if the cube has rotated
        }

        private async void R(int n)         // this is also a working function
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[6].rotateX(-6 * DEGREE);
                cube[7].rotateX(-6 * DEGREE);
                cube[8].rotateX(-6 * DEGREE);
                cube[14].rotateX(-6 * DEGREE);
                cube[15].rotateX(-6 * DEGREE);
                cube[16].rotateX(-6 * DEGREE);
                cube[23].rotateX(-6 * DEGREE);
                cube[24].rotateX(-6 * DEGREE);
                cube[25].rotateX(-6 * DEGREE);
                await Task.Delay(1);
                Invalidate(rec);
            }
            cube[6].rotateX(90 * DEGREE);
            cube[7].rotateX(90 * DEGREE);
            cube[8].rotateX(90 * DEGREE);
            cube[14].rotateX(90 * DEGREE);
            cube[15].rotateX(90 * DEGREE);
            cube[16].rotateX(90 * DEGREE);
            cube[23].rotateX(90 * DEGREE);
            cube[24].rotateX(90 * DEGREE);
            cube[25].rotateX(90 * DEGREE);
            CubeFaces = Rotations.R(1, CubeFaces);
            Invalidate(rec);
        }

        private async void U(int n)         // this function does not work. you will see why when i run it
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateY(6 * DEGREE);
                cube[3].rotateY(6 * DEGREE);
                cube[6].rotateY(6 * DEGREE);
                cube[9].rotateY(6 * DEGREE);
                cube[12].rotateY(6 * DEGREE);
                cube[14].rotateY(6 * DEGREE);
                cube[17].rotateY(6 * DEGREE);
                cube[20].rotateY(6 * DEGREE);
                cube[23].rotateY(6 * DEGREE);

                //cube[0].rotateX(cube[0].GetRotation().z);
                //cube[3].rotateX(-rotation.x);
                //cube[6].rotateX(-rotation.x);
                //cube[9].rotateX(-rotation.x);
                //cube[12].rotateX(-rotation.x);
                //cube[14].rotateX(-rotation.x);
                //cube[17].rotateX(-rotation.x);
                //cube[20].rotateX(-rotation.x);
                //cube[23].rotateX(-rotation.x);
                await Task.Delay(1);
                Invalidate();
            }
        }

        private async void F(int n)     // this also doesnt work and you will see why when i run it
        {
            for (int i = 0; i < 15 * n; i++)
            {
                cube[0].rotateZ(6 * DEGREE);            // so the x axis rotations work however the y and z do not
                cube[1].rotateZ(6 * DEGREE);
                cube[2].rotateZ(6 * DEGREE);
                cube[3].rotateZ(6 * DEGREE);
                cube[4].rotateZ(6 * DEGREE);
                cube[5].rotateZ(6 * DEGREE);
                cube[6].rotateZ(6 * DEGREE);
                cube[7].rotateZ(6 * DEGREE);
                cube[8].rotateZ(6 * DEGREE);
                await Task.Delay(1);
                Invalidate();
            }
            //cube[0].rotateZ(-90 * DEGREE);
            //cube[1].rotateZ(-90 * DEGREE);
            //cube[2].rotateZ(-90 * DEGREE);
            //cube[3].rotateZ(-90 * DEGREE);
            //cube[4].rotateZ(-90 * DEGREE);
            //cube[5].rotateZ(-90 * DEGREE);
            //cube[6].rotateZ(-90 * DEGREE);
            //cube[7].rotateZ(-90 * DEGREE);
            //cube[8].rotateZ(-90 * DEGREE);
            //CubeFaces = Rotations.F(1, CubeFaces);
        }

`

我尝试执行 U 函数中注释掉的操作,我认为它可以抵消立方体以奇怪的方式旋转,但这只会让情况变得更糟

c# winforms animation 3d rubiks-cube
2个回答
0
投票

您需要使用四元数旋转,否则您最终会得到“guimbal effetc”。 四元数可以根据轴/角度计算并且是累积的。你只需要这样做:

  • 为您的模型“MyQuaternion”定义四元数旋转
  • 计算专用于您想要执行“RotationQuaternion”的旋转的四元数。

然后旋转:

  • 我的四元数 = 我的四元数 * 旋转四元数
  • MyQuaternion = 旋转四元数 * MyQuaternion

取决于您是否想要在局部空间或全局空间中旋转。

您可以在 Git 上找到示例。


0
投票

好吧,让我们在这里写下一些定义。

  1. 您有 9 个独立的旋转平面。
  2. 即x 方向 3 个、y 方向 3 个、z 方向 3 个。
  3. 每个旋转平面都有自己独特的枢轴点 (但是沿每个轴的中间平面已经以原点为中心,因此这些情况更容易,因为枢轴已经位于原点)
  4. 对于任何这些旋转,您希望围绕给定枢轴应用动画旋转。
  5. 你有 6 个中心面 (有 1 种颜色)
  6. 你有8个角块(有3种颜色)
  7. 您有 12 个边缘片 (有 2 种颜色)
  8. 有一个没有颜色的中心部分,您可能可以忽略它,但是为了使代码更容易理解,我将假装它存在......

如何跟踪和识别 26/27 块中的哪一块实际上取决于您,所以我假设您已经完成了工作......

每一块都有一个当前位置和一个当前方向。有一种很好的方法可以在一个完整的实体中表示所有这些信息,那就是 4x4 矩阵(存储单独的平移和欧拉旋转可能是您的错误!)。应该提到的是,你可以代表那些具有位置+四元数的人,但这并不是真正需要的。

最初,对于这 27 件作品,它们都具有相同的方向,但翻译值不同。所以首先,初始化这些转换。

mat4x4 block_orientations[3 * 3 * 3];

for(int z = 0; z < 3; ++z) {
  for(int y = 0; y < 3; ++y) {
    for(int x = 0; x < 3; ++x) {
       int index = z * 9 + y * 3 + x;
       block_orientations[index] = mat4x4_translate_matrix(x-1, y-1, z-1); 
    }
  }
}

您还有 9 个旋转平面和关联的枢轴点。

enum CubeRotationPlane {
   XMin,
   XMid,
   XMax,

   YMin,
   YMid,
   YMax,

   ZMin,
   ZMid,
   ZMax,
};

vec4 pivot_points[9] {
   // pivots along the x axis
   {-1, 0, 0, 1},
   {0, 0, 0, 1},
   {1, 0, 0, 1},
   // pivots along the y axis
   {0, -1, 0, 1},
   {0, 0, 0, 1},
   {0, 1, 0, 1},
   // pivots along the z axis
   {0, 0, -1, 1},
   {0, 0, 0, 1},
   {0, 0, 1, 1},
};

从数学上正确的意义上来说,为了转换这些块,你应该做:

  • 将块平移到(平面的!)
  • 旋转
  • 将块从(平面的枢轴)平移回来

在代码中,这看起来像:

mat4x4 inverse_pivot = translateMatrix(-pivot);
mat4x4 rotation = computeYourRotationOffsetAsMatrix();
mat4x4 pivot = translateMatrix(pivot);

// this assumes the multiplication order of (child * parent),
// reverse if your lib is (parent * child).
mat4x4 final = inverse_pivot * rotation * pivot;

然而,在这种情况下,由于您仅使用轴对齐旋转,因此您实际上可以完全跳过枢轴点,直接跳到旋转......

void compute_transforms_for_9_blocks_to_be_moved(
    CubeRotationPlane plane, ///< the plane of the cube being rotated
    int block_indices[9],    ///< the 9 blocks that need to be rotated
    mat4x4 initial_block_orientations[3 * 3 * 3],
    mat4x4 current_block_orientations[3 * 3 * 3],
    float degrees) {

    // the rotation to apply
    mat4x4 rotate_matrix;

    if(plane < yMin) {
        // rotating around X
        rotate_matrix = mat4x4_rotateX(degrees * M_PI / 180);
    }
    else
    if(plane < zMin) {
        // rotating around Y
        rotate_matrix = mat4x4_rotateY(degrees * M_PI / 180);
    }
    else
    if(plane) {
        // rotating around Z
        rotate_matrix = mat4x4_rotateZ(degrees * M_PI / 180);
    }

    // copy prior values
    memcpy(current_block_orientations, 
           initial_block_orientations, 
           sizeof(mat4x4) * 27);

    // now update the 9 blocks that are in motion... 
    for(int i = 0; i < 9; ++i) {
        int index = block_indices[i];
        current_block_orientations[index] = 
              initial_block_orientations[index] * rotate_matrix;
    }
}

这假设动画完成后,您将更新整组矩阵。

或者,您可以指定上面的旋转1度,然后返回最终矩阵(如果您只想在动画时累积变换,避免使用临时数组)。然而,这样做可能会随着时间的推移积累浮点误差。

必须认识到,您必须存储块的累积变换。您在上面的代码中看到的问题是您有一个 XYZ 欧拉旋转顺序(并且您只是更新欧拉值)。因此,这适用于 X 和 Z 旋转,但是一旦您触摸中间旋转,Z 轴就不再位于其原始平面中,并且事情开始出错!

© www.soinside.com 2019 - 2024. All rights reserved.