使用画布

时间:2017-05-23 12:46:27

标签: javascript html5 canvas

我正在制作一个Agar.io克隆,而我却陷入了创建网格背景的困境。我已经看到了关于根据相机绘制背景的问题,但它们都只是用于图像。

以下是我的代码:

click = split

var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var width = innerWidth;
var height = innerHeight;
var evt = "mousemove";
var touchX = 0;
var touchY = 0;

var config = {
  gameWidth: width,
  gameHeight: height,
  playerConfig: {
    mass: 10,
    hue: Math.round(Math.random() * 360),
    border: 6
  },
  virusConfig: {
    mass: 100,
    fill: "#33ff33",
    stroke: "#19D119",
    border: 10
  }
}

var maxSplits = 16;
var foods = [];
var viruses = [];
var clients = [];
var player = {
  blobs: [],
  lastSplit: 0,
  extent: {
    minX: 0,
    minY: 0,
    maxX: 0,
    maxY: 0
  }
};
var camera = {
  x: 0,
  y: 0
};

function movePlayer() {
  for (var i = 0; i < player.blobs.length; i++) {
    var b = player.blobs[i];
    var e = player.extent;
    var x = touchX + camera.x - b.x;
    var y = touchY + camera.y - b.y;
    var dist = Math.hypot(x, y);

    b.velocity = {
      x: x / dist * b.speed,
      y: y / dist * b.speed
    };

    b.speed -= b.speed > 4 ? 0.25 : 0;
    b.speed -= b.speed > 4 ? 0.25 : 0;
    b.x += b.velocity.x * Math.min(1, Math.pow(x / b.r, 2));
    b.y += b.velocity.y * Math.min(1, Math.pow(y / b.r, 2));
    b.x = Math.min(Math.max(b.x, -config.gameWidth), config.gameWidth);
    b.y = Math.min(Math.max(b.y, -config.gameHeight), config.gameHeight);

    if (i == 0) {
      e.minX = e.maxX = b.x;
      e.minY = e.maxY = b.y;
    } else {
      e.minX = Math.min(b.x, e.minX);
      e.minY = Math.min(b.y, e.minY);
      e.maxX = Math.max(b.x, e.maxX);
      e.maxY = Math.max(b.y, e.maxY);
    }
  }
}

function handleSelfCollision() {
  for (var i = 0; i < player.blobs.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      if (j !== i && player.blobs[i] !== undefined) {
        var b1 = player.blobs[i];
        var b2 = player.blobs[j];
        var radii = b1.r + b2.r;
        var x = b2.x - b1.x;
        var y = b2.y - b1.y;
        var dist = Math.hypot(x, y);

        if (dist < radii) {
          if (player.lastSplit + 10000 > new Date().getTime()) {
            x /= dist;
            y /= dist;
            // test
            b1.x += ((b2.x - x * radii) - b1.x) * 0.6;
            b1.y += ((b2.y - y * radii) - b1.y) * 0.5;
            //test
          } else if (dist < radii * 0.75) {
            b1.mass = b1.mass + b2.mass;
            b1.r = massToRadius(b1.mass);
            player.blobs.splice(j, 1);
          }
        }
      }
    }
  }
}

function handleVirusCollision() {
  for (var i = 0; i < viruses.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      var b = player.blobs[j];
      var v = viruses[i];
      var x = v.x - b.x;
      var y = v.y - b.y;
      var dist = Math.hypot(x, y);
      var radii = v.r + b.r;

      if (dist < radii) {
        b.r += v.r;
        viruses.splice(i, 1);
        if (player.blobs.length < maxSplits) {
          while (b.r >= defaultPlayerR * 2) {
            splitBlob(b);
          }
        }
      }
    }
  }
}

function splitBlob(blob) {
  if (blob.mass >= config.playerConfig.mass * 2 && player.blobs.length < maxSplits) {
    blob.mass /= 2;
    blob.r = massToRadius(blob.mass)
    player.lastSplit = new Date().getTime();
    player.blobs.push({
      x: blob.x,
      y: blob.y,
      mass: blob.mass,
      r: massToRadius(blob.mass),
      speed: 20
    });
  }
}

function moveCamera() {
  var e = player.extent;
  camera.x = (e.maxX + e.minX) / 2;
  camera.y = (e.maxY + e.minY) / 2;
  camera.x -= width / 2;
  camera.y -= height / 2;
}

function addFood(num) {
  var rnd = random(1, 1.5);
  while (num--) {
    foods.push({
      x: random(-config.gameWidth, config.gameWidth),
      y: random(-config.gameHeight, config.gameHeight),
      mass: rnd,
      r: massToRadius(rnd),
      hue: Math.round(Math.random() * 360)
    });
  }
}

function addVirus(num) {
  while (num--) {
    viruses.push({
      x: random(-width, width),
      y: random(-height, height),
      mass: config.virusConfig.mass,
      r: massToRadius(config.virusConfig.mass)
    });
  }
}

function updateFood() {
  for (var i = 0; i < foods.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      var b = player.blobs[j];
      var f = foods[i];
      var x = f.x - b.x;
      var y = f.y - b.y;
      var dist = Math.hypot(x, y);
      var radii = f.r + b.r;

      if (dist < radii) {
        b.mass += f.mass;
        b.r = massToRadius(b.mass);
        foods.splice(i, 1);
        break;
      }
    }
  }
  if (foods.length < 100) {
    addFood(1);
  }
}

function drawPlayer(order) {
  var sortedArr = player.blobs.sort(function(b1, b2) {
    return b1.mass - b2.mass;
  });

  for (var i = 0; i < sortedArr.length; i++) {
    var b = sortedArr[i];

    ctx.fillStyle = "hsl(" + config.playerConfig.hue + ", 100%, 50%)";
    ctx.strokeStyle = "hsl(" + config.playerConfig.hue + ", 100%, 45%)";
    ctx.lineWidth = config.playerConfig.border;
    ctx.beginPath();
    ctx.arc(b.x - camera.x, b.y - camera.y, b.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
  }
}

function drawFood() {
  for (var i = 0; i < foods.length; i++) {
    var f = foods[i];

    ctx.fillStyle = 'hsl(' + f.hue + ', 100%, 50%)';
    ctx.beginPath();
    ctx.arc(f.x - camera.x, f.y - camera.y, f.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
  }
}

function drawVirus() {
  for (var i = 0; i < viruses.length; i++) {
    var v = viruses[i];

    ctx.fillStyle = config.virusConfig.fill;
    ctx.strokeStyle = config.virusConfig.stroke;
    ctx.lineWidth = config.virusConfig.border;
    ctx.beginPath();
    ctx.arc(v.x - camera.x, v.y - camera.y, v.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
  }
}

function drawPlatform(bgcol, borcol, thickness) {
  var x = -config.gameWidth - camera.x;
  var y = -config.gameHeight - camera.y;
  var w = 2 * config.gameWidth;
  var h = 2 * config.gameHeight;

  ctx.fillStyle = bgcol;
  ctx.fillRect(x, y, w, h);
  ctx.lineWidth = thickness;
  ctx.strokeStyle = borcol;
  ctx.strokeRect(x, y, w, h);
}

function drawBackground(col) {
  ctx.fillStyle = col;
  ctx.fillRect(0, 0, width, height);
}

function drawGrid() {

}

function updateAndDraw() {
  movePlayer();
  moveCamera();
  updateFood();
  handleSelfCollision();
  //handleVirusCollision();

  drawBackground("#f2fbff");
  drawPlatform("#fff", "#000", 10);
  drawGrid();
  drawFood();
  drawPlayer();
  drawVirus();

  requestAnimationFrame(updateAndDraw);
}

function handleEvent(event) {
  touchX = event.clientX;
  touchY = event.clientY;
}

function random(min, max) {
  return (min + (Math.random() * (max - min)));
}

function massToRadius(mass) {
  return 4 + Math.sqrt(mass) * 6;
}

function startTheGame() {
  evt = width < 600 ? "click" : "mousemove";
  canvas.width = width;
  canvas.height = height;

  addFood(100);
  addVirus(5);
  player.blobs.push({
    x: width / 2,
    y: height / 2,
    mass: config.playerConfig.mass,
    r: massToRadius(config.playerConfig.mass),
    speed: 8
  });

  //test
  canvas.addEventListener("click", function() {
    var blobs = player.blobs.length;
    for (var i = 0; i < blobs; i++) {
      if (blobs < maxSplits) {
        splitBlob(player.blobs[i]);
      } else {
        break;
      }
    }
  });

  addEventListener("resize", function() {
    width = innerWidth;
    height = innerHeight
  });
  // test
  canvas.addEventListener(evt, handleEvent);
  updateAndDraw();
}


startTheGame();
body {
  padding: 0;
  margin: 0;
}
<title>Gaario - A simple Agario clone</title>
<canvas>Awww! Your browser doesn't support canvas.</canvas>

1 个答案:

答案 0 :(得分:1)

可以通过绘制水平线,然后添加垂直线来绘制网格。将ctx.lineTo()ctx.moveTo()函数与ctx.beginPath()ctx.stroke()结合使用,正在绘制网格。

我填充了drawGrid()函数的主体:

function drawGrid() {
    var gridSize = 30;    // define the space between each line
    var x = -config.gameWidth - camera.x;  // x start point of the field
    var y = -config.gameHeight - camera.y  // y start point of the field
    var width = 2 * config.gameWidth;
    var height = 2 * config.gameHeight;

    ctx.lineWidth = 1;
    ctx.beginPath();
    for(var i = 0; i * gridSize < height; i++) { // draw the horizontal lines
       ctx.moveTo(x, i * gridSize + y);
       ctx.lineTo(x + width, i * gridSize + y);
    }
    for(var i = 0; i * gridSize < width; i++) {  // draw the vertical lines
       ctx.moveTo(i * gridSize + x,  y);
       ctx.lineTo(i * gridSize + x, y + height);
    }
    ctx.stroke();
}

在用于绘制线条的for循环中,我们使用了这个事实,即我们知道容器左上角的位置。变量分别为xy。 因此,为了绘制垂直线,我们知道那些点从y坐标yy + height。 我们可以通过在for循环中运行i * gridSize + x来计算每一行的x坐标。 对于左起第一行i = 0,我们只返回x,这是我们游戏区域的左边界。 第二行是1 * gridSize + x,第三行是2 * gridSize + x,依此类推。

通过将i增加为i * gridSize < width为止,我们会在游戏字段中绘制所有垂直线。

for(var i = 0; i * gridSize < width; i++) {  // draw the vertical lines
   ctx.moveTo(i * gridSize + x,  y);
   ctx.lineTo(i * gridSize + x, y + height);
}

下面你会找到你的代码示例,添加了函数。

var canvas = document.querySelector("canvas");
var ctx = canvas.getContext("2d");
var width = innerWidth;
var height = innerHeight;
var evt = "mousemove";
var touchX = 0;
var touchY = 0;

var config = {
  gameWidth: width,
  gameHeight: height,
  playerConfig: {
    mass: 10,
    hue: Math.round(Math.random() * 360),
    border: 6
  },
  virusConfig: {
    mass: 100,
    fill: "#33ff33",
    stroke: "#19D119",
    border: 10
  }
}

var maxSplits = 16;
var foods = [];
var viruses = [];
var clients = [];
var player = {
  blobs: [],
  lastSplit: 0,
  extent: {
    minX: 0,
    minY: 0,
    maxX: 0,
    maxY: 0
  }
};
var camera = {
  x: 0,
  y: 0
};

function movePlayer() {
  for (var i = 0; i < player.blobs.length; i++) {
    var b = player.blobs[i];
    var e = player.extent;
    var x = touchX + camera.x - b.x;
    var y = touchY + camera.y - b.y;
    var dist = Math.hypot(x, y);

    b.velocity = {
      x: x / dist * b.speed,
      y: y / dist * b.speed
    };

    b.speed -= b.speed > 4 ? 0.25 : 0;
    b.speed -= b.speed > 4 ? 0.25 : 0;
    b.x += b.velocity.x * Math.min(1, Math.pow(x / b.r, 2));
    b.y += b.velocity.y * Math.min(1, Math.pow(y / b.r, 2));
    b.x = Math.min(Math.max(b.x, -config.gameWidth), config.gameWidth);
    b.y = Math.min(Math.max(b.y, -config.gameHeight), config.gameHeight);

    if (i == 0) {
      e.minX = e.maxX = b.x;
      e.minY = e.maxY = b.y;
    } else {
      e.minX = Math.min(b.x, e.minX);
      e.minY = Math.min(b.y, e.minY);
      e.maxX = Math.max(b.x, e.maxX);
      e.maxY = Math.max(b.y, e.maxY);
    }
  }
}

function handleSelfCollision() {
  for (var i = 0; i < player.blobs.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      if (j !== i && player.blobs[i] !== undefined) {
        var b1 = player.blobs[i];
        var b2 = player.blobs[j];
        var radii = b1.r + b2.r;
        var x = b2.x - b1.x;
        var y = b2.y - b1.y;
        var dist = Math.hypot(x, y);

        if (dist < radii) {
          if (player.lastSplit + 10000 > new Date().getTime()) {
            x /= dist;
            y /= dist;
            // test
            b1.x += ((b2.x - x * radii) - b1.x) * 0.6;
            b1.y += ((b2.y - y * radii) - b1.y) * 0.5;
            //test
          } else if (dist < radii * 0.75) {
            b1.mass = b1.mass + b2.mass;
            b1.r = massToRadius(b1.mass);
            player.blobs.splice(j, 1);
          }
        }
      }
    }
  }
}

function handleVirusCollision() {
  for (var i = 0; i < viruses.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      var b = player.blobs[j];
      var v = viruses[i];
      var x = v.x - b.x;
      var y = v.y - b.y;
      var dist = Math.hypot(x, y);
      var radii = v.r + b.r;

      if (dist < radii) {
        b.r += v.r;
        viruses.splice(i, 1);
        if (player.blobs.length < maxSplits) {
          while (b.r >= defaultPlayerR * 2) {
            splitBlob(b);
          }
        }
      }
    }
  }
}

function splitBlob(blob) {
  if (blob.mass >= config.playerConfig.mass * 2 && player.blobs.length < maxSplits) {
    blob.mass /= 2;
    blob.r = massToRadius(blob.mass)
    player.lastSplit = new Date().getTime();
    player.blobs.push({
      x: blob.x,
      y: blob.y,
      mass: blob.mass,
      r: massToRadius(blob.mass),
      speed: 20
    });
  }
}

function moveCamera() {
  var e = player.extent;
  camera.x = (e.maxX + e.minX) / 2;
  camera.y = (e.maxY + e.minY) / 2;
  camera.x -= width / 2;
  camera.y -= height / 2;
}

function addFood(num) {
  var rnd = random(1, 1.5);
  while (num--) {
    foods.push({
      x: random(-config.gameWidth, config.gameWidth),
      y: random(-config.gameHeight, config.gameHeight),
      mass: rnd,
      r: massToRadius(rnd),
      hue: Math.round(Math.random() * 360)
    });
  }
}

function addVirus(num) {
  while (num--) {
    viruses.push({
      x: random(-width, width),
      y: random(-height, height),
      mass: config.virusConfig.mass,
      r: massToRadius(config.virusConfig.mass)
    });
  }
}

function updateFood() {
  for (var i = 0; i < foods.length; i++) {
    for (var j = 0; j < player.blobs.length; j++) {
      var b = player.blobs[j];
      var f = foods[i];
      var x = f.x - b.x;
      var y = f.y - b.y;
      var dist = Math.hypot(x, y);
      var radii = f.r + b.r;

      if (dist < radii) {
        b.mass += f.mass;
        b.r = massToRadius(b.mass);
        foods.splice(i, 1);
        break;
      }
    }
  }
  if (foods.length < 100) {
    addFood(1);
  }
}

function drawPlayer(order) {
  var sortedArr = player.blobs.sort(function(b1, b2) {
    return b1.mass - b2.mass;
  });

  for (var i = 0; i < sortedArr.length; i++) {
    var b = sortedArr[i];

    ctx.fillStyle = "hsl(" + config.playerConfig.hue + ", 100%, 50%)";
    ctx.strokeStyle = "hsl(" + config.playerConfig.hue + ", 100%, 45%)";
    ctx.lineWidth = config.playerConfig.border;
    ctx.beginPath();
    ctx.arc(b.x - camera.x, b.y - camera.y, b.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
  }
}

function drawFood() {
  for (var i = 0; i < foods.length; i++) {
    var f = foods[i];

    ctx.fillStyle = 'hsl(' + f.hue + ', 100%, 50%)';
    ctx.beginPath();
    ctx.arc(f.x - camera.x, f.y - camera.y, f.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.closePath();
  }
}

function drawVirus() {
  for (var i = 0; i < viruses.length; i++) {
    var v = viruses[i];

    ctx.fillStyle = config.virusConfig.fill;
    ctx.strokeStyle = config.virusConfig.stroke;
    ctx.lineWidth = config.virusConfig.border;
    ctx.beginPath();
    ctx.arc(v.x - camera.x, v.y - camera.y, v.r, 0, Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.closePath();
  }
}

function drawPlatform(bgcol, borcol, thickness) {
  var x = -config.gameWidth - camera.x;
  var y = -config.gameHeight - camera.y;
  var w = 2 * config.gameWidth;
  var h = 2 * config.gameHeight;

  ctx.fillStyle = bgcol;
  ctx.fillRect(x, y, w, h);
  ctx.lineWidth = thickness;
  ctx.strokeStyle = borcol;
  ctx.strokeRect(x, y, w, h);
}

function drawBackground(col) {
  ctx.fillStyle = col;
  ctx.fillRect(0, 0, width, height);
}

function drawGrid() {
  var gridSize = 30;    // define the space between each line
  var x = -config.gameWidth - camera.x;  // x start point
  var y = -config.gameHeight - camera.y  // y start point
  var width = 2 * config.gameWidth;
  var height = 2 * config.gameHeight;
  ctx.lineWidth = 1;
  ctx.beginPath();
  for(var i = 0; i * gridSize < height; i++) { // draw the horizontal lines
     ctx.moveTo(x, i * gridSize + y);
     ctx.lineTo(x + width, i * gridSize + y);
  }
  for(var i = 0; i * gridSize < width; i++) {  // draw the vertical lines
     ctx.moveTo(i * gridSize + x,  y);
     ctx.lineTo(i * gridSize + x, y + height);
  }
  ctx.stroke();
}

function updateAndDraw() {
  movePlayer();
  moveCamera();
  updateFood();
  handleSelfCollision();
  //handleVirusCollision();

  drawBackground("#f2fbff");
  drawPlatform("#fff", "#000", 10);
  drawGrid();
  drawFood();
  drawPlayer();
  drawVirus();

  requestAnimationFrame(updateAndDraw);
}

function handleEvent(event) {
  touchX = event.clientX;
  touchY = event.clientY;
}

function random(min, max) {
  return (min + (Math.random() * (max - min)));
}

function massToRadius(mass) {
  return 4 + Math.sqrt(mass) * 6;
}

function startTheGame() {
  evt = width < 600 ? "click" : "mousemove";
  canvas.width = width;
  canvas.height = height;

  addFood(100);
  addVirus(5);
  player.blobs.push({
    x: width / 2,
    y: height / 2,
    mass: config.playerConfig.mass,
    r: massToRadius(config.playerConfig.mass),
    speed: 8
  });

  //test
  canvas.addEventListener("click", function() {
    var blobs = player.blobs.length;
    for (var i = 0; i < blobs; i++) {
      if (blobs < maxSplits) {
        splitBlob(player.blobs[i]);
      } else {
        break;
      }
    }
  });

  addEventListener("resize", function() {
    width = innerWidth;
    height = innerHeight
  });
  // test
  canvas.addEventListener(evt, handleEvent);
  updateAndDraw();
}


startTheGame();
body {
  padding: 0;
  margin: 0;
}
<title>Gaario - A simple Agario clone</title>
<canvas>Awww! Your browser doesn't support canvas.</canvas>