P5.js有自己的循环,每秒调用draw()
函数FPS
次数。但是,有一个函数noLoop()
可以放在setup()
函数中,它会禁用P5.js自己的循环。
所以我做了自己的循环(这对我的项目来说是必要的),如下所示:
customLoop = function(){
while(somethingIsTrue){
//custom code here
draw();
}
}
所以我希望看到画布更新非常快,因为没有FPS设置。但是,仅在customLoop
完成后才会重新绘制画布。
简而言之:自定义循环仍在运行时画布不会更新,它只会在自定义循环完成后显示更新。
如何在我自己的循环中连续更新画布?
人们要求更多背景,所以我会尽力解释。我已经制作了own JS library,可以很容易地为神经网络创建遗传算法。
我的库中的遗传算法设置如下:
var evol = new Evolution({
size: 50,
mutationRate: 0.05,
networkSize: [4,10,1],
mutationMethod: [Mutate.MODIFY_RANDOM_BIAS, Mutate.MODIFY_RANDOM_WEIGHT],
crossOverMethod: [Crossover.UNIFORM, Crossover.AVERAGE],
selectionMethod: [Selection.FITNESS_PROPORTIONATE],
elitism: 5,
fitnessFunction: function(network){
//something here that computes for every network, AT THE SAME TIME showing it LIVE for the user to watch
}
});
注意fitnessFunction中的评论。然后我按如下方式运行遗传算法循环:
var notFinished = true;
while(notFinished){
evol.evaluate();
if(evol.getAverage() > 2000){
notFinished = false;
console.log('done');
}
evol.select();
evol.crossOver();
evol.mutate();
evol.replace();
};
如您所见,我的代码不断运行遗传算法。但整个问题在于健身功能。这种特定的遗传算法用于训练神经网络如何玩蛇。所以在健身功能中,我实际上运行神经网络,直到蛇死亡。但是,对于每个经过测试的蛇,我都希望用户看神经网络的运作方式。所以我的健身功能看起来如下:
fitnessFunction = function(network){
while(snakeIsNotDead){
computeNeuralNetworkOutput();
giveSnakeNewMovementDirectionBasedOnOutput();
// most importantly
redrawTheSnakeWithNewPosition(); // which is redraw() with P5.js
}
因此,对于经过测试的每一代人,我都想看到所有50只蛇的表现。然而,我在网络完成后看到的只是蛇在最后一次蛇评估中的最后位置。
答案 0 :(得分:2)
请尝试将您的代码发布为我们实际可以运行的MCVE。这是一个显示问题的小例子:
function setup(){
createCanvas(500, 500);
for(var i = 0; i < 600; i ++){
redraw();
}
}
function draw(){
ellipse(random(width), random(height), 20, 20);
}
这将显示一个画布,上面已经绘制了一堆随机圆圈。我知道你想要的是看到圈子累积,就像你快速转发&#34;草图。
使用你自己的循环有点可疑。 Processing已经有了自己的循环机制,它包含用于双缓冲画布的逻辑。这就是为什么在绘图完成之前你没有看到任何东西的原因。您可以依靠Processing的内置计时机制。这是一个例子:
function setup(){
createCanvas(500, 500);
frameRate(120);
}
function draw(){
if(frameCount == 600){
frameRate(60);
}
ellipse(random(width), random(height), 20, 20);
}
请注意,如果您运行的设备限制了帧速率,则可能无法使用此功能。但重点是你不应该创建自己的循环。只需使用Processing的内置循环逻辑。
编辑:就像我说的那样,你没有看到任何帧的原因是因为Processing是双缓冲的。这意味着Processing将等到它通过所有事件并将所有内容绘制到屏幕上。这是一个过于简单化的过程,但您可以将上述循环视为向事件队列添加600个重绘事件。处理通过它们,但在完成之前实际上并未显示结果。
您可以通过在清空此事件队列后调用每次重绘来解决此问题。一种稍微犹豫不决的方法是使用setTimeout()
函数:
function setup(){
createCanvas(500, 500);
for(var i = 0; i < 600; i ++){
setTimeout(redraw, 0);
}
}
function draw(){
ellipse(random(width), random(height), 20, 20);
}
现在,代码连续600次调用redraw()
函数,代码会向事件队列添加600个超时事件。它们中的每一个都将被单独处理,因此您可以看到间歇性帧。
我会说这仍然是实现目标的一种黑客方式。如果我是你,我仍然会尝试将draw()
函数与实际逻辑分开。让您的模拟设置变量,并让draw()
函数使用这些变量。这是&#34;正确的&#34;解决这个问题的方法。使用setTimeout()
功能现在可以使用,但我的猜测是,它会给你带来问题。
答案 1 :(得分:1)
根据文档,您希望避免调用&#34; draw()&#34;直。尝试类似:
customLoop = function(){
while(somethingIsTrue){
//custom code here
redraw(); // Causes the code inside "draw()" to execute once
}
}
https://p5js.org/reference/#/p5/draw
具体来说: draw()是自动调用的,绝不应该显式调用。 应始终使用noLoop(),redraw()和loop()来控制它。
此外,由于JavaScript是单线程的,只要while循环正在消失,其他任何东西都无法执行。这是一篇相关的文章:
Asynchronous function in a while-loop
一个很好的,彻底的解释:
http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/
您可以尝试以下方式:
var timerId = 0;
timerId = setTimeout(myCustomRedraw(), 10); // Every 10 milliseconds.
myCustomRedraw = function () {
// custom code here
if (<stop loop condition>) {
clearTimeout(timer); // Will stop the timeout loop.
}
redraw()
}