如何检测JS画布中的碰撞



我正在使用JS画布在浏览器上制作一个简单的平台游戏,我目前试图检测Player类和Platform类之间的碰撞。我也针对不同的游戏风格做过碰撞。这是我在JS中的第一次。

const canvas = document.querySelector('canvas')
const dimensions = canvas.getBoundingClientRect()
canvas.width = dimensions.width * devicePixelRatio
canvas.height = dimensions.height * devicePixelRatio
const c = canvas.getContext('2d')

canvas.height = (innerHeight / 12) * 11
canvas.width = (innerWidth / 10) * 9
const centerx = canvas.width / 2
const centery = canvas.height / 2
const width = canvas.width
const height = canvas.height

player_acc = 0.5
gravity = 0.01
fricktion = -0.12
class Player {
constructor(x, y, radius, color, speedX, speedY) {
this.x = x
this.y = y
this.radius = radius
this.color = color
this.speedX = speedX
this.speedY = speedY
this.accY = 0
this.accX = 0
}
draw() {
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false)
c.fillStyle = this.color
c.fill()
}
update() {
this.accX = 0
this.accY = 0.5
// apply fricktion
this.accX += this.speedX * fricktion
// equations of motion
this.speedX += this.accX
this.speedY += this.accY
this.x += this.speedX + 0.5 * this.accX
this.y += this.speedY + 0.5 * this.accY

}
}
class Platform {
constructor(x, y, width, height, color) {
this.x = x
this.y = y
this.width = width
this.height = height
this.color = color
}
draw() {
c.beginPath()
this.rect = c.rect(this.x, this.y, this.width, this.height)
c.fillStyle = this.color
c.fill()
}
}

// create things
let player = new Player(
(width / 12), // X
(canvas.height / 10) * 7, // Y
10, // radius
'#205FE6', // color
0, // vertical speed
0, // horizontal speed
) 
// create entity lists
// list of all platforms
let platforms = [
[0, (height / 10) * 8, 200, 50, '#0E2A66']
]
let plats = []

let animationId
function animate() {
animationId = requestAnimationFrame(animate)
c.fillStyle = 'rgba(0, 0, 0, 0.1)'
c.fillRect(0, 0, canvas.width, canvas.height)
player.update()
player.draw()
// get the list of platforms created above
platforms.forEach((plat, index) => {
plats.push(new Platform(plat[0], plat[1], plat[2], plat[3], plat[4]))
})
// draw each platform accordingly
plats.forEach((p) => {
p.draw()
// check for collision between player and platform
// how do I do this <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

})
}


// start animate loop
animate()
.gameWindow {
border: 2px solid white;
background-color: #6790EA;
}
<canvas class="gameWindow"></canvas>

当前玩家从平台掉落,这是意料之中的,但我需要它做的是让玩家圆圈的底部在平台的顶部

下面是如何实现这一点的一个简单实现。首先你必须删除这个

platforms.forEach((plat, index) => {
plats.push(new Platform(plat[0], plat[1], plat[2], plat[3], plat[4]))
})

从你的动画循环。除非你的意图是在每一帧调用时将另一个平台推入plats数组,否则你不希望这样做。这阻碍了演出。只要在你的循环中添加console.log(plats.length),看看它有多大。

这个函数应该只在循环外调用一次,以创建平台。

对于碰撞,你可以使用像这样的东西,只要盒子是轴对齐的。

function intersection(min, max, value) {
return Math.max(min, Math.min(max, value));
}

我在你的平台类中添加了一个intersectPt参数来跟踪每个与玩家的交叉点。从玩家中心到平台的矢量图像。我们需要知道向量的交点

update() {
this.intersectPt = { x: intersection(this.x, this.x + this.width, player.x),
y: intersection(this.y, this.y + this.width, player.x)};
}

正如你所看到的,我们将把这些参数传递给前面提到的交集函数,以计算从玩家中心到平台的向量的x和y值。一旦我们知道了交点在哪里,我们就可以确定到交点的距离是否小于半径;

function distance(x, y) {
return Math.hypot(x, y);
}

JS有一个内置的Math。它的假设。你也可以用任何你喜欢的勾股定理方程。

如果返回true表示距离小于那么我们可以调用响应函数做一些事情

function collisionResponse(plat) {
plat.color = "red";
player.y = plat.y - player.radius;
player.speedY = 0;
}

下面是一个片段

const canvas = document.querySelector("canvas");
const dimensions = canvas.getBoundingClientRect();
canvas.width = dimensions.width * devicePixelRatio;
canvas.height = dimensions.height * devicePixelRatio;
const c = canvas.getContext("2d");
canvas.height = (innerHeight / 12) * 11;
canvas.width = (innerWidth / 10) * 9;
const centerx = canvas.width / 2;
const centery = canvas.height / 2;
const width = canvas.width;
const height = canvas.height;
let player_acc = 0.5;
let gravity = 0.01;
let fricktion = -0.12;
let playerToPlat;
class Player {
constructor(x, y, radius, color, speedX, speedY) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.speedX = speedX;
this.speedY = speedY;
this.accY = 0;
this.accX = 0;
}
draw() {
c.beginPath();
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.fillStyle = this.color;
c.fill();
}
update() {
this.accX = 0;
this.accY = 0.5;
// apply fricktion
this.accX += this.speedX * fricktion;
// equations of motion
this.speedX += this.accX;
this.speedY += this.accY;
this.x += this.speedX + 0.5 * this.accX;
this.y += this.speedY + 0.5 * this.accY;
}
}
class Platform {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
this.intersectPt = { x: this.x, y: this.y };
}
draw() {
c.beginPath();
this.rect = c.rect(this.x, this.y, this.width, this.height);
c.fillStyle = this.color;
c.fill();
}
update() {
this.intersectPt = {
x: intersection(this.x, this.x + this.width, player.x),
y: intersection(this.y, this.y + this.width, player.x)
};
}
}
// create things
let player = new Player(
width / 12, // X
10, // Y
10, // radius
"#205FE6", // color
0, // vertical speed
0 // horizontal speed
);
// create entity lists
// list of all platforms
let platforms = [[0, 75, 200, 50, "#0E2A66"]];
let plats = [];
// get the list of platforms created above
platforms.forEach((plat, index) => {
plats.push(new Platform(plat[0], plat[1], plat[2], plat[3], plat[4]));
});
function intersection(min, max, value) {
return Math.max(min, Math.min(max, value));
}
function distance(x, y) {
return Math.hypot(x, y);
}
function collisionResponse(plat) {
plat.color = "red";
player.y = plat.y - player.radius;
player.speedY = 0;
}
let animationId;
function animate() {
animationId = requestAnimationFrame(animate);
c.fillStyle = "rgba(0, 0, 0, 0.1)";
c.fillRect(0, 0, canvas.width, canvas.height);
player.update();
player.draw();
// draw each platform accordingly
plats.forEach((p) => {
p.draw();
p.update();
playerToPlat = distance(
p.intersectPt.x - player.x,
p.intersectPt.y - player.y
);
if (playerToPlat < player.radius) {
collisionResponse(p);
}
});
}
// start animate loop
animate();
.gameWindow {
border: 2px solid white;
background-color: #6790EA;
}
<canvas id="gameWindow"></canvas>

请注意,我确实改变了播放器和平台的y值,使其在Stackoverflow预览窗口中可见。

还应始终使用let、var或const以正确的方式赋值变量。

最新更新