简体   繁体   中英

How to run multiple instances of a single object JavaScript

Currently attempting to make a physics simulation for elastic collisions of circles. I am having an issue where I do not know how to run the simulation with two circles interacting at the same time. I am not yet looking to create the interaction between the circles just to have them both running simultaneously. Any help is much appreciated. This is my first post so I apologize if I formatted something incorrectly.

 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> 

Here is how I would do it:

Instead of making the ball a kind of static object I made a constructor function ( More about that here ).

Then I made a ball array to store all the balls.

To make the dragging possible I store a seperate ball, which is not being moved by "physics" in the newBall variable. This ball is either invisible or is the ball currently being dragged.

In mouseDown() the newBall gets positioned under the cursor. In mouseUp() it gets it's velocity and gets added to the array of animated balls . Also a new newBall gets created.

In loop() I loop two times through the array of animated balls . Once for the physics, once for the painting. (Normally you would use two different methods with different tickRates to make animation more smooth, because physics calculation doesn't need to happen 60 times per second.

 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> 

Now to get a bit more complex:

I added tickDelay and tickTimer to use them in a tickLoop

The ball constructor now has two methods:

show() draws the ball on the canvas

tick() does the pysics stuff ( dt = deltaTime: time since last tick)

newBall is now null if the mouse isn't pressed

setup() initializes the width and height according to the <canvas> elements real size

tick() loops through the balls and calls .tick() tickDelay is in milliseconds so it gets divided by 1000

drawFrame() is your former loop() and does the drawing stuff

 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> 

A really simple way would to do exactly the same as you do now, but not initiate all functions as a variable. Change all the variables that are functions to just functions, and where you call them. At least the variable called ball. Then after that you could make two variables like this

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

Your script is kind of messy so hard for me to say if this will go through without any errors, but if it does, I am more than happy to help. This is not the very best solution if you only go for the way i presented now so please do not use this as you solution, but more as a way to get started. Also you will not really learn anything of it if we just gave you the answer

Edit:

Another thing to mark is that using setInterval for games and graphical projects may be a bad idea since JavaScript is single threaded. A better solution is using requestAnimationFrame()

It would look something like this

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

// Start things off
requestAnimationFrame(mainLoop);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM