Copy link to clipboard
Copied
Hi all,
I'm sure there must be a way to do this but can't find a way/work it out. I'd like a simple way of randomly mixing up the positions of the blocks but keeping within the square - like a jigsaw puzzle (shown below). I'm currently using keyframe positions and copy and pasting into each layer to mix them up but I'm hoping theres a way of using these keyframe positions as seeds and then randomly assigning to each layer?
If this isnt going to work is there another work around to keep the structure but jumble up the sqaures instead of copy and pasting the positions in manually?
Thank you in advance.
Copy link to clipboard
Copied
You need to sort the values in an array and randomize that or have an extra loop that checks whether a position already has a card assigned. Check this old example by Dan Ebberts:
https://www.motionscript.com/expressions-lab-ae65/random-grid-movement.html
You can easily gut it and remove the motion part to just produce a static grid. The basics are all there.
Mylenium
Copy link to clipboard
Copied
Oh, that's a really elegant bit of code from @Dan Ebberts . I was curious so tried it out, but it initially threw an error. I'm guessing the original is using ExtendScript and I was trying in Javascript.
this_comp
needs to be replaced with
thisComp
Here's the whole thing:
columns = 10; //number of columns in grid
tHold= .2; //hold time (must be less than tmin)
tMin = .5; //minimum cycle time (can't be zero)
tMax = 1; //maximum cycle time
gap = thisComp.width/columns;
origin = [gap,gap];
xGrid = columns - 1;
yGrid = Math.floor(thisComp.height/gap) - 1;
start = 0;
end = 0;
j = 1;
while (time >= end){
j += 1;
seedRandom(j,true);
start = end;
end += random(tMin,tMax);
}
targetX = Math.floor(random(0,xGrid));
targetY = Math.floor(random(0,yGrid));
seedRandom(j-1,true);
x = random(); //this is a throw-away value
oldX = Math.floor(random(0,xGrid));
oldY = Math.floor(random(0,yGrid));
if(targetX == oldX && targetY == oldY){
origin + [oldX,oldY]*gap;
}else if (time - start < tHold){
origin + [oldX,oldY]*gap;
}else{
deltaX = Math.abs(targetX - oldX);
deltaY = Math.abs(targetY - oldY);
xTime = (end - start - tHold)*(deltaX/(deltaX + deltaY));
yTime = (end - start - tHold)*(deltaY/(deltaX + deltaY));
if (time < start + tHold + xTime){
startPos = origin + [oldX,oldY]*gap;
targetPos = origin + [targetX,oldY]*gap;
easeOut((time - start - tHold)/xTime, startPos, targetPos);
}else{
startPos = origin + [targetX,oldY]*gap;
targetPos = origin + [targetX,targetY]*gap
easeIn((time - start - tHold - xTime)/yTime, startPos, targetPos);
}
}
Copy link to clipboard
Copied
Thank you too @ShiveringCactus I will bare all the above in mind!
Copy link to clipboard
Copied
You are a legend - thank you for your help, I will give that a shot!
Copy link to clipboard
Copied
The trick with something like this is that you need to calculate the random order for all blocks in a single expression and then "publish" the result so that each block layer can use that result to determine its own position. I'd do it with a text layer named "order" at the bottom of the layer stack (below all the block layers) with a source text expression like this:
n = 16;
seed = 17;
a = [];
for (i = 0; i < n; i++){
a.push(i+1);
}
seedRandom(seed,true);
for (i = 0; i < n; i++){
idx = Math.floor(random(n));
temp = a[i];
a[i] = a[idx];
a[idx] = temp;
}
a.toString()
To change the random order, just change the seed variable (or tie it to a slider, maybe). Then on each of the blocks, you'd have a position expression, something like this:
order = thisComp.layer("order").text.sourceText;
nCols = 4;
a = order.split(",");
for ( i = 0; i < a.length; i++){
if (parseInt(a[i]) == index) break;
}
col = i%nCols;
row = Math.floor(i/nCols);
origin = [width,height]/2;
xOffset = col*width;
yOffset = row*height;
origin + [xOffset,yOffset]
I set this up with 16 solid layers and it would be somewhat different if you're using shape layers, but this should give you the idea.