有没有一种方法可以进一步简化这些javascript函数?

时间:2018-10-04 02:01:27

标签: javascript arrays canvas html5-canvas simplify

我在画布上创建了许多小点,在创建过程中,它会获取它们的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的游戏,除了它将只是一个脱机的单人游戏版本。

2 个答案:

答案 0 :(得分:4)

我邀请您考虑学习有关 Functional Programming 范例的更多信息。 几年前,我是一名游戏开发人员,发现在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

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

答案 1 :(得分:1)

绘制很多点时,您需要对如何管理它们很聪明。

绘制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);
}
<canvas id="canvas"></canvas>