目前,我有一个碰撞检测的圆反弹的墙壁,其他圆和矩形。我可以选择矩形由鼠标控制,但我在代码段中禁用了它,因为我希望其中一个圆由鼠标移动控制,我尝试通过将与bat类相同的值赋予player类来实现这一点,但值变为nan。我有什么办法可以做到这一点吗?
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext("2d");
const mouse = { x: 0, y: 0, button: false }
function mouseEvents(e) {
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down", "up", "move"].forEach(name => document.addEventListener("mouse" + name, mouseEvents));
// short cut vars
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
const gravity = 0;
var balls = []
var bats = []
// constants and helpers
const PI2 = Math.PI * 2;
const setStyle = (ctx, style) => { Object.keys(style).forEach(key => ctx[key] = style[key]) };
function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function distance(x1, y1, x2, y2) {
const xDist = x2 - x1;
const yDist = y2 - y1;
return Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2));
}
// the ball
class ball {
constructor() {
this.r = 25
this.x = random(50, 1500)
this.y = random(50, 1500)
this.dx = 15
this.dy = 15
this.mass = 1
this.maxSpeed = 15
this.style = {
lineWidth: 12,
strokeStyle: "green"
}
}
draw(ctx) {
setStyle(ctx, this.style);
ctx.beginPath();
ctx.arc(this.x, this.y, this.r - this.style.lineWidth * 0.45, 0, PI2);
ctx.stroke();
}
update() {
this.dy += gravity;
var speed = Math.sqrt(this.dx * this.dx + this.dy * this.dy);
var x = this.x + this.dx;
var y = this.y + this.dy;
if (y > canvas.height - this.r) {
y = (canvas.height - this.r) - (y - (canvas.height - this.r));
this.dy = -this.dy;
}
if (y < this.r) {
y = this.r - (y - this.r);
this.dy = -this.dy;
}
if (x > canvas.width - this.r) {
x = (canvas.width - this.r) - (x - (canvas.width - this.r));
this.dx = -this.dx;
}
if (x < this.r) {
x = this.r - (x - this.r);
this.dx = -this.dx;
}
this.x = x;
this.y = y;
if (speed > this.maxSpeed) { // if over speed then slow the ball down gradualy
var reduceSpeed = this.maxSpeed + (speed - this.maxSpeed) * 0.9; // reduce speed if over max speed
this.dx = (this.dx / speed) * reduceSpeed;
this.dy = (this.dy / speed) * reduceSpeed;
}
for (var i = 0; i < balls.length; i++) {
if (this === balls[i]) continue
if (distance(this.x, this.y, balls[i].x, balls[i].y) < this.r * 2) {
resolveCollision(this, balls[i])
}
}
}
}
class player {
constructor() {
this.r = 50
this.x = random(50, 1500)
this.y = random(50, 1500)
this.dx = 0.2
this.dy = 0.2
this.mass = 1
this.maxSpeed = 1000
this.style = {
lineWidth: 12,
strokeStyle: "blue"
}
}
draw(ctx) {
setStyle(ctx, this.style);
ctx.beginPath();
ctx.arc(this.x, this.y, this.r - this.style.lineWidth * 0.45, 0, PI2);
ctx.stroke();
}
update() {
this.dx = mouse.x - this.x;
this.dy = mouse.y - this.y;
var x = this.x + this.dx;
var y = this.y + this.dy;
x < this.width / 2 && (x / 2);
y < this.height / 2 && (y / 2);
x > canvas.width / 2 && (x = canvas.width / 2);
y > canvas.height / 2 && (y = canvas.height / 2);
this.dx = x - this.x;
this.dy = y - this.y;
this.x = x;
this.y = y;
for (var i = 0; i < balls.length; i++) {
if (this === balls[i]) continue
if (distance(this.x, this.y, balls[i].x, balls[i].y) < this.r * 2) {
resolveCollision(this, balls[i])
}
}
}
}
const ballShadow = {
r: 50,
x: 50,
y: 50,
dx: 0.2,
dy: 0.2,
}
//bat
class bat {
constructor(x, y, w, h) {
this.x = x
this.y = y
this.dx = 0
this.dy = 0
this.width = w
this.height = h
this.style = {
lineWidth: 2,
strokeStyle: "black",
}
}
draw(ctx) {
setStyle(ctx, this.style);
ctx.strokeRect(this.x - this.width / 2, this.y - this.height / 2, this.width, this.height);
}
update() {
this.dx = mouse.x - this.x;
this.dy = mouse.y - this.y;
var x = this.x + this.dx;
var y = this.y + this.dy;
x < this.width / 2 && (x = this.width / 2);
y < this.height / 2 && (y = this.height / 2);
x > canvas.width - this.width / 2 && (x = canvas.width - this.width / 2);
y > canvas.height - this.height / 2 && (y = canvas.height - this.height / 2);
this.dx = x - this.x;
this.dy = y - this.y;
this.x = x;
this.y = y;
}
}
function doBatBall(bat, ball) {
var mirrorX = 1;
var mirrorY = 1;
const s = ballShadow; // alias
s.x = ball.x;
s.y = ball.y;
s.dx = ball.dx;
s.dy = ball.dy;
s.x -= s.dx;
s.y -= s.dy;
// get the bat half width height
const batW2 = bat.width / 2;
const batH2 = bat.height / 2;
// and bat size plus radius of ball
var batH = batH2 + ball.r;
var batW = batW2 + ball.r;
// set ball position relative to bats last pos
s.x -= bat.x;
s.y -= bat.y;
// set ball delta relative to bat
s.dx -= bat.dx;
s.dy -= bat.dy;
// mirror x and or y if needed
if (s.x < 0) {
mirrorX = -1;
s.x = -s.x;
s.dx = -s.dx;
}
if (s.y < 0) {
mirrorY = -1;
s.y = -s.y;
s.dy = -s.dy;
}
// bat now only has a bottom, right sides and bottom right corner
var distY = (batH - s.y); // distance from bottom
var distX = (batW - s.x); // distance from right
if (s.dx > 0 && s.dy > 0) { return } // ball moving away so no hit
var ballSpeed = Math.sqrt(s.dx * s.dx + s.dy * s.dy) // get ball speed relative to bat
// get x location of intercept for bottom of bat
var bottomX = s.x + (s.dx / s.dy) * distY;
// get y location of intercept for right of bat
var rightY = s.y + (s.dy / s.dx) * distX;
// get distance to bottom and right intercepts
var distB = Math.hypot(bottomX - s.x, batH - s.y);
var distR = Math.hypot(batW - s.x, rightY - s.y);
var hit = false;
if (s.dy < 0 && bottomX <= batW2 && distB <= ballSpeed && distB < distR) { // if hit is on bottom and bottom hit is closest
hit = true;
s.y = batH - s.dy * ((ballSpeed - distB) / ballSpeed);
s.dy = -s.dy;
}
if (!hit && s.dx < 0 && rightY <= batH2 && distR <= ballSpeed && distR <= distB) { // if hit is on right and right hit is closest
hit = true;
s.x = batW - s.dx * ((ballSpeed - distR) / ballSpeed);;
s.dx = -s.dx;
}
if (!hit) { // if no hit may have intercepted the corner.
// find the distance that the corner is from the line segment from the balls pos to the next pos
const u = ((batW2 - s.x) * s.dx + (batH2 - s.y) * s.dy) / (ballSpeed * ballSpeed);
// get the closest point on the line to the corner
var cpx = s.x + s.dx * u;
var cpy = s.y + s.dy * u;
// get ball radius squared
const radSqr = ball.r * ball.r;
// get the distance of that point from the corner squared
const dist = (cpx - batW2) * (cpx - batW2) + (cpy - batH2) * (cpy - batH2);
// is that distance greater than ball radius
if (dist > radSqr) { return } // no hit
// solves the triangle from center to closest point on balls trajectory
var d = Math.sqrt(radSqr - dist) / ballSpeed;
// intercept point is closest to line start
cpx -= s.dx * d;
cpy -= s.dy * d;
// get the distance from the ball current pos to the intercept point
d = Math.hypot(cpx - s.x, cpy - s.y);
// is the distance greater than the ball speed then its a miss
if (d > ballSpeed) { return } // no hit return
s.x = cpx; // position of contact
s.y = cpy;
// find the normalised tangent at intercept point
const ty = (cpx - batW2) / ball.r;
const tx = -(cpy - batH2) / ball.r;
// calculate the reflection vector
const bsx = s.dx / ballSpeed; // normalise ball speed
const bsy = s.dy / ballSpeed;
const dot = (bsx * tx + bsy * ty) * 2;
// get the distance the ball travels past the intercept
d = ballSpeed - d;
// the reflected vector is the balls new delta (this delta is normalised)
s.dx = (tx * dot - bsx);
s.dy = (ty * dot - bsy);
// move the ball the remaining distance away from corner
s.x += s.dx * d;
s.y += s.dy * d;
// set the ball delta to the balls speed
s.dx *= ballSpeed
s.dy *= ballSpeed
hit = true;
}
// if the ball hit the bat restore absolute position
if (hit) {
// reverse mirror
s.x *= mirrorX;
s.dx *= mirrorX;
s.y *= mirrorY;
s.dy *= mirrorY;
// remove bat relative position
s.x += bat.x;
s.y += bat.y;
// remove bat relative delta
s.dx += bat.dx;
s.dy += bat.dy;
// set the balls new position and delta
ball.x = s.x;
ball.y = s.y;
ball.dx = s.dx;
ball.dy = s.dy;
}
}
function rotate(velocity, angle) {
const rotatedVelocities = {
x: velocity.x * Math.cos(angle) + velocity.y * Math.sin(angle),
y: -velocity.x * Math.sin(angle) + velocity.y * Math.cos(angle)
};
return rotatedVelocities;
}
function resolveCollision(particle, otherParticle) {
const xVelocityDiff = particle.dx - otherParticle.dx;
const yVelocityDiff = particle.dy - otherParticle.dy;
const xDist = otherParticle.x - particle.x;
const yDist = otherParticle.y - particle.y;
if (xVelocityDiff * xDist + yVelocityDiff * yDist >= 0) {
const angle = Math.atan2(otherParticle.y - particle.y, otherParticle.x - particle.x);
const m1 = particle.mass;
const m2 = otherParticle.mass;
const u1 = rotate({ x: particle.dx, y: particle.dy }, angle);
const u2 = rotate({ x: otherParticle.dx, y: otherParticle.dy }, angle);
const v1 = {
x: u1.x * (m1 - m2) / (m1 + m2) + u2.x * 2 * m2 / (m1 + m2),
y: u1.y
};
const v2 = {
x: u2.x * (m1 - m2) / (m1 + m2) + u1.x * 2 * m2 / (m1 + m2),
y: u2.y
};
const vFinal1 = rotate(v1, -angle);
const vFinal2 = rotate(v2, -angle);
particle.dx = vFinal1.x;
particle.dy = vFinal1.y;
otherParticle.dx = vFinal2.x;
otherParticle.dy = vFinal2.y;
}
}
for (let i = 0; i < 10; i++) {
balls.push(new ball())
}
balls.push(new player())
//x,y,w,h
bats.push(new bat((window.innerWidth / 2) - 150, window.innerHeight / 2, 10, 100))
bats.push(new bat((window.innerWidth / 2) + 150, window.innerHeight / 2, 10, 100))
// main update function
function update(timer) {
if (w !== innerWidth || h !== innerHeight) {
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
}
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0, 0, w, h);
// move bat and ball
for (var i = 0; i < balls.length; i++) {
for (var j = 0; j < bats.length; j++) {
doBatBall(bats[j], balls[i])
}
balls[i].update()
balls[i].draw(ctx)
}
bats.forEach(bat => {
//bat.update();
bat.draw(ctx);
})
// check for bal bat contact and change ball position and trajectory if needed
// draw ball and bat
requestAnimationFrame(update);
}
requestAnimationFrame(update);
<body>
<canvas></canvas>
</body>
暂无答案!
目前还没有任何答案,快来回答吧!