Tile Map Collide Not Working As Expected In Javascript
I have been thinking of a way to set-up tile map collision with a player in the same way I have developed the game so far (or similar way if possible, so I do not have to redo ever
Solution 1:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
position: absolute;
margin: auto;
left: 0;
right: 0;
border: solid 1px white;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// IIFE
// https://developer.mozilla.org/en-US/docs/Glossary/IIFE
void function() {
// Enforce strict mode inside the IIFE
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
"use strict";
// Var declaration
var canvasWidth = 320;
var canvasHeight = 320;
var canvas = null;
var ctx = null;
var player = {
// Position
x: 35.0,
y: 35.0,
// Velocity
dx: 0.0,
dy: 0.0,
// Size
width: 20,
height: 20,
// Input
up: false,
down: false,
left: false,
right: false
};
// Defined this way the map will need to be indexed as
// map[y][x]
// 0 = not solid
// 1 = solid
var mapTileSize = 32; // size for each tile in pixels
var map = [
[1,1,1,1,1,1,1,1,1,1],
[1,0,1,0,0,0,0,0,1,1],
[1,0,0,1,1,1,0,1,0,1],
[1,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,1],
[1,0,1,1,1,1,0,1,0,1],
[1,0,0,0,0,0,0,1,0,1],
[1,0,0,0,0,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1,1]
];
// Runs after the page has finished loading
window.onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
ctx = canvas.getContext("2d");
loop();
}
// Key Input
window.onkeydown = function(e) {
switch(e.key) {
case "w": player.up = true; break;
case "s": player.down = true; break;
case "a": player.left = true; break;
case "d": player.right = true; break;
}
}
window.onkeyup = function(e) {
switch(e.key) {
case "w": player.up = false; break;
case "s": player.down = false; break;
case "a": player.left = false; break;
case "d": player.right = false; break;
}
}
function loop() {
// Tick
// Player movement
if (player.up) {
player.dy = -2;
}
if (player.down) {
player.dy = +2;
}
if (!player.up && !player.down) {
player.dy = 0;
}
if (player.left) {
player.dx = -2;
}
if (player.right) {
player.dx = +2;
}
if (!player.left && !player.right) {
player.dx = 0;
}
// Collision detection & resolution
// Only perform collision detection in either axis
// if the player is actually moving on that axis
// Map grid coordinates at the corners of the player's bounding box
/*
-> *-----* <-
| |
| |
| |
-> *-----* <-
*/
var invMapTileSize = 1.0 / mapTileSize;
var nextX = player.x + player.dx;
var nextY = player.y + player.dy;
// Current position
var currentMinX = (player.x * invMapTileSize) | 0;
var currentMaxX = ((player.x + player.width) * invMapTileSize) | 0;
var currentMinY = (player.y * invMapTileSize) | 0;
var currentMaxY = ((player.y + player.height) * invMapTileSize) | 0;
// Next position
var nextMinX = (nextX * invMapTileSize) | 0;
var nextMaxX = ((nextX + player.width) * invMapTileSize) | 0;
var nextMinY = (nextY * invMapTileSize) | 0;
var nextMaxY = ((nextY + player.height) * invMapTileSize) | 0;
/*
Collision checks are performed down along each axis of the player's
collision box, this way it won't matter if the player is larger
or smaller then the map tiles.
*/
// X axis collision
if (player.dx !== 0.0) {
for (var x = nextMinX; x <= nextMaxX; ++x) {
for (var y = currentMinY; y <= currentMaxY; ++y) {
if (map[y][x]) {
player.dx = 0.0;
break;
}
}
}
}
// Y axis collision
if (player.dy !== 0.0) {
for (var y = nextMinY; y <= nextMaxY; ++y) {
for (var x = currentMinX; x <= currentMaxX; ++x) {
if (map[y][x]) {
player.dy = 0.0;
break;
}
}
}
}
// Update player position
player.x = player.x + player.dx;
player.y = player.y + player.dy;
// render
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
// draw map
ctx.fillStyle = "darkred";
ctx.beginPath();
for (var y = 0; y < map.length; ++y) {
for (var x = 0; x < map[0].length; ++x) {
if (map[y][x]) {
ctx.rect(
x * mapTileSize,
y * mapTileSize,
mapTileSize,
mapTileSize
);
}
}
}
ctx.fill();
// draw player
ctx.fillStyle = "darkblue";
ctx.fillRect(
player.x,
player.y,
player.width,
player.height
);
//
requestAnimationFrame(loop);
}
}();
</script>
</body>
</html>
Solution 2:
After about a week was giving up on this one :D and finally figured it out. I was switching the places of my X and Y indexes and that is why it was not acting as it should be. Showing up the fix here if anyone is interested (only the Player movement has to be updated):
function playerMovement(){
//------------------------------
//CHECK PLAYER X AND Y IN TILE INDEXES
heroX = -updateX + player.x;
heroY = -updateY + player.y;
heroIndexX = heroX/64; // get the X index
heroIndexY = heroY/64; // get the Y index
heroIndexXnew = Math.ceil(heroIndexX);
heroIndexYnew = Math.ceil(heroIndexY);
if(rightPressed) {
player_image.src = './images/horseright1.png';
if(player.x>updateX+1258){ // Nothing happens because you hit the right wall
}else if (isPathTile(mapArray, heroIndexYnew, heroIndexXnew)){ // Nothing happens because you hit a tile
}else {
moveRight(); // if you are not at the wall, you go right
}
}
//------------------------------
else if(leftPressed) {
player_image.src = './images/horseleft1.png';
if(player.x<updateX+128){ // Nothing happens because you hit the left wall
}else if (isPathTile(mapArray, heroIndexYnew, heroIndexXnew - 1)){ // Nothing happens because you hit a tile
}else {
moveLeft(); // if you are not at the wall, you go left
}
}
//------------------------------
if(downPressed) {
if(player.y>updateY+834){ // Nothing happens because you hit the bottom wall
}else if (isPathTile(mapArray, heroIndexYnew, heroIndexXnew)){ // Nothing happens because you hit a tile
}else {
moveDown(); // if you are not at the wall, you go down
}
}
//------------------------------
else if(upPressed) {
if(player.y<updateY+64){ // Nothing happens because you hit the top wall
}else if (isPathTile(mapArray, heroIndexYnew - 1, heroIndexXnew)){ // Nothing happens because you hit a tile
}else {
moveUp(); // if you are not at the wall, you go up
}
}
}
A simple mistake and took me one hell of a time for debugging, though the logic is good and simple. Needs a bit of a touch on how it collides exactly as there are few things to be updated, but overall it works great!
Post a Comment for "Tile Map Collide Not Working As Expected In Javascript"