I wanted boxes like the one upon which Mario stands. |
The tileset I was using had some things like that in it, so I used them and made some nice platforms in my sandbox level. It was then I realized that I had a problem. If I set the tiles that made up the boxes to collide with the frog, he could jump on them but not walk in front of them. Maybe if I just set the top tiles as having collisions, or put the lower halves in the background layer, I could walk in front of them. Yes, but I still couldn't jump straight up and land on top because the frog would bump into the top tiles from below.
I want to jump on these. |
Phaser supports one-way collisions for physics bodies with the Arcade Physics module that I am using, but since I was dealing with a tilemap there didn't seem to be a way to set it up. When you want to set up collision with some of your tiles, you use one of the setCollision functions (setCollision, setCollisionBetween, setCollisionByExclusion). They don't take any parameters to fine-tune the collisions. Internally, those functions call setCollisionByIndex. Looking at the source for that function, I could see that every tile was indeed set to collide on all sides. It took me a little while, but I found a way to override that behavior and get the effect I wanted.
Here is the function I came up with:
function setTileCollision(mapLayer, idxOrArray, dirs) { var mFunc; // tile index matching function if (idxOrArray.length) { // if idxOrArray is an array, use a function with a loop mFunc = function(inp) { for (var i = 0; i < idxOrArray.length; i++) { if (idxOrArray[i] === inp) { return true; } } return false; }; } else { // if idxOrArray is a single number, use a simple function mFunc = function(inp) { return inp === idxOrArray; }; } // get the 2-dimensional tiles array for this layer var d = mapLayer.map.layers[mapLayer.index].data; for (var i = 0; i < d.length; i++) { for (var j = 0; j < d[i].length; j++) { var t = d[i][j]; if (mFunc(t.index)) { t.collideUp = dirs.top; t.collideDown = dirs.bottom; t.collideLeft = dirs.left; t.collideRight = dirs.right; t.faceTop = dirs.top; t.faceBottom = dirs.bottom; t.faceLeft = dirs.left; t.faceRight = dirs.right; } } } }
Despite the weird-looking top half of the function that is just setting up a way of matching against either a single tile id value or an array of values, overall the function is very simple. It gets the 2-dimensional array of all the tiles in the given layer and loops over them. If the tile index matches one of the specified index values, the collision settings for that tile are altered in accordance with the dirs object. The dirs object should have 4 boolean fields, top, bottom, left, and right. A value of true for any of them indicates that you want Phaser to check for collisions with other objects coming from that direction. In my case, only top was set to true. I only want to cause a collision when the frog hits the tile from above.
Here is how I use it from my create function:
function create() { game.physics.startSystem(Phaser.Physics.ARCADE); game.physics.arcade.gravity.y = 1500; var map = game.add.tilemap("stage1"); map.addTilesetImage("ground", "tiles"); layer = map.createLayer("layer1"); layer.resizeWorld(); map.setLayer(layer); // set collisions on ground tiles map.setCollisionBetween(1, 275); // set collisions on tiles that are the top parts of the boxes map.setCollisionBetween(3354, 3356); map.setCollisionBetween(2900, 2903); map.setCollisionBetween(3714, 3716); // set those box top tiles to only collide from above setTileCollision(layer, [3354, 3355, 3356, 2900, 2901, 2902, 2903, 3714, 3715, 3716], { top: true, bottom: false, left: false, right: false }); // ... other stuff ... }
The end result is just what I wanted - the frog can walk in front of the boxes and jump up to land on top of them. The following video shows the box-jumping in action.
And speaking of jumping on boxes - if it's that time of year at your workplace in which you check boxes on reviews of your coworkers, you might enjoy today's comic. In it, we find that the frogs have a more basic approach to the classic forced ranking system.
Amphibian.com comic for 9 November 2015 |
it's now support on phaser 3 :
ReplyDeletethis.layer.forEachTile(tile => {
if (tile.index === 1)
{
tile.collideLeft = false;
tile.collideRight = false;
}
});