To practice vertex and acceleration (simulating natural mechanics):
This code includes:
A bottle with dynamic starry sky background, the water in the bottle has dynamic effects.
⭐️Stars in the bottle:
When the mouse is pressed, it floats upwards, the big stars float slowly, and the small stars float slowly
When on the water surface, the stars float up and down, the big stars fluctuate slowly, and the small stars fluctuate quickly
Release the mouse, sink down, the big stars float slowly, and the small stars float slowly
When at the bottom of the bottle, the stars bounce slightly and then stop, the big stars stop at the top, and the small stars stop at the bottom
Going up and floating + (wind effect)
Going down and bouncing
Code
let mover = [];
let bgstars = [];
let liquid;
let c;
let cw;
let bubbles = [];
let brightnessUpdateInterval = 30;
function setup() {
createCanvas(600, 600);
for (let i = 0; i < 30; i++) {
mover[i] = new Mover(random(1, 5), random(230, 360), random(160, 170), 45, 70, 6);
}
liquid = new Water(220, height / 2 - 80, 160, 315, 0.5);
for (let i = 0; i < 100; i++) {
bgstars.push({ x: random(width), y: random(height), brightness: random(150, 255) });
}
}
function draw() {
background(0);
drawStars();
let starfloat = createVector(0, 0);
if (mouseIsPressed) {
starfloat = createVector(0, -1);
}
bottle();
for (let i = 0; i < mover.length; i++) {
if (liquid.contains(mover[i])) {
let dragForce = liquid.calculateDrag(mover[i]);
mover[i].applyForce(dragForce);
}
let m = mover[i].mass;
let gravity = createVector(0, 0.1 * m);
mover[i].applyForce(starfloat);
mover[i].applyForce(gravity);
mover[i].update();
mover[i].show();
mover[i].checkEdges();
liquid.show();
}
//bubble
if (random(1) < 0.1) {
bubbles.push(new Bubble(random(240,360), height - 90));
}
for (let i = bubbles.length - 1; i >= 0; i--) {
let b = bubbles[i];
b.applyForce(createVector(random(-0.1, 0.1), random(-0.1, -0.05)));
if (mouseIsPressed) {
//wind?
let wind = createVector(random(0.05, 0.15), 0);
b.applyForce(wind);
}
b.move();
b.checkOutOfCanvas();
b.display();
//splice bubble
if (b.isDone) {
bubbles.splice(i, 1);
}
}
}
//bg
function drawStars() {
for (let i = 0; i < bgstars.length; i++) {
let star = bgstars[i];
fill(255, star.brightness);
noStroke();
ellipse(star.x, star.y, 2, 2);
if (frameCount % brightnessUpdateInterval === 0) {
star.brightness = random(100, 255);
}
}
}
function bottle() {
push();
drawingContext.shadowOffsetX = 0;
drawingContext.shadowOffsetY = 0;
drawingContext.shadowBlur = 10;
drawingContext.shadowColor = "#f4f1de";
fill(220, 220);
noStroke();
push();
rectMode(CENTER);
rect(width / 2, 80, 40, 30, 20);
rect(width / 2, 100, 130, 20, 10);
rect(width / 2, 130, 100, 50, 10);
rect(width / 2, height / 2 + 50, 180, 400, 50);
rect(width / 2, height / 2 + 150, 180, 200, 20);
pop();
pop();
}
class Mover {
constructor(m, x, y, radius1, radius2, npoints) {
this.radius1 = radius1;
this.radius2 = radius2;
this.npoints = npoints;
this.angle = (5 / 2) * PI / this.npoints;
this.halfAngle = this.angle / 2.0;
this.mass = m;
this.pos = createVector(x, y);
this.vel = createVector(0, 0);
this.acc = createVector(0.0);
this.d = this.mass * 10;
this.yama = this.radius1 * this.mass / 25;
this.tani = this.radius2 * this.mass / 15;
}
applyForce(force) {
let f = p5.Vector.div(force, this.mass);
this.acc.add(f);
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.acc.mult(0);
}
show() {
stroke(255);
c = color("#f4f1de");
fill(c);
push();
drawingContext.shadowOffsetX = 0;
drawingContext.shadowOffsetY = 0;
drawingContext.shadowBlur = 30;
drawingContext.shadowColor = "#f4f1de";
beginShape();
for (let a = 0; a < TWO_PI; a += this.angle) {
let sx = this.pos.x + cos(a) * this.yama;
let sy = this.pos.y + sin(a) * this.yama;
vertex(sx, sy);
sx = this.pos.x + cos(a + this.halfAngle) * this.tani;
sy = this.pos.y + sin(a + this.halfAngle) * this.tani;
vertex(sx, sy);
}
endShape(CLOSE);
pop();
}
checkEdges() {
if (this.pos.y > 540 - this.d) {
this.vel.y *= -1;
this.pos.y = 540 - this.d;
} else if (this.pos.x > 380) {
this.pos.x = 380;
this.vel.x *= -1;
} else if (this.pos.x < 240) {
this.vel.x *= -1;
this.pos.x = 240;
} else if (this.pos.y < height / 2 - 100) {
this.vel.y *= -1;
this.pos.y = height / 2 - 100;
}
}
}
class Water {
constructor(x, y, w, h, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.c = c;
}
contains(mover) {
let l = mover.pos;
return l.x > this.x && l.x < this.x + this.w &&
l.y > this.y && l.y < this.y + this.h;
}
calculateDrag(mover) {
let speed = mover.vel.mag();
let dragMagnitude = this.c * speed * speed;
let dragForce = mover.vel.copy();
dragForce.mult(-1);
dragForce.normalize();
dragForce.mult(dragMagnitude);
return dragForce;
}
show() {
noStroke()
fill(60, 100, 200, 10);
//wave
beginShape();
for (let i = 0; i <= this.w; i += 5) {
let x = this.x + i;
let y = this.y + sin((i + frameCount * 0.5) * 0.1) * 5;
vertex(x, y);
}
vertex(this.x + this.w, this.y + this.h); // 右下角
vertex(this.x, this.y + this.h); // 左下角
endShape(CLOSE);
}
}
class Bubble {
constructor(x, y) {
this.pos = createVector(x, y);
this.vel = createVector();
this.acc = createVector();
this.rad = random(4) ** 2;
this.r = 200;
this.g = 200;
this.b = random(200,250);
this.isDone = false;
}
move() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.acc.mult(0); //!!!!!!!!!!!!
}
applyForce(f) {
this.acc.add(f);
}
checkOutOfCanvas() {
if (this.pos.y < 220||this.pos.x > 360||this.pos.x < 240) {
this.isDone = true; // beyond water
}
}
display() {
push();
translate(this.pos.x, this.pos.y);
fill(this.r, this.g, this.b, 100);
circle(0, 0, this.rad * 2);
pop();
}
}