本文主要是介绍互动媒体编程习作:processing+《代码本色》Chap 0-4,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《代码本色》编程练习
- C h a p Chap Chap 0 0 0 引言
- 效果
- 代码解释
- C h a p Chap Chap 1 1 1 向量
- 效果
- 代码解释
- C h a p Chap Chap 2 2 2 力
- 效果
- 代码解释
- C h a p Chap Chap 3 3 3 振荡
- 效果
- 代码解释
- C h a p Chap Chap 4 4 4 粒子系统
- 效果
- 代码解释
C h a p Chap Chap 0 0 0 引言
效果
实现了0.5的概率朝鼠标移动的效果以及Perlin噪声的应用。
代码解释
案例参考:
- 0.6 Perlin噪声 n o i s e noise noise 函数示例
- 0.6.1 映射噪声 m a p map map 函数、示例代码0-5
- 0.3 概率和非均匀分布课后练习0.3提出的思考
关键代码
不断变化的颜色的生成,用到了Perlin平滑噪声和对它的映射噪声。
void render(){float t=random(1000);float n=noise(t);float co=map(n,0,1,0,255);//产生随机的颜色参数float r=map(n,0,1,0,100);//产生随机的半径noFill();stroke(co,random(255),150);strokeWeight(3);ellipse(x,y,r,r);}
使运动对象有50%的概率向着鼠标所在的方向移动,另一半的概率运动情况随意定义了x与y方向的前进。
void step(){float r = random(1);if (r < 0.5)//如果r小于0.5,朝着鼠标方向移动{if((x<mouseX) && (y<mouseY)){ x+=3;//间隔设置较大y+=3;}else if((x<mouseX) && (y>mouseY)){x+=3;y-=3;}else if((x>mouseX) && (y>mouseY)){x-=3;y-=3;}else if((x>mouseX) && (y<mouseY)){x-=3;y+=3;}}else//另外一半的概率{x+=random(3);y+=random(3);} x = constrain(x,1,width-1);//固定区域y = constrain(y,1,height-1);}
整体代码
Walker w;//游走对象void setup()
{size(600,600);w = new Walker();background(0);
}float t=3;
void draw()
{w.step();w.render();
}
class Walker
{float x,y;Walker(){x = random(0, width);y = random(0, height);}void render(){……}void step(){……}
}
C h a p Chap Chap 1 1 1 向量
效果
实现了朝鼠标加速、靠近鼠标时减速、互相连线的效果。
代码解释
案例参考:
- 想法来自于平时在浏览网页时看到的效果
- 1.9 静态函数和非静态函数 示例代码1-11——实现朝鼠标加速的效果
- 1.4.1 向量的减法 示例代码1-3——实现求屏幕中心点与鼠标所在点之间的差并连成线
关键代码
Mover类,定义了位置、速度、加速度、最大速度、与鼠标的距离、距离界限。 - 通过与鼠标位置的向量差等赋予移动点加速度;
- 通过比较与鼠标的距离和定义的界限距离的大小,判断移动点应该向鼠标加速还是减速;
- 给鼠标与移动点连线;
- 详见代码注释。
class Mover
{PVector location;PVector velocity;PVector acceleration;float maxspeed;//最大速度float distance;//与鼠标的距离float closedistance;//距离界限,用于调整加速度Mover(){location = new PVector(random(width),random(height));velocity = new PVector(0,0);maxspeed = 5;closedistance = 200;}void update(){PVector mouse = new PVector(mouseX,mouseY);acceleration = PVector.sub(mouse,location);distance = location.dist(mouse);if(distance < closedistance)//如果与鼠标的距离小于距离界限,减速{acceleration.normalize();//单位化向量,使其长度为1acceleration.mult(-0.2);//反向加速即减速}if(distance > closedistance)//如果与鼠标的距离大于距离界限,加速{acceleration.normalize();acceleration.mult(0.2);}velocity.add(acceleration);//执行加速度调整velocity.limit(maxspeed);//限制速度上限location.add(velocity);//移动location.x = constrain(location.x,1,width-1);//限制活动区域location.y = constrain(location.y,1,height-1);}void display(){fill(127,200,150);rectMode(CENTER);rect(location.x,location.y,15,15);//每一个移动点都是一个小正方形stroke(127,200,150);line(location.x,location.y,mouseX,mouseY);//与鼠标连成线模拟吸引}
}
主程序
- 定义了12个移动点对象组成组;
- 将所有移动点两两连线,形成牵制效果;
- d r a w draw draw 函数的前几行很关键,用于画布等大的白色矩形实现刷屏效果,这样视觉上就不会看到对象以及它们连线的运动轨迹了;
- 详见代码注释。
Mover[] movers = new Mover[12];//新建12个对象组成的组void setup()
{size(1000,800);background(255);for (int i = 0; i < movers.length; i++){movers[i] = new Mover(); }
}void draw()
{fill(255,255);rectMode(CENTER);rect(width/2,height/2,width,height); //每次执行draw的时候都用等大的白色矩形刷屏,不会有轨迹的重合for (int i = 0; i < movers.length; i++){movers[i].update();movers[i].display(); }//每两个移动点之间都连线形成牵制效果for(int i = 0; i < movers.length; i++){for(int j = 0; j < movers.length; j++){stroke(127,200,150);line(movers[i].location.x,movers[i].location.y,movers[j].location.x,movers[j].location.y);}}
}
整体代码
两端代码合并就是完整代码。
C h a p Chap Chap 2 2 2 力
效果
实现了流体阻力、鼠标单击施力的效果。
(说明:本来小球在上方空白区域自由下落时是会保持在一条线上的,但是施加了向上的力后,下落状态就会变得不一致。上述单击鼠标左键实现的就是这个效果。)
代码解释
案例参考:
- 2.8 空气和流体阻力
示例代码2-5 流体阻力
- 2.3 力的累加,使用 a p p l y F o r c e applyForce applyForce 函数实现多种不同力的施加。
- 2.4 处理质量
关键代码
Mover类,定义不同质量的对象施加力的方式(mass)。
void applyForce(PVector force) {// 力根据质量度量的不同而不同PVector f = PVector.div(force, mass);// 施加力acceleration.add(f);}
Liquid类,定义流体阻力的施加方式,用到了简化版的阻力公式,关键在于对小球是否入水的判断、阻力方向的确定以及阻力向量的返回。
详见代码及注释。
//判断小球是否在水中boolean contains(Mover m){PVector loc = m.location;return loc.x > x && loc.x < x + w && loc.y > y && loc.y < y + h;}//计算阻力PVector drag(Mover m){// 阻力公式的应用float speed = m.velocity.mag();//velocityde向量的长度就是速度float dragMagnitude = c * speed * speed;//c*v^2// 阻力的方向PVector dragForce = m.velocity.get();//获取速度的方向dragForce.mult(-1);//反向延伸,即阻力的方向使速度的反方向,即向上dragForce.normalize();dragForce.mult(dragMagnitude);//根据计算出的阻力值赋予阻力向量大小return dragForce;//返回阻力向量}
鼠标左键力的施加,增加鼠标点击的判断,使用 n o i s e noise noise函数随机产生阻力的大小,施力过程与流体阻力一致。
最后运行的时候,左键点击完,较轻的小球会有悬浮的效果,如果不想连续施力可以将鼠标从运行框中移开。
详见代码及注释。
if (mouseButton == LEFT){float t=random(0,1000);float n=noise(t);drag=n/4;//单击左键产生一个大小随机的力PVector force = movers[i].velocity.get();//获取速度的方向force.mult(-1);//反向延伸,即阻力的方向使速度的反方向,即向上force.normalize();force.mult(drag);movers[i].applyForce(force);}
鼠标右键重置,这一段还有一些问题,因为 d r a w draw draw函数在单位时间执行次数很多,而我们点击鼠标对然可以很快但仍然需要一些时间,所以会多次执行重置导致小球有闪烁的效果,这是还存在的问题。
详见代码及注释。
if (mouseButton == RIGHT){reset();}
// 重新加载
void reset()
{for (int i = 0; i < movers.length; i++){movers[i] = new Mover(random(0.5, 3), 30+i*70, 0);}
}
整体代码
主程序
Mover[] movers = new Mover[12];
Liquid liquid;
float drag=0;void setup()
{size(900,600);reset();liquid = new Liquid(0, height/2, width, height/2, 0.1);
}void draw()
{background(255);liquid.display();for (int i = 0; i < movers.length; i++){//判断小球是否在水中if (liquid.contains(movers[i])){//如果在,水施力PVector dragForce = liquid.drag(movers[i]);movers[i].applyForce(dragForce);}//重力设置,重力大小根据小球的大小不同PVector gravity = new PVector(0, 0.1*movers[i].mass);movers[i].applyForce(gravity);if (mouseButton == LEFT){float t=random(0,1000);float n=noise(t);drag=n/4;//单击左键产生一个大小随机的力PVector force = movers[i].velocity.get();//获取速度的方向force.mult(-1);//反向延伸,即阻力的方向使速度的反方向,即向上force.normalize();force.mult(drag);movers[i].applyForce(force);}movers[i].update();movers[i].display();movers[i].checkEdges();}if (mouseButton == RIGHT){reset();}fill(0);text("鼠标右键重置,左键施力",10,30);}// 重新加载
void reset()
{for (int i = 0; i < movers.length; i++){movers[i] = new Mover(random(0.5, 3), 30+i*70, 0);}
}
Mover类
class Mover
{ PVector location;PVector velocity;PVector acceleration;float mass;Mover(float m, float x, float y){mass = m;location = new PVector(x, y);velocity = new PVector(0, 0);acceleration = new PVector(0, 0);}void applyForce(PVector force) {// 力根据质量度量的不同而不同PVector f = PVector.div(force, mass);// 施加力acceleration.add(f);}void update(){velocity.add(acceleration);location.add(velocity);acceleration.mult(0);}void display(){noStroke();fill(127,200,150);float size=20*mass;ellipse(location.x, location.y, size, size);}//边缘判断void checkEdges(){if (location.y > height){velocity.y *= -0.9; // A little dampening when hitting the bottomlocation.y = height;}}
}
Liquid类
class Liquid
{float x,y,w,h;//规格float c;//阻力系数Liquid(float x_, float y_, float w_, float h_, float c_){x = x_;y = y_;w = w_;h = h_;c = c_;}//判断小球是否在水中boolean contains(Mover m){PVector loc = m.location;return loc.x > x && loc.x < x + w && loc.y > y && loc.y < y + h;}//计算阻力PVector drag(Mover m){// 阻力公式的应用float speed = m.velocity.mag();//velocityde向量的长度就是速度float dragMagnitude = c * speed * speed;//c*v^2// 阻力的方向PVector dragForce = m.velocity.get();//获取速度的方向dragForce.mult(-1);//反向延伸,即阻力的方向使速度的反方向,即向上dragForce.normalize();dragForce.mult(dragMagnitude);//根据计算出的阻力值赋予阻力向量大小return dragForce;//返回阻力向量}void display(){noStroke();fill(0,182,221);rect(x,y,w,h);}
}
C h a p Chap Chap 3 3 3 振荡
效果
实现了多对象弹力效果,有固定的有可动的。
代码解释
案例参考:
- 3.10 弹力
- 练习3.16,钟摆连钟摆与钟摆连固定点。
关键代码
小球Bob类与弹簧String类,这两个类在很多参考例子上都有,详见代码及注释。
class Bob
{ PVector location;PVector velocity;PVector acceleration;float mass; float damping = 0.95; 阻尼// 鼠标操作响应PVector dragOffset;boolean dragging = false;//是否被点击拖动Bob(float x, float y, float m){mass=m;location = new PVector(x,y);velocity = new PVector();acceleration = new PVector();dragOffset = new PVector();} void update(){ velocity.add(acceleration);velocity.mult(damping);//有阻尼的运动location.add(velocity);acceleration.mult(0);}void applyForce(PVector force){PVector f = force.get();f.div(mass);//根据质量度量施加力acceleration.add(f);}void display(){ stroke(127,200,150);strokeWeight(2);fill(127,200,150,150);if (dragging)//鼠标拖拽判断{fill(127,200,150,255);//如果被选中了拖拽,改变透明度}ellipse(location.x,location.y,mass*2,mass*2);} void clicked(int mx, int my)//鼠标点击判断{float d = dist(mx,my,location.x,location.y);if (d < mass){dragging = true;dragOffset.x = location.x-mx;//获取与鼠标点击处的位置差dragOffset.y = location.y-my;}}void stopDragging()//鼠标放开{dragging = false;}void drag(int mx, int my)//鼠标拖拽{if (dragging){location.x = mx + dragOffset.x;//保持初点击时的位置差location.y = my + dragOffset.y;}}
}
class Spring
{ PVector anchor;//位置float len;//初始长度float k = 0.2;//弹性系数//连接的两个小球Bob a;Bob b;Spring(Bob a_, Bob b_, int l){a = a_;b = b_;len = l;} // 弹力计算void update(){PVector force = PVector.sub(a.location, b.location);float d = force.mag();//计算向量长度,即距离float stretch = d - len;//伸缩量//利用公式“力=弹性系数*形变长度”计算弹力force.normalize();force.mult(-1 * k * stretch);a.applyForce(force);force.mult(-1);b.applyForce(force);}void display(){strokeWeight(2);stroke(127,200,150);line(a.location.x, a.location.y, b.location.x, b.location.y);}
}
主程序
主程序主要是小球的分布与连接,因为最后没有实现循环有规律地排布小球(因为没想出来怎么编号和连接),所以这里是最傻的坐标计算。
void setup()
{//十字形结构size(640, 360);bobs[0]=new Bob(width/2,0,10);bobs[1]=new Bob(width/2,height/4,10);bobs[2]=new Bob(width/2,height/2,20);bobs[3]=new Bob(width/2,height*3/4,10);bobs[4]=new Bob(width/2,height,10);bobs[5]=new Bob(0,height/2,10);bobs[6]=new Bob(width/4,height/2,10);bobs[7]=new Bob(width*3/4,height/2,10);bobs[8]=new Bob(width,height/2,10);for (int i = 0; i < 4; i++){springs[i] = new Spring(bobs[i], bobs[i+1],50);}springs[4] = new Spring(bobs[5], bobs[6],50);springs[5] = new Spring(bobs[6], bobs[2],50);springs[6] = new Spring(bobs[2], bobs[7],50);springs[7] = new Spring(bobs[7], bobs[8],50);
}
有动与不动的小球是依靠在运行到小球 u p d a t e update update前对小球做执行判断完成的。
for (int i=0;i<bobs.length;i++){//显示所有的小球bobs[i].display();if (i!=0&&i!=4&&i!=5&&i!=8)//四个端的小球固定不动{bobs[i].update();bobs[i].drag(mouseX, mouseY);}}
整体代码
主程序如下,与两个类结合就是所有程序。
Bob[] bobs = new Bob[9];
Spring[] springs = new Spring[8];void setup()
{//十字形结构size(640, 360);bobs[0]=new Bob(width/2,0,10);bobs[1]=new Bob(width/2,height/4,10);bobs[2]=new Bob(width/2,height/2,20);bobs[3]=new Bob(width/2,height*3/4,10);bobs[4]=new Bob(width/2,height,10);bobs[5]=new Bob(0,height/2,10);bobs[6]=new Bob(width/4,height/2,10);bobs[7]=new Bob(width*3/4,height/2,10);bobs[8]=new Bob(width,height/2,10);for (int i = 0; i < 4; i++){springs[i] = new Spring(bobs[i], bobs[i+1],50);}springs[4] = new Spring(bobs[5], bobs[6],50);springs[5] = new Spring(bobs[6], bobs[2],50);springs[6] = new Spring(bobs[2], bobs[7],50);springs[7] = new Spring(bobs[7], bobs[8],50);
}void draw()
{background(255); for (Spring s : springs){s.update();s.display();}for (int i=0;i<bobs.length;i++){//显示所有的小球bobs[i].display();if (i!=0&&i!=4&&i!=5&&i!=8)//四个端的小球固定不动{bobs[i].update();bobs[i].drag(mouseX, mouseY);}}
}
//鼠标点击触发clicked
void mousePressed()
{for (Bob b : bobs){b.clicked(mouseX, mouseY);}
}
//鼠标弹起结束拖拽
void mouseReleased()
{for (Bob b : bobs){b.stopDragging();}
}
C h a p Chap Chap 4 4 4 粒子系统
效果
实现了图像纹理的加法混合用在粒子系统上,气泡状粒子系统随鼠标游走,点击获得类似小烟圈的效果。
代码解释
案例参考:
- 4.13 图像纹理和加法混合
- 示例代码4-9 加法混合
- 4.11 受力作用的粒子系统,示例代码4-6
关键代码
单个粒子类Particle,关键是粒子受力的处理以及图像纹理的展示。
void applyForce(PVector f){acceleration.add(f);}
//展示void render(){imageMode(CENTER);//图像纹理模式tint(lifespan);image(img, location.x, location.y, 32, 32);}
粒子系统ParticleSystem,关键是图像纹理的叠加,给每一个单粒子的施力。
void addParticle(float x, float y){int r = int(random(textures.length));particles.add(new Particle(x,y,textures[r]));//图像纹理任意}
//给粒子施力void applyForce(PVector f){for (Particle p : particles){p.applyForce(f);}}
主程序中图像纹理的导入(图像数据要放在和程序同路径的data文件夹中)对鼠标点击事件的处理,通过多次生成粒子叠加产生小烟圈效果。
PImage[] imgs;imgs = new PImage[4];imgs[0] = loadImage("1.png");imgs[1] = loadImage("2.png");imgs[2] = loadImage("3.png");imgs[3] = loadImage("4.png");
void mousePressed()
{PVector down = new PVector(0,0.2);ps.applyForce(down);ps.run();for (int i = 0; i < 80; i++){ps.addParticle(mouseX,mouseY);//鼠标点击处循环运行,形成小烟圈效果}
整体代码
Particle类
class Particle
{PVector location;PVector velocity;PVector acceleration;float lifespan;//生命周期PImage img;//图像纹理Particle(float x, float y, PImage img_){acceleration = new PVector(0, 0);velocity = PVector.random2D();location = new PVector(x, y);lifespan = 255;img = img_;}void run(){update();render();}void applyForce(PVector f){acceleration.add(f);}// 更新位置void update(){velocity.add(acceleration);location.add(velocity);acceleration.mult(0);lifespan -= 2.0;}//展示void render(){imageMode(CENTER);//图像纹理模式tint(lifespan);image(img, location.x, location.y, 32, 32);}//判断是否消亡boolean isDead(){if (lifespan <= 0.0) { return true; } else { return false; }}
}
ParticleSystem类
class ParticleSystem
{ArrayList<Particle> particles;//粒子数组PImage[] textures;//图像纹理ParticleSystem(PImage[] imgs, PVector v){textures = imgs;particles = new ArrayList();}void run(){for (int i = particles.size()-1; i >= 0; i--){//逐个运行Particle p = particles.get(i);p.run();if (p.isDead()){//周期结束的移除particles.remove(i);}}}void addParticle(float x, float y){int r = int(random(textures.length));particles.add(new Particle(x,y,textures[r]));//图像纹理任意}//给粒子施力void applyForce(PVector f){for (Particle p : particles){p.applyForce(f);}}void addParticle(Particle p){particles.add(p);}//生命周期判断boolean dead(){if (particles.isEmpty()){ return true; } else { return false; }}
}
主程序
ParticleSystem ps;PImage[] imgs;void setup() {size(800, 600, P2D);imgs = new PImage[4];imgs[0] = loadImage("1.png");imgs[1] = loadImage("2.png");imgs[2] = loadImage("3.png");imgs[3] = loadImage("4.png");ps = new ParticleSystem(imgs, new PVector(width/2, 50));
}void draw()
{//混合模式中的加法模式blendMode(ADD);background(0);PVector up = new PVector(0,-0.2);ps.applyForce(up);ps.run();ps.addParticle(mouseX,mouseY);//随鼠标游走
}void mousePressed()
{PVector down = new PVector(0,0.2);ps.applyForce(down);ps.run();for (int i = 0; i < 80; i++){ps.addParticle(mouseX,mouseY);//鼠标点击处循环运行,形成小烟圈效果}
}
这篇关于互动媒体编程习作:processing+《代码本色》Chap 0-4的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!