一、背景
此交互应用《The nature of Code》一书中的技术,基于Processing的Java框架,主要以粒子动画来展现几种自然变化,融入音乐,带来轻松闲适的观感。
二、总体框架
交互系统一共有五个场景,每个场景环环相扣,层层递进,均可进行交互。
在每个场景点击特定位置即可进入下一场景。随着场景的切换,音乐也会产生变化。
由海面大雨到电闪雷鸣再到旭日东升最后到广袤宇宙,总体呈现由暗到明。
三、 动画与交互
3.1、初始界面
初始界面中,运用了粒子的旋转平移来达到动画效果,交互为随着鼠标移动粒子运动轨迹改变。点击中央的圆即可进入下一场景。
没有任何复杂的原理技术,直接上关键代码:
float i=0.001;
PVector location;
PVector velocity = new PVector(-0.1,-0.1,0);
void update()
{
i+=0.005;
location.add(velocity);
rotateZ(i);
rotateY(90);
rotateX(45);
}
void display() {
pushMatrix();
translate(location.x*2,location.y,location.z);
noStroke();
sphere(10);
popMatrix();
}
void checkEdge()
{
if((location.x<-width/3.5) ||(location.x>width/3.5)) {
velocity.x *= -1;
}
if((location.y<-height/3.5)||(location.y>height/3.5)) {
velocity.y *= -1;
}
if((location.z<0) ||(location.z>10)) {
velocity.z *= -1;
}}
3.2 Raining Sea
这一场景以粒子系统来模拟暴雨之时波涛汹涌的海面,为了模拟出雨落的效果,给每个粒子y方向上0.1的力使其自由落体。初始化时粒子的位置由随机数控制,使其尽可能自然。同时,每个粒子有不同的加速度,以模拟出错落不一的雨滴,加速度也是随机数控制。
交互方面,点击鼠标会给粒子施加力,力的大小取决于MouseY的大小。点击鼠标可模拟出海面翻涌。同时整体会随着鼠标移动而旋转,这个取决于MouseX。
if ((frameCount % 3) == 0) {
float x = random(-box, box);
float z = random(-box, box);
PVector acc = new PVector(random(-1, 1), random(-1, 1), random(-1, 1));
for (int i = 0; i < 180; i++) {
particles.add(new Particlee(new PVector(x+random(2, 12), -box+random(2, 5), z+random(2, 5)), acc));
} }
……
for (int i = 0; i < particles.size (); i++) {
Particlee p = (Particlee) particles.get(i);
p.move();
p.applyForce(new PVector(0, 0.1, 0));
float b =abs(sin(radians(p.loc.x+frameCount)))*sin(radians(p.loc.z+frameCount))*cos(radians(p.loc.y))*40;
……
}
void changeVel(float b) {
if (loc.y > box-b) hits++;
if (loc.y > box-b && hits == 1) acc = new PVector(random(-1, 1), random(-1, 1), random(-1, 1));
}
void applyForce(PVector force) {
acc.add(PVector.div(force, 1));
}
void boundary(float b) {
if (loc.x < -box) {
vel.x *= bounce;
loc.x = -box;
}
if (loc.x > box) {
vel.x *= bounce;
loc.x = box;
}
if (loc.y < -box) {
……
}
3.3 Rain-Thunder
下雨的模拟与前一场景大同小异在此不多赘述,闪电的实现主要运用了分形。
鼠标每点击一次会出现一次闪电。
Tree(PVector startPoint, PVector direction, float initialWeight) {
int generation = floor( random(2.0, 5.0) );
generationMap = new int[generation][];
generationMap[0] = new int[1];
generationMap[0][0] = generation ;
treeSize = generationMap[0][0] + 1;
int generationSize = generationMap[0][0];
for(int j = 1; j < generation; j++) {
if(generationSize == 0) {
generationMap[j] = new int[1];
generationMap[j][0] = -1;
break;
}
generationMap[j] = new int[generationSize];
for(int k = 0; k < generationSize; k++) {
generationMap[j][k] = floor( random(0.0, 3.0) ); }
generationSize = 0;
for(int k = 0; k < generationMap[j].length; k++) {
generationSize = generationSize + generationMap[j][k];
}
treeSize = treeSize + generationSize;
}
branches = new Twig[treeSize];
branches[0] = new Twig(startPoint, direction, initialWeight);
int treeIndex = 1;
PVector currentDirection = direction;
for(int j = 0; j < generationMap.length; j++) {
if(generationMap[j][0] == -1) {
break; }
for(int k = 0; k < generationMap[j].length; k++) {
int branchNum = generationMap[j][k];
for(int n = 0; n < branchNum; n++){
int branchIndex = floor(random(0.0, branches[j+k].getPointSum()*0.75));
branches[treeIndex] = new Twig(branches[j+k].getPoint(branchIndex), currentDirection, branches[j+k].getWeight(branchIndex));
treeIndex ++;
} } } }
3.4 Sun
此处单纯地运用了随机数和向量控制轨迹来绘制,因为颜色没有设置好所以有点午夜蹦迪的感觉。太阳会追随鼠标,鼠标点击住太阳中心即可进入下一场景。
因为太阳的中心位置会随着鼠标移动,所以需要将位置的x、y值设置为全局变量,再在overButton函数中进行判断是否进入下一场景。
这样的图形绘制网上有很多,也没什么技术含量,就不放代码了。
3.5 Galaxy
这里运用的依然是粒子的旋转等,最主要的还是参数的设置。
随着鼠标点击次数增加,会转的越来越快。同时整体会随着鼠标移动而整体旋转。
if (first==0) {
first = 1;
} else if (first ==1) {
xs = new float[n];
rs = new float[n];
ss = new float[n];
zs = new float[n];
int m = 2;
for (int i = 0; i<n; i++) {
xs[i] = sq(random(1.4))+random(0.05);
rs[i] = ((sqrt(sin(random(PI/2))))+(int)random(m)+random(0.01))/m*TWO_PI
-(9*sqrt(xs[i]));
ss[i]= random(1, 4);
zs[i] = sq((random(0.1)))*(random(1)>.5?-1:1)*xs[i]*10;
}
first = 2;
} else {
mx+=(mouseX-mx)/4;
my+=(mouseY-my)/4;
background(0);
translate(width/2, height/2);
fill(255, 150);
rotate(tilt);
for (int i = 0; i<n; i++) {
pushMatrix();
rr = rs[i]-(float)mx/1000;
float x = xs[i];
float close = 1+sin(rr-0.5)*sqrt(x);
translate(0, zs[i]*width);
scale(1/pow(2, 1-close));
ellipse(x*width*(cos(rr)+sin(rr)),
x*width*(sin(rr)-cos(rr))*map(my, 0, height, -0.3, 0.3), ss[i], ss[i]);
rs[i]+=(x/(x+1)+0*close/4)*speed/xs[i]*(20/frameRate);
popMatrix();
}
}
四、总体展示
五、参考资料
《The Nature of Code》 Chapter 0.5、Chapter 1.1、Chapter 2.5、Chapter 4。