要求:创作一组编程习作,体现随机行为及牛顿运动学
- 一、序章——随机游走
- 二、第一章——向量
- 三、第二章——力
- 四、第三章——振荡
- 五、第四章——粒子系统
一、序章——随机游走
规律:根据鼠标位置的x、y坐标决定随机游走的方向
技术:
- 概率和非均匀分布
绘制了一个小球使他有60%的概率向着鼠标所在的方向移动。
改变使用random()函数的方式,使它产生“非均匀”分布的随机数。 - Perlin噪声
Processing的noise()函数告诉我们噪声是通过几个“八度”计算出来的。调用noiseDetail()函数会改变“八度”的数量以及各个八度的重要性,这反过来会影响noise()函数的行为。
代码:
程序入口:
Walker w;
void setup() {
size(640,360);
// Create a walker object
w = new Walker();
background(200);
}
void draw() {
// Run the walker object
w.step();
w.render();
}
Walker.pde
class Walker {
int x, y;
Walker() {
x = width/2;
y = height/2;
}
void render() {
stroke(100,100,100);
ellipse(x,y,40,40);
}
// Randomly move up, down, left, right, or stay in one place
void step() {
float r = random(1);
// A 50% of moving towards the mouse
if (r < 0.6) {
int xdir = (mouseX-x);
int ydir = (mouseY-y);
if (xdir != 0) {
xdir /= abs(xdir);
}
if (ydir != 0) {
ydir /= abs(ydir);
}
x += xdir;
y += ydir;
} else {
int xdir = int(random(-2, 2));
int ydir = int(random(-2, 2));
println(xdir);
x += xdir;
y += ydir;
}
x = constrain(x, 0, width-1);
y = constrain(y, 0, height-1);
}
}
结果:
二、第一章——向量
规律:一组同时超向鼠标,速度加速度均不同的物体组成的朝向鼠标运动的圆管形状
技术:
- 向量的运动:速度
- 当前位置加上速度得到一个新的位置;
- 在新的位置上绘制对象。
- 加速度的交互
我们想要根据某个规则或公式计算一个向量,都必须同时算出两部分数据:大小和方向。先从方向开始,加速度的方向是物体朝向鼠标的方向,假设物体的位置是(x,y),鼠标的位置是(mouseX,mouseY)。
步骤:
- 计算由物体指向目标位置(鼠标)的向量;
- 单位化该向量(将向量的大小缩短为1);
- 改变以上单位向量的长度(乘以某个合适的值);
- 将步骤3中得到的向量赋给加速度。
代码:
程序入口:
Mover[] movers = new Mover[20];
void setup() {
background(255);
size(1000,1000);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover();
}
}
void draw() {
for (int i = 0; i < movers.length; i++) {
movers[i].update();
movers[i].display();
}
}
Mover.pde:
class Mover {
// The Mover tracks position, velocity, and acceleration
PVector position;
PVector velocity;
PVector acceleration;
// The Mover's maximum speed
float topspeed;
Mover() {
// Start in the center
position = new PVector(random(width),random(height));
velocity = new PVector(0,0);
topspeed = random(10);
}
void update() {
// Compute a vector that points from position to mouse
PVector mouse = new PVector(mouseX,mouseY);
acceleration = PVector.sub(mouse,position);
// Set magnitude of acceleration
//acceleration.setMag(0.2);
acceleration.normalize();
acceleration.mult(0.2);
// Velocity changes according to acceleration
velocity.add(acceleration);
// Limit the velocity by topspeed
velocity.limit(topspeed);
// position changes by velocity
position.add(velocity);
}
void display() {
stroke(0);
strokeWeight(2);
fill(127,200);
ellipse(position.x,position.y,48,48);
}
}
结果:
三、第二章——力
规律:受朝向中心圆形引力作用的三色小球的运动轨迹
技术:
- 主角对于其他Mover在距离不同时具有不同的作用,距离远的时候表现为吸引,距离近时表现为排斥。(分子之间距离变小,表现为斥力,距离变大,变大,表现为引力)
距离不同时,力的作用效果不一样,距离大时表现为引力,距离小时表现为斥力,即距离到达临界小值时,力的方向改变,代码表现为:
float strength = (G * mass * m.mass) / (d * d); // Calculate gravitional force magnitude
if(d < 20){
strength *= -1;
}
force.mult(strength);
- 主角对于不同颜色的其他Mover的作用效果不同。
代码:
程序入口:
//Mover m;
Attractor a;
Mover[] movers = new Mover[30];
void setup() {
size(1000,1000);
for (int i = 0; i < movers.length/3; i++) {
movers[i] = new Mover(1, random(width), random(height), 'R');
}
for (int i = 10; i < movers.length/3+10; i++) {
movers[i] = new Mover(3, random(width), random(height), 'G');
}
for (int i = 20; i < movers.length/3+20; i++) {
movers[i] = new Mover(5, random(width), random(height), 'B');
}
a = new Attractor();
background(255);
}
void draw() {
PVector wind = new PVector(0.01,0);
for (int i = 0; i < movers.length; i++) {
PVector force = a.attract(movers[i]);
movers[i].applyForce(wind);
movers[i].applyForce(force);
movers[i].update();
movers[i].display();
}
a.drag();
a.hover(mouseX,mouseY);
a.display();
}
Attracter.pde:
class Attractor {
float mass; // Mass, tied to size
float G; // Gravitational Constant
PVector position; // position
boolean dragging = false; // Is the object being dragged?
boolean rollover = false; // Is the mouse over the ellipse?
PVector dragOffset; // holds the offset for when object is clicked on
Attractor() {
position = new PVector(width/2,height/2);
mass = 20;
G = 1;
dragOffset = new PVector(0.0,0.0);
}
PVector attract(Mover m) {
PVector force = PVector.sub(position,m.position); // Calculate direction of force
float d = force.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
force.normalize(); // Normalize vector (distance doesn't matter here, we just want this vector for direction)
float strength = (G * mass * m.mass) / (d * d); // Calculate gravitional force magnitude
if(d < 20){
strength *= -1;
}
force.mult(strength); // Get force vector --> magnitude * direction
return force;
}
// Method to display
void display() {
ellipseMode(CENTER);
strokeWeight(4);
stroke(0);
if (dragging) fill (255);
else if (rollover) fill(255);
else fill(255);
ellipse(width/2,height/2,mass*2,mass*2);
}
void hover(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
rollover = true;
}
else {
rollover = false;
}
}
void stopDragging() {
dragging = false;
}
void drag() {
if (dragging) {
position.x = mouseX + dragOffset.x;
position.y = mouseY + dragOffset.y;
}
}
}
Mover.pde:
class Mover {
char col;
PVector position;
PVector velocity;
PVector acceleration;
float mass;
Mover(float m, float x, float y, char c) {
mass = m;
position = new PVector(x, y);
velocity = new PVector(1, 0);
acceleration = new PVector(0, 0);
col = c;
}
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() {
stroke(0);
strokeWeight(2);
if(col == 'R'){
fill(200,0,0);
}
if(col == 'G'){
fill(0,200,0);
}
if(col == 'B'){
fill(0,0,200);
}
ellipse(position.x,position.y,16,16);
}
void checkEdges() {
if (position.x > width) {
position.x = 0;
} else if (position.x < 0) {
position.x = width;
}
if (position.y > height) {
position.y = 0;
} else if(position.y < 0){
position.y = height;
}
}
}
结果:
第三章——振荡
规律:以三角函数的运动形式体现振荡以及加速旋转和力的作用模拟自然系统。
技术:
动态的波形曲线体现三角函数的运动,颜色会随着位置和时间变化,波也在不停地变化。
代码:
程序入口:
float startAngle = 0;
float angleVel = 0.2;
float angle = 0;
float anglec = 0;
// Mover object
Bob bob;
// Spring object
Spring spring;
void setup() {
size(500,500);
smooth();
spring = new Spring(width/2,10,100);
bob = new Bob(width/2,height);
}
int C = 1;
void draw() {
background(175);
fill(127);
stroke(0);
rectMode(CENTER);
translate(width/2, height/2);
rotate(angle);
line(-50, 0, 50, 0);
stroke(0);
strokeWeight(2);
fill(127);
ellipse(50, 0, 16, 16);
ellipse(-50, 0, 16, 16);
anglec += 0.001;
angle += anglec;
rotate(-angle+anglec);
translate(-width/2, -height/2);
startAngle += 0.05;
float angle = startAngle;
float angleColor = 15 * angle;
if (angleColor > 255*C){
angleColor = angleColor - 255*C;
C += 1;
}
for (int x = 0; x <= width; x += 10) {
float y = map(sin(angle),-1,1,0,height);
stroke(175);
fill(angleColor,y,x);
strokeWeight(2);
ellipse(x,y,48,48);
angle += angleVel;
}
// Apply a gravity force to the bob
PVector gravity = new PVector(0,2);
bob.applyForce(gravity);
// Connect the bob to the spring (this calculates the force)
spring.connect(bob);
// Constrain spring distance between min and max
spring.constrainLength(bob,30,200);
// Update bob
bob.update();
// If it's being dragged
bob.drag(mouseX,mouseY);
// Draw everything
//spring.displayLine(bob); // Draw a line between spring and bob
//bob.display();
//spring.display();
}
// For mouse interaction with bob
void mousePressed() {
bob.clicked(mouseX,mouseY);
}
void mouseReleased() {
bob.stopDragging();
}
Mover.pde:
class Bob {
PVector position;
PVector velocity;
PVector acceleration;
float mass = 24;
// Arbitrary damping to simulate friction / drag
float damping = 0.98;
// For mouse interaction
PVector dragOffset;
boolean dragging = false;
// Constructor
Bob(float x, float y) {
position = new PVector(x,y);
velocity = new PVector();
acceleration = new PVector();
dragOffset = new PVector();
}
// Standard Euler integration
void update() {
velocity.add(acceleration);
velocity.mult(damping);
position.add(velocity);
acceleration.mult(0);
}
// Newton's law: F = M * A
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
// Draw the bob
void display() {
stroke(0);
strokeWeight(2);
fill(175);
if (dragging) {
fill(50);
}
ellipse(position.x,position.y,mass*2,mass*2);
}
// The methods below are for mouse interaction
// This checks to see if we clicked on the mover
void clicked(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
void stopDragging() {
dragging = false;
}
void drag(int mx, int my) {
if (dragging) {
position.x = mx + dragOffset.x;
position.y = my + dragOffset.y;
}
}
}
Spring.pde:
class Spring {
// position
PVector anchor;
// Rest length and spring constant
float len;
float k = 0.2;
// Constructor
Spring(float x, float y, int l) {
anchor = new PVector(x, y);
len = l;
}
// Calculate spring force
void connect(Bob b) {
// Vector pointing from anchor to bob position
PVector force = PVector.sub(b.position, anchor);
// What is distance
float d = force.mag();
// Stretch is difference between current distance and rest length
float stretch = d - len;
// Calculate force according to Hooke's Law
// F = k * stretch
force.normalize();
force.mult(-1 * k * stretch);
b.applyForce(force);
}
// Constrain the distance between bob and anchor between min and max
void constrainLength(Bob b, float minlen, float maxlen) {
PVector dir = PVector.sub(b.position, anchor);
float d = dir.mag();
// Is it too short?
if (d < minlen) {
dir.normalize();
dir.mult(minlen);
// Reset position and stop from moving (not realistic physics)
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
// Is it too long?
}
else if (d > maxlen) {
dir.normalize();
dir.mult(maxlen);
// Reset position and stop from moving (not realistic physics)
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
}
}
void display() {
stroke(0);
fill(175);
strokeWeight(2);
rectMode(CENTER);
rect(anchor.x, anchor.y, 10, 10);
}
void displayLine(Bob b) {
strokeWeight(2);
stroke(0);
line(b.position.x, b.position.y, anchor.x, anchor.y);
}
}
结果:
第四章——粒子系统
规律:一个粒子发射器,在黑色系统中发生彩色圆点。
代码:
程序入口:
ParticleSystem ps;
void setup() {
size(640, 360);
ps = new ParticleSystem(new PVector(width/2, 50));
}
void draw() {
background(0);
ps.addParticle();
ps.run();
}
Particle.pde:
class Particle {
PVector location;
PVector velocity;
PVector acceleration;
float lifespan;
Particle(PVector l) {
// The acceleration
acceleration = new PVector(0, 0.05);
// circel's x and y ==> range
velocity = new PVector(random(-1, 1), random(-2, 0));
// apawn's position
location = l.copy();
// the circle life time
lifespan = 255.0;
}
void run() {
update();
display();
}
void update() {
velocity.add(acceleration);
location.add(velocity);
lifespan-=1.0;
}
boolean isDead() {
if (lifespan <= 0) {
return true;
} else {
return false;
}
}
void display() {
// border
stroke(0, lifespan);
// border's weight
strokeWeight(1);
float r = random(0,255);
float g = random(0,255);
float b = random(0,255);
// random the circle's color
fill(r,g,b, lifespan);
// draw circle
ellipse(location.x, location.y, 3, 3);
}
}
ParticleSystem.pde:
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(PVector position) {
origin = position.copy();
particles = new ArrayList<Particle>();
}
void addParticle() {
particles.add(new Particle(origin));
}
void run() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
}
}
结果: