Javascript 将P5.js draw函数放置在自定义循环函数中

Javascript 将P5.js draw函数放置在自定义循环函数中,javascript,p5.js,Javascript,P5.js,P5.js有自己的循环,它调用draw()函数FPS每秒的次数。但是,有一个函数noLoop()可以放在setup()函数中,该函数禁用P5.js自己的循环 因此,我创建了自己的循环(这是我的项目所必需的),如下所示: customLoop = function(){ while(somethingIsTrue){ //custom code here draw(); } } 因此,我希望看到画布更新相当快,因为没有FPS设置。但是,画布仅在完成customLoop后重

P5.js有自己的循环,它调用
draw()
函数
FPS
每秒的次数。但是,有一个函数
noLoop()
可以放在
setup()
函数中,该函数禁用P5.js自己的循环

因此,我创建了自己的循环(这是我的项目所必需的),如下所示:

customLoop = function(){
  while(somethingIsTrue){
    //custom code here
    draw();
  }
}
因此,我希望看到画布更新相当快,因为没有FPS设置。但是,画布仅在完成
customLoop
后重新绘制

简而言之:画布不会在自定义循环仍在运行时更新,它只会在自定义循环完成后显示更新

如何在自己的循环中不断更新画布


人们要求提供更多的上下文,所以我会尽力解释。我已经做了我的工作,这使得为神经网络创建遗传算法变得很容易

我的库中的遗传算法设置如下:

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
  }
});
 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
}
请注意fitnessFunction中的注释。然后我运行一个遗传算法循环,如下所示:

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
  }
});
 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
}
如你所见,我的代码持续运行遗传算法。但整个问题在于适应度函数。这个特定的遗传算法是用来训练神经网络如何玩。所以在适应度函数中,我实际上在运行神经网络,直到蛇死。然而,对于每一条被测试的蛇,我希望用户看到神经网络是如何工作的。因此,我的适应度函数如下所示:

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
  }
});
 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条蛇都在表演。但是,在完成网络后,我看到的只是snake在上次snake评估中的最后位置。

根据文档,您希望避免直接调用“draw()”。尝试以下方法:

customLoop = function(){
    while(somethingIsTrue){
        //custom code here
        redraw();  // Causes the code inside "draw()" to execute once
    }
}
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()
}

特别是:draw()是自动调用的,不应显式调用。 它应该始终由noLoop()、redraw()和loop()控制

此外,由于JavaScript是单线程的,所以只要while循环在滴答滴答地运行,就不能执行其他任何操作。以下是一篇相关文章:

还有一个好的,彻底的解释:

您可以尝试以下方法:

customLoop = function(){
    while(somethingIsTrue){
        //custom code here
        redraw();  // Causes the code inside "draw()" to execute once
    }
}
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()
}
var-timerId=0;
timerId=setTimeout(myCustomRedraw(),10);//每10毫秒。
myCustomRedraw=函数(){
//这里是自定义代码
如果(){
clearTimeout(计时器);//将停止超时循环。
}
重画
}

请尝试将您的代码发布为我们可以实际运行的。下面是一个小示例,它显示了您的问题:

function setup(){
  createCanvas(500, 500);
  for(var i = 0; i < 600; i ++){
    redraw();
  }
}

function draw(){
  ellipse(random(width), random(height), 20, 20);
}
请注意,如果您正在运行的设备限制帧速率,这可能不起作用。但关键是,您不需要创建自己的循环。只需使用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);
}
函数设置(){
createCanvas(500500);
对于(变量i=0;i<600;i++){
设置超时(重绘,0);
}
}
函数绘图(){
椭圆(随机(宽度),随机(高度),20,20);
}
现在,代码不再连续600次调用
redraw()
函数,而是将600个超时事件添加到事件队列中。它们中的每一个都将被单独处理,因此您可以看到间歇帧


我要说的是,这似乎仍然是实现你目标的一种不切实际的方式。如果我是你,我仍然会尝试将
draw()
函数与实际逻辑分开。让您的模拟设置变量,并让
draw()
函数使用这些变量。这是解决这个问题的“正确”方法。使用
setTimeout()
函数暂时可以工作,但我猜这会给您带来问题。

这对我有一点帮助。但在循环完成后它仍然不会更新:“这是因为redraw()不会立即运行draw()(它只设置了一个标志,指示需要更新)。”我认为下面的代码有问题,也许可以检查一下?但奇怪的是,在我的循环过程中,draw()函数实际上被称为-->它正在输出到控制台,但它没有更新画布!你是指我提供的示例代码吗?这是psuedo代码,您需要使用循环条件和自定义代码对其进行更新。复制/粘贴就行了。你能分享一下你的具体实现吗?我很乐意看一看。我的问题有更多的上下文!我已经使用p5很长一段时间了,我试着做一些类似的事情,据我所知,没有办法完全实现你想要的,也许你可以提供更多关于你的项目的背景信息,这样人们就可以帮助你找到替代方案?这看起来像一条死胡同是的。我已经更新了帖子提供的更多上下文。您是否考虑过存储每一条蛇的最后n个位置的历史,这些位置每隔一代就会被覆盖,然后您可以在
draw
中循环这些元素,并在这些位置绘制蛇。这需要一种变通方法,但当然是可能的。我来看看。谢谢,这对我有点帮助。但整个问题是,我正在使用一个javascript库来实现遗传算法,它有自己的适应度评估循环。我想通过调用f中的draw()函数,将这个遗传算法循环与p5js绘图循环“连接”