《代码本色》创意编程

《代码本色》0-4章创意编程——互动媒体技术作业

1.第0章 随机游走

实现了矩形在二维空间中,朝着鼠标所在方向随机游走,颜色变化通过泊林噪声和映射噪声控制。
在这里插入图片描述
(1)路线的选择
设置一个[0,1]范围的随机数,根据随机数值的大小确定当前方块的走向。若随机数小于0.5,朝鼠标所在方向前进,即xdir=mouseX-x,ydir=mouseY-y;若随机数大于0.5,朝随机方向前进,利用random函数获得指定范围的随机数。

void step() {
    float r = random(1);
    // 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);
  }

(2)颜色变化
颜色R,G,B对应的值都是动态变化的,它们的参数都是根据泊林噪声和映射噪声计算而来。Perlin噪声的实现是利用noise()函数,random()函数的参数是目标随机数的最小值和最大值,但是noise()函数并非如此。noise()函数的结果范围是固定的,它总是会返回一个介于 0~1的结果。后面再通过map()函数来改变结果的范围。
观察运行时的图像发现,图像颜色具有一定规律,在渐变的基础上,当方块移动到左边时开始变黑,移动到右边时开始变红,且在不同区域的颜色都有一定规律。

 void render() {
   strokeWeight(2);
    //point(x, y);
     float n = noise(t); 
     float k = map(n,0,1,0,width);   
     float v = map(n,0,1,0,height);   
     t += 0.01; 
     fill(k,v,v);
     stroke(x);
     rect(x,y,50,10);
  }

2.第1章 向量

利用了位移、速度、加速度等知识,实现鼠标引导,在二维平面中画出具有三维效果的图案。

在这里插入图片描述物体的运动中,对于位置有position=position+velocity,对于速度有velocity=velocity+accelerate,我们利用这一知识,结合向量的加减乘除、模运算等实现随鼠标的加速运动。
向量计算不直接用运算符号,而要使用PVector类的函数,其使用方法有两种,一种是通过对象调用,例如mouse.sub(position)表示mouse向量减去position向量,另一种表达方法是通过调用静态方法PVector.sub(mouse,position)来计算。
为了便于实现,设置Mover类,变量包含位置、速度、加速度等信息,同时具有计算移动和绘制的函数。

class Mover{
  PVector position;    //位置
  PVector velocity;    //速度
  PVector accelerate;  //加速度
  float maxspeed=7;
  Mover(){
   position=new PVector(width/2,height/2);
   velocity=new PVector(0,0);
  }
  void update(){
    PVector mouse=new PVector(mouseX,mouseY);
    accelerate=PVector.sub(mouse,position);
    accelerate.normalize();  //单位化,求出加速度方向
    accelerate.mult(2);    //乘以加速度大小
    velocity.add(accelerate);
    velocity.limit(maxspeed);   //限制最大速度
    position.add(velocity);
  }
  void display(){
    float r=random(30,100);
    fill(r*2,r,r+10);
    ellipse(position.x,position.y,r,r);
  }
}
Mover[] movers=new Mover[10];
void setup(){
  background(255);
  size(800,800);
  float m,n;
  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();
  }
}

3.第2章 力

结合了小球进入水中的浮力,挡板的弹力,小球在水面上向右的风力和进入水中向右的水流,四种力作用下,小球的运动。
在这里插入图片描述流体阻力的模拟,原先物理中,浮力的公式为F=ρgV,但此处为了计算的方便,我们将阻力公式简化为如下形式:
在这里插入图片描述
其中相同速度进入液体中的物体,液体的阻力系数越大,物体的速度下降越快。
风力和水流的造成的效果,其实就是在水平方向施加力,由F=ma知,其作用相当于给球一个恒定的水平加速度。
Water类:

class Water {
  // Liquid is a rectangle
  float x, y, w, h;
  // Coefficient of drag
  float c;
  Water(float x_, float y_, float w_, float h_, float c_) {
    x = x_;
    y = y_;
    w = w_;
    h = h_;
    c = c_;
  }
  // Is the Mover in the Liquid?
  boolean contains(Mover m) {
    //判断物体是否进入水中
    PVector l = m.position;
    return l.x > x && l.x < x + w && l.y > y && l.y < y + h;
  }
  // Calculate drag force
  PVector drag(Mover m) {
    // Magnitude is coefficient * speed squared
    float speed = m.velocity.mag();
    float dragMagnitude = c * speed * speed;
    // Direction is inverse of velocity
    PVector dragForce = m.velocity.get();
    dragForce.mult(-1);
    // Scale according to magnitude
    // dragForce.setMag(dragMagnitude);
    dragForce.normalize();
    dragForce.mult(dragMagnitude);
    return dragForce;
  }
  void display() {
    noStroke();
    //fill(50);
    rect(x, y, w, h);
  }
}

实现:

Mover[] movers = new Mover[9];
// Liquid
Water water;
Water ban;
void setup() {
  size(640, 360);
  reset();
  // Create liquid object
  water = new Water(0, 2*height/3, width, height/3, 0.1);
  ban=new Water(0,height/2,width/2,20,2.5);
}
void draw() {
  background(255);
  PVector wind=new PVector(0.01,0);   //风
  PVector flow=new PVector(0.02,0);   //水流
  // Draw water
  fill(121,205,205);
  water.display();
  fill(0);
  ban.display();
  for (int i = 0; i < movers.length; i++) {
    // Is the Mover in the liquid?
    if (water.contains(movers[i])) {    
      // Calculate drag force
      PVector dragForce = water.drag(movers[i]);
      // Apply drag force to Mover
      movers[i].applyForce(dragForce);
    }
    else{
     movers[i].applyForce(wind);  
    }
    if(movers[i].position.y+movers[i].mass*16>ban.y&&movers[i].position.y-movers[i].mass*16<ban.y+width&&movers[i].position.x<width/2+movers[i].mass*16){
       PVector dragForce2 = ban.drag(movers[i]);
      // Apply drag force to Move
      movers[i].applyForce(dragForce2);
    }
    // Gravity is scaled by mass here!
    PVector gravity = new PVector(0, 0.1*movers[i].mass);
    // Apply gravity
    movers[i].applyForce(gravity);
    movers[i].applyForce(flow);  
    // Update and display
    movers[i].update();
    movers[i].display();
    movers[i].checkEdges();
  }
  fill(0);
  text("click mouse to reset", 10, 30);
}
void mousePressed() {
  reset();
}
// Restart all the Mover objects randomly
void reset() {
  for (int i = 0; i < movers.length; i++) {
    movers[i] = new Mover(random(0.5, 3), 40+i*70, 0);
  }
}

4.第3章 振荡

标点击黄色圆球可以进行拖拽,改变绳子张开角度和绳子长度,整体的钟摆效果也随之变化。
在这里插入图片描述

构造两个类,分别为Pendulum类控制最中间绳子和黄色小球的钟摆运动,Oscillator类控制彩色小球围绕黄色小球做震荡。若不拖拽,黄色小球最终会竖直垂挂在绳子上,而周围小球一直围绕它震荡。
在模拟震荡中需要考虑到圆周运动的角速度、角加速度等,且震荡与三角函数紧密相关。在角运动中:
角度=角度+角速度
角速度=角速度+角加速度
且需注意角度与弧度的转换。

摆钟运动由于具有能量损失,所以一定会不断减速直到停止,摆动的最大高度也因此不断下降,最终竖直下垂。根据受力分析可知,钟摆运动时加速度与绳子与垂直方向夹角有关,且是与其sin值成一定比例,因此需要在update函数中,更新角加速度的值,同时更新速度与角度的值。

 void update() {
    // As long as we aren't dragging the pendulum, let it swing!
    if (!dragging) {
      float gravity = 0.4;                              // Arbitrary constant
      aAcceleration = (-1 * gravity / r) * sin(angle);  // Calculate acceleration (see: http://www.myphysicslab.com/pendulum1.html)
      aVelocity += aAcceleration;                 // Increment velocity
      aVelocity *= damping;                       // Arbitrary damping
      angle += aVelocity;                         // Increment angle
    }
  }

添加dragging判断条件,判断鼠标是否选中圆球,从而实现选择拖拽的功能,在拖拽时,需根据鼠标的位置重新计算绳子长度以及夹角。

  //判断鼠标点击
  void clicked(int mx, int my) {
    float d = dist(mx, my, position.x, position.y);
    if (d < ballr) {
      dragging = true;
    }
  }
void drag() {
    // If we are draging the ball, we calculate the angle between the 
    // pendulum origin and mouse position
    // we assign that angle to the pendulum
    if (dragging) {
      PVector diff = PVector.sub(origin, new PVector(mouseX, mouseY));      //两点之间差值
      angle = atan2(-1*diff.y, diff.x) - radians(90);        
       //拉动绳子的长度
       PVector mouse=new PVector(mouseX,mouseY);
       r=PVector.sub(mouse,origin).mag();
       position=new PVector(mouse.x,mouse.y);
    }
  }

多个小球围绕中心小球震荡的实现:

  void display() {   
    float x = sin(angle.x)*amplitude.x;
    float y = sin(angle.y)*amplitude.y;
    pushMatrix();
    translate(width/2, height/2);
    stroke(0);
    strokeWeight(2);
    fill(127,127);
    line(center.x, center.y, x+center.x, y+center.y);  
    fill(random(0,255),random(0,255),random(0,255));
    ellipse(x+center.x, y+center.y, 32, 32);  
    popMatrix();
  }

5.第4章 粒子系统

模拟了水中环境,鼠标位置会不断产生上浮的气泡(泡泡的效果利用了png),且遇到障碍物时会产生作用力,产生粒子由于阻碍分开的效果。
在这里插入图片描述
定义三个类,分别为Particle类控制单个粒子的行为,ParticleSystem控制整个粒子系统,Repeller创造一个具有作用力的障碍物。
典型的粒子系统中都有一个发射器,发射器是粒子的源头,它控制粒子的初始属性, 包括位置、速度等。发射器发射的粒子一般是一股粒子或粒子流。关键的一点在于:在一个典型的粒子系统中,粒子在发射器中诞生,但并不会永远存在,应该伴随着新的粒子产生,旧的粒子消亡。因此我们需要为单个粒子设定生存时间,用一个lifespan变 量代表粒子的生存期,以一个较大值为初值,不断递减,当值变为0时粒子消亡。
为了便于管理这些粒子对象,我们使用ArrayList类,它与数组的作用类似。
for (Particle p : particles) {
p.run();
}
用for循环对每个粒子进行操作

Particle类中:

 void update() {
    vel.add(acc);
    pos.add(vel);
    acc.mult(0);
    lifespan -= 2.0;
  }
// 判断粒子是否死亡
  boolean isDead() {
    if (lifespan <= 0.0) {
      return true;
    }
    else {
      return false;
    }
  }

ParticleSystem类中:

class ParticleSystem {
  ArrayList<Particle> particles;    // An arraylist for all the particles
  PImage textures;
  ParticleSystem(PImage imgs, PVector v) {
    textures = imgs;
    particles = new ArrayList();              // Initialize the 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) {
    particles.add(new Particle(x,y,textures));
  }
  void applyForce(PVector f) {
    for (Particle p : particles) {
      p.applyForce(f);
    }
  }
  void applyRepeller(Repeller r) {
    for (Particle p: particles) {
      PVector force = r.repel(p);        
      p.applyForce(force);
    }
  }
  void addParticle(Particle p) {
    particles.add(p);
  }
  // A method to test if the particle system still has particles
  boolean dead() {
    if (particles.isEmpty()) {
      return true;
    } 
    else {
      return false;
    }
  }
}

受力的例子系统将更加真实,我们为系统中添加了一个排斥对象,粒子在接触到它时,会排斥开来,它对每个粒子的作用都不相同,需要分别计算。
Repeller类:

class Repeller {
  // Gravitational Constant
  float G = 100;
  // position
  PVector position;
  float r = 10;
  Repeller(float x, float y)  {
    position = new PVector(x,y);
  }
  void display() {
    stroke(0,0,0);
    strokeWeight(2);
     
    fill(255,0,0);
    ellipse(position.x,position.y,60,60);
  }
  // Calculate a force to push particle away from repeller
  PVector repel(Particle p) {
    PVector dir = PVector.sub(position,p.pos);     
    float d = dir.mag();                    
    dir.normalize();                          
    d = constrain(d,5,100);                  
    float force = -1 * G / (d * d);            
    dir.mult(force);                          
    return dir;
  }  
}

总结

通过本次作业,接触到了用代码的方式模拟各种力的作用以及物体运动,有些情况下并不是完全按照其物理形式进行模拟,因为最终目标是用图像显示出较真实的物理效果,但核心思想与其物理原理是一致的,并且如何发挥创意,将多种技术点结合生成一幅有意思的作品,也需要不断思考。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值