如何 运行 单个对象的多个实例 JavaScript

How to run multiple instances of a single object JavaScript

目前正在尝试对圆的弹性碰撞进行物理模拟。我遇到一个问题,我不知道如何 运行 两个圆圈同时交互的模拟。我还没有打算创建圈子之间的交互,只是为了让它们同时 运行ning。任何帮助深表感谢。这是我的第一个 post 如果我的格式不正确,我深表歉意。

var width = 400;
var height = 400;
var canvas = ctx = false;
var frameRate = 1 / 60; // Seconds
var frameDelay = frameRate * 1000; // ms
var loopTimer = false;
var ball = {
  position: {
    x: width / 2,
    y: height / 2
  },
  velocity: {
    x: 0,
    y: 0
  },
  radius: 15, // 1px = 1cm
  restitution: -1
};
var mouse = {
  x: 0,
  y: 0,
  isDown: false
};

function getMousePosition(event) {
  mouse.x = event.pageX - canvas.offsetLeft;
  mouse.y = event.pageY - canvas.offsetTop;
}
var mouseDown = function(event) {
  if (event.which == 1) {
    getMousePosition(event);
    mouse.isDown = true;
    ball.position.x = mouse.x;
    ball.position.y = mouse.y;
  }
}
var mouseUp = function(event) {
  if (event.which == 1) {
    mouse.isDown = false;
    ball.velocity.y = (ball.position.y - mouse.y) / 10;
    ball.velocity.x = (ball.position.x - mouse.x) / 10;
  }
}
var setup = function() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  canvas.onmousemove = getMousePosition;
  canvas.onmousedown = mouseDown;
  canvas.onmouseup = mouseUp;
  ctx.fillStyle = 'blue';
  ctx.strokeStyle = '#000000';
  loopTimer = setInterval(loop, frameDelay);
}
var loop = function() {
  if (!mouse.isDown) {
    ball.position.x += ball.velocity.x * frameRate * 100;
    ball.position.y += ball.velocity.y * frameRate * 100;
  }
  if (ball.position.y > height - ball.radius) {
    ball.velocity.y *= ball.restitution;
    ball.position.y = height - ball.radius;
  }
  if (ball.position.x > width - ball.radius) {
    ball.velocity.x *= ball.restitution;
    ball.position.x = width - ball.radius;
  }
  if (ball.position.x < ball.radius) {
    ball.velocity.x *= ball.restitution;
    ball.position.x = ball.radius;
  }
  if (ball.position.y < ball.radius) {
    ball.velocity.y *= ball.restitution;
    ball.position.y = ball.radius;
  }
  ctx.clearRect(0, 0, width, height);
  ctx.save();
  ctx.translate(ball.position.x, ball.position.y);
  ctx.beginPath();
  ctx.arc(0, 0, ball.radius, 0, Math.PI * 2, true);
  ctx.fill();
  ctx.closePath();
  ctx.restore();
  if (mouse.isDown) {
    ctx.beginPath();
    ctx.moveTo(ball.position.x, ball.position.y);
    ctx.lineTo(mouse.x, mouse.y);
    ctx.stroke();
    ctx.closePath();
  }
}
setup();
#canvas {
  border: solid 1px #ccc;
}
<canvas id="canvas"></canvas>

以下是我的做法:

我没有让球成为一种静态对象,而是创建了一个构造函数 (More about that here)。

然后我做了一个球数组来存放所有的球

为了使拖动成为可能,我在 newBall 变量中存储了一个单独的球,它不会被 "physics" 移动。这个球要么是不可见的,要么是当前正在拖动的球。

mouseDown() 中,newBall 位于光标下方。 在 mouseUp() 中,它获得速度并添加到动画数组 balls 中。还创建了一个新的 newBall

loop() 中,我在动画数组 balls 中循环两次。一次是物理,一次是绘画。 (通常你会使用两种不同的方法和不同的 tickRates 来使动画更流畅,因为物理计算不需要每秒发生 60 次。

var width = 400;
var height = 400;
var canvas = ctx = false;
var frameRate = 1 / 60; // Seconds
var frameDelay = frameRate * 1000; // ms
var loopTimer = false;

function ball() {
  this.position = {
    x: width / 2,
    y: height / 2
  };
  this.velocity = {
    x: 0,
    y: 0
  };
  this.radius = 15; // 1px = 1cm
  this.restitution = -1
};
var balls = [];
var newBall = new ball();


var mouse = {
  x: 0,
  y: 0,
  isDown: false
};

function getMousePosition(event) {
  mouse.x = event.pageX - canvas.offsetLeft;
  mouse.y = event.pageY - canvas.offsetTop;
}

var mouseDown = function(event) {
  if (event.which == 1) {
    getMousePosition(event);
    mouse.isDown = true;
    newBall.position.x = mouse.x;
    newBall.position.y = mouse.y;
  }
}
var mouseUp = function(event) {
  if (event.which == 1) {
    mouse.isDown = false;
    newBall.velocity.y = (newBall.position.y - mouse.y) / 10;
    newBall.velocity.x = (newBall.position.x - mouse.x) / 10;
    balls.push(newBall);
    newBall = new ball();
  }
}
var setup = function() {
  canvas = document.getElementById("canvas");
  ctx = canvas.getContext("2d");
  canvas.onmousemove = getMousePosition;
  canvas.onmousedown = mouseDown;
  canvas.onmouseup = mouseUp;
  ctx.fillStyle = 'blue';
  ctx.strokeStyle = '#000000';
  loopTimer = setInterval(loop, frameDelay);
}
var loop = function() {
  for (var ball of balls) {
    ball.position.x += ball.velocity.x * frameRate * 100;
    ball.position.y += ball.velocity.y * frameRate * 100;

    if (ball.position.y > height - ball.radius) {
      ball.velocity.y *= ball.restitution;
      ball.position.y = height - ball.radius;
    }
    if (ball.position.x > width - ball.radius) {
      ball.velocity.x *= ball.restitution;
      ball.position.x = width - ball.radius;
    }
    if (ball.position.x < ball.radius) {
      ball.velocity.x *= ball.restitution;
      ball.position.x = ball.radius;
    }
    if (ball.position.y < ball.radius) {
      ball.velocity.y *= ball.restitution;
      ball.position.y = ball.radius;
    }
  }
  ctx.clearRect(0, 0, width, height);
  for (var ball of balls) {
    ctx.save();
    ctx.translate(ball.position.x, ball.position.y);
    ctx.beginPath();
    ctx.arc(0, 0, ball.radius, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  }

  ctx.save();
  ctx.translate(newBall.position.x, newBall.position.y);
  ctx.beginPath();
  ctx.arc(0, 0, newBall.radius, 0, Math.PI * 2, true);
  ctx.fill();
  ctx.closePath();
  ctx.restore();

  if (mouse.isDown) {
    ctx.beginPath();
    ctx.moveTo(newBall.position.x, newBall.position.y);
    ctx.lineTo(mouse.x, mouse.y);
    ctx.stroke();
    ctx.closePath();
  }
}
setup();
#canvas {
  border: solid 1px #ccc;
}
<canvas id="canvas"></canvas>

现在要复杂一点:

我添加了 tickDelaytickTimer 以在 tickLoop

中使用它们

ball 构造函数现在有两个方法:

show() 画球 canvas

tick() 做物理方面的事情(dt= deltaTime:自上次滴答以来的时间)

如果未按下鼠标,

newBall 现在是 null

setup()根据<canvas>元素实际大小

初始化widthheight

tick() 遍历球并调用 .tick() tickDelay 以毫秒为单位,因此它被除以 1000

drawFrame() 是你以前的 loop() 并且会画画

var width = 400;
var height = 400;
var canvas = ctx = false;
var frameRate = 1 / 60; // Seconds
var frameDelay = frameRate * 1000; // ms
var tickDelay = frameDelay * 2; //ticks 2 times slower than frames
var frameTimer;
var tickTimer;

function ball() {
  this.position = {
    x: width / 2,
    y: height / 2
  };
  this.velocity = {
    x: 0,
    y: 0
  };
  this.radius = 15; // 1px = 1cm
  this.restitution = -.99;
  this.show = function() {
    ctx.save();
    ctx.translate(this.position.x, this.position.y);
    ctx.beginPath();
    ctx.arc(0, 0, this.radius, 0, Math.PI * 2, true);
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  };
  this.tick = function(dt) {
    this.position.x += this.velocity.x * dt;
    this.position.y += this.velocity.y * dt;

    if (this.position.y > height - this.radius) {
      this.velocity.y *= this.restitution;
      this.position.y = height - this.radius;
    }
    if (this.position.x > width - this.radius) {
      this.velocity.x *= this.restitution;
      this.position.x = width - this.radius;
    }
    if (this.position.x < this.radius) {
      this.velocity.x *= this.restitution;
      this.position.x = this.radius;
    }
    if (this.position.y < this.radius) {
      this.velocity.y *= this.restitution;
      this.position.y = this.radius;
    }
  }
};
var balls = [];
var newBall;


var mouse = {
  x: 0,
  y: 0,
  isDown: false
};

function getMousePosition(event) {
  mouse.x = event.pageX - canvas.offsetLeft;
  mouse.y = event.pageY - canvas.offsetTop;
}

function mouseDown(event) {
  if (event.which == 1) {
    getMousePosition(event);
    mouse.isDown = true;
    if (!newBall) newBall = new ball();
    newBall.position.x = mouse.x;
    newBall.position.y = mouse.y;
  }
}

function mouseUp(event) {
  if (event.which == 1) {
    mouse.isDown = false;
    newBall.velocity.y = (newBall.position.y - mouse.y);
    newBall.velocity.x = (newBall.position.x - mouse.x);
    balls.push(newBall);
    newBall = null;
  }
}

function setup() {
  canvas = document.getElementById("canvas");
  width = canvas.getBoundingClientRect().width;
  height = canvas.getBoundingClientRect().height;
  ctx = canvas.getContext("2d");
  canvas.onmousemove = getMousePosition;
  canvas.onmousedown = mouseDown;
  canvas.onmouseup = mouseUp;
  ctx.fillStyle = 'blue';
  ctx.strokeStyle = '#000000';
  requestAnimationFrame(drawFrame);
  frameTimer = setInterval(drawFrame, frameDelay);
  tickTimer = setInterval(tick, tickDelay);
}

function tick() {
  for (var ball of balls) ball.tick(tickDelay * .001);
}

function drawFrame() {
  ctx.clearRect(0, 0, width, height);
  for (var ball of balls) ball.show();
  if (newBall) newBall.show(ctx);

  if (mouse.isDown && newBall) {
    ctx.beginPath();
    ctx.moveTo(newBall.position.x, newBall.position.y);
    ctx.lineTo(mouse.x, mouse.y);
    ctx.stroke();
    ctx.closePath();
  }
}
setup();
#canvas {
  border: solid 1px #ccc;
}
<canvas id="canvas"></canvas>

一个非常简单的方法就是和你现在做的完全一样,但不是把所有的函数都作为一个变量来初始化。将所有作为函数的变量更改为函数,以及您调用它们的位置。至少变量叫球。然后你可以像这样制作两个变量

ball1 = new ball();
ball2 = new ball();

你的脚本有点乱,我很难说这是否会没有任何错误,但如果是这样,我非常乐意提供帮助。如果您只采用我现在介绍的方式,这不是最好的解决方案,所以请不要将其用作您的解决方案,而是将其作为一种入门方式。如果我们只是给你答案,你也不会真正学到任何东西

编辑:

另一件需要注意的事情是,对游戏和图形项目使用 setInterval 可能不是一个好主意,因为 JavaScript 是单线程的。更好的解决方案是使用 requestAnimationFrame()

看起来像这样

function mainLoop() {
update();
draw();
requestAnimationFrame(mainLoop);
}

// Start things off
requestAnimationFrame(mainLoop);