Skip to content Skip to sidebar Skip to footer

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"