《代码本色》的实践和扩展

互动媒体:创意编程

第零章 引言

在代码中使用random()函数计算概率的方法有很多。一种常见的方法是:在数组中存放 一堆选好的数字,然后从这个数组中选择随机数,根据这些选择判定事件是否发生。
但这里使用的是random(1),会在0-1之前随机产生一个数,用小数来表示概率,比较符合直觉。
生活中总有很多例子无法用均匀分布的随机数模拟,高斯分布有时也无能为力。所以也经常会用自定义分布随机数。
这里我同时使用了这两个随机性,让小球有一定概率向鼠标移动,但距离却是使用自定义的随机数来决定,达到一个奇妙的效果。在这里插入图片描述

Walker w;

void setup() {
  size(640,360);
  w = new Walker();
  background(255);
}

void draw() {
  w.step();
  w.render();
}
class Walker {
  int x, y;

  Walker() {
    x = width/2;
    y = height/2;
  }

  void render() {
    stroke(  142, 229, 238);
    circle(x, y,25);
    strokeWeight(2);
    
  }

  void step() {

    float r = random(1);
    if (r < 0.5) { 
      float j=random(1);
      int xdir;
      int ydir;
      if(j<0.5){
      xdir = 5*(mouseX-x);
      ydir = 5*(mouseY-y);
    }else
      {xdir = (mouseX-x);
      ydir = (mouseY-y);}
      if (xdir != 0) {
        xdir /= abs(xdir);
      } 
      if (ydir != 0) {
        ydir /= abs(ydir);
      }
      x += xdir;
      y += ydir;
    } else {
      float v=random(1);
      int xdir;
      int ydir;
      if(v<0.5){
      xdir = int(random(-20, 20));
      ydir = int(random(-20, 20));}
      else{
       xdir = int(random(-2, 2));
      ydir = int(random(-2, 2));}
      println(xdir);
      x += xdir;
      y += ydir;
    }

    x = constrain(x, 0, width-1);
    y = constrain(y, 0, height-1);
  }
}

第一章 向量

自然的很多类似概念,如加速度,速度,位置等都可以用向量来表示。除了加法,还有很多常用的向量运算。比如向量相减sub,相乘mult等。使用这些运算律做了一堆小球以一个指向鼠标的加速度运动的程序。并给了他一个随机的颜色,参考了后面的内容在边缘处做了处理。让碰到边缘的球从另一侧出来。
在这里插入图片描述

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(); 
  }
}
class Mover {

  // The Mover tracks position, velocity, and acceleration 
  PVector position;
  PVector velocity;
  PVector acceleration;
  float topspeed;

  Mover() {
    position = new PVector(random(width),random(height));
    velocity = new PVector(0,0);
    topspeed = 5;
  }

  void update() {
    
    PVector mouse = new PVector(mouseX,mouseY);
    acceleration = PVector.sub(mouse,position);
    acceleration.normalize();
    acceleration.mult(0.2);
    
    velocity.add(acceleration);
    velocity.limit(topspeed);
    position.add(velocity);
    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;
  }
  }

  void display() {
    float R;
    float G;
    float B;
    R=random(258);
    G=random(258);
    B=random(258);
    stroke(0);
    strokeWeight(2);
    fill(R,G,B);
    ellipse(position.x,position.y,48,48);    
  }

}

第二章 力

物体的加速度等于力。所以我们可以用加速度去表示力。我们可以在对象上施加力,比如重力和风力。但如果有多个力同时作用的话,是需要进行处理的。
合力等于质量乘以加速度。或者可以说成:加速度等于所有力的和除以质量。这样就同时也满足牛顿第 一运动定律:如果所受外力的合力为零,物体就处于平衡状态(没有加速度)。我们会用力的累加方法实现牛顿第二定律,这种方法非常简单,只需将所有力相加。在任 意时刻,物体都可能受多种外力作用,它只需要知道如何将这些外力累加在一起,而不需要知道到底有多少种外力。
流体的阻力对物体的作用也是很明显的。在程序中有一部分灰色的区域,是模仿了流体的阻力。给一堆小球施加了重力,让他们掉入水中。在掉入水中之前按键盘的上下左右键的话,可以在空间中添加相应方向的风力。鼠标左键可以重置球的位置。 并在边界添加了事件监测,如果碰到了边界则弹回。

在这里插入图片描述

Mover[] movers = new Mover[9];
PVector wind = new PVector(0,0);
Liquid liquid;

void setup() {
  size(640, 360);
  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);
movers[i].applyForce(wind);
    movers[i].update();
    movers[i].display();
    movers[i].checkEdges( );
  }

  fill(0);
  text("click mouse to reset", 10, 30);
}

void mousePressed() {
  reset();
}
void keyPressed(){  
    PVector power1=new PVector(0,-0.02);
    PVector power2=new PVector(0,0.02);
    PVector power3=new PVector(-0.02,0);
    PVector power4=new PVector(0.02,0);
    if(keyCode==UP)
    {
     wind.add(power1);
    }
    if(keyCode==DOWN)
    {
    wind.add(power2);
    }
    if(keyCode==LEFT)
    {
     wind.add(power3);
    }
    if(keyCode==RIGHT)
    {
     wind.add(power4);
    }   
}
void reset() {
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(random(0.5, 3), 40+i*70, 0);
  }
}
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 l = m.position;
    return l.x > x && l.x < x + w && l.y > y && l.y < y + h;
  }

  PVector drag(Mover m) {
    float speed = m.velocity.mag();
    float dragMagnitude = c * speed * speed;

    PVector dragForce = m.velocity.get();
    dragForce.mult(-1);

    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
  }

  void display() {
    noStroke();
    fill(50);
    rect(x, y, w, h);
  }
}
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);
  }

  void display() {
    stroke(0);
    strokeWeight(2);
    fill(127, 200);
    ellipse(position.x, position.y, mass*16, mass*16);
  }

  void checkEdges() {
    if(position.x > width)
    {
      position.x = width;
      velocity.x *= -1;
    } 
    else if (position.x < 0) 
    {
      velocity.x *= -1;
      position.x = 0;
    }
    if (position.y > height) {
      velocity.y *= -1;
      position.y = height;
    }
    else if(position.y<0)
    {
      velocity.y *= -1;
      position.y = 0;
    }
    }
  }

第三章 振荡

弹簧的弹力可以根据胡克定律计算得到,弹簧的弹力与弹簧的伸长量成正比。也就是说,弹簧被拉伸的越长,它的弹力也越大;被拉伸的越短,弹力越小。从数学上,这么表示该定律:
在这里插入图片描述
要想实现多个球的用弹簧连接的系统,则需要一个弹簧类。弹簧类用于管理弹簧的枢轴点位置、静止长度和计算作用在钟摆上的弹力。利用这个思想和之前的钟摆部分代码,我实现了一个有固定点的弹簧系统。
在这里插入图片描述

Bob[] bobs = new Bob[5];
Spring spring;
Spring[] springs = new Spring[4];
PVector gravity = new PVector(0,2);
void setup() {
  size(640, 360);
  bobs[0]=new Bob(320,100);
  spring=new Spring(320,10,100);
  for (int i = 1; i < bobs.length; i++) {
    bobs[i] = new Bob(width/2, 100+i*40);
  }
  for (int i = 0; i < springs.length; i++) {
    springs[i] = new Spring(bobs[i], bobs[i+1],40);
  }
}

void draw() {
  background(255); 
  spring.connect(bobs[0]);
  spring.displayLine(bobs[0]);
  for (Spring s : springs) {
    s.update();
    s.display();
  }

  for (Bob b : bobs) {
    b.update();
    b.display();
    b.drag(mouseX, mouseY);
    b.applyForce(gravity); 
    b.checkEdges();
  }
}



void mousePressed() {
  for (Bob b : bobs) {
    b.clicked(mouseX, mouseY);
  }
}

void mouseReleased() {
  for (Bob b : bobs) {
    b.stopDragging();
  }
 
}
class Bob { 
  PVector position;
  PVector velocity;
  PVector acceleration;
  float mass = 8;
  
  float damping = 0.95;

  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();
  } 

  void update() { 
    velocity.add(acceleration);
    velocity.mult(damping);
    position.add(velocity);
    acceleration.mult(0);
  }

  void applyForce(PVector force) {
    PVector f = force.get();
    f.div(mass);
    acceleration.add(f);
  }


  void display() { 
    stroke(0);
    strokeWeight(2);
    fill(175,120);
    if (dragging) {
      fill(50);
    }
    ellipse(position.x,position.y,mass*2,mass*2);
  } 

  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;
    }
  }
  void checkEdges() {
    if(position.x > width)
    {
      position.x = width;
      velocity.x *= -1;
    } 
    else if (position.x < 0) 
    {
      velocity.x *= -1;
      position.x = 0;
    }
    if (position.y > height) {
      velocity.y *= -1;
      position.y = height;
    }
    else if(position.y<0)
    {
      velocity.y *= -1;
      position.y = 0;
    }
    }
}
class Spring { 

  // position
  PVector anchor=new PVector(320,10);

  // Rest length and spring constant
  float len;
  float k = 0.2;
  
  Bob a;
  Bob b;
Spring(float x,float y, int l){
  anchor=new PVector(x,y);
  l=1;}
  Spring(Bob a_, Bob b_, int l) {
    a = a_;
    b = b_;
    len = l;
  } 
 void connect(Bob b) {
 PVector force =PVector.sub(b.position,anchor);  
 float d = force.mag();    
 float stretch = d - len;
 force.normalize(); 
 force.mult(-1*k*stretch); 
 b.applyForce(force);
  } 
  void update() {
    PVector force = PVector.sub(a.position, b.position);
    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(0);
    line(a.position.x, a.position.y, b.position.x, b.position.y);
    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);
  }
}

第四章 粒子系统

粒子系统是由许多粒子组成的用于代表模糊对象的集合。在一段特定时间内,粒子在系统中生成、移动、转化,最后消亡。在一个典型的粒子系统中,粒子在发 射器中诞生,但并不会永远存在。假设粒子永不消亡,系统中的粒子将越积越多,运行速度也会越来越慢,最后程序会挂起。新的粒子不断产生,与此同时, 旧的粒子应该不断消亡,只有这样,程序的性能才不会受到影响。决定粒子何时消亡 的方法很多,比如,粒子可以和另一个粒子结合在一起,或在离开屏幕时消亡。这是 本章的第一个粒子类,我希望它尽可能简单,因此用一个lifespan变 量代表粒子的生存期,这个变量从255开始,逐步递减,递减到0时粒子消亡。
粒子组成的系统可以再组成一个系统的系统,这时我就需要创建一个ArrayList用于存放多个粒子系统对象,并在setup()函 数中将这个ArrayList初始为空。
基于以上的粒子系统知识,给粒子增加了一个重力和鼠标移动方向的加速度。在产生一个较大的粒子同时在周围也产生一些较小的粒子。下面是一个长按鼠标就会一直产生粒子系统的程序。
在这里插入图片描述

ParticleSystem ps;

void setup() {
  size(600, 600);
  colorMode(HSB, 25);
  ps = new ParticleSystem();
}

void draw() {
  background(255, 0, 255);
  ps.run();

  if (mousePressed && mouseButton == LEFT) {
    ps.addnew(mouseX, mouseY);
  }
}
class lizi extends Particle {
  ArrayList<PVector> plist;
  lizi(float x, float y) {
    super(x, y);

    PVector v = new PVector(mouseX - pmouseX, mouseY - pmouseY);
    v.mult(0.1);
    velocity.mult(getSpeed(100)).add(v);

    plist = new ArrayList();
    for (int i = 0; i < 3; i++) {
      float xOffset = random(-10, 10);
      float yOffset = random(-10, 10);
      float hue = random(50);
      
      plist.add(new PVector(xOffset, yOffset, hue));
    }
  }

  void update() {
    super.update();

  }


  void drawShape() {
    for (PVector p : plist) {
      stroke(p.z, 25, 125, 20);
      strokeWeight(50);
      point(p.x, p.y);

      stroke(p.z, 255, 25, 10000);
      strokeWeight(30);
      point(p.x, p.y);
    }
  }
}
class Particle {
  PVector location, velocity, acceleration, origin;
  float angle, aVelocity, aAcceleration;
  float lifespan, lifeRate, maxLifespan;
  int type, hue;

  Particle(float x, float y) {
    origin = new PVector(x, y);
    
    location = new PVector();
    
    acceleration = new PVector(0, 0.05);
    
    velocity = PVector.random2D();
    lifespan = maxLifespan = 50;
    
    lifeRate = random(0.35, 1);
    hue = 20;
    type = 1;
  }
  
  float getSpeed(float s){
    float t = maxLifespan / lifeRate;
    return s / t;
  }

  void run() {
    update();
    display();
  }
  
  void update() {
    velocity.add(acceleration);
    location.add(velocity);
    
    aVelocity += aAcceleration;
    angle += aVelocity;
    
    lifespan -= lifeRate;
  }

  boolean isDead() {
    if (lifespan < 0.0) {
      return true;
    } else {
      return false;
    }
  }

  void display() {
    pushMatrix();
    translate(origin.x, origin.y);
    rotate(radians(angle));
    translate(location.x, location.y);
    scale(map(lifespan, 0, maxLifespan, 0, 1));
    drawShape();
    popMatrix();
  }

  void drawShape() {
    stroke(hue, 255, 255);
    strokeWeight(20);
    point(0, 0);
  }
}
class ParticleSystem  {
  ArrayList<Particle> plist;

  ParticleSystem() {
    plist = new ArrayList<Particle>();
  }

  void run() {
    for (int i = plist.size() - 1; i >= 0; i--) {
      Particle p = plist.get(i);
      p.run();
    
      if (p.isDead()) {
        plist.remove(i);
      }
    }
  }

  void addnew(float x, float y) {
    plist.add(new lizi(x, y));
  }


  int getSize() {
    int cnt = 0;
    for(Particle p: plist){
        cnt += p.type;
    }
    return cnt;
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值