二维正方形到矩形的碰撞检测和动作(物理)
2d Square to rectangle collision detection and action(physics)
所以我用这种方法来检测移动和接触其他方块的方块并做出反应。它使用毕达哥拉斯定理来制作第三个参数,因此当正方形接触时您不会得到两个 true if 语句。我最近一直在尝试在方形到矩形上使用此方法,但似乎无法正常工作。我画了线来帮助可视化代码在做什么。有人对如何使此碰撞正常工作有任何建议吗?
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') { this.right1 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left1 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up1 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw(){
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update(){
this.draw();
this.y += this.yv;
this.yv *= 0.9;// friction
}
}
let platform1 = new Platform(canvas.width/2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform (canvas.width/4, canvas.height/2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2){
//center point of each side of obj1
let objLeft = {x: obj.x, y: obj.y + obj.h/2};
let objTop = {x: obj.x + obj.w/2, y: obj.y};
let objRight = {x: obj.x + obj.w, y: obj.y + obj.h/2};
let objBottom = {x: obj.x + obj.w/2, y: obj.y + obj.h};
//center point of each side a obj2
let obj2Left = {x: obj2.x, y: obj2.y + obj2.h/2};
let obj2Top = {x: obj2.x + obj2.w/2, y: obj2.y};
let obj2Right = {x: obj2.x + obj2.w, y: obj2.y + obj2.h/2};
let obj2Bottom = {x: obj2.x + obj2.w/2, y: obj2.y + obj2.h};
//distance between obj1 and obj2 opposing sides
let rightDistX = objRight.x - obj2Left.x;
let rightDistY = objRight.y - obj2Left.y;
let leftDistX = objLeft.x - obj2Right.x;
let leftDistY = objLeft.y - obj2Right.y;
let topDistX = objTop.x - obj2Bottom.x;
let topDistY = objTop.y - obj2Bottom.y;
let bottomDistX = objBottom.x - obj2Top.x;
let bottomDistY = objBottom.y - obj2Top.y;
//pythagorean theorem for distance. dRight is from the right side of obj1 to the left of obj2. the rest follow suit.
let dRight = Math.sqrt(rightDistX*rightDistX + rightDistY*rightDistY);
let dLeft = Math.sqrt(leftDistX*leftDistX + leftDistY*leftDistY);
let dTop = Math.sqrt(topDistX*topDistX + topDistY*topDistY);
let dBottom = Math.sqrt(bottomDistX*bottomDistX + bottomDistY*bottomDistY);
//Math.min return the smallest value thus variable minimum will be which ever sides are closest together
let minimum = Math.min(dRight, dLeft, dBottom, dTop);
let val = 0;
//compare minimum to pythagorean theorem and set val based on which ever side is closest
if (dTop == minimum) {
val = 1;
//the context stuff can be deleted. It's just here for visual. The if statements can be one line each.
context.lineWidth = 2;
context.strokeStyle = 'blue';
context.beginPath();
context.moveTo(objTop.x, objTop.y);
context.lineTo(obj2Bottom.x, obj2Bottom.y);
context.stroke();
}
else if (dRight == minimum) {
val = 2;
context.strokeStyle = 'orange';
context.beginPath();
context.moveTo(objRight.x, objRight.y);
context.lineTo(obj2Left.x, obj2Left.y);
context.stroke();
}
else if (dBottom == minimum) {
val = 3;
context.strokeStyle = 'green';
context.beginPath();
context.moveTo(objBottom.x, objBottom.y);
context.lineTo(obj2Top.x, obj2Top.y);
context.stroke();
}
else if (dLeft == minimum) {
val = 4;
context.strokeStyle = 'pink';
context.beginPath();
context.moveTo(objLeft.x, objLeft.y);
context.lineTo(obj2Right.x, obj2Right.y);
context.stroke();
}
//pass the objects and val
platformAction(obj, obj2, val);
}
//ACTION
function platformAction(obj, obj2, val){
//player1 top to player2 bottom
if (obj.y <= obj2.y + obj2.h && obj2.y + obj2.h >= obj.y && val == 1) {
obj2.y = obj.y - obj2.h;
obj.y = obj2.y + obj2.h;
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (obj.x + obj.w >= obj2.x && obj2.x <= obj.x + obj.w && val == 2) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (obj.y + obj.h >= obj2.y && obj2.y <= obj.y + obj.h && val == 3) {
obj.y = obj2.y - obj.h;
obj2.y = obj.y + obj.h;
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (obj.x <= obj2.x + obj2.w && obj2.x + obj2.w >= obj.x && val == 4) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj){
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
body {
height:100vh;
width:100vh;
margin: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src = "Js/Tone.js" type = "text/javascript"></script>
</body>
</html>
@MPdoor2“我做了……”。任何人,当我给你那个代码时,我确实明确地说我已经从我创建的用于 tilemaps 的方法中破解了它。该方法为此目的完美无瑕,尽管代码更多,是的,它是为正方形构建的,因为这就是瓷砖地图的主要内容。
我一直在尝试其他制作 CD 的方法。这是一个较短的方法(到目前为止)似乎运行良好。这种方法仍然确定每边之间的距离,但方式不同。一旦 broadphase 确定发生了碰撞,它就会调用 narrow phase,并且距离最短的一侧就是被穿透的一侧。即,当您与从玩家右侧到物体左侧的另一个方块发生碰撞时,我们知道即使是 Y 轴也会穿透(玩家的顶角和底角)。这计算了所有三个之间的距离,并且由于 X 之间的距离为 0,因此它是最短的,并且不会调用用于在 Y 方向上移动播放器的 CD。
试试下面的代码片段,看看是否适合您。
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = 700;
canvas.height = 500;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, vx, vy, c, j) {
//each player must have separate values for control purposes i.e. velocity/jump/attack etc.
this.x = x;
this.y = y;
this.w = 50;
this.h = 50;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
this.canvasCollision() //must be after other updates
}
}
let player = new Player(0, 0, 0, 0, 'red', false);
function controlPlayer1(obj) {
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
class Platform {
constructor(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = 'blue';
context.fillRect(this.x, this.y, this.w, this.h);
}
}
let platforms = [];
function createPlatforms() {
for (let i=0; i<4; i++) {
platforms.push(new Platform(200 * i, 450 - (i * 75), 100, 25));
}
}
createPlatforms();
let platform1 = platforms.push(new Platform(300, 400, 50, 100));
let platform2 = platforms.push(new Platform(400, 400, 50, 50));
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
function collisionDetection(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
}
narrowPhase(obj);
}
function narrowPhase(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.vy = 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.jumping = false;
player.vy = 0;
}
if ((player.x + player.w >= obj.x && player.x < obj.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
player.x = obj.x - player.w;
player.vx = 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.vx = 0;
}
}
function animate() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
controlPlayer1(player);
for (let i=0;i<platforms.length;i++) {
platforms[i].draw();
collisionDetection(platforms[i])
};
requestAnimationFrame(animate)
}
animate();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
<title>CD</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="squareRectCollision2.js"></script>
</body>
</html>
要检测矩形之间的碰撞,请使用它们边的位置。例如,它们“垂直碰撞”如果:
- object1 的底部在 object2 的底部下方,object1 的顶部在 object2 的底部上方
- 或对象1的顶部在对象2的顶部之上,对象1的底部在对象2的顶部之下
如果它们同时“垂直”和“水平”碰撞,请考虑它们确实会发生碰撞。
现在,一旦确定了这一点,就需要检测从哪个边缘开始。我在这里使用最近的一个,评估所有“垂直碰撞”或“水平碰撞”的距离。
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = Math.min(window.innerWidth - 3, 800);
canvas.height = Math.min(window.innerHeight - 3, 200);
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {
this.y = canvas.height - this.h;
this.vy = 0;
this.jumping = false
};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height / 2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) {
obj.vy -= 25;
obj.jumping = true
};
if (controller1.left1) {
obj.vx -= 0.5
};
if (controller1.right1) {
obj.vx += 0.5
};
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') {
this.right1 = e.type === 'keydown'
}
if (e.code === 'KeyA') {
this.left1 = e.type === 'keydown'
}
if (e.code === 'KeyW') {
this.up1 = e.type === 'keydown'
}
if (e.code === 'KeyS') {
this.down1 = e.type === 'keydown'
}
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update() {
this.draw();
this.y += this.yv;
this.yv *= 0.9; // friction
}
}
let platform1 = new Platform(canvas.width / 2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform(canvas.width / 4, canvas.height / 2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2) {
let ol1 = obj.x;
let ot1 = obj.y;
let or1 = ol1 + obj.w;
let ob1 = ot1 + obj.h;
let ol2 = obj2.x;
let ot2 = obj2.y;
let or2 = ol2 + obj2.w;
let ob2 = ot2 + obj2.h;
let [collideTop, collideBottom, collideRight, collideLeft] = [-1, -1, -1, -1];
if (ob1 >= ob2) {
if (ot1 <= ob2) {
collideTop = ob2 - ot1;
}
}
if (ol1 <= ol2) {
if (or1 >= ol2) {
collideRight = or1 - ol2;
}
}
if (ot1 <= ot2) {
if (ob1 >= ot2) {
collideBottom = ob1 - ot2;
}
}
if (or1 >= or2) {
if (ol1 <= or2) {
collideLeft = or2 - ol1;
}
}
let min = minOfVals(collideTop, collideRight, collideBottom, collideLeft);
let val = 0;
if (min >= 0) {
if (min == collideTop && (collideLeft >= 0 || collideRight >= 0)) {
val = 1;
} else if (min == collideRight && (collideTop >= 0 || collideBottom >= 0)) {
val = 2;
} else if (min == collideBottom && (collideLeft >= 0 || collideRight >= 0)) {
val = 3;
} else if (min == collideLeft && (collideTop >= 0 || collideBottom >= 0)) {
val = 4;
}
}
if (val) console.log(val, min, collideTop, collideRight, collideBottom, collideLeft);
//pass the objects and val
platformAction(obj, obj2, val);
}
function minOfVals(...vals) {
let min = -1;
function isBelowMin(v) {
return v >= 0 && (min < 0 || v < min);
}
for (v of vals) {
if (isBelowMin(v)) min = v;
}
return min;
}
//ACTION
function platformAction(obj, obj2, val) {
//player1 top to player2 bottom
if (val == 1) {
console.log("colliding from top");
obj2.y = obj.y - obj2.h;
//obj.y = obj2.y + obj2.h; //useless
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (val == 2) {
console.log("colliding from right");
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (val == 3) {
console.log("colliding from bottom");
obj.y = obj2.y - obj.h;
//obj2.y = obj.y + obj.h; //useless
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (val == 4) {
console.log("colliding from left");
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj) {
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vh;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="Js/Tone.js" type="text/javascript"></script>
</body>
</html>
当你控制的玩家来自左侧或右侧时,仍然存在不同的行为:推动速度不一样。但这不是由于碰撞,所以我让你弄清楚这个(也许也是故意的)。
哟,谢谢贾斯汀的解决方案。我采用了该解决方案并将其添加到我之前问过的一个问题中,即如何对两个有运动的方块(带有箭头和 wasd)进行碰撞检测和操作。我是这样添加的:
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(canvas.width/2, canvas.height/2 + 75, 75, 75, 0, 0, 'darkgrey', false);
let player2 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
function controlPlayer2(obj) {
if (controller2.up2 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller2.right2) { obj.vx += 0.5 };
if (controller2.left2) { obj.vx -= 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
this.down = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
if (e.code === 'ArrowDown') { this.down = e.type === 'keydown' }
}
let controller2 = (e) => {
if (e.code === 'KeyD') { this.right2 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left2 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up2 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
window.addEventListener('keydown', controller2);
window.addEventListener('keyup', controller2);
}
}
let controller1 = new Controller();
let controller2 = new Controller();
//COLLISION DETECTION
function collisionDetection(obj, obj2){
if (obj.x + obj.w <= obj2.x ||
obj.x >= obj2.x + obj2.w ||
obj.y + obj.h < obj2.y ||
obj.y > obj2.y + obj2.h)
return
collisionAction(obj, obj2);
}
//ACTION
function collisionAction(obj, obj2){
let playerTop_ObjBottom = Math.abs(obj.y - (obj2.y + obj2.h));
let playerRight_ObjLeft = Math.abs((obj.x + obj.w) - obj2.x);
let playerLeft_ObjRight = Math.abs(obj.x - (obj2.x + obj2.w));
let playerBottom_ObjTop = Math.abs((obj.y + obj.h) - obj2.y);
//virtical obj 1 top
if ((obj.y <= obj2.y + obj2.h && obj.y + obj.h > obj2.y + obj2.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
obj2.y = obj.y - obj2.h;
obj2.jumping = false;
obj2.vy = 0;}
//virtical obj 1 bottom
if ((obj.y + obj.h >= obj2.y && obj.y < obj2.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
obj.y = obj2.y - obj.h;
obj.jumping = false;
obj.vy = 0;}
//horizontal obj 1 right
if ((obj.x + obj.w >= obj2.x && obj.x < obj2.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w;
obj.vx = 0;
obj2.vx = 0;}
//horizontal obj1 left
if ((obj.x <= obj2.x + obj2.w && obj.x + obj.w > obj2.x + obj2.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w;
obj.vx = 0;
obj2.vx = 0;}
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLAYER
controlPlayer1(player1);
controlPlayer2(player2);
collisionDetection(player1, player2)
requestAnimationFrame(loop)
}
loop();
所以我用这种方法来检测移动和接触其他方块的方块并做出反应。它使用毕达哥拉斯定理来制作第三个参数,因此当正方形接触时您不会得到两个 true if 语句。我最近一直在尝试在方形到矩形上使用此方法,但似乎无法正常工作。我画了线来帮助可视化代码在做什么。有人对如何使此碰撞正常工作有任何建议吗?
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') { this.right1 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left1 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up1 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw(){
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update(){
this.draw();
this.y += this.yv;
this.yv *= 0.9;// friction
}
}
let platform1 = new Platform(canvas.width/2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform (canvas.width/4, canvas.height/2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2){
//center point of each side of obj1
let objLeft = {x: obj.x, y: obj.y + obj.h/2};
let objTop = {x: obj.x + obj.w/2, y: obj.y};
let objRight = {x: obj.x + obj.w, y: obj.y + obj.h/2};
let objBottom = {x: obj.x + obj.w/2, y: obj.y + obj.h};
//center point of each side a obj2
let obj2Left = {x: obj2.x, y: obj2.y + obj2.h/2};
let obj2Top = {x: obj2.x + obj2.w/2, y: obj2.y};
let obj2Right = {x: obj2.x + obj2.w, y: obj2.y + obj2.h/2};
let obj2Bottom = {x: obj2.x + obj2.w/2, y: obj2.y + obj2.h};
//distance between obj1 and obj2 opposing sides
let rightDistX = objRight.x - obj2Left.x;
let rightDistY = objRight.y - obj2Left.y;
let leftDistX = objLeft.x - obj2Right.x;
let leftDistY = objLeft.y - obj2Right.y;
let topDistX = objTop.x - obj2Bottom.x;
let topDistY = objTop.y - obj2Bottom.y;
let bottomDistX = objBottom.x - obj2Top.x;
let bottomDistY = objBottom.y - obj2Top.y;
//pythagorean theorem for distance. dRight is from the right side of obj1 to the left of obj2. the rest follow suit.
let dRight = Math.sqrt(rightDistX*rightDistX + rightDistY*rightDistY);
let dLeft = Math.sqrt(leftDistX*leftDistX + leftDistY*leftDistY);
let dTop = Math.sqrt(topDistX*topDistX + topDistY*topDistY);
let dBottom = Math.sqrt(bottomDistX*bottomDistX + bottomDistY*bottomDistY);
//Math.min return the smallest value thus variable minimum will be which ever sides are closest together
let minimum = Math.min(dRight, dLeft, dBottom, dTop);
let val = 0;
//compare minimum to pythagorean theorem and set val based on which ever side is closest
if (dTop == minimum) {
val = 1;
//the context stuff can be deleted. It's just here for visual. The if statements can be one line each.
context.lineWidth = 2;
context.strokeStyle = 'blue';
context.beginPath();
context.moveTo(objTop.x, objTop.y);
context.lineTo(obj2Bottom.x, obj2Bottom.y);
context.stroke();
}
else if (dRight == minimum) {
val = 2;
context.strokeStyle = 'orange';
context.beginPath();
context.moveTo(objRight.x, objRight.y);
context.lineTo(obj2Left.x, obj2Left.y);
context.stroke();
}
else if (dBottom == minimum) {
val = 3;
context.strokeStyle = 'green';
context.beginPath();
context.moveTo(objBottom.x, objBottom.y);
context.lineTo(obj2Top.x, obj2Top.y);
context.stroke();
}
else if (dLeft == minimum) {
val = 4;
context.strokeStyle = 'pink';
context.beginPath();
context.moveTo(objLeft.x, objLeft.y);
context.lineTo(obj2Right.x, obj2Right.y);
context.stroke();
}
//pass the objects and val
platformAction(obj, obj2, val);
}
//ACTION
function platformAction(obj, obj2, val){
//player1 top to player2 bottom
if (obj.y <= obj2.y + obj2.h && obj2.y + obj2.h >= obj.y && val == 1) {
obj2.y = obj.y - obj2.h;
obj.y = obj2.y + obj2.h;
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (obj.x + obj.w >= obj2.x && obj2.x <= obj.x + obj.w && val == 2) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (obj.y + obj.h >= obj2.y && obj2.y <= obj.y + obj.h && val == 3) {
obj.y = obj2.y - obj.h;
obj2.y = obj.y + obj.h;
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (obj.x <= obj2.x + obj2.w && obj2.x + obj2.w >= obj.x && val == 4) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj){
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
body {
height:100vh;
width:100vh;
margin: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src = "Js/Tone.js" type = "text/javascript"></script>
</body>
</html>
@MPdoor2“我做了……”。任何人,当我给你那个代码时,我确实明确地说我已经从我创建的用于 tilemaps 的方法中破解了它。该方法为此目的完美无瑕,尽管代码更多,是的,它是为正方形构建的,因为这就是瓷砖地图的主要内容。
我一直在尝试其他制作 CD 的方法。这是一个较短的方法(到目前为止)似乎运行良好。这种方法仍然确定每边之间的距离,但方式不同。一旦 broadphase 确定发生了碰撞,它就会调用 narrow phase,并且距离最短的一侧就是被穿透的一侧。即,当您与从玩家右侧到物体左侧的另一个方块发生碰撞时,我们知道即使是 Y 轴也会穿透(玩家的顶角和底角)。这计算了所有三个之间的距离,并且由于 X 之间的距离为 0,因此它是最短的,并且不会调用用于在 Y 方向上移动播放器的 CD。
试试下面的代码片段,看看是否适合您。
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = 700;
canvas.height = 500;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, vx, vy, c, j) {
//each player must have separate values for control purposes i.e. velocity/jump/attack etc.
this.x = x;
this.y = y;
this.w = 50;
this.h = 50;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
this.canvasCollision() //must be after other updates
}
}
let player = new Player(0, 0, 0, 0, 'red', false);
function controlPlayer1(obj) {
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
class Platform {
constructor(x,y,w,h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = 'blue';
context.fillRect(this.x, this.y, this.w, this.h);
}
}
let platforms = [];
function createPlatforms() {
for (let i=0; i<4; i++) {
platforms.push(new Platform(200 * i, 450 - (i * 75), 100, 25));
}
}
createPlatforms();
let platform1 = platforms.push(new Platform(300, 400, 50, 100));
let platform2 = platforms.push(new Platform(400, 400, 50, 50));
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
function collisionDetection(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
}
narrowPhase(obj);
}
function narrowPhase(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.vy = 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.jumping = false;
player.vy = 0;
}
if ((player.x + player.w >= obj.x && player.x < obj.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
player.x = obj.x - player.w;
player.vx = 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.vx = 0;
}
}
function animate() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
controlPlayer1(player);
for (let i=0;i<platforms.length;i++) {
platforms[i].draw();
collisionDetection(platforms[i])
};
requestAnimationFrame(animate)
}
animate();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height">
<title>CD</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="squareRectCollision2.js"></script>
</body>
</html>
要检测矩形之间的碰撞,请使用它们边的位置。例如,它们“垂直碰撞”如果:
- object1 的底部在 object2 的底部下方,object1 的顶部在 object2 的底部上方
- 或对象1的顶部在对象2的顶部之上,对象1的底部在对象2的顶部之下
如果它们同时“垂直”和“水平”碰撞,请考虑它们确实会发生碰撞。
现在,一旦确定了这一点,就需要检测从哪个边缘开始。我在这里使用最近的一个,评估所有“垂直碰撞”或“水平碰撞”的距离。
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = Math.min(window.innerWidth - 3, 800);
canvas.height = Math.min(window.innerHeight - 3, 200);
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {
this.y = canvas.height - this.h;
this.vy = 0;
this.jumping = false
};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(75, canvas.height / 2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) {
obj.vy -= 25;
obj.jumping = true
};
if (controller1.left1) {
obj.vx -= 0.5
};
if (controller1.right1) {
obj.vx += 0.5
};
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.down1 = false;
let controller1 = (e) => {
if (e.code === 'KeyD') {
this.right1 = e.type === 'keydown'
}
if (e.code === 'KeyA') {
this.left1 = e.type === 'keydown'
}
if (e.code === 'KeyW') {
this.up1 = e.type === 'keydown'
}
if (e.code === 'KeyS') {
this.down1 = e.type === 'keydown'
}
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
}
}
let controller1 = new Controller();
//PLATFORM
class Platform {
constructor(x, y, w, h, yv, c) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.yv = yv;
this.color = c;
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
update() {
this.draw();
this.y += this.yv;
this.yv *= 0.9; // friction
}
}
let platform1 = new Platform(canvas.width / 2, canvas.height - 150, 100, 30, 0, '#202020');
let platform2 = new Platform(canvas.width / 4, canvas.height / 2, 75, 75, 0, '#202020');
//COLLISION DETECTION
function platformDetection(obj, obj2) {
let ol1 = obj.x;
let ot1 = obj.y;
let or1 = ol1 + obj.w;
let ob1 = ot1 + obj.h;
let ol2 = obj2.x;
let ot2 = obj2.y;
let or2 = ol2 + obj2.w;
let ob2 = ot2 + obj2.h;
let [collideTop, collideBottom, collideRight, collideLeft] = [-1, -1, -1, -1];
if (ob1 >= ob2) {
if (ot1 <= ob2) {
collideTop = ob2 - ot1;
}
}
if (ol1 <= ol2) {
if (or1 >= ol2) {
collideRight = or1 - ol2;
}
}
if (ot1 <= ot2) {
if (ob1 >= ot2) {
collideBottom = ob1 - ot2;
}
}
if (or1 >= or2) {
if (ol1 <= or2) {
collideLeft = or2 - ol1;
}
}
let min = minOfVals(collideTop, collideRight, collideBottom, collideLeft);
let val = 0;
if (min >= 0) {
if (min == collideTop && (collideLeft >= 0 || collideRight >= 0)) {
val = 1;
} else if (min == collideRight && (collideTop >= 0 || collideBottom >= 0)) {
val = 2;
} else if (min == collideBottom && (collideLeft >= 0 || collideRight >= 0)) {
val = 3;
} else if (min == collideLeft && (collideTop >= 0 || collideBottom >= 0)) {
val = 4;
}
}
if (val) console.log(val, min, collideTop, collideRight, collideBottom, collideLeft);
//pass the objects and val
platformAction(obj, obj2, val);
}
function minOfVals(...vals) {
let min = -1;
function isBelowMin(v) {
return v >= 0 && (min < 0 || v < min);
}
for (v of vals) {
if (isBelowMin(v)) min = v;
}
return min;
}
//ACTION
function platformAction(obj, obj2, val) {
//player1 top to player2 bottom
if (val == 1) {
console.log("colliding from top");
obj2.y = obj.y - obj2.h;
//obj.y = obj2.y + obj2.h; //useless
obj2.vy = 0;
obj2.jumping = false;
obj.jumping = true;
}
//player1 right to player2 left
if (val == 2) {
console.log("colliding from right");
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w - 1;
obj2.vx = 0;
}
//player1 bottom to player2 top
if (val == 3) {
console.log("colliding from bottom");
obj.y = obj2.y - obj.h;
//obj2.y = obj.y + obj.h; //useless
obj.vy = 0;
obj.jumping = false;
obj2.jumping = true;
}
//player1 left to player2 right
if (val == 4) {
console.log("colliding from left");
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w + 1;
obj.vx = 0;
obj2.vx = 0;
}
}
function initObj(obj) {
obj.update();
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLATFORM
initObj(platform1);
initObj(platform2);
//PLATFORM DETECTION
platformDetection(player1, platform1);
platformDetection(player1, platform2);
//PLAYER
controlPlayer1(player1);
requestAnimationFrame(loop)
}
loop();
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi">
<title>Tone.io</title>
<style>
html {
margin: 0;
padding: 0;
}
body {
height: 100vh;
width: 100vh;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="Js/Tone.js" type="text/javascript"></script>
</body>
</html>
当你控制的玩家来自左侧或右侧时,仍然存在不同的行为:推动速度不一样。但这不是由于碰撞,所以我让你弄清楚这个(也许也是故意的)。
哟,谢谢贾斯汀的解决方案。我采用了该解决方案并将其添加到我之前问过的一个问题中,即如何对两个有运动的方块(带有箭头和 wasd)进行碰撞检测和操作。我是这样添加的:
const canvas = document.getElementById('canvas');
const context = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
let gravity = 1.5;
let friction = 0.9;
//CHARACTER:
class Player {
constructor(x, y, w, h, vx, vy, c, j) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.vx = vx;
this.vy = vy;
this.color = c;
this.jumping = j;
this.center = {x: this.x + this.w/2, y: this.y + this.h/2};
this.topLeft = {x: this.x, y: this.y};
this.topRight = {x: this.x + this.w, y: this.y};
this.bottomRight = {x: this.x + this.w, y: this.y + this.h};
this.bottomLeft = {x: this.x, y: this.y + this.h};
}
draw() {
context.fillStyle = this.color;
context.fillRect(this.x, this.y, this.w, this.h);
}
canvasCollision() {
if (this.x <= 0) this.x = 0;
if (this.y <= 0) this.y = 0;
if (this.x + this.w >= canvas.width) this.x = canvas.width - this.w;
if (this.y + this.h >= canvas.height) {this.y = canvas.height - this.h; this.vy = 0; this.jumping = false};
}
update() {
this.draw();
this.vy += gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= friction;
this.vy *= friction;
this.canvasCollision() //must be after other updates
}
}
let player1 = new Player(canvas.width/2, canvas.height/2 + 75, 75, 75, 0, 0, 'darkgrey', false);
let player2 = new Player(75, canvas.height/2 + 75, 75, 75, 0, 0, '#8DAA9D', false);
function controlPlayer1(obj) {
//this order matters. If update is before jump then obj won't jump when on top of other block.
if (controller1.up1 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller1.left1) { obj.vx -= 0.5 };
if (controller1.right1) { obj.vx += 0.5 };
obj.update();
}
function controlPlayer2(obj) {
if (controller2.up2 && !obj.jumping) { obj.vy -= 25; obj.jumping = true };
if (controller2.right2) { obj.vx += 0.5 };
if (controller2.left2) { obj.vx -= 0.5 };
obj.update();
}
//MOVEMENT:
class Controller {
constructor() {
this.left1 = false;
this.up1 = false;
this.right1 = false;
this.left2 = false;
this.up2 = false;
this.right2 = false;
this.down = false;
let controller1 = (e) => {
if (e.code === 'ArrowRight') { this.right1 = e.type === 'keydown' }
if (e.code === 'ArrowLeft') { this.left1 = e.type === 'keydown' }
if (e.code === 'ArrowUp') { this.up1 = e.type === 'keydown' }
if (e.code === 'ArrowDown') { this.down = e.type === 'keydown' }
}
let controller2 = (e) => {
if (e.code === 'KeyD') { this.right2 = e.type === 'keydown' }
if (e.code === 'KeyA') { this.left2 = e.type === 'keydown' }
if (e.code === 'KeyW') { this.up2 = e.type === 'keydown' }
if (e.code === 'KeyS') { this.down = e.type === 'keydown' }
}
window.addEventListener('keydown', controller1);
window.addEventListener('keyup', controller1);
window.addEventListener('keydown', controller2);
window.addEventListener('keyup', controller2);
}
}
let controller1 = new Controller();
let controller2 = new Controller();
//COLLISION DETECTION
function collisionDetection(obj, obj2){
if (obj.x + obj.w <= obj2.x ||
obj.x >= obj2.x + obj2.w ||
obj.y + obj.h < obj2.y ||
obj.y > obj2.y + obj2.h)
return
collisionAction(obj, obj2);
}
//ACTION
function collisionAction(obj, obj2){
let playerTop_ObjBottom = Math.abs(obj.y - (obj2.y + obj2.h));
let playerRight_ObjLeft = Math.abs((obj.x + obj.w) - obj2.x);
let playerLeft_ObjRight = Math.abs(obj.x - (obj2.x + obj2.w));
let playerBottom_ObjTop = Math.abs((obj.y + obj.h) - obj2.y);
//virtical obj 1 top
if ((obj.y <= obj2.y + obj2.h && obj.y + obj.h > obj2.y + obj2.h) && (playerTop_ObjBottom < playerRight_ObjLeft && playerTop_ObjBottom < playerLeft_ObjRight)) {
obj2.y = obj.y - obj2.h;
obj2.jumping = false;
obj2.vy = 0;}
//virtical obj 1 bottom
if ((obj.y + obj.h >= obj2.y && obj.y < obj2.y) && (playerBottom_ObjTop < playerRight_ObjLeft && playerBottom_ObjTop < playerLeft_ObjRight)) {
obj.y = obj2.y - obj.h;
obj.jumping = false;
obj.vy = 0;}
//horizontal obj 1 right
if ((obj.x + obj.w >= obj2.x && obj.x < obj2.x) && (playerRight_ObjLeft < playerTop_ObjBottom && playerRight_ObjLeft < playerBottom_ObjTop)) {
obj2.x = obj.x + obj.w;
obj.x = obj2.x - obj.w;
obj.vx = 0;
obj2.vx = 0;}
//horizontal obj1 left
if ((obj.x <= obj2.x + obj2.w && obj.x + obj.w > obj2.x + obj2.w) && (playerLeft_ObjRight < playerTop_ObjBottom && playerLeft_ObjRight < playerBottom_ObjTop)) {
obj2.x = obj.x - obj2.w;
obj.x = obj2.x + obj2.w;
obj.vx = 0;
obj2.vx = 0;}
}
function loop() {
context.clearRect(0, 0, canvas.width, canvas.height);
context.fillStyle = 'grey';
context.fillRect(0, 0, canvas.width, canvas.height);
//PLAYER
controlPlayer1(player1);
controlPlayer2(player2);
collisionDetection(player1, player2)
requestAnimationFrame(loop)
}
loop();