代码本色第0章-第4章变成习作
习作1:引言(第0章)
运用到了随机游走类,构建了一个walk对象,既维持了自身数据,又能够执行某些动作。
在这个程序中实现的是随着鼠标的移动而形成的图像。
用到的高斯分布中的代码
float r = randomGaussian();
然后定义r的大小来改变位移。
效果图:
代码:
Walker.pde`
class Walker{
int x,y;
Walker() {
x = width/2;
y = height/2;
}
void render() {
stroke(255,128);
fill(120,0,120,128);
ellipse(x,y,30,30);
fill(120,120,0,128);
ellipse(x,y,15,15);
}
// Randomly move up, down, left, right, or stay in one place
void step() {
float r = randomGaussian();
// A 50% of moving towards the mouse
if (r < 0.5) {
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);
}
}
Main.pde
Walker w;
void setup() {
size(640,360);
// Create a walker object
w = new Walker();
background(255);
}
void draw() {
// Run the walker object
w.step();
w.render();
}
此代码用的是高斯函数,在高斯随机游走模型总,每次的移动长度(每次物体在指定方向的移动距离,即步长)都是根据正态分布产生的,并在我们的随机游走模型中实现这样的特性。
习作二:向量(第一章)
用PVector对象实现弹球程序
PVector location;
是用PVector对象表示速度,代替之前的浮点数
PVector velocity:
指我们现在有两个PVector变量
通过向量的使用,实现了鼠标和Mover之间的交互
效果图:
代码:
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 = 5;
}
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
PVector mouse = new PVector(mouseX,mouseY);
acceleration = PVector.sub(mouse,position);
// Set magnitude of acceleration
// Velocity changes according to acceleration
velocity.add(acceleration);
// Limit the velocity by topspeed
velocity.limit(topspeed);
// position changes by velocity
position.add(velocity);
if ((position.x > width) || (position.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((position.y > height) || (position.y < 0)) {
velocity.y = velocity.y * -1;
}
}
void display() {
stroke(0);
strokeWeight(2);
fill(127,200);
ellipse(position.x,position.y,16,16);
}
Main.pde
Mover[] movers=new Mover[20];
void setup() {
size(640,360);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover();
}
}
void draw() {
background(255);
for (int i = 0; i < movers.length; i++) {
movers[i].update();
movers[i].display();
}
}
Mover对象的功能也很简单。它需要能够移动,还需要被显示出来。我们将这两个功能实现为update()函数和display()函数。所有的运动逻辑代码都放在update() 函数中,而显示代码放在display()函数中。
习作三:力(第二章)
小球会向着鼠标的地方慢慢聚在一起,然后慢慢散开。使球同时拥有位置,方向和加速度。
效果图:
Mover.pde
代码:
class Mover{
PVector position;
PVector velocity;
PVector acceleration;
float mass;
Mover(float m, float x, float y) {
mass = m;
position = 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);
position.add(velocity);
acceleration.mult(0);
if ((position.x > width) || (position.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((position.y > height) || (position.y < 0)) {
velocity.y = velocity.y * -1;
}
}
void display() {
stroke(0);
strokeWeight(2);
fill(0, 100);
ellipse(position.x, position.y, mass*24, mass*24);
}
PVector attract(Mover m) {
PVector force = PVector.sub(position, m.position); // Calculate direction of force
float distance = force.mag(); // Distance between objects
distance = constrain(distance, 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) / (distance * distance); // Calculate gravitional force magnitude
force.mult(strength); // Get force vector --> magnitude * direction
return force;
}
}
Main.pde
Mover[] movers=new Mover[20];
float g = 0.4;
void setup() {
size(640,360);
for (int i = 0; i < movers.length; i++) {
movers[i] = new Mover(random(0.1,2),random(width),random(height));
}
}
void draw() {
background(255);
for (int i = 0; i < movers.length; i++) {
for (int j = 0; j < movers.length; j++) {
if (i != j) {
PVector force = movers[j].attract(movers[i]);
movers[i].applyForce(force);
}
}
PVector force = movers[j].attract(movers[i]);
movers[i].applyForce(force);
}
}
}
在代码中,每个Mover对象的初始值都是相同的。但我们应该创建质量不同、初 始位置也不相同的Mover对象。为了解决这个问题,我们可以在构造函数中添加几个 参数,让它变得更灵活。
物体的质量和初始位置就不再是硬性编码的数值了,我们可以通过构造函数来 确定它们。这样一来,我们就可以创建各种各样的Mover对象,它们的质量有大有 小,可以从屏幕左边开始运动,也可以从右边开始。
习作四:振荡(第三章)
小球一中间的球为定点来回匀速振荡,中间对小球进行吸引Attractor。
效果图:
代码:
attractor.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);
}
// Method to display
void display() {
float y = 100*sin(angle);
angle += 0.02;
float x = 100*sin(angle);
angle += 0.02;
ellipseMode(CENTER);
strokeWeight(4);
stroke(0);
if (dragging) fill (50);
else if (rollover) fill(100);
else fill(175,200);
ellipse(position.x,position.y,mass,mass);
fill(127);
float xx=position.x+x;
float yy=position.x+y;
line(position.x,position.y,xx,yy);
ellipse(xx,yy,16,16);
}
// The methods below are for mouse interaction
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 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;
}
}
}
Main.pde
Attractor a;
float angle = 0;
void setup() {
size(400,400);
a = new Attractor();
}
void draw() {
background(255);
a.drag();
a.hover(mouseX,mouseY);
a.display();
}
void mousePressed() {
a.clicked(mouseX,mouseY);
}
void mouseReleased() {
a.stopDragging();
}
我们可以把与角运动相关的 变量加入到Mover类中。
我们知道正切的计算公式:
我们知道速度向量,不知道这个夹角,因此需要通过某种方式计算这个夹 角。有一个专门的函数用于求解这个问题,这个函数就是反正切函数,用arctan或者 tan-1表示(此外还存在反正弦和反余弦函数)。
习作五:粒子系统(第四章)
一些彩色的小球在里面做无规则的运动,在实现过程中,我们会更深 入地使用面向对象方法。
效果图:
代码:
Particle.pde:
class Particle{
PVector position;
PVector velocity;
PVector acceleration;
float lifespan;
float r = 6;
Particle(float x, float y) {
acceleration = new PVector();
velocity = PVector.random2D();
position = new PVector(x, y);
lifespan = 255.0;
}
void run() {
update();
display();
}
void intersects(ArrayList<Particle> particles) {
for (Particle other : particles) {
if (other != this) {
PVector dir = PVector.sub(position, other.position);
if (dir.mag() < r*2) {
dir.setMag(0.5);
applyForce(dir);
}
}
}
}
void applyForce(PVector f) {
acceleration.add(f);
}
// Method to update position
void update() {
velocity.add(acceleration);
position.add(velocity);
acceleration.mult(0);
lifespan -= 0.25;
if ((position.x > width) || (position.x < 0)) {
velocity.x = velocity.x * -1;
}
if ((position.y > height) || (position.y < 0)) {
velocity.y = velocity.y * -1;
}
}
// Method to display
void display() {
float x,y,z;
x=random(255);
y=random(255);
z=random(255);
stroke(0, lifespan);
strokeWeight(2);
fill(x,y,z, lifespan);
ellipse(position.x, position.y, r*2, r*2);
}
// Is the particle still useful?
boolean isDead() {
if (lifespan < 0.0) {
return true;
}
else {
return false;
}
}
}
ParticleSystem.pde
class ParticleSystem{
ArrayList<Particle> particles;
ParticleSystem(PVector position) {
particles = new ArrayList<Particle>();
}
void addParticle(float x, float y) {
particles.add(new Particle(x, y));
}
void display() {
for (Particle p : particles) {
p.display();
}
}
void applyForce(PVector f) {
for (Particle p : particles) {
p.applyForce(f);
}
}
void intersection() {
for (Particle p : particles) {
p.intersects(particles);
}
}
void update() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.update();
if (p.isDead()) {
particles.remove(i);
}
}
}
}
Main.pde
ParticleSystem ps;
void setup() {
size(640,360);
ps = new ParticleSystem(new PVector(width/2,50));
}
void draw() {
background(255);
ps.addParticle(random(width),random(height));
//PVector gravity = new PVector(0,0.1);
//ps.applyForce(gravity);
ps.update();
ps.intersection();
ps.display();
}
粒子就是在屏 幕中移动的对象,它有位置、速度和加速度变量,有构造函数用于内部变量的初始 化,有display()函数用于绘制自身,还有update()函数用于更新位置。
在粒子类中添加applyForce()函数,让粒子可以 受力的作用。