博文索引目录:
1. 引言
2. 作品
- 2.1 第0章(引言)—— 我对我的梦有种迷恋,就像这漫天星的夜
- 2.2 第1章(向量)—— 心如止水,却为叶所动
- 2.3 第2章(力)—— 樱花飘来的方向,大概就是我思念追随的地方
- 2.4 第3章(振荡)—— 你看那活泼可爱的小蝌蚪,它在找荷叶还是妈妈呢?
- 2.5 第4章(粒子系统)——是彩虹桥呀!
3. 总结
1. 引言
这一次互动媒体技术的作品是利用processing创作五幅模拟随机行为和牛顿运动学的作品。下面将按《代码本色》章节顺序依次展示作品,自然这么美,跟小明一起去看看吧!~
2. 作品
- 2.1 第0章(引言)—— 我对我的梦有种迷恋,就像这漫天星的夜
效果图:
作品规律描述:
1.夜空中的星星闪闪发光,在随着夜幕慢慢向右移动。
2.流星雨从左上角向右下角滑动,速度不同,大小不同,深浅不同,方向也有差异。
原理技术参考:
本作品主要用柏林噪声实现对流星雨位置的平滑过渡,而星星的位置和大小则是伪随机形成的。
代码如下:
//星星
Point[] stars=new Point[50];
float[] txs=new float[50];
float[] tys=new float[50];
//流星雨
Point[] showers=new Point[10];
float[] tsh=new float[10];
float[][] k=new float[10][2];
float[][] off=new float[10][2];
float[] offset=new float[10];
void setup()
{
size(1000,600);
//满屏的星星
//fullScreen();
//星星初始位置
for(int i=0;i<50;i++)
{
stars[i]=new Point();
stars[i].x=random(0,width);
stars[i].y=random(0,height);
//柏林噪声随机时间点
txs[i]=random(0,width);
tys[i]=random(0,height);
}
//流星雨初始位置
for(int i=0;i<10;i++)
{
showers[i]=new Point();
showers[i].x=random(0,width);
showers[i].y=random(0,height);
//柏林噪声随机时间点
tsh[i]=random(0,width);
}
for(int i=0;i<10;i++)
{
off[i][0]=random(0,10000);
off[i][1]=random(0,10000);
}
for(int i=0;i<10;i++)
{
offset[i]=random(0,10000);
}
}
void draw()
{
//运行秒数
int time=millis()/1000;
//每帧刷新背景(夜空)
background(6,35,104);
//星星位置
for(int i=0;i<50;i++)
{
//星星位置平滑增加-》柏林噪声
//看上去像是往右移动
stars[i].x += 0.3*noise(txs[i]);
stars[i].y += 0.07*noise(tys[i]);
txs[i] += 0.005*randomGaussian();
tys[i] += 0.005*randomGaussian();
//如果出屏幕,取余
if(stars[i].x>width)
stars[i].x=stars[i].x%width;
if(stars[i].y>height)
stars[i].y=stars[i].y%height;
}
//画星星,随机造成闪烁效果
fill(255,255,0);
noStroke();
for(int i=0;i<50;i++)
ellipse(stars[i].x,stars[i].y,random(1,5),random(1,5));
//流星雨位置
for(int i=0;i<10;i++)
{
//星星位置平滑增加-》柏林噪声
//看上去像是往右移动
k[i][0]=map(noise(off[i][0]),0,1,6,12);
k[i][1]=map(noise(off[i][1]),0,1,4,10);
showers[i].x += k[i][0]*noise(tsh[i]);
showers[i].y += k[i][1]*noise(tsh[i]);
tsh[i] += 0.01;
off[i][0]+=0.01;
off[i][1]+=0.01;
//如果出屏幕,取余
if(showers[i].x>width)
showers[i].x=showers[i].x%width;
if(showers[i].y>height)
showers[i].y=showers[i].y%height;
}
//画流星雨
fill(255,255,255);
for(int i=0;i<10;i++)
{
float randnum=100*noise(offset[i]);
for(int j=0;j<randnum;j++)
ellipse(showers[i].x+j,showers[i].y+j*( k[i][1]/ k[i][0]),0.1+0.03*j,0.1+0.03*j);
offset[i]+=0.001;
}
}
- 2.1 第1章(向量)—— 心如止水,却为叶所动
效果图:
作品规律描述:
1.每一次运行生成的荷叶位置随机,大小和片数也是随机的。
2.鼠标点击,点击处出现水波,且在水波范围内的荷叶会随水波远离飘动。
3.不在水波范围内的不会飘动。
原理技术参考:
原理不难,主要是向量的各种运算:
在本作的交互部分采用的是鼠标位置与目标位置之间的向量差得到我们想要的向量。
荷叶的排斥作用主要是对这个向量取反,作为加速度加在荷叶上,使荷叶有原理鼠标原点的效果。
代码如下:
ArrayList<Leaf> leaves=new ArrayList<Leaf>();
boolean canSwim=false;
int times=0;
PVector wavecen;
void setup()
{
size(1000,600);
int randnum=int(random(7,12));
for (int i = 0; i < randnum; i++)
{
leaves.add(new Leaf(random(0,width), random(0,height),random(180,250)));
}
}
void draw() {
background(148,213,220);
//如果鼠标点击了,则画波浪,让荷叶游动
if(canSwim)
{
times++;
if(times%100!=99)
{
//画波浪
wave(times);
}
else
{
times=0;
canSwim=false;
}
//画荷叶
for (int i = 0; i < leaves.size(); i++)
{
leaves.get(i).update(wavecen);
leaves.get(i).display();
}
}
else
{
//画荷叶
for (int i = 0; i < leaves.size(); i++)
{
leaves.get(i).display();
}
}
}
//画波浪
void wave(float time)
{
noFill();
stroke(98,202,214,120-times%100);
ellipse(wavecen.x,wavecen.y,360+times%100,360+times%100);
stroke(94,179,188,120-times%100);
ellipse(wavecen.x,wavecen.y,300+times%100,300+times%100);
stroke(81,157,165,120-times%100);
ellipse(wavecen.x,wavecen.y,170+times%100,170+times%100);
stroke(57,112,118,120-times%100);
ellipse(wavecen.x,wavecen.y,40+times%100,40+times%100);
}
//鼠标点击,才可以形成波浪,荷叶游动
void mousePressed()
{
canSwim=true;
wavecen=new PVector(mouseX,mouseY);
}
Leaf类:
class Leaf
{
PVector location; //荷叶位置
PVector velocity; //荷叶速度
PVector acceleration; //荷叶加速度
float limitSpeed; //荷叶最大速度
float size; //荷叶大小
float size1; //荷叶大小
Leaf(float x,float y,float s)
{
location = new PVector(x,y);
velocity = new PVector(0,0);
size=s;
size1=s*(0.75+random(-0.2,0));
limitSpeed=0.3;
}
void update(PVector vec)
{
if(PVector.dist(vec,location)<300)
{
//计算指向鼠标的向量
PVector mouse = new PVector(vec.x,vec.y);
PVector dir = PVector.sub(mouse,location);
dir.normalize(); //单位化向量
dir.mult(-0.05); //改变长度
acceleration = dir; //赋给加速度
velocity.add(acceleration); //更新速度
velocity.limit(limitSpeed); //限制速度
location.add(velocity); //更新位置
}
}
//画图
void display()
{
stroke(51,153,93);
strokeWeight(0.2);
fill(51,183,93);
ellipse(location.x,location.y,size,size1);
stroke(53,132,78);
noFill();
strokeWeight(2);
arc(location.x+0.0*size,location.y-0.2*size1,0.5*size,0.5*size1,0,HALF_PI);
arc(location.x+0.0*size,location.y+0.3*size1,0.5*size,0.5*size1,PI,PI+HALF_PI);
arc(location.x-0.08*size,location.y-0.19*size1,0.5*size,0.5*size1,HALF_PI,0.88*PI);
arc(location.x-0.2*size,location.y-0.1*size1,0.4*size,0.4*size1,-0.6*HALF_PI,0.6*HALF_PI);
arc(location.x+0.15*size,location.y+0.08*size1,0.4*size,0.4*size1,0.6*HALF_PI,PI);
strokeWeight(2.5);
arc(location.x,location.y,0.98*size,0.98*size1,-0.2*HALF_PI,1.7*HALF_PI);
strokeWeight(0.8);
arc(location.x,location.y,0.98*size,0.98*size1,1.7*HALF_PI,3.8*HALF_PI);
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200105200058574.gif)
}
- 2.3 第2章(力)—— 樱花飘来的方向,大概就是我思念追随的地方
效果图:
作品规律描述:
1.一片片樱花从左上角向右下角飘落,受重力、水平向右的风力和空气阻力影响。
2.鼠标点击,会产生一个较大的水平风力,樱花运动速度突变(gif图帧率可能不太高,如运行程序,效果很明显)。
3.樱花无限生成,大小随机,旋转角度随机(形状各异)。
原理技术参考:
首先是空气阻力的运算公式:
代码中为了简化,变成了这样,即只需要用到速度和常数即可得到空气阻力的计算:
代码如下:
ArrayList<Flower> flowers=new ArrayList<Flower>();
//Flower[] flowers = new Flower[10];
PVector addwind;
boolean canadd;
int t=0;
void setup() {
size(1000,600);
for (int i = 0; i < 1; i++) {
flowers.add(new Flower(random(4, 9),0,random(0,0.35*height)));
//flowers[i] = new Flower(random(3, 6),random(0.1*width,0),random(0,0.5*height));
}
addwind = new PVector(0,0);
canadd=false;
}
void draw() {
t++;
t=t%1000;
background(240,226,226);
if(t%20==0)
{
flowers.add(new Flower(random(4,9),0,random(0,0.35*height)));
}
for (int i = 0; i < flowers.size(); i++) {
//风力
PVector wind;
if(!canadd)
wind= new PVector(random(0.3,0.6),0);
else
{
wind= new PVector(random(0.3,0.6), 0).add(addwind);
}
//重力
PVector gravity = new PVector(0, 0.03*flowers.get(i).mass);
//空气阻力
float c2 = 0.1;
float speed = flowers.get(i).velocity.mag();
float dragMagnitude = c2*speed*speed;
PVector drag = flowers.get(i).velocity.get();
drag.mult(-1);
drag.normalize();
drag.mult(dragMagnitude);
flowers.get(i).applyForce(wind);
flowers.get(i).applyForce(drag);
flowers.get(i).applyForce(gravity);
flowers.get(i).update();
flowers.get(i).display();
}
canadd=false;
}
//鼠标点击,有风力产生
void mousePressed()
{
canadd=true;
addwind.x=15;
addwind.y=5;
}
Flower类:
class Flower {
PVector position;
PVector velocity;
PVector acceleration;
float mass;
float theta; //旋转角度
Flower(float m, float x , float y) {
mass = m;
position = new PVector(x,y);
velocity = new PVector(0,0);
acceleration = new PVector(0,0);
theta=radians(random(0,36));
}
//加力
void applyForce(PVector force) {
PVector f = PVector.div(force,mass);
acceleration.add(f);
}
//更新
void update() {
velocity.add(acceleration);
position.add(velocity);
acceleration.mult(0);
}
//画樱花
void display() {
noStroke();
fill(229,143,146);
translate(position.x,position.y);
arc(0,-1*mass,2*mass,5*mass,PI,2*PI);
translate(0,-1*mass);
rotate(radians(72)+theta);
arc(0,0,2*mass,5*mass,PI,2*PI);
rotate(radians(72)+theta);
arc(0,0,2*mass,5*mass,PI,2*PI);
rotate(radians(72)+theta);
arc(0,0,2*mass,5*mass,PI,2*PI);
rotate(radians(72)+theta);
arc(0,0,2*mass,5*mass,PI,2*PI);
rotate(-4*(radians(72)+theta));
translate(0,1*mass);
translate(-position.x,-position.y);
}
}
- 2.4 第3章(振荡)—— 你看那活泼可爱的小蝌蚪,它在找荷叶还是妈妈呢?
效果图:
作品规律描述:
1.六只小蝌蚪随机生成位置,其受中间荷叶的引力作用,所以不会运动偏离荷叶太远。
2.荷叶可以移动,移动以后,小蝌蚪在一段时间内受引力影响也会到荷叶附近移动。
3.小蝌蚪的尾巴做简谐运动,一张一合造成游动效果。
原理技术参考:
简谐运动:
两物体之间引力的计算公式:
代码如下:
//蝌蚪数组
Tadpole[] crawlers = new Tadpole[6];
//中间的荷叶
Leaf a;
void setup() {
size(1000,600);
// 初始化蝌蚪
for (int i = 0; i < crawlers.length; i++) {
crawlers[i] = new Tadpole();
}
// 初始化荷叶
a = new Leaf(new PVector(width/2,height/2),20,0.4);
}
void draw() {
background(194,231,223);
for (int i = 0; i < crawlers.length; i++) {
// 计算荷叶对蝌蚪的引力
PVector f = a.attract(crawlers[i]);
//把这个力加到蝌蚪身上
crawlers[i].applyForce(f);
//更新并画出蝌蚪
crawlers[i].update();
crawlers[i].display();
}
a.start();
}
//如果鼠标点击,调用荷叶的点击函数更新位移
void mousePressed() {
a.clicked(mouseX,mouseY);
}
//如果鼠标释放了,即停止移动荷叶
void mouseReleased() {
a.stopDragging();
}
Leaf类:
class Leaf {
float mass; // 荷叶质量
float G; // 引力常量G
PVector pos; // 荷叶位置
boolean dragging = false; // 荷叶是否被鼠标拖拽
PVector drag; // 荷叶被拖拽时的位移向量
Leaf(PVector l_,float m_, float g_) {
pos = l_.get();
mass = m_;
G = g_;
drag = new PVector(0.0,0.0);
}
void start() {
render();
drag();
}
//荷叶对蝌蚪的引力
PVector attract(Tadpole c) {
PVector dir = PVector.sub(pos,c.pos); // Calculate direction of force
float d = dir.mag(); // Distance between objects
d = constrain(d,5.0,25.0); // Limiting the distance to eliminate "extreme" results for very close or very far objects
dir.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float force = (G * mass * c.mass) / (d * d); // Calculate gravitional force magnitude
dir.mult(force); // Get force vector --> magnitude * direction
return dir;
}
//画荷叶
void render() {
ellipseMode(CENTER);
display() ;
}
//如果鼠标点击在荷叶面积范围内,就更新位移
void clicked(int mx, int my) {
float d = dist(mx,my,pos.x,pos.y);
if (d < 2*mass) {
dragging = true;
drag.x = pos.x-mx;
drag.y = pos.y-my;
}
}
//停止拖拉
void stopDragging() {
dragging = false;
}
//拖拉荷叶计算偏移量
void drag() {
if (dragging) {
pos.x = mouseX + drag.x;
pos.y = mouseY + drag.y;
}
}
//画荷叶
void display()
{
stroke(51,153,93);
strokeWeight(0.2);
fill(51,183,93);
ellipse(pos.x,pos.y,4*mass,4*mass);
stroke(53,132,78);
noFill();
strokeWeight(2);
arc(pos.x+0.0*mass,pos.y-0.8*mass,2.0*mass,2.0*mass,0,HALF_PI);
arc(pos.x+0.0*mass,pos.y+1.2*mass,2.0*mass,2.0*mass,PI,PI+HALF_PI);
arc(pos.x-0.32*mass,pos.y-0.76*mass,2.0*mass,2.0*mass,HALF_PI,0.88*PI);
arc(pos.x-0.8*mass,pos.y-0.4*mass,1.6*mass,1.6*mass,-0.6*HALF_PI,0.6*HALF_PI);
arc(pos.x+0.6*mass,pos.y+0.32*mass,1.6*mass,1.6*mass,0.6*HALF_PI,PI);
strokeWeight(2.5);
arc(pos.x,pos.y,3.92*mass,3.92*mass,-0.2*HALF_PI,1.7*HALF_PI);
strokeWeight(0.8);
arc(pos.x,pos.y,3.92*mass,3.92*mass,1.7*HALF_PI,3.8*HALF_PI);
}
}
Tadpole类:
class Tadpole {
//蝌蚪的属性
PVector pos;
PVector vel;
PVector acc;
float mass;
//振荡器(蝌蚪尾巴)
Tail osc;
Tadpole() {
acc = new PVector();
vel = new PVector(random(-1,1),random(-1,1));
pos = new PVector(random(width),random(height));
mass = random(8,16);
osc = new Tail(mass*1);
}
//给蝌蚪施加力
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acc.add(f);
}
//更新蝌蚪的速度和位置
void update() {
vel.add(acc);
pos.add(vel);
acc.mult(0);
//更新尾巴变量
osc.update(vel.mag()/10);
}
// 显示蝌蚪身子
void display() {
//获取前进方向向量
float angle = vel.heading2D();
pushMatrix();
translate(pos.x,pos.y);
rotate(angle);
ellipseMode(CENTER);
//画蝌蚪尾巴
osc.display(pos);
stroke(0);
fill(70,70,70);
ellipse(0,0,mass*1.5,mass*1);
popMatrix();
}
}
Tail类:
class Tail {
//递增变量
float theta;
//振荡器振幅
float amplitude;
Tail(float r) {
theta = 0;
amplitude = r;
}
//递增变量递增
void update(float thetaVel) {
theta += thetaVel;
}
//显示振荡器
void display(PVector pos) {
float x = map(cos(theta),-1,1,-amplitude-5,-5);
stroke(0);
fill(50);
//line(0,0,x,0);
ellipse(x,0,20,4);
}
}
- 2.5 第4章(粒子系统)——是彩虹桥呀!
效果图:
作品规律描述:
1.鼠标点击出现彩虹粒子发射的效果。
2.相邻的彩虹的颜色的速度加速度角度都有相等偏差。
原理技术参考:
由于粒子系统是无限生成的,所以用之前的数组存储是不太方便,所以这里采用了链表,它的删除和遍历也很方便:
另外,给粒子设定生命周期符合常理,且可以在粒子发射一定时间后淡出消失,效果也很好。
代码如下:
ParticleSystem ps;
boolean isclicked; //是否鼠标点击
void setup() {
size(1000,600);
isclicked=false;
}
void draw() {
background(241,235,235);
if(isclicked)
{
ps.addParticle();
ps.run();
}
}
//鼠标点击,才可以下彩虹雨
void mousePressed()
{
isclicked=true;
ps = new ParticleSystem(new PVector(mouseX,mouseY));
}
ParticleSystem类:
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(PVector position) {
origin = position.get();
particles = new ArrayList<Particle>();
}
void addParticle() {
int randnum=int(random(7));
particles.add(new Particle(origin,randnum));
}
void run() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
}
}
Particle类:
class Particle {
PVector position;
PVector velocity;
PVector acceleration;
float lifespan;
PVector col; //彩虹颜色
Particle(PVector l,int index) {
switch(index)
{
case 0:velocity = new PVector(random(-3.5,-2.5),0); acceleration = new PVector(0.4,-0.1); col=new PVector(255,0,0); break;
case 1:velocity = new PVector(random(-2.5,-1.5),0); acceleration = new PVector(0.39,-0.09); col=new PVector(255,125,0); break;
case 2:velocity = new PVector(random(-1.5,-0.5),0); acceleration = new PVector(0.38,-0.08); col=new PVector(255,255,0);break;
case 3:velocity = new PVector(random(-0.5,0.5),0); acceleration = new PVector(0.37,-0.07); col=new PVector(0,255,0);break;
case 4:velocity = new PVector(random(0.5,1.5),0); acceleration = new PVector(0.36,-0.06); col=new PVector(0,0,255);break;
case 5:velocity = new PVector(random(1.5,2.5),0); acceleration = new PVector(0.35,-0.05); col=new PVector(82,89,245);break;
case 6:velocity = new PVector(random(2.5,3.5),0); acceleration = new PVector(0.34,-0.04); col=new PVector(199,37,199);break;
}
position = l.get();
lifespan = 255.0;
}
void run() {
update();
display();
}
void update() {
velocity.add(acceleration);
position.add(velocity);
lifespan -= 2.0;
}
void display() {
noStroke();
fill(col.x,col.y,col.z,lifespan);
ellipse(position.x,position.y,40,20);
}
boolean isDead() {
if (lifespan < 0.0) {
return true;
} else {
return false;
}
}
}
3. 总结
这一次互动媒体技术的作品是学习《代码本色》这本书来创作的,学习的过程中懂得了很多,很多之前看似很简单的东西也有了新的感悟,也觉得Processing面向对象的编程方式十分方便!模拟自然真的很有趣,希望下学期能够继续学习未学的篇章!~