我的js平台游戏中的碰撞检测。无法停止物体,但检测有效



我正在创建一个平台游戏,我正在添加碰撞,但我不确定在检测到碰撞后如何停止对象。这是我的javascript,我有一个带有标记的基本html文档。在我检测到碰撞后,有人能帮我把物体停下来吗?我觉得我的解决方案太复杂了。

const canvas = document.getElementById('canvas')
const ctx = canvas.getContext('2d')
const characterImage = document.getElementById('character')
const level = 1
canvas.width = document.body.scrollWidth
canvas.height = document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
let isGrounded; // Check if player is on the ground
class Main {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = .01;
// this.gravitySpeed = 0;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.dx = 0;
this.dy = 0;
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
if (this.x <= -0) {
this.x = -0
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50
}
const player = {
image: characterImage,
x: this.x,
y: this.y,
w: 50,
h: 50
}
const obstacle1 = {
x: this.centerX,
y: canvas.height - 100,
w: 50,
h: 50
}
const lava = {
}
const ground = {
x: 0,
y: canvas.height - 50,
w: canvas.width,
h: 50
}
//collision detection

if (player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y) {
}


ctx.beginPath();
ctx.fillStyle = '#9b7653'
ctx.fillRect(ground.x, ground.y, ground.w, ground.h)
ctx.closePath();
ctx.beginPath();
ctx.drawImage(player.image, player.x, player.y, player.w, player.h);
ctx.closePath();
//obstacles
ctx.beginPath();
ctx.fillStyle = '#df4759'
ctx.fillRect(obstacle1.x, obstacle1.y, obstacle1.w, obstacle1.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
}
update() {

// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
// Update y-position based speed in y-direction
// If we jump this.speed will be set to this.jumpSpeed
this.y += this.speedY * deltaTime;
// Gravity should always affect the player!
// The ground check will make sure we don't fall through the floor
this.y += this.gravity * deltaTime;
// Make sure to reduce our player's speed in y by gravity!
this.speedY += this.gravity * deltaTime;
// Only allow the player to jump if he is on the ground
if (controller1.up && isGrounded) {
// Set the player y-speed to jump speed
this.speedY = this.jumpSpeed;
};
if (controller1.right) {
this.dx += 0.7
};
if (controller1.left) {
this.dx -= 0.7
};
this.x += this.dx;
// this.y += this.dy;
this.dx *= 0.9;
this.dy *= 0.9;
// Ground check
if (this.y >= canvas.height - 100) {
this.y = canvas.height - 100;
isGrounded = true;
} else {
isGrounded = false;
}

// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == 'keydown'
};
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == 'keydown'
};
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == 'keydown'
};
}
addEventListener('keydown', keyEvent);
addEventListener('keyup', keyEvent);
addEventListener('mousemove', keyEvent)
}
}
let main1 = new Main(50, canvas.height - 150, 50, 50)
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
main1.update();
main1.draw();
requestAnimationFrame(animate)
}
function updatePos() {
main1.newPos();
}
animate()
setInterval(updatePos, 10)
<canvas id="canvas"></canvas>
<img src="http://placekitten.com/50/50" id="character" style="display: none">

看起来您的代码为角色提供了两组速度:this.speedY提供跳跃的垂直速度,this.dx提供控制器输入的水平速度,而this.dy实际上没有使用。因此,如果你想让角色停止碰撞,你可以使用这样的东西:

if (player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y) {
this.dx = this.speedY = 0;
}

您可能希望将速度统一为一对值,例如this.dxthis.dy,并在玩家的速度变化时(从任何动作或重力(进行调整。

如果您想在玩家碰撞时禁用某些操作,您可能需要设置一个可以在update()函数中使用的变量。例如:

this.isColliding =
player.x < obstacle1.x + obstacle1.w &&
player.x + obstacle1.w > obstacle1.x &&
player.y < obstacle1.y + obstacle1.h &&
player.y + player.h > obstacle1.y;
if (this.isColliding) {
this.dx = this.speedY = 0;
}

另一个注意事项是,prevTimeisGrounded实际上应该是类(this.prevTime等(的属性,而不是全局变量。并且time可以声明为update()的本地,因为在其他情况下不需要它。

这种方式的碰撞检测并不像一个代码块那么简单。这是确定对象是否发生碰撞的良好开端,但您需要将该信息传递给另一个函数,以处理应根据碰撞一侧采取的操作。这被称为"宽相位"one_answers"窄相位"。

请注意,没有一个完美的公式可以处理每个矩形CD。你可能会发现自己需要在某个时候更改主函数来处理游戏中的特定对象,因为它们有不同的属性。

现在,我将在这里使用您的代码为您提供一个工作示例,但我必须强烈建议您不要像现在这样继续将所有游戏对象堆积到一个类中。这已经让事情变得困难,从长远来看只会让你的游戏变得更加复杂。你应该打破你的障碍,熔岩,地面等,并给每个人自己的类。我还注意到你正试图把所有的CD都放进课堂。这有时是可以的,但对于你目前正在努力实现的目标,我会有单独的功能。

在这个片段中,你会注意到玩家在回到原地之前确实进入了地面和障碍物。由于代码的编写方式,代码总体上有点挑剔。

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const characterImage = document.getElementById("character");
const level = 1;
canvas.width = document.body.scrollWidth;
canvas.height = 400//document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
let player, obstacle1, obstacle2, ground;
class Main {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = 0.01;
this.jumping = false;
//this.gravitySpeed = 0;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.dx = 0;
this.dy = 0;
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
if (this.x <= -0) {
this.x = -0;
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50;
}
player = {
image: characterImage,
x: this.x,
y: this.y,
w: 50,
h: 50
};
obstacle1 = {
x: this.centerX,
y: canvas.height - 100,
w: 50,
h: 50
};
obstacle2 = {
x: this.centerX + 50,
y: canvas.height - 100,
w: 50,
h: 50
};
const lava = {};
ground = {
x: 0,
y: canvas.height - 50,
w: canvas.width,
h: 50
};
ctx.beginPath();
ctx.fillStyle = "#9b7653";
ctx.fillRect(ground.x, ground.y, ground.w, ground.h);
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = "pink";
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.closePath();
//obstacles
ctx.beginPath();
ctx.fillStyle = "#df4759";
ctx.fillRect(obstacle1.x, obstacle1.y, obstacle1.w, obstacle1.h);
ctx.closePath();
ctx.beginPath();
ctx.fillStyle = "#df4759";
ctx.fillRect(obstacle2.x, obstacle2.y, obstacle2.w, obstacle2.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
this.x += this.speedX;
}
update() {
// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
if (controller1.up && !this.jumping) {
// Set the player y-speed to jump speed
this.speedY = this.jumpSpeed;
this.jumping = true; //prevents jumping while already in air
}
if (controller1.right) {
this.dx += 0.7;
}
if (controller1.left) {
this.dx -= 0.7;
}

this.y += this.speedY * deltaTime;
this.y += this.gravity * deltaTime;
this.speedY += this.gravity * deltaTime;
this.x += this.dx;
// this.y += this.dy;
this.dx *= 0.9;
this.dy *= 0.9;
// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == "keydown";
}
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == "keydown";
}
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == "keydown";
}
};
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
addEventListener("mousemove", keyEvent);
}
}
function collision(player, obj) {
if (
player.x + player.w < obj.x ||
player.x > obj.x + obj.w ||
player.y + player.h < obj.y ||
player.y > obj.y + obj.h
) {
return;
}
this.narrowPhase(player, obj);
}
function narrowPhase(player, obj) {
let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
let playerRight_ObjLeft = Math.abs(player.x + player.w - obj.x);
let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
let playerBottom_ObjTop = Math.abs(player.y + player.h - obj.y);
if (
player.y <= obj.y + obj.h &&
player.y + player.h > obj.y + obj.h &&
playerTop_ObjBottom < playerRight_ObjLeft &&
playerTop_ObjBottom < playerLeft_ObjRight
) {
main1.y = obj.y + obj.h;
main1.speedY = 0;
}
if (
player.y + player.h >= obj.y &&
player.y < obj.y &&
playerBottom_ObjTop < playerRight_ObjLeft &&
playerBottom_ObjTop < playerLeft_ObjRight
) {
main1.y = obj.y - player.h - 0.05;
main1.speedY = 0;
main1.jumping = false;
}
if (
player.x + player.w >= obj.x &&
player.x < obj.x &&
playerRight_ObjLeft < playerTop_ObjBottom &&
playerRight_ObjLeft < playerBottom_ObjTop
) {
main1.x = obj.x - obj.w - 0.05;
main1.dx = 0;
}
if (
player.x <= obj.x + obj.w &&
player.x + player.w > obj.x + obj.w &&
playerLeft_ObjRight < playerTop_ObjBottom &&
playerLeft_ObjRight < playerBottom_ObjTop
) {
main1.x = obj.x + obj.w + 0.05;
main1.dx = 0;
}
}
let main1 = new Main(50, canvas.height - 250, 50, 50);
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
main1.draw();
main1.update();
collision(player, obstacle2);
collision(player, obstacle1);
collision(player, ground);
requestAnimationFrame(animate);
}
function updatePos() {
main1.newPos();
}
animate();
setInterval(updatePos, 10);
<canvas id="canvas"></canvas>

我将提供第二个例子,在这个例子中,我已经从主类中删除了对象,并使每个对象都成为自己的对象。我还删除了dxdy,因为我认为它们存在混淆,并将它们与speedXspeedY混合。另一方面,在你的newPos()函数中,你有this.x += this.speedX;,但在更新函数中也有,所以实际上你正在加倍。我不这么认为;这就是你想要的。

你会在这个例子中看到碰撞更加平滑

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const characterImage = document.getElementById("character");
const level = 1;
canvas.width = document.body.scrollWidth;
canvas.height = 400//document.body.scrollHeight
let time; // Current time
let prevTime = Date.now(); // Store previous time
class Entity {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.lives = 3;
this.speedX = 0;
this.speedY = 0;
this.gravity = 0.01;
this.jump = false;
this.jumpSpeed = -1.5; // How fast to jump upwards
this.centerX = canvas.width / 2;
this.centerY = canvas.height / 2;
}
draw() {
ctx.beginPath();
ctx.fillStyle = "pink";
ctx.fillRect(player.x, player.y, player.w, player.h);
ctx.closePath();
}
newPos() {
this.gravitySpeed += this.gravity;
//this.x += this.speedX;
}
collision() {
if (this.x <= -0) {
this.x = -0;
}
if (this.x >= canvas.width - 50) {
this.x = canvas.width - 50;
}
}
update() {
// Calculate how much time has passed since last update
time = Date.now();
const deltaTime = time - prevTime;
if (controller1.up && !this.jump) {
this.speedY = this.jumpSpeed;
this.jump = true;
}
if (controller1.right) {
this.speedX += 0.7;
}
if (controller1.left) {
this.speedX -= 0.7;
}

this.y += this.speedY * deltaTime;
this.y += this.gravity * deltaTime;
this.speedY += this.gravity * deltaTime;
this.x += this.speedX;
this.speedX *= 0.9;
this.speedY *= 0.9;
// Store the current time to use for calculation in next update
prevTime = Date.now();
}
}
class Obstacle {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = '#df4759';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
/*
class Lava {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = 'red';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
*/
class Ground {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = '#8a6c4e';
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
}
}
class Controller {
constructor() {
this.up = false;
this.right = false;
this.down = false;
this.left = false;
let keyEvent = (e) => {
if (e.code == "KeyW" || e.code == "ArrowUp" || e.code == "Space") {
this.up = e.type == "keydown";
}
if (e.code == "KeyD" || e.code == "ArrowRight") {
this.right = e.type == "keydown";
}
if (e.code == "KeyA" || e.code == "ArrowLeft") {
this.left = e.type == "keydown";
}
};
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
addEventListener("mousemove", keyEvent);
}
}
function collision(player, obj) {
if (
player.x + player.w < obj.x ||
player.x > obj.x + obj.w ||
player.y + player.h < obj.y ||
player.y > obj.y + obj.h
) {
return;
}
this.narrowPhase(player, obj);
}
function narrowPhase(player, obj) {
let playerTop_ObjBottom = Math.abs(player.y - (obj.y + obj.h));
let playerRight_ObjLeft = Math.abs(player.x + player.w - obj.x);
let playerLeft_ObjRight = Math.abs(player.x - (obj.x + obj.w));
let playerBottom_ObjTop = Math.abs(player.y + player.h - obj.y);
if (
player.y <= obj.y + obj.h &&
player.y + player.h > obj.y + obj.h &&
playerTop_ObjBottom < playerRight_ObjLeft &&
playerTop_ObjBottom < playerLeft_ObjRight
) {
player.y = obj.y + obj.h;
player.speedY = 0;
}
if (
player.y + player.h >= obj.y &&
player.y < obj.y &&
playerBottom_ObjTop < playerRight_ObjLeft &&
playerBottom_ObjTop < playerLeft_ObjRight
) {
player.y = obj.y - player.h;
// player.dy = 0;
player.speedY = 0;
player.jump = false;
}
if (
player.x + player.w >= obj.x &&
player.x < obj.x &&
playerRight_ObjLeft < playerTop_ObjBottom &&
playerRight_ObjLeft < playerBottom_ObjTop
) {
player.x = obj.x - obj.w;
player.speedX = 0;
}
if (
player.x <= obj.x + obj.w &&
player.x + player.w > obj.x + obj.w &&
playerLeft_ObjRight < playerTop_ObjBottom &&
playerLeft_ObjRight < playerBottom_ObjTop
) {
player.x = obj.x + obj.w;
player.speedX = 0;
}
}
let obstacle1 = new Obstacle(canvas.width/2, canvas.height - 100, 50, 50)
let obstacle2 = new Obstacle(canvas.width/2 + 50, canvas.height - 100, 50, 50)
let ground = new Ground(0, canvas.height - 50, canvas.width, 50)
let player = new Entity(50, canvas.height - 250, 50, 50);
let controller1 = new Controller();
function animate() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
collision(player, obstacle2);
collision(player, obstacle1);
collision(player, ground);
player.draw();
player.update();
ground.draw();
obstacle1.draw();
obstacle2.draw();
requestAnimationFrame(animate);
}
function updatePos() {
player.newPos();
}
animate();
setInterval(updatePos, 10);
<canvas id="canvas"></canvas>

最新更新