魔方

魔方成绩.gif

下载场景:文件:rubiks_cube.hipnc

和往常一样,我刚才解决了这个问题,忘记了,花了一些时间来记住我是如何做到的,相当肯定这个方法比原来的方法更干净。非常感谢来自 Discord 论坛的 Aeoll 帮助解决了我遗漏的最后一点问题。

这是一个 3x3 的点框,立方体复制到每个点。此设置需要两件事作为开始,一个随机的 x/y/z 轴来转动,并选择该轴上的一个切片来转动。

可能有很多非常聪明的超级紧凑的方法来选择随机轴,我做了一些相当低俗的事情。假设我们有一个可以是 0 1 或 2 的整数 ‘randaxis’ 和一个向量 ‘axis’:

if (randaxis==0) axis = {1,0,0}; if (randaxis==1) axis = {0,1,0}; if (randaxis==2) axis = {0,0,1};

要生成 randaxis,假设我们希望它每秒随机生成 0 1 或 2。为此,我们显然需要使用@Time 作为输入。为了使时间步进,我们可以使用 floor(@Time)。将其输入 rand 以生成一个介于 0 和 1 之间的随机数,乘以 3 使其介于 0 和 2.999 之间,最后转换为 int 使其仅为 0 1 或 2:

int randaxis = int(rand(floor(@Time))*3);

假设我们随机选择了 x 轴 {1,0,0}。现在我们需要选择要旋转的左、中或右切片。立方体是 1 个单位宽,这意味着我们可以肯定地说左侧点的 @Px 为 -0.5,中间为 0,右侧为 0.5。在 y 轴上选择切片时,@Py 和 z 轴上的 @Pz 也是如此。因此,我们将随机设置一个浮点数“切片”为 -0.5、0 或 0.5 并在稍后使用(randslice 的生成方式与 randaxis 类似):

if (randslice==0) slice = -0.5; if (randslice==1) slice = 0; if (randslice==2) slice = 0.5;

现在我们有一个轴和一个切片,我们如何使用它?我们需要为轴和切片的每个点构建一个测试,如果它们通过,就做一些事情。这是我使用的测试:

if (sum(@P*axis)==slice)

Sum(vector) 返回向量分量的总和,因此 sum( {1,2,3} ) 返回 6(即 1+2+3)。对于我们立方体的右上角,即 {0.5,0.5,0.5},我们得到 1.5。

但这里我们首先将@P 乘以’axis’。让我们看看这对几个不同的点有什么影响。如果axis = {1,0,0},即x轴:

{0.5,0.5,0.5} {1,0,0} = {0.5,0,0} {-0.5,0,-0.5} {1,0,0} = (-0.5,0,0} {0,0,0} * {1,0,0} = {0,0,0}

即具有抵消y和分量的作用。对上述结果运行 sum() 返回 0.5、-0.5、0。换句话说,我们已经从向量中提取 x 分量作为浮点数。

测试检查的下一部分将该结果与“切片”变量进行比较。比方说 slice 是 0.5,这些结果如何比较?

0.5 == 0.5,合格 -0.5 != 0.5, 失败 0 != 0.5,失败

所以这里我们说只有那些 @Px 与我们感兴趣的切片 (0.5) 匹配的点通过,所有其他点都失败。

为什么要这样骗人?好吧,我们可以做一个大的多级 if 语句,说明如果 x 轴做这个,否则如果 y 轴做这个,否则如果 z 轴做这个,然后如果 slice1 做这个等等……这变得非常笨拙并且容易出错。在这里,我们利用了向量如何相乘的一些特性来获得更清晰的测试。

从这里开始一帆风顺。我们知道要旋转的轴,所以我们进行通常的矩阵旋转舞蹈,并更新@P 和@orient:

  1. matrix3 m = ident();
  2. float angle = $PI/2*@Time%1;
  3. rotate(m, angle, axis);
  4. @P *= m;
  5. @orient = quaternion(m);

如果我们在立方体上没有颜色或运动模糊,我们可以将其留在正常的Vex中并完成。一旦你添加了颜色,你就会得到 gif 左边的结果;立方体每秒重置一次,它从不洗牌。这是程序主义与模拟的一个很好的例子;逐渐下降到越来越混沌的状态将很难在程序上保持,因为您需要提前记录整个动作序列,并跟踪所有动作,并在每个时间步应用它们。我认为这不是不可能,但并不容易。

相反,我们可以使用求解器来执行正确的立方体中发生的事情。我们没有设置明确的旋转,而是设置了一个小的旋转增量,并在每一帧中累积它。这意味着在上面的代码块中改变“角度”;它不是由@Time%1 驱动所以它循环,而是使用@TimeInc(即标准 Houdini 中的 1/24 秒):

  1. //angle = $PI/2*@Time%1;
  2. angle = $PI/2*@TimeInc;

嗯,差不多。实际上,最终的旋转在数学上并不是很完美,所以它不是突然变成 -0.5、0 或 0.5,而是变成 0.000000001 或 0.49999999999。这足以进行测试,因此碎片开始破裂。来自 Discord 的 Aeoll 非常友好地展示了如何绕过它,即通过修改测试以允许一点松弛:

  1. if (abs(sum(@P*axis) - slice) <= 0.001) {

hip 文件中有更多细微变化(一切都由时间变量“t”而不是@Time 驱动,因此我可以加快或减慢速度,以及其他一些东西),但这是效果的核心。