基于processing实现
效果演示
包含重力,风力,吸引力与水的浮力
Processing实现
主体逻辑
重力
加速度和力成正比, 和质量成反比。 这意味着如果你受一个推力作用产生运动, 那么推力越大, 运动越快( 加速度越大) ; 质量越大, 运动越慢。
重量和质量:
- 质量是物质量的度量( 以千克为单位) 。
- 重量, 通常被误认为质量, 实际上指的是物体所受重力的大小。 根据牛顿第二运动定律, 重量等于质量乘以重力加速度( w = m ∗ g w =m*g w=m∗g) 。 重量以“牛顿”为单位。
- 密度等于质量除以物体的体积( 例如以“克/立方厘米”为单位) 。
加速度是一切运动的起因。而在程序中力变成了运动的起因。
因此直接将重力定义为:
PVector gravity;
gravity = new PVector(0, 0.3);
风力
风是一个具有方向、大小随机性的力,因此在此我通过一个噪声进行模拟风力。
float wAngle = noise(xOff, yOff, zOff) * TWO_PI;
PVector wind = PVector.fromAngle(wAngle);
吸引力
模仿万有引力的概念,吸引力由两个物体之间的一个相互作用力形成,但在此我不需要将此力同时作用于两个物体,只需将其作用与需要被吸引的物体,也就是我们的雪花。
引力主要由两方面因素决定:
- 物体间的距离
- 物体的质量
引力的计算主要在attract函数内实现。
完整代码如下所示:
float g = 1;
class Attractor {
float mass; // Mass, tied to size
PVector location; // Location
boolean dragging = false; // Is the object being dragged?
boolean rollover = false; // Is the mouse over the ellipse?
PVector drag; // holds the offset for when object is clicked on
Attractor(int x,int y) {
//location = new PVector(width/2,height/2);
location = new PVector(x,y);
mass = 10;
drag = new PVector(0.0,0.0);
}
PVector attract(Snowflake m) {
PVector force = PVector.sub(location,m.pos); // 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
force.mult(strength); // Get force vector --> magnitude * direction
return force;
}
// Method to display
void display() {
ellipseMode(CENTER);
stroke(0);
if (dragging) fill (50);
else if (rollover) fill(100);
else fill(0);
ellipse(location.x,location.y,mass*6,mass*6);
}
// The methods below are for mouse interaction
void clicked(int mx, int my) {
float d = dist(mx,my,location.x,location.y);
if (d < mass) {
dragging = true;
drag.x = location.x-mx;
drag.y = location.y-my;
}
}
void rollover(int mx, int my) {
float d = dist(mx,my,location.x,location.y);
if (d < mass) {
rollover = true;
}
else {
rollover = false;
}
}
void stopDragging() {
dragging = false;
}
void drag() {
if (dragging) {
location.x = mouseX + drag.x;
location.y = mouseY + drag.y;
}
}
}
液体浮力
液体提供的浮力计算为:
F
=
c
∗
v
2
F=c*v^2
F=c∗v2
其中
F
F
F为液体施加浮力,c为液体的系数,v为物体的运动速度。
完整代码:
class Liquid {
// Liquid is a rectangle
float x,y,w,h;
// Coefficient of drag
float c;
Liquid(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(Snowflake m) {
PVector l = m.pos;
if (l.x > x && l.x < x + w && l.y > y && l.y < y + h) {
return true;
}
else {
return false;
}
}
// Calculate drag force
PVector drag(Snowflake m) {
// Magnitude is coefficient * speed squared
float speed = m.vel.mag() * 0.3;
float dragMagnitude = c * speed * speed;
// Direction is inverse of velocity
PVector dragForce = m.vel.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);
}
}
雪花粒子
初始定义
雪花粒子为同时拥有位置( location) 、 速度( velocity) 和加速度( acceleration) 的Snowflake类。
在初始化雪花时需通过图片进行加载,
class Snowflake {
PImage img;
PVector pos;
PVector vel;
PVector acc;
float angle;
float dir;
float xOff;
float r;
float mass;
Snowflake(float m,float sx, float sy, PImage simg) {
mass = m;
float x = sx;
float y = sy;
img = simg;
pos = new PVector(x, y);
vel = new PVector(0, 0);
acc = new PVector();
angle = random(TWO_PI);
dir = (random(1) > 0.5) ? 1 : -1;
xOff = 0;
r = getRandomSize();
}
...
}
float getRandomSize() {
float r = pow(random(0, 1), 3);
return constrain(r * 32, 2, 32);
}
力的累加
力的累加通过applyForce函数实现:
void applyForce(PVector force) {
PVector f = force.copy();
f.mult(r);
acc.add(f);
}
举个例子,在屏幕上创建一个雪花对象, 这个雪花受风力和重力的作用,则代码如下所示:
Snowflake.applyForce(wind);
Snowflake.applyForce(gravity);
Snowflake.update();
其中update函数用于更新雪花的位置与偏移信息:
void update() {
xOff = sin(angle * 2) * 2 * r;
vel.add(acc);
vel.limit(r * 0.2);
if (vel.mag() < 1) {
vel.normalize();
}
pos.add(vel);
acc.mult(0);
if (pos.y > height + r) {
randomize();
}
if (pos.x < -r) {
pos.x = width + r;
}
if (pos.x > width + r) {
pos.x = -r;
}
angle += dir * vel.mag() / 200;
}
主程序
完全按照主题逻辑进行书写。
完整代码:
ArrayList<Snowflake> snow;
PVector gravity;
Attractor a;
Liquid liquid;
float zOff = 0;
PImage spritesheet;
ArrayList<PImage> textures;
void setup() {
size(800,600);
spritesheet = loadImage("flakes32.png");
snow = new ArrayList<Snowflake>();
textures = new ArrayList<PImage>();
a = new Attractor(width/2,height/2);
gravity = new PVector(0, 0.3);
for (int x = 0; x < spritesheet.width; x += 32) {
for (int y = 0; y < spritesheet.height; y += 32) {
PImage img = spritesheet.get(x, y, 32, 32);
image(img, x, y);
textures.add(img);
}
}
for (int i = 0; i < 600; i++) {
float x = random(width);
float y = random(height);
float mass = random(4,12);
int designIndex = floor(random(textures.size()));
PImage design = textures.get(designIndex);
snow.add(new Snowflake(mass,x, y/2, design));
}
// Create liquid object
liquid = new Liquid(0, height*3/4, width, height/2, 0.1);
}
void draw() {
background(0);
// Draw water
liquid.display();
a = new Attractor(mouseX,mouseY);
//snow.add(new Snowflake());
zOff += 0.1;
for (int i = 0; i < snow.size(); i++) {
Snowflake flake = snow.get(i);
float xOff = flake.pos.x / width;
float yOff = flake.pos.y / height;
float wAngle = noise(xOff, yOff, zOff) * TWO_PI;
PVector wind = PVector.fromAngle(wAngle);
wind.mult(0.1);
PVector force = a.attract(flake);
if (liquid.contains(flake)) {
// Calculate drag force
PVector dragForce = liquid.drag(flake);
// Apply drag force to Mover
flake.applyForce(dragForce);
gravity = new PVector(0, 0.01);
flake.applyForce(gravity);
}
else{
flake.applyForce(force);
flake.applyForce(wind);
gravity = new PVector(0, 0.3);
flake.applyForce(gravity);
}
flake.update();
flake.render();
}
}