效果图
碰撞检测表达式允许每一层检测它的可见区域是否与任何其他层的可见区域相接触
设计
现在,我们的过程开始成形。如果有一个内置的方法来获得一个图层的包围框,那就太好了,但是没有,所以我们必须自己做。一旦开始计算我们层的包围框, 然后就是为合成中的每个其他层(直到我们检测到碰撞或者所有层都检查过了)计算它的包围框, 计算相交(重叠)的包围框, 然后在两层的相交处寻找 alpha 值非零的像素。
总而言之, 我们需要创建一个代码块, 其将计算一层的包围框, 另一个代码块用于计算两个重叠的包围框的相交矩形, 以及一个代码块用于检查在交叉区域的每个像素, 直到 alpha 非零的像素在两层被找到。做这些事情所需的表达式有点长,但它相当直接。为了适应图层缩放、旋转和设置父级,我们只需要确保我们在世界空间坐标系中进行计算。所以你会注意到使用 toWorld() 和 fromWorld() 来进行层空间的转换,以使我们包围框的角点转入和转出世界空间坐标系。
除非你在碰撞发生时采取行动否则检测碰撞是没有意义的,对吧? 为了这个演示的目的,如果它碰到了其他图层,我们将设置图层的不透明度为 100% ,如果没有,则为 30% 。好了,是时候看看这个代码了。
需要注意的是,边界框可以通过建立两个对角点来完全定义。在本例中,我们将使用左上角(我们将缩写为 “UL” )和右下角( “LR” )。
表达式代码
function getMin(a, b, c, d) {
return Math.min(Math.min(a, b), Math.min(c, d));
}
function getMax(a, b, c, d) {
return Math.max(Math.max(a, b), Math.max(c, d));
}
/// @note 计算该层的包围框(2 个角点坐标)
/// bb[0] 左上角的 x 分量
/// bb[1] 左上角的 y 分量
/// bb[2] 右下角的 x 分量
/// bb[3] 右下角的 y 分量
function getBoundingBox(theLayer) {
bb = [];
c1 = theLayer.toWorld([0, 0]);
c2 = theLayer.toWorld([theLayer.width, 0]);
c3 = theLayer.toWorld([theLayer.width, theLayer.height]);
c4 = theLayer.toWorld([0, theLayer.height]);
bb[0] = getMin(c1[0], c2[0], c3[0], c4[0]);
bb[1] = getMin(c1[1], c2[1], c3[1], c4[1]);
bb[2] = getMax(c1[0], c2[0], c3[0], c4[0]);
bb[3] = getMax(c1[1], c2[1], c3[1], c4[1]);
return bb;
}
/// @note 检查是否有其他层和本层相交
/// 相交则返回 true,否则返回 false
function checkLayers() {
cUL = [];
cLR = [];
for (idx = 1; idx <= thisComp.numLayers; idx++) {
if (index == idx) continue;
L = thisComp.layer(idx);
if (!L.active) continue;
BB = getBoundingBox(L);
UL = [BB[0], BB[1]];
LR = [BB[2], BB[3]];
/// @note 如果这四个条件有任何为 true,则没有碰撞发生
if (!(myLR[1] < UL[1] || LR[1] < myUL[1] ||
myLR[0] < UL[0] || LR[0] < myUL[0])) {
/// @note 层发生重叠,确定相交矩形的角点
cUL[1] = (myUL[1] < UL[1]) ? UL[1] : myUL[1];
cUL[0] = (myUL[0] < UL[0]) ? UL[0] : myUL[0];
cLR[1] = (myLR[1] < LR[1]) ? myLR[1] : LR[1];
cLR[0] = (myLR[0] < LR[0]) ? myLR[0] : LR[0];
/// @note 遍历相交矩形的每个像素
for (i = cUL[0]; i <= cLR[0]; i++) {
for (j = cUL[1]; j <= cLR[1]; j++) {
/// @note alpha 值在索引为 3 的通道上
/// 检查其是否在两层上同时非零
if (sampleImage(fromWorld([i, j]))[3] > 0 &&
L.sampleImage(L.fromWorld([i, j]))[3] > 0) {
return true;
}
}
}
}
}
return false;
}
myBB = getBoundingBox(thisLayer);
myUL = [myBB[0], myBB[1]];
myLR = [myBB[2], myBB[3]];
/// @note 根据是否碰撞来设置不透明度
if (checkLayers()) { 100 } else { 30 }
(完)