5

所以我已经在Processing工作了几个星期了,虽然我没有编程经验,但我已经转向更复杂的项目。我正在编写一个进化模拟器,它会产生具有随机属性的生物。

最后,我会添加复制,但到目前为止,这些生物只是在屏幕上漂浮,并稍微跟随鼠标。它与线路输入的声音相互作用,但我将这些部分注释掉以便可以在画布上查看它,它不应该真正改变问题,我只是想我会指出它。

到目前为止,帧速率对我来说远不理想,并且随着更多生物的产生而逐渐降低。我是否犯了一些根本性的错误,或者我只是每帧运行了太多功能?

是源代码,您可以在浏览器中使用它:

//import ddf.minim.*;
//import ddf.minim.signals.*;
//import ddf.minim.analysis.*;
//import ddf.minim.effects.*;

//Minim minim;
//AudioInput in;
boolean newCreature = true;
boolean matured[];
int ellipses[];
int hair[];
int maxCreatureNumber = 75;
//int volume;
//int volumeTolerance = 1;
int creatureIndex = -1;
int creatureX[];
int creatureY[];
float strokeWeightAttribute[];
float creatureSize[];
float creatureEndSize[];
float creatureXIncrement[];
float creatureYIncrement[];
float bubbleSize;
float easing = 0.05;
float angle = 0.00;
color colorAttribute[];

void setup() {
    background(0);
    size(1000,500);
    noFill();
    //minim = new Minim(this);
    //in = minim.getLineIn(Minim.STEREO, 512);
    creatureX = new int[maxCreatureNumber];
    creatureY = new int[maxCreatureNumber];
    ellipses = new int[maxCreatureNumber];
    hair = new int[maxCreatureNumber];
    strokeWeightAttribute = new float[maxCreatureNumber];
    creatureEndSize = new float[maxCreatureNumber];
    creatureSize = new float[maxCreatureNumber];
    creatureXIncrement = new float[maxCreatureNumber];
    creatureYIncrement = new float[maxCreatureNumber];
    matured = new boolean[maxCreatureNumber];
    colorAttribute = new color[maxCreatureNumber];
}

void draw() {
  angle += 0.05;
  fill(0, 50);
  rect(-1, -1, 1001, 501);
  // for(int i = 0; i < in.bufferSize() - 1; i++) {
  //     if(in.mix.get(i) * 50 > volumeTolerance) {
  //         volume++;
  //     }
  // }
  if(newCreature && creatureIndex < maxCreatureNumber - 1) {
      initSpontaneousCreature();
  }
  updateCreatures();
  // bubbleSize = volume/250;
  bubbleSize += 0.01;
  // volume = 0;
}

//void stop() {
//    minim.stop();
//    super.stop();
//}

void initSpontaneousCreature() {
    creatureIndex++;
    creatureEndSize[creatureIndex] = int(random(5, 20));
    creatureX[creatureIndex] = int(random(1000));
    if(creatureX[creatureIndex] >= 500) {
        creatureX[creatureIndex] -= creatureEndSize[creatureIndex];
    }
    else {
        creatureX[creatureIndex] += creatureEndSize[creatureIndex];
    }
    creatureY[creatureIndex] = int(random(500));
    if(creatureY[creatureIndex] >= 250) {
        creatureY[creatureIndex] -= creatureEndSize[creatureIndex];
    }
    else {
        creatureY[creatureIndex] += creatureEndSize[creatureIndex];
    }
    ellipses[creatureIndex] = int(random(4));
    hair[creatureIndex] = int(random(4));
    strokeWeightAttribute[creatureIndex] = random(1, 4);
    colorAttribute[creatureIndex] = color(int(random(20,255)), int(random(20,255)), int(random(20,255)));
    matured[creatureIndex] = false;
    newCreature = false;
    while(ellipses[creatureIndex] == 0 && hair[creatureIndex] == 0) {
        ellipses[creatureIndex] = int(random(4));
        hair[creatureIndex] = int(random(4));
    }
}

void updateCreatures() {
    for(int n = 0; n <= creatureIndex; n++) {
        if(matured[n]) {
            creatureX[n] += ((((mouseX) - creatureX[n]) * easing) / (60/*-abs(volume/5))*/)) + random(-5, 6);
            creatureY[n] += ((((mouseY) -creatureY[n]) * easing) / (60/*-abs(/*volume/5))*/)) + random(-5,6);
            drawCreature();
        }
        else {
            if(creatureEndSize[n] != creatureSize[n]) {
                creatureSize[n] += bubbleSize;
                if(creatureSize[n] > creatureEndSize[n]) {
                    creatureSize[n] -= (creatureSize[n] - creatureEndSize[n]);
                }
            }
            else {
                newCreature = true;
                matured[n] = true;
                // bubbleSize = 0;
            }
            drawCreature();
        }
    }
}

void drawCreature() {
    for(int n = 0; n <= creatureIndex; n++) {
        if(matured[n]) {
            stroke(colorAttribute[n]);
            strokeWeight(strokeWeightAttribute[n]);
            for(int i = 0; i <= 4; i++) {
                if(ellipses[n] == i) {
                    if(i == 0) {

                    }
                    else if (i == 1) {
                      pushMatrix();
                      translate(creatureX[n], creatureY[n]);
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(180));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      popMatrix();
                    }
                    else if(i == 2) {
                      pushMatrix();
                      translate(creatureX[n], creatureY[n]);
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(180));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(270));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      popMatrix();

                    }
                    else if(i == 3) {
                      pushMatrix();
                      translate(creatureX[n], creatureY[n]);
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(90));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(180));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      rotate(radians(270));
                      ellipse(creatureSize[n], creatureSize[n], creatureSize[n], creatureSize[n]);
                      popMatrix();
                    }
                }
                if(hair[n] == i) {
                    if(i == 0) {

                    }
                    else if (i == 1) {
                        pushMatrix();
                        translate(creatureX[n], creatureY[n]);
                        for(int j = 0; j <= 360; j+=70) {
                            rotate(j);
                            stroke(colorAttribute[n], random(255));
                            line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
                        }
                        popMatrix();
                    }
                    else if(i == 2) {
                        pushMatrix();
                        translate(creatureX[n], creatureY[n]);
                        for(int j = 0; j <= 360; j+=30) {
                            rotate(j);
                            stroke(colorAttribute[n], random(255));
                            line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
                        }
                        popMatrix();
                    }
                    else if(i == 3) {
                        pushMatrix();
                        translate(creatureX[n], creatureY[n]);
                        for(int j = 0; j <= 360; j+=1) {
                            rotate(j);
                            stroke(colorAttribute[n], random(255));
                            line(0,0, creatureSize[n] + random(10), creatureSize[n] + random(10));
                        }
                        popMatrix();
                    }
                }
            }
        }
        if(!matured[n]) {
            stroke(abs(sin(angle) * 255));
            //strokeWeight(5);
            ellipse(creatureX[n], creatureY[n], creatureSize[n] * 5,  creatureSize[n] * 5);
            noStroke();
        }
    }
}
4

3 回答 3

14

没错,正如我所怀疑的,所有不必要的pushMatrix(),popMatrix()调用和大量的行似乎是罪魁祸首,仍然有很多冗余代码。

我只是以更简洁的方式重构了代码,它似乎运行良好。这是我的“改进”版本:

int maxCreatures = 75;
int numCreatures = 0;
int spawnNthFrame = 50;//spawn a creature every 50 frames
Creature[] creatures;

void setup() {
  background(0);
  size(1000,500);
  noFill();
  creatures = new Creature[maxCreatures];
}

void draw() {
  fill(0, 50);
  rect(-1, -1, 1001, 501);
  if(frameCount % spawnNthFrame == 0){
    println("creatures: " + numCreatures);
    if(numCreatures < maxCreatures) {
      //Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
      creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
      numCreatures++;
    }
  }
  for(int i = 0; i < numCreatures; i++) creatures[i].update();
}

和生物类:

class Creature{
 int x,y,cXInc,cYInc;//if x,y are ints, increments would be into, right?
 float cStrokeWeight,cSize,cEndSize,cSizeInc = 0.01,easing = 0.05,angle = 0.00;
 color cColor;
 int hair,numHair,ellipses;
 boolean matured = false;

 Creature(float endSize,int x, int y,int ellipses,int hair,float strokeW,color c){
    cEndSize = endSize;
    this.x = x;
    if(x >= 500) x -= cEndSize;
    else         x += cEndSize;
    this.y = y;
    if(y >= 250) x -= cEndSize;
    else         x += cEndSize;
    this.ellipses = ellipses;
    this.hair = hair;
    if(hair == 1) numHair = 3;//~5, half that, draw through centre, etc.
    if(hair == 2) numHair = 6;
    if(hair == 3) numHair = 30;//no default value
    cStrokeWeight = strokeW;
    this.cColor = c;
 }
 void update(){
   if(matured) { 
    x += (((mouseX - x) * easing) / 60) + random(-5, 6);
    y += (((mouseY - y) * easing) / 60) + random(-5, 6);
   }else {
    if(cSize < cEndSize) cSize += cSizeInc;
    else matured = true;
    angle += 0.05;
   } 
   this.draw();
 }
 void draw(){
   if(matured){
     stroke(cColor);
     strokeWeight(cStrokeWeight);
     if(ellipses == 1){//2 ellipses diagonally
       ellipse(x,y,cSize,cSize);
       ellipse(x+cSize,y+cSize,cSize,cSize);
     }
     if(ellipses == 2){
       ellipse(x,y,cSize,cSize);
       ellipse(x,y+cSize,cSize,cSize);
       ellipse(x+cSize,y+cSize,cSize,cSize);
     }
     if(ellipses == 3){
       ellipse(x,y,cSize,cSize);
       ellipse(x+cSize,y,cSize,cSize);
       ellipse(x,y+cSize,cSize,cSize);
       ellipse(x+cSize,y+cSize,cSize,cSize);
     }
     float hairAngleInc = TWO_PI/numHair;//angle increment for each piece = 360/number of hair lines
     float hairAngle,hairLength,hairCos,hairSin;
     for(int i = 0; i < numHair; i++){
       hairAngle = hairAngleInc * i;
       hairCos = cos(hairAngle);
       hairSin = sin(hairAngle);
       hairLength = random(20);
       stroke(cColor, random(255));
       line(x + (hairCos * -hairLength),y + (hairSin * -hairLength), x + (hairCos * hairLength),y + (hairSin * hairLength)); 
     }
   }else{
     stroke(abs(sin(angle) * 255));
     ellipse(x,y, cSize * 5,  cSize * 5);
   }
 }
}

好的,现在进行解释。

首先,我将与一个生物相关的所有变量与决定草图如何运行(产生多少生物等)的“全局”变量分开。

这使得主代码长约 25 行,总共略低于 100 行,不到原始代码的一半。

第一部分没有做任何特别的事情。在draw()函数中,我不是每帧都创建一个 Creature,而是使用spawnNthFrame变量每第 N 帧绘制一个,这样可以很容易地看到该生物的哪个状态使其变慢。如果你给这个变量设置一个像 2 这样的小数字,它应该每帧产生很多生物。

Creature 类具有存储在数组中的原始代码的所有属性。

而不是做

pushMatrix();
translate();
ellipse();
rotate()
ellipse()
popMatrix();

我只是在 x,y 处绘制椭圆。关于旋转的一点提示。我注意到它们是 90 度的增量。对于90,180,360度的弧度, Processing有一些很好的常数: HALF_PI、PI、TWO_PI,它们有时会很方便。

现在对于“毛茸茸”的情况,这是我为自己评论的内容:

//if(i == 1) for(int j = 0; j <= 360; j+=70) , well 360/70 is about 5, if (i == 2) , 12 hair
//if = 3-> 360 lines ? do you really need that many lines, that thick ? how about 30 ? 5*12=60, but if you draw the lines through the centre, not from the centre, you can get away with half the lines

所以画线有3个循环,每个循环都有不同的增量。基本上有 360/70 线、360/30 线和 360 线。大约 5,12 和 360 行。关于 5,12 条线,我通过在中心绘制“直径”线而不是从中心绘制“半径”线,将其减半。

这就是我的意思, 半线

另外我认为 360 线具有 strokeWeight 和抖动运动可能看起来像一堆难以计数的线,所以我想,为什么要分裂头发?:P

也许这个生物看起来很相似,大约有 60 个半径,这意味着 30 个直径。

现在解释一些用于此的三角函数。最主要的是“极坐标到笛卡尔”坐标转换:

极地会是这样的:

“我在一个圆圈上向一个由角度(很像时钟的一个手柄)和半径(到中心的距离)描述的方向移动。”

和笛卡尔

“我基于两个(水平/X 和垂直/Y)移动,有点像曼哈顿的街道,但我作弊并且还沿对角线穿过墙壁。”

如果这有任何意义... :) 无论如何,您可以使用以下公式将角度和半径对转换为x 和 y对:

x = cos(angle) * radius
y = sin(angle) * radius

对于每一行:

angle = hairAngle
radius = hairLength

所以带有 *x + (hairCos * -hairLength)* 的line()看起来有点像这样:

x + (hairCos * -hairLength) = 
move to x and from there move by hairLength
to the left(-) for the current angle (hairCos)

与 y 类似,但使用 cos,因此这会将线的第一个点置于从中心(即生物的 x)移动的角度的相反方向(-hairLength)上,第二个点是“对角线”。想象一下绘制“对角线”(从 (-x,-y) 到 (+x,+y)),但您也可以旋转它们。

更新

显然复制/粘贴此代码也适用于 javascript(最好在 Chromium/Chrome 中查看)。你也可以在这里运行它:

var maxCreatures = 75;
var numCreatures = 0;
var spawnNthFrame = 50;//spawn a creature every 50 frames
var creatures = [];

function setup() {
  background(0);
  createCanvas(1000,500);
  noFill();
}

function draw() {
  fill(0, 50);
  rect(-1, -1, 1001, 501);
  if(frameCount % spawnNthFrame === 0){
    println("creatures: " + numCreatures);
    if(numCreatures < maxCreatures) {
      //Creature constructor float endSize,int x, int y,int ellipses,int hair,float strokeW,color c
      creatures[numCreatures] = new Creature(random(5, 20),int(random(1000)),int(random(500)),int(random(4)),int(random(4)),random(1, 4),color(int(random(20,255)), int(random(20,255)), int(random(20,255))));
      numCreatures++;
    }
  }
  for(var i = 0; i < numCreatures; i++) creatures[i].update();
}




function Creature(endSize,x,y,ellipses,hair,strokeW,c){
 this.x = x;
 this.y = y;
 this.ellipses = ellipses;
 this.hair = hair;
 this.numHair = 0;
 this.cStrokeWeight = strokeW;
 this.cColor = c;
 this.cXInc = 0;
 this.cYInc = 0.01;
 this.cSize = 0;
 this.cEndSize = endSize;
 this.easing = 0.05;
 this.angle = 0.0;
 this.matured = false;
 
    if(x >= 500) x -= this.cEndSize;
    else         x += this.cEndSize;
    if(y >= 250) x -= this.cEndSize;
    else         x += this.cEndSize;
    if(hair == 1) this.numHair = 3;//~5, half that, draw through centre, etc.
    if(hair == 2) this.numHair = 6;
    if(hair == 3) this.numHair = 30;//no default value


 this.update = function(){
   if(this.matured) { 
    this.x += (((mouseX - this.x) * this.easing) / 60) + random(-5, 6);
    this.y += (((mouseY - this.y) * this.easing) / 60) + random(-5, 6);
   }else {
    if(this.cSize < this.cEndSize) this.cSize += this.cSizeInc;
    else this.matured = true;
    this.angle += 0.05;
   } 
   this.draw();
 }
 this.draw = function(){
   if(this.matured){
     stroke(this.cColor);
     strokeWeight(this.cStrokeWeight);
     if(this.ellipses == 1){//2 ellipses diagonally
       ellipse(this.x,this.y,this.cSize,this.cSize);
       ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
     }
     if(this.ellipses == 2){
       ellipse(this.x,this.y,this.cSize,this.cSize);
       ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
       ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
     }
     if(this.ellipses == 3){
       ellipse(this.x,this.y,this.cSize,this.cSize);
       ellipse(this.x+this.cSize,this.y,this.cSize,this.cSize);
       ellipse(this.x,this.y+this.cSize,this.cSize,this.cSize);
       ellipse(this.x+this.cSize,this.y+this.cSize,this.cSize,this.cSize);
     }
     var hairAngleInc = TWO_PI/this.numHair;//angle increment for each piece = 360/number of hair lines
     var hairAngle,hairLength,hairCos,hairSin;
     for(var i = 0; i < this.numHair; i++){
       hairAngle = hairAngleInc * i;
       hairCos = cos(hairAngle);
       hairSin = sin(hairAngle);
       hairLength = random(20);
       stroke(this.cColor, random(255));
       line(this.x + (hairCos * -hairLength),this.y + (hairSin * -hairLength), this.x + (hairCos * hairLength),this.y + (hairSin * hairLength)); 
     }
   }else{
     stroke(abs(sin(this.angle) * 255));
     ellipse(this.x,this.y, this.cSize * 5,  this.cSize * 5);
   }
 }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.4.4/p5.min.js"></script>

生物 JS 草图预览

于 2010-12-01T23:31:54.217 回答
7

嗯,有很好的旧随机暂停方法。它是“穷人的剖析器”。

随便拍几张。这将准确地告诉你什么是花费最多的时间。如果你能做得更快,这些就是你应该看看的东西。它将以增加的帧速率显示。

于 2010-12-01T17:26:39.707 回答
7

您可以使用该frameRate(fps)功能。它的作用是指定每秒要显示的帧数。但是,如果处理器速度不够快,无法维持指定的速率,则无法实现。例如,函数调用frameRate(30)将尝试每秒刷新 30 次。建议将帧速率设置在setup().

请记住,draw()在不指定帧速率的情况下使用,默认情况下它将以 60 fps 运行。

于 2010-12-01T15:05:34.453 回答