Правильно, как я и подозревал, все ненужные вызовы 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 () вместо создания Существа в каждом кадре я рисую по одному в каждом N-м кадре, используя переменную spawnNthFrame , что позволяет легко увидеть, какое состояние существа создало это медленно. Если вы установите небольшое значение, например 2, в эту переменную, то должно появиться много существ в кадре.
Класс Creature имеет все свойства исходного кода, хранящиеся в массивах.
Вместо того, чтобы делать
pushMatrix();
translate();
ellipse();
rotate()
ellipse()
popMatrix();
Я просто рисую эллипсы в точке x, y.
Небольшой намек на повороты. Я заметил, что они были приращениями
90 градусов. Обработка имеет несколько хороших констант для 90,180,360 градусов
в радианах: 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 линий с этим штрихом и дрожанием, вероятно, будут выглядеть как набор линий, которые трудно сосчитать, поэтому я подумал, зачем ломать волосы? : P
Возможно, существо будет выглядеть примерно одинаково с радиусом около 60, что означает 30 диаметров.
Теперь объясним немного тригонометрических функций, используемых для этого.
Главное - преобразование полярных в декартовые координаты:
Polar будет что-то вроде:
«Я двигаюсь по кругу в направлении, описанном углом (очень похоже на одну ручку часов) и радиусом (расстояние от центра)."
и декартово
«Я двигаюсь по двум осям (по горизонтали / X и по вертикали / Y), вроде улиц Манхэттена, но я обманываю и также перемещаюсь по диагонали сквозь стены".
Если это имеет смысл ... :)
В любом случае, вы конвертируете пару angle и radius в пару x и y по формуле:
x = cos(angle) * radius
y = sin(angle) * radius
Для каждой строки:
angle = hairAngle
radius = hairLength
Так что line () с * x + (hairCos * -hairLength) * выглядит примерно так:
x + (hairCos * -hairLength) =
move to x and from there move by hairLength
to the left(-) for the current angle (hairCos)
Аналогично для y, но с использованием cos, поэтому первая точка линии помещается в противоположную прямую (-hairLength) угла, движущегося от центра (который является x существа), а вторая - «диагональ». Представьте, что вы рисуете «диагонали» (от (-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>