To practice sin, cos, noise:
First Version:Basic mountain shape
let offset = frameCount * 0.02;
The mountain range changes slowly over time
let noiseFactor = noise(x * 0.005, frameCount * 0.01);
⭐️ noise(x, time) is actually 2D noise, frameCount / 50 introducing the time dimension, a dynamic effect is achieved. Generates smooth random values to simulate the ups and downs of real terrain.
let y = height / 2 + sin(x * 0.02 + offset) * 50 + noiseFactor * 100;
sin(x * 0.02 + offset) * 50 Generates periodic fluctuations
noiseFactor * 100 Increases irregularity to make the mountain more natural
Second, third Version: Improved mountains + added plants (using class)
class Mountain {
constructor() {
this.yPositions = [];
}
⭐️ this.yPositions workflow:
Record the shape of the mountain:
In the display method of the Mountain class, loop through each x value and calculate the corresponding y value (the height of the mountain).
⭐️ Store { x, y } in the this.yPositions array.
this.yPositions.push({ x, y });
Draw plants based on the shape of the mountain:
In the draw function, pass this.yPositions to the plant's display.
plant.display(mountain.yPositions);
mainGraphics.curveVertex(x, y);
curveVertex(x, y) Draws a smooth curve connecting the points of the mountain
⭐️ One of the light green is all over the mountain, but more green is
if (y > height * 0.45) {
Because more plants should appear in the valley, y must be below a certain height.
y is the vertical position of the current point (the height of the mountain).
height * 0.45 is 45% of the canvas height
Color gradient: sin()
mainGraphics.fill(
50 + sin(x) * 20, // R: Slight change
150 + sin(x) * 100, // G: Major change
50 + sin(x) * 20, // B: Slight change
180 // Transparency
);
Final Version:
class Plants {
display(yPositions) {
for (let i = 0; i < yPositions.length; i += 2) {
let { x, y } = yPositions[i];
Traversing yPositions, the plant will automatically grow at the foot of the mountain.
if (y > height * 0.45) {
let plantY = y + noise(x / 100, frameCount / 50) * 15;
}
Let the plant height change with noise(x, time) to simulate swaying in the wind.
Code
let mainGraphics;
let mountain;
let plant;
function setup() {
createCanvas(1000, 600);
mainGraphics = createGraphics(width, height);
mainGraphics.background(0);
mountain = new Mountain();
plant = new Plants();
}
function draw() {
mainGraphics.image(mainGraphics, 0, 2);
mountain.display();
plant.display(mountain.yPositions); // !!
image(mainGraphics, 0, 0);
}
class Mountain {
constructor() {
this.yPositions = [];
}
display() {
this.yPositions = [];
mainGraphics.noFill();
mainGraphics.stroke(0, 150);
mainGraphics.strokeWeight(2);
mainGraphics.beginShape();
let mouseRatio = noise(frameCount / 50, mouseX / 50) * 1.5;
for (let x = 0; x < width; x += 5) {
let y =
mouseRatio * sin(x / 80 + frameCount / 50) * 50 +
mouseRatio * sin(x / 20 + frameCount / 50) * 20 +
mouseRatio * noise(x / 100, frameCount / 50) *
noise(x / 500, frameCount / 50) *
(map(sin(x / (10 + noise(x / 2000, frameCount / 500) * 40)), -1, 1, 0, 1)) *
height / 5 + height / 3 +
noise(x / 50, frameCount / 50) * 100;
this.yPositions.push({ x, y }); // !!!
mainGraphics.curveVertex(x, y);
// plants1
if (y > height * 0.45) {
mainGraphics.push();
mainGraphics.noStroke();
mainGraphics.fill(
50 + sin(x) * 20,
150 + sin(x) * 100,
50 + sin(x) * 20,
180
);
mainGraphics.ellipse(x + random(-1, 1), y + random(-1, 1), 5);
mainGraphics.pop();
}
}
mainGraphics.stroke(255, map(sin(frameCount / (20 + (1 - mouseRatio) * 500)), -1, 1, 50, 255));
mainGraphics.endShape();
}
}
class Plants {
constructor() {}
display(yPositions) {
if (!yPositions || yPositions.length === 0) return;
mainGraphics.strokeWeight(1);
mainGraphics.noFill();
for (let i = 0; i < yPositions.length - 1; i += 2) {
let { x, y } = yPositions[i];
// plants2
let plantY = y + noise(x / 100, frameCount / 50) * 15 + sin(frameCount / 20 + x / 50) * 5;
mainGraphics.push();
mainGraphics.noStroke();
mainGraphics.fill(50, 255, 190, 90);
mainGraphics.ellipse(x + random(-2, 2), plantY + random(-2, 2), 4);
mainGraphics.pop();
}
}
}