Итак, в общем, вам нужно проверить столкновение между многими точками в игроке.
В фрагменте вы можете показать много точек, представленных в проигрывателе.
- Снизу почти слева и почти справа (синим цветом), проверьте против блоков ниже.Они не полностью влево или вправо, чтобы предотвратить состояние гонки, которое позволит игроку взобраться на стены.В этом случае, если игрок толкает стену и прыгает, коллайдер обнаружит как боковое, так и нижнее столкновение как истинное, тогда игрок будет быстро двигаться к вершине, пока не останется больше блоков.
- Левая и правая точки (в черном цвете), проверьте по краям блоков.Это просто точка вместо двух, как нижний край, потому что нам не нужно больше для этого конкретного случая.Еще один для каждой стороны может быть легко добавлен для лучшего обнаружения.
- Верхняя точка (в красном) проверяет верхние блоки.Это посередине, чтобы позволить игроку более простой способ пересечь карту.Если это не нужно, вам нужно добавить еще одну точку, как в нижнем крае (но никогда не достигать дальнего края, потому что это создаст состояние гонки).
Итак, в итоге, кчтобы иметь хорошее обнаружение столкновений по точкам (вместо лучевых трансляций), вам нужно определять игрока, как если бы он находился в округлой форме, чтобы избежать странного поведения.
Вы можете перемещаться с макетом карты с помощьюизменение переменной layout
.0
- это пустые места, 1
- коричневые блоки, а 2
- зеленые блоки.
Функция collisionDetector
имеет комментарии, чтобы понять, что происходит.
Также я добавил функцию прыжка, поскольку, как я понимаю, она вам тоже понадобится.
const c = document.getElementById('canvas');
c.width = window.innerWidth;
c.height = window.innerHeight;
const ctx = c.getContext('2d');
// map layout
const layout =
`000000001
001000001
000000101
100110111
222222222`;
// convert layout to blocks
const blocks = [...layout].reduce((a, c, i) => {
if (i === 0 || c === "\n") a.push([]);
if (c === "\n") return a;
const y = a.length - 1;
const row = a[y];
const x = row.length;
row.push({x: x * 32, y: y * 32, t:c, w:32, h:32});
return a;
}, []).reduce((a, c) => a.concat(c), []);
// player starting position
const myPlayer = {x: 32*1.5, y: 0, h: 32, w: 16, onGround: true};
const gravity = -1;
let pkl = 0, pkr = 0;
let pvely = 0;
function render() {
// player logic
const pvelx = pkr + pkl;
const speed = 2;
myPlayer.x += pvelx * speed;
myPlayer.y -= pvely;
if (pvely > -2) pvely += gravity;
const debugColliders = collisionDetector();
ctx.clearRect(0, 0, c.width, c.height);
// player render
ctx.fillStyle = '#FFD9B3';
ctx.fillRect(myPlayer.x, myPlayer.y, myPlayer.w, myPlayer.h);
renderLayout();
debugColliders();
window.requestAnimationFrame(render);
}
function renderLayout() {
const colors = {'1': '#A3825F', '2': '#7FAC72'}
blocks.forEach(b => {
if (+b.t > 0) {
ctx.fillStyle = colors[b.t];
ctx.fillRect(b.x, b.y, b.w, b.h);
}
});
}
window.addEventListener('keydown', e => {
if (e.key == 'ArrowRight') {
pkr = 1;
e.preventDefault();
} else if (e.key == 'ArrowLeft') {
pkl = -1;
e.preventDefault();
} else if (e.key == 'ArrowUp') {
if (myPlayer.onGround)
pvely = 8;
myPlayer.onGround = false;
e.preventDefault();
}
});
window.addEventListener('keyup', e => {
if (e.key == 'ArrowRight') {
pkr = 0;
} else if (e.key == 'ArrowLeft') {
pkl = 0;
}
});
function collisionDetector(){
const p = myPlayer;
const playerTop = p.y;
const playerLeft = p.x;
const playerRight = playerLeft + p.w;
const playerBottom = playerTop + p.h;
const playerHalfLeft = playerLeft + p.w * .25;
const playerHalfRight = playerLeft + p.w * .75;
const playerHMiddle = playerLeft + p.w * .5;
const playerVMiddle = playerTop + p.h * .5;
if(playerBottom > c.height){ //Bottom of the canvas
p.vy = 0;
p.ay = 0;
p.y = c.height - p.h;
p.onGround = true;
}
if(playerRight >= c.width){ //right side of canvas
p.x = c.width - p.w;
p.vx = 0;
}
if(playerLeft <= 0){ //Left side of canvas
p.x = 0;
p.vx = 0;
}
blocks.forEach(b => { //Loop through blocks
if (b.t === "0") return; // If not collidable, do nothing
const blockTop = b.y;
const blockLeft = b.x;
const blockRight = blockLeft + b.w;
const blockBottom = b.y + b.h;
// Player bottom against block top
if ((playerBottom > blockTop && playerBottom < blockBottom) && // If player bottom is going through block top but is above block bottom.
((playerHalfLeft > blockLeft && playerHalfLeft < blockRight) || // If player left is inside block horizontal bounds
(playerHalfRight > blockLeft && playerHalfRight < blockRight))) { // Or if player right is inside block horizontal bounds
p.y = blockTop - p.h;
p.onGround = true;
}
// Player top against block bottom
if ((playerTop < blockBottom && playerTop > blockTop) && // If player top is going through block bottom but is below block top.
((playerHMiddle > blockLeft && playerHMiddle < blockRight))) { // If player hmiddle is inside block horizontal bounds
p.y = blockBottom;
p.onGround = false;
}
// Player right against block left, or player left against block right
if (playerVMiddle > blockTop && playerVMiddle < blockBottom) { // If player vertical-middle is inside block vertical bounds
if ((playerRight > blockLeft && playerRight < blockRight)) { // If player vmiddle-right goes through block-left
p.x = blockLeft - p.w;
} else if ((playerLeft < blockRight && playerRight > blockLeft)) { // If player vmiddle-left goes through block-right
p.x = blockRight;
}
}
});
return function debug() {
ctx.fillStyle = 'black';
ctx.fillRect(playerLeft, playerVMiddle, 1, 1);
ctx.fillRect(playerRight, playerVMiddle, 1, 1);
ctx.fillStyle = 'red';
ctx.fillRect(playerHMiddle, playerTop, 1, 1);
ctx.fillStyle = 'blue';
ctx.fillRect(playerHalfLeft, playerBottom, 1, 1);
ctx.fillRect(playerHalfRight, playerBottom, 1, 1);
}
}
window.requestAnimationFrame(render);
html, body{ width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
canvas { background: #7AC9F9; display: block; }
<canvas id="canvas"></canvas>