Jump to content

Another Canvas Problem


astralaaron

Recommended Posts

This is related to a previous thread about a small game I am making: http://w3schools.invisionzone.com/index.php?showtopic=48682

 

The problem I am having now is as the player walks around the map, as soon as the camera starts scrolling to follow the player, the map tiles seem to show a tiny space between them, resulting in lines through the map.

 

I realized, that I was not doing any kind of "flip" function (drawing everything to a temporary canvas, then copying the entire canvas onto the game screen) so I added that, which gives a better result, but still you see the lines between tiles.

 

Here is a link so you can test it yourself to see (press enter to get to the map screen)

 

The one thing that I haven't implemented yet is only drawing tiles that are within the game gamera. As of right now, the entire map draws out each frame. Do you think this might fix it? Or do you have any other ideas of why the tiles seem to slightly split apart? Thanks for any advice.

Link to comment
Share on other sites

Nevermind, I have fixed the problem... I can't fully explain it..basically the camera positions itself based on the player X position, and since I had my player moving at a speed of 2.5 instead of a whole number like 2 or 3, there was a .5 position difference in the tiles... as soon as I changed it to a whole number..problem fixed

Link to comment
Share on other sites

 

no that is the link with the display problem.

 

this link the most recent

 

And I ran into another problem that is driving me insane....if you make your way over to the far right wall at the end of the map on both level 1 or level 2... you walk right through the wall. I can't for the life of me get the collision detection working with the rightmost tile. I've been messing with it for hours. Going to give it a rest and try again tomorrow after work...

 

btw..First map is 30 tiles wide, second is 50 tiles wide..

 

(level two is through the black holes in the wall)

Link to comment
Share on other sites

Hmmm... In FF24 I still see issues. The whole thing is not centered in a wrapper and begins at the left side of the screen and then goes off the left side of the screen when the "camera" moves. Also 1340px is wider than my entire laptop screen, but most of it is blank.

Link to comment
Share on other sites

yeah it isn't meant to be centered at the moment, I was doing a test with the temporary canvas next to the gamescreen. I know what you mean by the page scrolling when you push the arrow, I just threw a big number into the div. it's not really a problem with the game though.

Link to comment
Share on other sites

It looks kinda complicated. You have some collision code commented out so that may be the issue. I'd be interested to hear an overview of how the map is defined. I see it is somehow made up of sprite blocks.

Link to comment
Share on other sites

The commented out collision is between game objects, I just wrote that up for in the future when I make some different games with more game objects than just the one player running around an empty map. its the worldCollision function that deals with the map tiles. I'm still at work, I can write an overview about how the map is defined later tonight.

Link to comment
Share on other sites

I'm guessing that a set of arrays is used to generate the canvas image, and that the collision detection looks at those array values, rather than looking at the canvas. Could you add a button to redraw the canvas so that you can verify the arrays are not getting corrupted?

Link to comment
Share on other sites

so the map files look something like this:

06 0601 02 01 02 01 0201 02 01 01 02 0101 02 01 01 02 0101 02 01 02 01 0201 02 01 01 02 0101 02 01 01 02 01

I use an ajax call to grab that text.

 

then I use javascript split("n"); to separate each row into one array

 

then I loop through that array and use split(" ") to get each tile number into an array.

 

so I have a two dimensional array of numbers...

 

Then I have a tile class that can be as basic as this:

function Tile() {}Tile.prototype.init = function(box, type, id) {		this.X = box.x;	this.Y = box.y;	this.W = box.w;	this.H = box.h;		this.type = type;	this.id = id;}Tile.prototype.clean = function() { delete this; }

so I loop through that array of numbers, and for each number I create a new Tile object which holds the x, y, width, and height. It also holds the tile type..which I use to draw the correct tile from the sprite sheet.

I am just using the id to track which tile is a door, also something I didnt do yet, but I am going to give the sand (yellow tiles) an id, which when you collide with them it slows the player velocity..

 

then I add each tile object to a new array, so I have a two dimensional array of Tile objects.

 

 

that loadMap() function looks like this:

this.loadMap = function(filePath, spritePath) {		engine.tileSheet.src = spritePath;		var rand = Math.floor(Math.random() * 6) + 1;		var url = filePath + '?r='+rand;		var call_to = url;		var call = new Com(call_to);		var call_back = function() {			var mapRows = call.response.split("n");			var map = new Array();			var row = null;			for(var i=0; i < mapRows.length; i++) {				row = mapRows[i].split(" ");				map[i] = row;			}						var mapWidth = map[0][0];			var mapHeight = map[0][1];			// convert each tile to object			engine.tiles = new Array();			var doorCount = 0;			var tileId = 0;			for(var i = 0; i <= mapHeight; i++) {				var tRow = new Array();				for (var j = 0; j <= mapWidth; j++) {					tileId = 0;					var tile = map[i][j];					var box = {					'x': j * TILESIZE,					'y': (i-1) * TILESIZE,					'w':TILESIZE,					'h':TILESIZE					};					if (tile == 6) {					tileId = doorCount++;					//alert('setting door id = '+ tileId);					}					var newTile = new Tile();					newTile.init(box, tile, tileId);					tRow.push(newTile);				}				//alert(tRow.length);				engine.tiles.push(tRow);			}			//alert(engine.tiles[0].length);		}		call.GET(call_back);			}

So that is basically it for loading the map...then there is a showMap() function which runs each frame before drawing the game objects:

	this.showMap = function() {		for(var i=0; i < engine.tiles.length; i++) {			for(var j=0; j<engine.tiles[0].length; j++) {				if (engine.tiles[i][j].type==0) continue;				var spriteOffset = {					'x': engine.tiles[i][j].W * (engine.tiles[i][j].type - 1),					'y': 0,					'w': engine.tiles[i][j].W,					'h': engine.tiles[i][j].H 				}				if(((engine.tiles[i][j].X + 50) >= engine.getCamera().x) &&				    ((engine.tiles[i][j].X - 50) <= (engine.getCamera().x + engine.getCamera().w)) &&				    ((engine.tiles[i][j].Y + 50) >= engine.getCamera().y) &&				    ((engine.tiles[i][j].Y - 50) <= (engine.getCamera().y + engine.getCamera().h))) {				    engine.getScreen().drawImage(engine.tileSheet, spriteOffset.x, spriteOffset.y, (spriteOffset.w -2), spriteOffset.h, engine.tiles[i][j].X - engine.getCamera().x, engine.tiles[i][j].Y - engine.getCamera().y, engine.tiles[i][j].W, engine.tiles[i][j].H);				}			}		}	}

the sprite offset gets the right position of the spritesheet to draw. The if() condition is checking to see if the tile is within the camera box, if it is, it draws that tile.

 

 

 

the world collision function looks like this, which loops through each tile and checks to see if the player is colliding with it, if so, it checks the tile ID (tiles 03 and 04 are walls, and run the stop() function, which counters the movement velocity, stopping the player object from moving) (06 tiles are doors, and change the gamestate and send you to the other level, at different positions based on the id of the tile) that function looks like this:

LevelTwoState.prototype.worldCollision = function() {	for(var i=0; i < this.game.tiles.length; i++) {		for(var j=0; j < this.game.tiles[0].length; j++) {			if (this.game.tiles[i][j].type==0) continue;			if(this.game.collision(this.player.X, this.player.Y, this.player.frameWidth, this.player.frameHeight, this.game.tiles[i][j].X, this.game.tiles[i][j].Y, this.game.tiles[i][j].W, this.game.tiles[i][j].H)) {				switch(this.game.tiles[i][j].type) {					case '03':					case '04':						this.player.stop();					break;					case '06':												switch (this.game.tiles[i][j].id) {														case 0:								this.game.changeState(LEVELONE, 202);								break;														case 1:								this.game.changeState(LEVELONE, 201);								break;							case 2:								this.game.changeState(LEVELONE, 203);								break;							case 3:								this.game.changeState(LEVELONE, 204);								break;							case 4:								this.game.changeState(LEVELONE, 205);								break;							case 5:								this.game.changeState(LEVELONE, 206);								break;							case 6:								this.game.changeState(LEVELTWO, 207);								break;							case 7:								this.game.changeState(LEVELONE, 208);								break;							case 8:								this.game.changeState(LEVELONE, 209);								break;							case 9:								this.game.changeState(LEVELONE, 210);								break;							default:								this.game.changeState(TITLEMENU);								break;						}											break;					default:break;				}							}		}	}}

Something is wrong, either in the loading, or worldCollision function, which is missing the final tile of each row (the rightmost tiles)

 

 

Sorry I couldn't be more detailed with this explanation, I got home late from my son's birthday party and I am pretty tired, rushed through this because I need to get to sleep for work in the morning. I didn't get a chance to try fixing the error today either, hopefully tomorrow I will have time.

Link to comment
Share on other sites

I should probably get rid of the first two numbers in the map files (the amount of tiles in each row, and column) I was following the way I load the map when I write in C++. But I think I don't need that in Javascript,it's probably not helping anything, might be part of the problem.

Link to comment
Share on other sites

So, let me see if I can digest what you have described...

 

You have text files which describe each map in a way that allows you to edit them visually. You read these files and convert them into 2 dimensional arrays. You then use the arrays to paint the canvas with sprites. As the game is running you use the arrays to keep track of the player position and to detect collisions.

 

With the above in mind, I don't understand why the worldCollision function seems to scan the entire map. If you always know the coordinates of the player then shouldn't you only need to scan the surrounding eight array values?

 

Also since JS uses jagged arrays as multi-dimensional arrays maybe you might need to use some extra caution to verify the created map array was indeed cleanly rectangular?

Edited by davej
Link to comment
Share on other sites

Add some console.log output to the world collision function, where it lists every tile that the player is colliding with. It might not correctly detect that they are even colliding with the rightmost tile. If that's the case, check the game.collision function to see what's going on there, why it doesn't detect the collision. Log the variables that you're sending to it and the calculations that it's doing to figure out why it doesn't detect the collision if that's the case.

Link to comment
Share on other sites

"As the game is running you use the arrays to keep track of the player position and to detect collisions."

 

"With the above in mind, I don't understand why the worldCollision function seems to scan the entire map. If you always know the coordinates of the player then shouldn't you only need to scan the surrounding eight array values?"

 

I don't use the tiles to keep track of the player location. The player just has an x / y property. I am not sure how I would scan only the surrounding tiles.

 

 

 

Also since JS uses jagged arrays as multi-dimensional arrays maybe you might need to use some extra caution to verify the created map array was indeed cleanly rectangular?

 

Will you please explain what you mean by jagged arrays? Does it mean, one row can be longer than the next?

 

 

 

Add some console.log output to the world collision function, where it lists every tile that the player is colliding with.

 

Good idea, I really need to get with the times and start using the consol to track things... I am still using alerts, which obviously is not ideal for checking every collision each frame. I am going to give this a try.

 

 

But first I am going to modify the loading function and not use the first two numbers to define the width and height, as it just seems to make it more complicated than it needs to be.

 

By the way, thanks both of you for the input

Link to comment
Share on other sites

Yes, a "jagged array" is an array of arrays so each row can have a different length, or worse.

 

So the player position is defined by a pixel coordinate because the player animation moves pixel-by-pixel. Wouldn't some simple modulo math give you the current cell of the player? Then you could just check the surrounding eight cells. Or is this another over-simplification neglecting details?

Edited by davej
Link to comment
Share on other sites

Then you could just check the surrounding eight cells.

Well I don't see how to check which tiles are around the player without looping through all of them. I guess you could possibly stop after the loop finds tiles the player is around, and not continue on to the rest of the map. That might be something I can think about to make it perform better in the future.

Link to comment
Share on other sites

Ok, I'm missing something. The animation part of the program knows exactly where the player is located because it just drew the player. Why does another part of the program need to search for the player?

Link to comment
Share on other sites

I am not having any luck with the console.log()

 

I have confirmed all of the tiles are being added to the arrays, and confirmed the rightmost tiles have the correct type.

 

The loops seem correct in the worldCollision function.

 

I am having a hard time with console.log() working with the collision functions because they are constantly getting called 60 times a second. the console log reaches its max really fast.

Link to comment
Share on other sites

Ok, I'm missing something. The animation part of the program knows exactly where the player is located because it just drew the player. Why does another part of the program need to search for the player?

 

well it has to search through to find out where the player is in relation to the map. each tile needs to be checked for it's x,y,w,h and compared against the player x,y,w,h

Link to comment
Share on other sites

Maybe throw in a validity checking block something like this...

this.collision = function(objAx, objAy, objAw, objAh, objBx, objBy, objBw, objBh) {		var leftA, rightA, topA, bottomA, leftB, rightB, topB, bottomB = null;if (isNaN(objAx) || objAx==null || objAx<0 || objAx>480) alert('objAx='+objAx);if (isNaN(objAy) || objAy==null || objAy<0 || objAy>480) alert('objAy='+objAy);if (isNaN(objAw) || objAw==null || objAw<0 || objAw>480) alert('objAw='+objAw);if (isNaN(objAh) || objAh==null || objAh<0 || objAh>480) alert('objAh='+objAh);if (isNaN(objBx) || objBx==null || objBx<0 || objBx>480) alert('objBx='+objBx);if (isNaN(objBy) || objBy==null || objBy<0 || objBy>480) alert('objBy='+objBy);if (isNaN(objBw) || objBw==null || objBw<0 || objBw>480) alert('objBw='+objBw);if (isNaN(objBh) || objBh==null || objBh<0 || objBh>480) alert('objBh='+objBh);	    leftA = objAx + 5;	    rightA = objAx + objAw - 8;	    topA = objAy + 5;	    bottomA = objAy + objAh - 5;
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...