Jump to content

Tile Based Maps && Pixel Movement [Collision Detection Woes]


SoothingOcean
 Share

Recommended Posts

Hello friends,

 

This is my very first post and I have a question that I have been pulling my hair out over for a good couple of days now.

 

Before I introduce the problem, I think it should be obvious that by this post I intend on making a game.  I want to write it from scratch I do not wish to use a prexisting engine that someone has created as I feel that takes a way the fun of programming games in general (although it would be a hell of a lot easier). :)

 

I also hope that I have posted this in the correct section and that I haven't broken any rules.

 

Now I don't want you to write my game for me, but some examples and some logic/explanation would help me oh so much!

 

The Problem!

The problem I am running into at the moment with what I currently have written is the fact that I am using a multidemensional array to draw map map (x && y co-ordinates) and each "tile" in the array has dimensions of 50x50 pixels.  I am trying to move my player by pixel based movement taking into account things like gravity for example.

 

What I run into and keep running into is issues with creating "platforms" in the tile map and trying to logically write some "Collision Detection" code for this to actually work.

 

The only way that my brain can comprehend it at the moment is you need to find your "next" tile (mapData[x + 1][y] for example if you're going right) and divide it  where ever you're Player.X location happens to be by 50 (rounding of course) and somehow implement it that way.  This is difficult and not working for me :(

 

My current code, keep in mind this is very rough.

Please help.. before I have no more hair. Haha.

	// FUNCTION USED FOR REFRESHING SCREEN / RENDERING;	(function () {		var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;		window.requestAnimationFrame = requestAnimationFrame;	}) ();	// MAP 1;	var mapOne = [		['W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ','W'],		['W','W','W','W','W','W','W','W','W','W','W','W','W','W','W','W'],	];	// VARIABLES FOR CANVAS;	var canvas = document.getElementById("canvas");	var g = canvas.getContext("2d");	width = 800;	height = 600;	// PLAYER VARIABLES;	var player = new Object(50,50,50,100);	player =  {		x: 50,		y: 100,		width: 50,		height: 100,		speed: 6,		velX: 0,		velY: 0,		jumping: false,		grounded: false	};		// MORE VARIABLES, YAY;	keys = [];	friction = 0.8;	gravity = 0.4;	canvas.width = width;	canvas.height = height;	// FUNCTION TO MAKE A MAP;	function mkMap() {		var i,j, tile;		var tilesX = width / 50;		var tilesY = height / 50;				for (i = 0; i < tilesX; i++) {			for (j = 0; j < tilesY; j++) {					tile = (mapOne[j] && mapOne[j][i]) ? mapOne[j][i] : 'W';						mkTile(i * 50,j * 50, tile);			}		}	};	// FUNCTION TO MAKE TILES;	function mkTile(x,y,img) {		switch (img) {			case ' ':			g.fillStyle = "black";			g.fillRect(x,y,50,50);			break;			case 'W':			var wall = new Object(x,y,50,50);  // NOT WORKING;			g.fillStyle = "pink";			g.fillRect(x,y,50,50);			break;		}	};		// IGNORE THIS, NOT WORKING;	function Object(x,y,width,height) {		this.X = x;		this.Y = y;		this.Width = width;		this.Height = height;	};	function playerInput() {		// SPACE BAR & 'W' TO JUMP;		if (keys[38] || keys[32]) {			if(!player.jumping) {				player.jumping = true;				player.velY = -player.speed*2;			}		}		// LEFT KEY;		if (keys[37]) {			if (player.velX > -player.speed) {				player.velX--;			}		}		// RIGHT KEY;		if (keys[39]) {			if (player.velX < player.speed) {				player.velX++;			}		}	};	function update() {		g.clearRect(0,0,width,height);		mkMap();		g.fillStyle = "red";		g.fillRect(player.x,player.y,player.width,player.height);		playerInput();		player.velX *= friction;		player.velY += gravity;		player.x += player.velX;		player.y += player.velY;	/**if (player.x >= width-player.width) {	player.x = width-player.width;	} else if (player.x <= 0) {	player.x = 0;	}**/		if (player.y >= height-player.height) {			player.y = height - player.height;			player.jumping = false;		}			requestAnimationFrame(update);	};	document.body.addEventListener("keydown", function(e) {		keys[e.keyCode] = true;	});	document.body.addEventListener("keyup", function(e) {		keys[e.keyCode] = false;	});	window.addEventListener("load", function() {		update();	});
Link to comment
Share on other sites

Just to say you can't call a function "Object", Object is a reserved word in JavaScript :)

 

Anyway to your problem, just going to give you some pointers here:

 

As part of your update loop you ought to take your players velocity, gravity and any other factor into account that updates their position and calculate their new position from that. Now check this new position against your tile map. If it collides you need to determine by how much (how many pixels) and adjust the new position accordingly. Once you've finished doing all of this you apply the new position to the player. You should separate the player from the tile along the x and y axis independently. If for example your tiles are "bouncy" and you know the player has collided then you separate him and then reverse his velocity to send him off in the opposite direction.

Link to comment
Share on other sites

Just to say you can't call a function "Object", Object is a reserved word in JavaScript :)

 

Anyway to your problem, just going to give you some pointers here:

 

As part of your update loop you ought to take your players velocity, gravity and any other factor into account that updates their position and calculate their new position from that. Now check this new position against your tile map. If it collides you need to determine by how much (how many pixels) and adjust the new position accordingly. Once you've finished doing all of this you apply the new position to the player. You should separate the player from the tile along the x and y axis independently. If for example your tiles are "bouncy" and you know the player has collided then you separate him and then reverse his velocity to send him off in the opposite direction.

 

Haha yes, I figured out that object thing.  I was like why is the word object being highlighted! Doh.

 

Thanks for your tips, I will take velocity and gravity into account.  However the issue I'm having is if I fall on a "tile" that is in mid air, when I walk over it say I'm moving 10 pixels at a time I have to use a Math.round for example to get tile co-ordinates based on my player.x & player.y locations.  Image the tile starts from 100px too 150px across,  once I go over 120pixels it thinks I'm on the next tile which just happens to be one that I can fall through.

 

Man this is hard to explain sometimes lol!

 

Edit:

I really think I'm failing hard somewhere with my maths!

Link to comment
Share on other sites

It's because you need to calculate what the player position WILL be and run all the math off that - not what it currently is because by that point it is too late. You need to do this every single frame. If your player is moving so fast that it clears 20 pixels in one frame then you basically need to think of an alternative way to project where it's going to travel, normally projecting a ray in the direction the player is moving works best for this.

Link to comment
Share on other sites

I know that you need to check the "next" tile.  So in my example, Mr. Red Guy is falling.

So I'd need to change the update() function to reflect something like;

var testX = Math.ceil(player.x / 50);var testY = Math.ceil((player.Y + 1) / 50);if (mapOne[testX][testY] == 'W') {  player.velY = 0;}

Is that me basically on the right track there?

 

edit: not taking velocity etc.. into account there.

Link to comment
Share on other sites

Yeah, that sounds reasonable.

 

In my entity-to-map collision code, I call "move(xspeed, yspeed)" on the entity every frame. xspeed and yspeed are the amounts the entity WANTS to move. My move functions sees how much they are actually ALLOWED to move. The psuedo-code goes like this:

 

1. Get all the map cells my entity will be touching if I allow it to move fully on the Y axis, and 0 along the X axis.

2. If the entity is moving upwards, and either of the cells at its head are walls, figure out how many pixels between then entity's head, and the wall.

3.If the entity is moving downwards, and either of the cells at its feet are walls, figure out how many pixels between then entity's feet, and the wall.

4. Add the Y allowed amount to the entity's position.
 
5 - 8: Same again, but along the X axis, with the new Y position.
 
The reason I split up the Y axis and X axis checks is so the player can slide along the wall when moving diagonally.
 
You can have a look at my implementation on github: It's not tooooo hairy!
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...