递归调用混淆了游戏中的渲染

时间:2016-08-26 03:05:07

标签: javascript recursion processing

我有以下代码(忽略sleep事物:

function game_loop(snake){
    sleep(200);

    background(200);
    draw_borders();

    const r = snake_draw(snake_move(snake.x, snake.y));
    game_loop(r);
}

function setup() {
    createCanvas(w, h);
    frameRate(10);
    noLoop();
}

function draw() {
    game_loop({x: snake_initial_x, y: snake_initial_y});
}

function sleep(delay) {
    var start = new Date().getTime();
    while (new Date().getTime() < start + delay);
}

function snake_move(x, y){
    const x_speed = 1;
    const y_speed = 0;

    return {x: x + x_speed, y: y + y_speed };
}

function snake_draw(position){
    const snake_color = 255 ;

    fill(snake_color);
    rect(position.x * sqr_size, position.y * sqr_size, sqr_size, sqr_size);

    return position;
};

const border_color = 81;

function draw_borders(){
    draw_row_borders(0);
    draw_column_borders(1);
}

function draw_row_borders(x){
    if(x >= 0 && x < cols) {
        fill(border_color);
        rect(x * sqr_size, 0, sqr_size, sqr_size);
        fill(border_color);
        rect(x * sqr_size, (rows - 1) * sqr_size, sqr_size, sqr_size);

        draw_row_borders(x + 1, 0);
    }
}

function draw_column_borders(y){
    if(y >= 1 && y < rows - 1) {
        fill(border_color);
        rect(0, y * sqr_size, sqr_size, sqr_size);
        fill(border_color);
        rect((cols - 1) * sqr_size,  y * sqr_size, sqr_size, sqr_size);

        draw_column_borders(y + 1);
    } 
}

不需要代码的最后部分,但我添加它们以防万一。

我的问题是,当我拨打game_loop(r)game_loop(snake_draw(snake_move(snake.x, snake.y)))时,游戏会停止渲染所有内容,但如果我在console.log()内放置game_loop,我就可以看到它#39;工作(即调用函数,更新snake.x, snake.y)。

为什么这个递归调用会搞乱一切?

顺便说一下,我正在使用p5js框架。

3 个答案:

答案 0 :(得分:1)

Javascript和浏览器的绘图机制紧密耦合在一个线程中。如果你的javascript循环时间太长,它会阻止绘图过程并基本上挂起浏览器。

在大多数游戏框架中,有一种update()函数,每个“帧”调用一次。这可以通过多种方式完成,例如使用setInterval()requestAnimationFrame()在特定的毫秒数后调用函数。

function update() {
    console.log('updated');
}

// call update function every 100 milliseconds 
setInterval(update, 100);

如果您想要一种功能稍强的方法,可以递归调用setTimeout()

function update() {
  console.log('updated');
  // perform game logic here
  setTimeout(update, 500); // call update again in 500 milliseconds
}

答案 1 :(得分:1)

获取“功能样式” - 捕获下一次迭代的参数并使用setInterval进行计划:

function game_loop(r) {
     // draw r
     r = nextStep(r); // compute next immutable state

     var next_iteration = function(){ 
        game_loop(r); // capture state 
     } 
     setInterval(next_iteration, 10);
}

答案 2 :(得分:0)

处理会自动为您调用draw()功能,每秒60次。每次调用draw()函数都是框架,这就是你应该放置游戏逻辑的地方。

您似乎正在尝试创建自己的游戏循环,这是不必要的,并将破坏Processing已经为您自动执行的操作。

draw()功能需要在实际绘制到屏幕之前完成。

您阻止draw()功能完成,因此屏幕上不会显示任何内容。在程序开始时,您正在调用game_loop()函数。但是从game_loop()函数中,您调用game_loop()函数,该函数调用game_loop()函数,该函数无限期地继续运行,因此您永远不会超过第一帧!

更一般地说,你正在做的事情被称为recursion,你错过了一个基本情况(阻止递归继续进行的事情)。它实际上不会永远存在,因为你最终会得到一个堆栈溢出 - 错误,而不是这个网站!

要解决您的问题,您需要添加一个基本案例,以防止您的递归永远继续,或者更好,只是摆脱它!处理已经为您每秒调用draw()函数60次,因此请将其用于绘制循环。

编辑:仔细观察一下,也许你正试图通过绘制单个部分然后递归到下一部分来使用递归来绘制你的蛇?如果是这样,那么你只需要一个基本情况,通过递增你正在查看的部分,然后检查该部分是否还在蛇中。

编辑2 :告诉您致电setInterval() 的答案可能有效(尽管如果它导致处理内部机制出现问题我不会感到惊讶) ,但这样做真的没有意义。 Processing已经为您设置了一个游戏循环。只需使用它。