魔方
魔方成绩.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:
matrix3 m = ident();
float angle = $PI/2*@Time%1;
rotate(m, angle, axis);
@P *= m;
@orient = quaternion(m);
如果我们在立方体上没有颜色或运动模糊,我们可以将其留在正常的Vex中并完成。一旦你添加了颜色,你就会得到 gif 左边的结果;立方体每秒重置一次,它从不洗牌。这是程序主义与模拟的一个很好的例子;逐渐下降到越来越混沌的状态将很难在程序上保持,因为您需要提前记录整个动作序列,并跟踪所有动作,并在每个时间步应用它们。我认为这不是不可能,但并不容易。
相反,我们可以使用求解器来执行正确的立方体中发生的事情。我们没有设置明确的旋转,而是设置了一个小的旋转增量,并在每一帧中累积它。这意味着在上面的代码块中改变“角度”;它不是由@Time%1 驱动所以它循环,而是使用@TimeInc(即标准 Houdini 中的 1/24 秒):
//angle = $PI/2*@Time%1;
angle = $PI/2*@TimeInc;
嗯,差不多。实际上,最终的旋转在数学上并不是很完美,所以它不是突然变成 -0.5、0 或 0.5,而是变成 0.000000001 或 0.49999999999。这足以进行测试,因此碎片开始破裂。来自 Discord 的 Aeoll 非常友好地展示了如何绕过它,即通过修改测试以允许一点松弛:
if (abs(sum(@P*axis) - slice) <= 0.001) {
hip 文件中有更多细微变化(一切都由时间变量“t”而不是@Time 驱动,因此我可以加快或减慢速度,以及其他一些东西),但这是效果的核心。