I have implemented some basic wall collisions where I project the velocity vector onto the wall the player is colliding with so they slide across the wall. The problem I am having is when you slide into the corner of a wall the player goes through the wall. What am I missing? Am I using the wrong method for this type of collision.
You can see the code at it's original location or below:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var lastTime = Date.now();
var deltaTime = 0;
var sector = {
walls: [
{
start: {
x: 150,
y: 100
},
end: {
x: 300,
y: 100
}
},
{
start: {
x: 300,
y: 100
},
end: {
x: 350,
y: 150
}
},
{
start: {
x: 350,
y: 150
},
end: {
x: 350,
y: 300
}
},
{
start: {
x: 350,
y: 300
},
end: {
x: 300,
y: 350
}
},
{
start: {
x: 300,
y: 350
},
end: {
x: 150,
y: 350
}
},
{
start: {
x: 150,
y: 350
},
end: {
x: 100,
y: 300
}
},
{
start: {
x: 100,
y: 300
},
end: {
x: 100,
y: 150
}
},
{
start: {
x: 100,
y: 150
},
end: {
x: 150,
y: 100
}
}
]
}
var player = {
rotation: 180,
radius: 20,
moveSpeed: 1,
turnSpeed: 90,
maxSpeed: 1,
friction: 2,
speed: {
x: 0,
y: 0,
z: 0
},
position: {
x: 200,
y: 200,
z: 0
}
}
var keys = {
backspace: 8,
tab: 9,
enter: 13,
shift: 16,
ctrl: 17,
alt: 18,
pause: 19,
capslock: 20,
escape: 27,
space: 32,
pageUp: 33,
pageDown: 34,
home: 36,
end: 35,
left: 37,
up: 38,
right: 39,
down: 40,
insert: 45,
del: 46,
zero: 48,
one: 49,
two: 50,
three: 51,
four: 52,
five: 53,
six: 54,
seven: 55,
eight: 56,
nine: 57,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
select: 93,
numpad0: 96,
numpad1: 97,
numpad2: 98,
numpad3: 99,
numpad4: 100,
numpad5: 101,
numpad6: 102,
numpad7: 103,
numpad8: 104,
numpad9: 105,
multiply: 106,
add: 107,
subtract: 109,
decimal: 110,
divide: 111,
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
numlock: 144,
scrollLock: 145,
semicolon: 186,
equal: 187,
comma: 188,
dash: 189,
period: 190,
forwardSlash: 191,
graveAccent: 192,
openBracket: 219,
backSlash: 220,
closeBraket: 221,
singleQuote: 222
}
var key = {
up: false,
down: false,
left: false,
right: false,
down: [],
next: []
}
/*==================================================
CORE CODE
==================================================*/
function toRadians(degrees){
return degrees * Math.PI / 180;
}
function update(){
updateTime();
updateKeyboard();
updatePlayer();
clearCanvas();
drawPlayer();
drawSector();
window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);
function clearCanvas(){
context.clearRect(0, 0, canvas.width, canvas.height);
}
function updateTime(){
var currentTime = Date.now();
deltaTime = (currentTime - lastTime) / 1000.0;
lastTime = currentTime;
}
function lineIntersection(a, b, c, d){
var denominator = ((b.x - a.x) * (d.y - c.y)) - ((b.y - a.y) * (d.x - c.x));
var numerator1 = ((a.y - c.y) * (d.x - c.x)) - ((a.x - c.x) * (d.y - c.y));
var numerator2 = ((a.y - c.y) * (b.x - a.x)) - ((a.x - c.x) * (b.y - a.y));
if (denominator == 0) return numerator1 == 0 && numerator2 == 0;
var r = numerator1 / denominator;
var s = numerator2 / denominator;
return (r >= 0 && r <= 1) && (s >= 0 && s <= 1);
}
/*==================================================
PLAYER CODE
==================================================*/
function updatePlayer(){
if(key.down[keys.up]){
player.speed.y = Math.max(-player.maxSpeed, player.speed.y - player.moveSpeed * deltaTime);
}
else if(key.down[keys.down]){
player.speed.y = Math.min(player.maxSpeed, player.speed.y + player.moveSpeed * deltaTime);
}
else {
friction();
}
if(key.down[keys.left]){
player.rotation = player.rotation - player.turnSpeed * deltaTime;
}
else if(key.down[keys.right]){
player.rotation = player.rotation + player.turnSpeed * deltaTime;
}
var radians = toRadians(player.rotation);
var move = {
x: Math.cos(radians) * player.speed.y,
y: Math.sin(radians) * player.speed.y
}
var remainder = moveRemainder(move);
player.position.x += remainder.x;
player.position.y += remainder.y;
}
function moveRemainder(move){
var remainder = move;
var currentPosition = {
x: player.position.x,
y: player.position.y
}
var nextPosition = {
x: player.position.x + move.x,
y: player.position.y + move.y
}
var checkWalls = function(index){
for(var i=0; i<sector.walls.length; i++){
var wall = sector.walls[i];
if(lineIntersection(currentPosition, nextPosition, wall.start, wall.end)){
var xLength = Math.max(wall.start.x, wall.end.x) - Math.min(wall.start.x, wall.end.x);
var yLength = Math.max(wall.start.y, wall.end.y) - Math.min(wall.start.y, wall.end.y);
var equation = (move.x*xLength + move.y*yLength) / (xLength*xLength + yLength*yLength);
remainder.x = xLength * equation;
remainder.y = yLength * equation;
if(index == 0){
checkWalls(1);
}
return;
}
}
}
checkWalls(0);
return remainder;
}
function drawPlayer(){
context.beginPath();
context.fillStyle = 'red';
context.arc(player.position.x, player.position.y, 3, 0, Math.PI * 2);
context.fill();
context.closePath();
}
function friction(){
if(player.speed.x < 0){
player.speed.x = Math.min(0, player.speed.x * deltaTime);
}
else if(player.speed.x > 0){
player.speed.x = Math.max(0, player.speed.x * deltaTime);
}
if(player.speed.y < 0){
player.speed.y = Math.min(0, player.speed.y + player.friction * deltaTime);
}
else if(player.speed.y > 0){
player.speed.y = Math.max(0, player.speed.y - player.friction * deltaTime);
}
}
/*==================================================
SECTOR CODE
==================================================*/
function drawSector(){
context.strokeStyle = 'black';
sector.walls.forEach(function(wall){
context.beginPath();
context.moveTo(wall.start.x, wall.start.y);
context.lineTo(wall.end.x, wall.end.y);
context.stroke();
context.closePath();
})
}
/*==================================================
INPUT CODE
==================================================*/
function updateKeyboard(){
key.down = key.next;
}
window.addEventListener('keydown', function(e){
key.next[e.keyCode] = true;
})
window.addEventListener('keyup', function(e){
key.next[e.keyCode] = false;
})
<!doctype html>
<html>
<head>
<title>Retro3d proof of concept</title>
<style>
body {
margin: 0;
background: #f4f6f8;
}
#canvas {
margin: 20px auto;
display: block;
background: #fff;
border-radius: 2px;
box-shadow: 0 0 0 1px rgba(63,63,68,0.05), 0 1px 3px 0 rgba(63,63,68,0.15);
}
</style>
</head>
<body>
<canvas width="600" height="600" id="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>