我在将资产和对象放入草图中时遇到了一些困难。目前我遇到的两个问题是植物需要展示在飞机前面,还有一个小问题是我如何清理画布。
对于第一期,我尝试重新排列代码的顺序,但由于某种原因,植物总是在飞机后面,而它应该在飞机前面。
我尝试为第二个问题实施 createGraphics,以便在绘制植物的一次迭代后保留类创建的绘图。但是,我不确定如何实现背景或如何清除它以便始终显示植物。我不想实现 noLoop(),因为植物最终会生长。
任何帮助将不胜感激,谢谢!
let gui;
let ui;
let plantGraphics;
const len = 4;
const ang = 25;
let drawRules;
let word = "X";
let rules = {
X: [
// Original rule
{ rule: "F[+X][-X]FX", prob: 0.5 },
// Fewer limbs
{ rule: "F[-X]FX", prob: 0.05 },
{ rule: "F[+X]FX", prob: 0.05 },
// Extra rotation
{ rule: "F[++X][-X]FX", prob: 0.1 },
{ rule: "F[+X][--X]FX", prob: 0.1 },
// Berries/fruits
{ rule: "F[+X][-X]FA", prob: 0.1 },
{ rule: "F[+X][-X]FB", prob: 0.1 },
],
F: [
// Original rule
{ rule: "FF", prob: 0.85 },
// Extra growth
{ rule: "FFF", prob: 0.05 },
// Stunted growth
{ rule: "F", prob: 0.1 },
],
};
const planeWidth = 200;
const planeHeight = 200;
let platformRotation = 0;
let sapling;
let saplingPlanted = false;
function setup() {
createCanvas(400, 400, WEBGL);
background(220);
ui = {
// regrow: function() {
// console.log('button clicked')
// growPlant();
// },
// Camera Settings
X: 0,
Y: -150,
Z: -400,
centerX: 0,
centerY: 0,
centerZ: 0,
upX: 0,
upY: 1,
upZ: 0,
};
gui = new dat.GUI();
// gui.add(ui, 'regrow').name('Click Me!');
let cameraFolder = gui.addFolder('Camera Settings');
cameraFolder.add(ui, 'X', -2000, 400);
cameraFolder.add(ui, 'Y', -2000, 400);
cameraFolder.add(ui, 'Z', -2000, 400);
cameraFolder.add(ui, 'centerX', -2000, 400);
cameraFolder.add(ui, 'centerY', -2000, 400);
cameraFolder.add(ui, 'centerZ', -2000, 400);
cameraFolder.add(ui, 'upX', -1, 1);
cameraFolder.add(ui, 'upY', -1, 1);
cameraFolder.add(ui, 'upZ', -1, 1);
drawRules = {
"A": () => {
// Draw circle at current location
// noStroke();
// fill("#E5CEDC");
stroke("#E5CEDC");
strokeWeight(5);
// circle(0, 0, len*2);
point(0, 0, len*2)
},
"B": () => {
// Draw circle at current location
// noStroke();
// fill("#FCA17D");
// circle(0, 0, len*2);
stroke("#FCA17D");
strokeWeight(2);
point(0, 0, len*2)
},
"F": () => {
// Draw line forward, then move to end of line
// const nextY = currentY - len;
// if (nextY > -maxY) {
stroke("#9ea93f");
line(0, 0, 0, -len);
// beginShape();
// vertex(0, 0, 0);
// vertex(0, 0, -len);
// endShape();
translate(0, -len);
// }
},
"+": () => {
// Rotate right
rotate(PI/180 * -ang);
},
"-": () => {
// Rotate right
rotate(PI/180 * ang);
},
// Save current location
"[": push,
// Restore last location
"]": pop,
};
plantGraphics = createGraphics(width, height, WEBGL);
plantGraphics.sapling = new Plant(0, 0, 0);
}
function draw() {
if (
mouseIsPressed &&
mouseX >= 0 &&
mouseX <= width &&
mouseY >= 0 &&
mouseY <= height) {
platformRotation += mouseX - pmouseX;
}
rotateX(PI / 2);
rotateZ(radians(platformRotation));
push();
noStroke();
fill(255)
plane(planeWidth, planeHeight);
pop();
// background(211, 217, 219);
// sapling.plantSapling();
plantGraphics.sapling.grow();
image(plantGraphics, -width/2, -height/2);
// sapling.translate(-width/2, -height/2, 0); // center graphics buffer
// image(sapling.pg, 0, 0);
// background(211, 217, 219);
let cameraZ = ui.Z;
let cameraX = ui.X;
let cameraY = ui.Y;
let centerX = ui.centerX;
let centerY = ui.centerY;
let centerZ = ui.centerZ;
let upX = ui.upX;
let upY = ui.upY;
let upZ = ui.upZ;
camera(cameraX, cameraY, cameraZ, centerX, centerY, centerZ, upX, upY, upZ);
}
class Plant {
constructor(x, y, z) {
this.pos = createVector(x, y, z);
this.vel = createVector();
this.acc = createVector();
this.grown = false;
this.len = 3;
this.angle = 25;
this.numGens = 6;
this.word = "X";
// this.next = ""
this.next;
this.drawRules = {
A: () => {
// Draw circle at current location
// noStroke();
// fill("#E5CEDC");
// circle(0, 0, this.len * 2);
stroke("#E5CEDC");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
B: () => {
// Draw circle at current location
// noStroke();
// fill("#FCA17D");
// circle(0, 0, this.len * 2);
stroke("#FCA17D");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
F: () => {
// Draw line forward, then move to end of line
stroke("#9ea93f");
// line(0, 0, 0, -this.len);
// translate(0, -this.len);
line(this.pos.x, this.pos.y, this.pos.z, this.pos.x, this.pos.y, -this.len);
translate(this.pos.x, this.pos.y, this.len);
},
"+": () => {
// Rotate right
rotate((PI / 180) * -this.angle);
},
"-": () => {
// Rotate right
rotate((PI / 180) * this.angle);
},
// Save current location
"[": push,
// Restore last location
"]": pop,
};
}
grow() {
if (this.grown) return;
let next = "";
let word = "X";
for (let i = 0; i < this.numGens; i++) {
word = this.generateWord(word);
}
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (c in this.drawRules) {
this.drawRules[c]();
}
}
this.grown = true;
}
generateWord(word) {
let next = "";
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (c in rules) {
let rule = rules[c];
if (Array.isArray(rule)) {
next += this.chooseOne(rule);
} else {
next += rules[c];
}
} else {
next += c;
}
}
return next;
}
chooseOne(ruleSet) {
let n = random(); // Random number between 0-1
let t = 0;
for(let i = 0; i < ruleSet.length; i++) {
t += ruleSet[i].prob; // Keep adding the probability of the options to total
if(t > n) { // If the total is more than the random value
return ruleSet[i].rule; // Choose that option
}
}
return "";
}
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/addons/p5.sound.min.js"></script>
<!-- import dat.gui -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<main>
</main>
<script src="sketch.js"></script>
</body>
</html>
您的代码有几个问题:
你正试图将你的树苗绘制到一个单独的缓冲区,但你所有的绘图指令都使用全局绘图函数。将
Plant
类的实例分配为 Graphics
实例的属性没有任何效果。如果你真的想把你的树苗画到 Graphics
你需要在从 Graphics
.返回的
createGraphics
对象上使用实例方法
第 1 点已经完成,我不认为将你的树苗绘制到单独的缓冲区是一个好的解决方案。您必须尝试这样做的原因是因为绘制树苗的逻辑和迭代 L 系统规则的逻辑在
grow()
函数中混合在一起,该函数只能调用一次。如果将绘图代码移动到单独的函数中,则可以调用一次grow()
,但每帧为Plant
调用一次绘图函数。
rotate()
功能在绘制规则函数中,您在 3d 中绘制线条,唯一的变化是沿 Z 轴,然后使用
rotate()
函数进行旋转指令。这是有问题的,因为rotate()
函数旋转aroundZ轴,所以这对随后绘制的线没有影响。
let rules = {
X: [
// Original rule
{ rule: "F[+X][-X]FX", prob: 0.5 },
// Fewer limbs
{ rule: "F[-X]FX", prob: 0.05 },
{ rule: "F[+X]FX", prob: 0.05 },
// Extra rotation
{ rule: "F[++X][-X]FX", prob: 0.1 },
{ rule: "F[+X][--X]FX", prob: 0.1 },
// Berries/fruits
{ rule: "F[+X][-X]FA", prob: 0.1 },
{ rule: "F[+X][-X]FB", prob: 0.1 },
],
F: [
// Original rule
{ rule: "FF", prob: 0.85 },
// Extra growth
{ rule: "FFF", prob: 0.05 },
// Stunted growth
{ rule: "F", prob: 0.1 },
],
};
const planeWidth = 200;
const planeHeight = 200;
let sapling;
function setup() {
createCanvas(400, 400, WEBGL);
sapling = new Plant(0, 0, 0);
sapling.grow();
console.log(sapling.word);
}
function draw() {
background(150);
orbitControl();
rotateX(PI / 2);
noStroke();
fill(255);
plane(planeWidth, planeHeight);
sapling.draw();
}
class Plant {
constructor(x, y, z) {
this.pos = createVector(x, y, z);
this.vel = createVector();
this.acc = createVector();
this.grown = false;
this.len = 3;
this.angle = 25;
this.numGens = 6;
this.word = "X";
this.drawRules = {
A: () => {
// Draw circle at current location
// noStroke();
// fill("#E5CEDC");
// circle(0, 0, this.len * 2);
stroke("#E5CEDC");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
B: () => {
// Draw circle at current location
// noStroke();
// fill("#FCA17D");
// circle(0, 0, this.len * 2);
stroke("#FCA17D");
strokeWeight(this.len * 2);
point(this.pos.x, this.pos.y, this.pos.z);
},
F: () => {
// Draw line forward, then move to end of line
stroke("#9ea93f");
// line(0, 0, 0, -this.len);
// translate(0, -this.len);
line(
this.pos.x,
this.pos.y,
this.pos.z,
this.pos.x,
this.pos.y,
-this.len
);
translate(this.pos.x, this.pos.y, this.len);
},
"+": () => {
// Rotate right
rotateY((PI / 180) * -this.angle);
},
"-": () => {
// Rotate right
rotateY((PI / 180) * this.angle);
},
// Save current location
"[": push,
// Restore last location
"]": pop,
};
}
grow() {
if (this.grown) return;
let next = "";
for (let i = 0; i < this.numGens; i++) {
this.word = this.generateWord(this.word);
}
this.grown = true;
}
draw() {
for (let i = 0; i < this.word.length; i++) {
let c = this.word[i];
if (c in this.drawRules) {
this.drawRules[c]();
}
}
}
generateWord(word) {
let next = "";
for (let i = 0; i < word.length; i++) {
let c = word[i];
if (c in rules) {
let rule = rules[c];
if (Array.isArray(rule)) {
next += this.chooseOne(rule);
} else {
next += rules[c];
}
} else {
next += c;
}
}
return next;
}
chooseOne(ruleSet) {
let n = random(); // Random number between 0-1
let t = 0;
for (let i = 0; i < ruleSet.length; i++) {
t += ruleSet[i].prob; // Keep adding the probability of the options to total
if (t > n) {
// If the total is more than the random value
return ruleSet[i].rule; // Choose that option
}
}
return "";
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.6.0/p5.js"></script>