参考“自然代码”模拟牛顿随机行为(第五章)

参考自然代码的前五章模拟随机行为
平台:processing
第五章主要说的是运用processing中封装好的模拟物理系统来模拟一些自然现象。
我们这里实现的是模仿水和风的效果。
三个类:边界类、单个粒子类、粒子群类
在介绍这三个类之前先简要介绍一下封装好的模拟物理包:pbox2d类
PBox2D是一个用于模拟刚体物体运动的物理引擎,所谓的物理引擎,就是将对象相关物理属性的计算封装在类库中,由类库去维护物理属性的变化。
1.世界(world) 管理整个物理模拟过程,它知道坐标空间的所有信息,存放了世界中的所有物体。
2.物体(body) Box2D世界的基础元素,有自己的位置和速度。是否感到似曾相识?在前面力和向量的模拟程序中,我们开发了很多类,Box2D的物体就等同于这些类。
3.形状(shape) 记录所有与碰撞相关的几何信息。注意这个形状是抽象的,在Box2D中对物体的行为进行计算,并不包括渲染图像。4.夹具(fixture) 将形状赋给物体,设置物体的一些属性,比如密度、摩擦系数、复原性。
5.关节(joint) 充当两个物体之间的连接器(或者物体和世界之间的连接器)。

具体用法参见:
https://www.jianshu.com/p/0303377b3d4e

第一个是边界类,边界要实现的效果是当物体碰到边界时会产生碰撞的效果。
成员函数:

float x;
  float y;
  float w;
  float h;
  Body b;

Body b就是创建一个body的主题来实现控制。
构造函数:边界就是一个矩形

Boundary(float x_,float y_, float w_, float h_, float a) 
 {
    x = x_;
    y = y_;
    w = w_;
    h = h_;

用PolygonShape来定义多边形:
scalarPixelsToWorld是用来将世界坐标转到像素坐标。

    PolygonShape sd = new PolygonShape();
    //求出box2d坐标
    float box2dW = box2d.scalarPixelsToWorld(w/2);
    float box2dH = box2d.scalarPixelsToWorld(h/2);
    //设置盒子的大小
    sd.setAsBox(box2dW, box2dH);

用bodydef来创建一个物体的身体(在这个封装的类中,物体的内在和形状是分开的)

    BodyDef bd = new BodyDef();
    bd.type = BodyType.STATIC;
    bd.angle = a;
    bd.position.set(box2d.coordPixelsToWorld(x,y));
    b = box2d.createBody(bd);

我们用夹具把物体的内在和形状连接在一起。

 b.createFixture(sd,1);
 }

显示函数:画出边界
用getangle函数来获取物体的角度。

 void display()
  {
    noFill();
    stroke(0);
    strokeWeight(1);
    rectMode(CENTER);
    float a = b.getAngle();
    pushMatrix();
    translate(x,y);
    rotate(-a);
    rect(0,0,w,h);
    popMatrix();
  }

第二个是单个粒子类
成员变量有两个:
一个是物体,一个是跟踪的向量数组。

  Body body;
  PVector[] trail;

构造函数:

Particle(float x_, float y_) 
  {
    float x = x_;
    float y = y_;
    trail = new PVector[6];
    for (int i = 0; i < trail.length; i++)
    {
      trail[i] = new PVector(x, y);
    }

用makebody把box添加到box2d世界:

makeBody(new Vec2(x, y), 0.2f);

粒子消失的成员函数:
直接调用destory函数

void killBody() 
  {
    box2d.destroyBody(body);
  }

判断粒子是否要消亡,先找到粒子在屏幕上的位置,在判断粒子是否在底部:

 boolean done() 
  {
    Vec2 pos = box2d.getBodyPixelCoord(body);
    if (pos.y > height+20)
    {
      killBody();
      return true;
    }
    return false;
  }

显示函数,画出粒子
先获取每一个物体在屏幕上的位置,再记录粒子的历史位置放在跟踪数组中。

void display() 
  {
    Vec2 pos = box2d.getBodyPixelCoord(body);
    for (int i = 0; i < trail.length-1; i++) 
    {
      trail[i] = trail[i+1];
    }
    trail[trail.length-1] = new PVector(pos.x, pos.y);

画一个粒子作为轨迹
用beginShape和endShape的组合来画:

beginShape();
    noFill();
    strokeWeight(2);
    stroke(0, 150);
    for (int i = 0; i < trail.length; i++) 
    {
      vertex(trail[i].x, trail[i].y);
    }
    endShape();
  }

makebody函数
将box映射到box2d世界,先定义和创造物体

void makeBody(Vec2 center, float r) 
  {
    BodyDef bd = new BodyDef();
    bd.type = BodyType.DYNAMIC;
    bd.position.set(box2d.coordPixelsToWorld(center));
    body = box2d.createBody(bd);

给一个初始的随机速度:

 body.setLinearVelocity(new Vec2(random(-1, 1), random(-1, 1)));

把物体的形状定义为圆形:

    CircleShape cs = new CircleShape();
    cs.m_radius = box2d.scalarPixelsToWorld(r);
    FixtureDef fd = new FixtureDef();
    fd.shape = cs;

在设置一下摩擦力等参数:

    fd.density = 1;
    fd.friction = 0;  // 湿路滑!
    fd.restitution = 0.5;

最后还是用夹具连接:

 body.createFixture(fd);

第三个就是粒子系统类
成员变量有两个,分别是储存粒子的数组,和粒子群的起点。

class ParticleSystem  
{
  ArrayList<Particle> particles;    
  PVector origin;

构造函数:

ParticleSystem(int num, PVector v) 
  {
    particles = new ArrayList<Particle>();             // 初始化
    origin = v.get();                        // 存储原点
      for (int i = 0; i < num; i++) 
      {
      particles.add(new Particle(origin.x,origin.y));    // 向ArrayList中添加“num”数量的粒子
    }
  }

运行函数:
显示粒子群,循环调用单个粒子的显示函数

void run() 
  {
    //显示所有的粒子
    for (Particle p: particles)
    {
      p.display();
    }

粒子离开屏幕时从box2d世界的列表中删除

 for (int i = particles.size()-1; i >= 0; i--) 
    {
      Particle p = particles.get(i);
      if (p.done()) 
      {
        particles.remove(i);
      }
    }
  }

添加粒子系统函数:
也是循环调用

void addParticles(int n) 
  {
    for (int i = 0; i < n; i++)
    {
      particles.add(new Particle(origin.x,origin.y));
    }
  }

还需要加一个判断粒子系统里是否还有粒子的函数:

 boolean dead() 
  {
    if (particles.isEmpty()) 
    {
      return true;
    } 
    else 
    {
      return false;
    }
  }

最后是主函数
运用pbox2d的模拟类要先导入包:

import pbox2d.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;

创建一个box2d世界的引用

PBox2D box2d;

创建一个边缘类实例的列表

ArrayList<Boundary> boundaries;

创建一个粒子系统类的列表

ArrayList<ParticleSystem> systems;

在setup函数中对上述的对象进行初始化

void setup() 
{
  size(400,300);
  box2d = new PBox2D(this);
  box2d.createWorld(); 
  systems = new ArrayList<ParticleSystem>();
  boundaries = new ArrayList<Boundary>();
  boundaries.add(new Boundary(50,100,300,5,-0.3));
  boundaries.add(new Boundary(250,175,300,5,0.5));
}

因为是模仿水的效果,水也会收到重力的影响,所以还要添加一个持续的“重力”

 box2d.setGravity(0, -20);

draw函数
!!!!运用此模拟物理系统,必须要调用step函数!!!!
要不然画面不会动。。。

box2d.step();

运行所有的粒子系统

 for (ParticleSystem system: systems) 
  {
    system.run();
    int n = (int) random(0,2);
    system.addParticles(n);
  }

显示所有的边界:

// 显示所有的边界
  for (Boundary wall: boundaries)
  {
    wall.display();
  }

添加鼠标点击事件
每点一次就添加一个粒子系统

void mouseClicked()
{
  systems.add(new ParticleSystem(0, new PVector(mouseX,mouseY)));
}

让我们看看效果
参考“自然代码”模拟牛顿随机行为(第五章)_第1张图片

下面给粒子系统加上风吹的效果
修改单个粒子类
添加一个加力函数:

void applyForce(Vec2 force) 
  {
    Vec2 pos = body.getWorldCenter();
    body.applyForce(force, pos);
  }

在粒子群类中调用:

 void blow()
  {
     Vec2 wind;
     wind = new Vec2(0,0);
     for (int i = particles.size()-1; i >= 0; i--) 
    {
      Particle p = particles.get(i);
      wind = new Vec2(0.025,-0.025);
      p.applyForce(wind);
    }
  }

还要在draw函数中调用:
鼠标按压函数

if (mousePressed)
  {
    for (ParticleSystem system: systems) 
   {
    system.blow();
   }
  }

看看怎么样:
参考“自然代码”模拟牛顿随机行为(第五章)_第2张图片

但是现在风都往一个方向吹,选择改为鼠标在哪里就往鼠标的反方向吹
在粒子群类中修改:

void blow()
  {
     Vec2 wind;
     wind = new Vec2(0,0);
     for (int i = particles.size()-1; i >= 0; i--) 
    {
      Particle p = particles.get(i);
      if(mouseX<(width/2) && mouseY<(height/2))
      {
        wind = new Vec2(0.025,-0.025);
      }
      else if(mouseX>(width/2) && mouseY<(height/2))
      {
        wind = new Vec2(-0.025,-0.025);
      }
      else if(mouseX<(width/2) && mouseY>(height/2))
      {
        wind = new Vec2(0.025,0.025);
      }
      else if(mouseX>(width/2) && mouseY>(height/2))
      {
        wind = new Vec2(-0.025,0.025);
      }
      p.applyForce(wind);
    }
  }

演示:

你可能感兴趣的:(参考“自然代码”模拟牛顿随机行为(第五章))