【问题标题】:Is there a way to simplify these javascript functions more? [closed]有没有办法进一步简化这些 javascript 函数? [关闭]
【发布时间】:2019-03-09 07:52:23
【问题描述】:

我在画布上创建了很多小点,在创建过程中,它会获取它们的 x、y 坐标和它们的半径,以便稍后用于我制作的碰撞函数。即使我摆脱了它的间隔,这也会导致页面变得无响应。我认为在无响应之前要处理的功能太长了。任何帮助将非常感激。这是与我的问题相关的代码。

var foodX=[]; // array for the x coordinate of the points
var foodY=[]; // array for the y coordinate of the points
var foodR=[]; // array for the radius of the points
var points=[]; //array to store the variable used to create the points for later deletion
function drawFood() { //draws all the points on my canvas
    for (var food=0; food<10000; food++) { //creates 10000 points
        var foodPosX=randInt(0,10000); //create a random x coordinate between 0 and 10000 on the canvas
        var foodPosY=randInt(0,10000); //create a random y coordinate between 0 and 10000 on the canvas
        var r=randInt(3,5) //create a random radius create a random radius between 3 and 5
        ctx.beginPath();
        var point=ctx.arc(foodPosX, foodPosY, r, 0, 2*Math.PI); //this variable draws each point onto the canvas
        ctx.fillStyle= colors[randInt(0,7)]; // uses an array I have with different colors so I can draw different colored points randomly
        ctx.fill();
        ctx.closePath();
        foodX[food]=foodPosX; //stores my x coordinate in an array for the point currently being created 
        foodY[food]=foodPosY; //stores my y coordinate in an array for the point currently being created
        foodR[food]=r; //stores the radius of the point being created
        points[food]=point; //stores the variable creating the point so it can be deleted when the players collides with it
    }
}
function checkCollision() {
    for (var i=foodX.length-1; i<foodX.length; i--) { //loop through the array backwards to check for collisions
        var fXD=Math.abs(player.pX-foodX[i]); //calculates the distance between the players x coordinate and the points x coordinate
        var fYD=Math.abs(player.pY-foodY[i]); //calculates the distance between the players y coordinate and the points y coordinate
        var rSum=circR+foodR[i]; //adds the radius's together for the player's radius and the foods radius
     if (fXD<=rSum && fYD<=rSum) { //checks if the player is currently touching the point being checked
           foodX.splice(i,1); //deletes the points x coordinate from array
           foodY.splice(i, 1); //deletes the points y coordinate from array
           foodR.splice(i, 1); //deletes the points radius from array
           points.splice(i, 1); //deletes the point the was just collided with
          
            eatFood(); //function for when the player eats the point
        }
    }
}
function randInt(min, max) { //This function creates a random integer between the selected numbers
    min=Math.ceil(min);
    max=Math.floor(max);
    return Math.floor(Math.random()*(max-min))+min;
}

我尽量保持简短,如果它太多或太少,我很抱歉。我正在尝试创建一个类似于 agar.io 的游戏,但它只是一个离线单人游戏版本。

【问题讨论】:

  • 导致页面无响应
  • 我不完全确定它是否有效。该网站是否会更好地发布此内容,以便更简化功能以使页面不会无响应
  • 我一定错过了关于代码无法完全运行的部分。我删除了我的评论。
  • 您是否尝试过使用较小的循环并以一定的间隔运行它以防止页面无响应?
  • 我投票结束这个问题,因为它属于codereview.stackexchange.com

标签: javascript arrays canvas html5-canvas simplify


【解决方案1】:

我邀请您考虑更多地了解函数式编程范式。 几年前我是一名游戏开发人员,我发现在 FP 中思考极大地清理了我的代码并帮助我更好地概念化游戏对象。

以下是您的代码问题的部分解决方案,以 FP 风格编写;我试图表达的主要思想是

  1. 将您的 food 对象视为属性的集合,并且可以表示为单个 JS 对象。
  2. 行为(即在屏幕上绘图)成为一个单独的函数,作用于单个对象
  3. 分离行为列表迭代(使用地图功能)

代码如下:

// Assuming the ff:
// 1. an object 'ctx' exists that knows how to draw stuff
// 2. an array 'colors' exists and contains colors you have

const ctx = document.createElement('canvas').getContext('2d')

const colors = ['blue','red','yellow','black','silver','gray','navy','aqua']

// function "newRandomFood" returns a food object whose properties are randomized
function newRandomFood() {
  return {
    x: randInt(0,10000),
    y: randInt(0,10000),
    r: randInt(3,5),
    color: colors[randInt(0,7)] // uses an array I have with different colors so I can draw different colored points randomly
  }
}

// function "drawFood" draws given food object to canvas as a path
function drawFood(food) {
  ctx.beginPath();
  ctx.arc(food.x, food.y, food.r, 0, 2*Math.PI); //this variable draws each point onto the canvas. Method doesn't return anything
  ctx.fillStyle = food.color;
  ctx.fill();
  ctx.closePath();
}

// function "randInt" creates a random integer between the selected numbers
function randInt(min, max) { 
    min=Math.ceil(min);
    max=Math.floor(max);
    return Math.floor(Math.random()*(max-min))+min;
}

/* generate your foodstuffs */

const foods = Array(1000)     // create an array with 1000 elements
  .fill('')                   // fill each element with anything so iteration won't skip
  .map(_ => newRandomFood())  // fill each element with a random food item

console.log(foods)           // display all the food objects you have

foods.map(food =>             // for every food item...
     drawFood(food))          // ...draw that food

希望这会有所帮助。 干杯,

【讨论】:

  • 这点很好,但不会提高性能。而ctxdocument.createElement('canvas').getContext('2d');
  • 嗨@Kaiido,它帮助我优化了开发时间,因为 (1) 每个函数都有一个集中的范围,因此可以更快地进行概念化,(2) 由于代码更细化,调试速度更快,(3) 单元测试- 能力,(4)多人协作更容易,因为依赖较少。 FP 是表达 SOLID 原则的好方法。
  • 是的,我同意,尽管我认为这一切也适用于 OOP,但这与问题无关。不管这段代码是怎么写的,它总是会遇到 Original Poster 面临的性能问题,以及当前的问题。
  • @Kaiido 嗯,既然你提到了它,我明白你的意思了。
  • 这是一个很好的例子,如果你问错了问题,你会得到错误的答案 :) 但是,如果出现性能问题,那么很高兴看到能重现问题的沙盒,因为解决性能问题的第一步,了解原因
【解决方案2】:

绘制这么多点时,您需要巧妙地管理它们。

绘制 10000 条弧线并填充 10000 次是消耗性的。相反,尝试调用实际光栅化最小可能的上下文方法,例如通过将所有相同颜色的弧合并到单个子路径中。 就性能而言,最好的甚至是按颜色对这些点进行排序,但通常看起来很奇怪。

对于碰撞检测,您当前每次都检查每个点。相反,将您的点打包在一个网格中,每个 n 个单元格包含 n 个。然后在您的 checkCollision 中,仅检查该单元格中的点(最好,您还要检查相邻的点)。 这样,您将避免在每次检查时检查场景中的所有点。

这个网格的一个好处是,您还可以检查某些点是否真的被其他点隐藏,因此可以被绘图功能丢弃。

这是一个非常粗略的起点,绘图将在一个子路径中打包共享相同颜色的连续弧,并将它们打包在碰撞函数使用的网格中。

var colors = generateColors(7);
canvas.width = canvas.height = 2000;
var ctx = canvas.getContext('2d');
var grid = generateGrid(100, 100);
var points = generatePoints(10000);
var dirty = true; // a flag to know when we need to redraw

points.forEach(putInGrid);
// ToDo: mark hidden points
canvas.addEventListener('mousemove', onmousemove);

anim();

function generateColors(nb) {
  var list = [];
  for(var i = 0; i<nb; i++) {
    list.push(randColor());
  }
  return list;
}
function randColor() {
  return '#'+(Math.random()*0xFFFFFF|0).toString(16);
}
  
function generateGrid(width, height) {
  var grid = [];
  for(var i = 0; i<width*height; i++) {
    grid.push([]);
  }
  grid.width = width;
  grid.height = height;
  return grid;
}

function generatePoints(nb) {
  var list = [];
  for(var i=0; i<nb; i++) {
    list.push(new Point());
  }
  return list;
}

function Point() {
  this.x = Math.random() * canvas.width;
  this.y = Math.random() * canvas.height;
  this.rad = Math.random() * 10 + 2;
  this.color = colors[Math.random() * colors.length | 0];
}

function putInGrid(point, i) {
  var index = getCellIndex(point.x, point.y);
  grid[index].push(point);
}

function getCellIndex(x, y) {
  if(x > canvas.width - 1) x = canvas.width - 1;
  if(y > canvas.height - 1) y = canvas.height - 1;
  var ratio_x = grid.width/canvas.width;
  var ratio_y = grid.height/canvas.height;
  var norm_y = Math.floor(y * ratio_y);
  var norm_x = Math.floor(x * ratio_x);
  return (norm_y * grid.width) + norm_x;
}

function draw() {
  ctx.clearRect(0,0,canvas.width, canvas.height);
  var point = points[0];
  ctx.fillStyle = point.color;
  ctx.beginPath();
  for(var i=0; i<points.length; i++) {
    point = points[i];
    if(point.color !== ctx.fillStyle) {
      ctx.fill();
      ctx.fillStyle = point.color;
      ctx.beginPath();
    }
    ctx.moveTo(point.x + point.rad, point.y);
    ctx.arc(point.x, point.y, point.rad, 0, Math.PI*2);
  }
  ctx.fill();
}

function checkCollision(x, y) {
  // ToDo: loop through adjacent cells too
  var index = getCellIndex(x, y);
  var cell = grid[index];
  if(cell) {
    cell.forEach(checkPointCollision);
  }
  function checkPointCollision(point, pt_index) {
    if(Math.hypot(x - point.x, y - point.y) <= point.rad) {
      cell.splice(pt_index, 1);
      var newPoint = new Point();
      grid[getCellIndex(newPoint.x, newPoint.y)]
        .push(newPoint);
      points.splice(points.indexOf(point), 1, newPoint);
      dirty = true;
    }
  }
}

function onmousemove(e) {
  var rect = canvas.getBoundingClientRect();
  checkCollision(e.clientX - rect.left,  e.clientY - rect.top);
}


function anim() {
  if(dirty)
    draw();
  modified = false;
  requestAnimationFrame(anim);
}
&lt;canvas id="canvas"&gt;&lt;/canvas&gt;

【讨论】:

    猜你喜欢
    • 2023-01-07
    • 1970-01-01
    • 2018-05-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多