Jump to content

Very confused about the Z-ordering system - can manually setting a sprite's z in a group affect the order?


zack_falcon
 Share

Recommended Posts

First off, please take note that I'm not just new to this forum, I'm new to phaser in general. I know a lot of people had z-ordering issues before. I've read about them. And most of the replies amount to "use the built-in sorting system." Which is great... if or when it works.

However, I haven't seen anything that approaches my code, so I'm not entirely sure what to do. And boy does it produce some unique issues when the group sort is applied.

There is one difference between the samples I see online and my code: I don't use +/-x and +/-y to move my character; instead, I use tween. This is so I can have a "snap to tile".

Without further ado, my (mess of a) code:

class Gameplay extends Phaser.State {
  create() {
    this.backgroundGroup = this.game.add.group();
    this.mainGroundGroup = this.game.add.group();
    this.foregroundGroup = this.game.add.group();
    // GET MAP
    this.mapFile = this.game.cache.getJSON('protomap');
    // SETUP GRID AND WORLD
    this.colCount = this.mapFile.columns;
    this.rowCount = this.mapFile.rows;
    this.game.world.setBounds(0, 0, (this.colCount * CONST.TILESIZE), (this.rowCount * CONST.TILESIZE));    
    this.grid = new Grid(this.game, this.colCount, this.rowCount);
    this.grid.x = 0;
    this.grid.y = 0;
    this.mainGroundGroup.add(this.grid);
    // SETUP OBJECTS
    this.spawnManager = new SpawnManager(this.game, this.grid, this.backgroundGroup, this.mainGroundGroup, this.foregroundGroup, this.mapFile);
    // SETUP PLAYER
    this.player = this.spawnManager.player;
    this.game.camera.follow(this.player);
  }

  update() {
    this.mainGroundGroup.sort('y', Phaser.Group.SORT_ASCENDING);
    this.player.z = this.player.currentTile.z;    
  }
}


class Grid extends Phaser.Group {
  constructor(game, p_columnCount, p_rowCount) {
    super(game);
    game.add.existing(this);
    this.game = game;
    this.columnCount = p_columnCount;
    this.rowCount = p_rowCount;
    this.gridTiles = [];
    this.grid2DArray = [[]];

    for(var i = 0; i < this.rowCount; i++) {
      this.grid2DArray[i] = [];
      for(var j = 0; j < this.columnCount; j++) {
        var gridTileObj = new GridTile(this.game, (CONST.TILESIZE * j), (CONST.TILESIZE * i + yAnchorOffset), zArray, j, i);
        this.add(gridTileObj);
        this.gridTiles.push(gridTileObj);
        this.grid2DArray[i][j] = gridTileObj;
      }
    }
  }
}

class SpawnManager {
  constructor(game, grid, back, main, fore, mapFile) {
    this.game = game;
    this.grid = grid;
    this.main = main;
    this.mapFile = mapFile;
    this.playerStartX = 0;
    this.playerStartY = 0;
    this.playerStartTile = null;
    this.initializeFloor();
    this.initializeObjects();
  }

  initializeFloor() {
    for(var i = 0; i < this.mapFile.floors.length; i++) {
      if (this.mapFile.floors[i] === CONST.TILE.BASE.FLOOR) {
        var gridTile = this.grid.getTileAtIndex(i);
        gridTile.tileBaseObj = new Floor(this.game, gridTile.x, gridTile.y, null, gridTile);
        gridTile.tileBaseType = CONST.TILE.BASE.FLOOR;
      }
    }
  }

  initializeObjects() {
    for(var i = 0; i < this.mapFile.objects.length; i++) {
      if (this.mapFile.objects[i] === CONST.TILE.OBJECT.WALL) {
        var gridTile = this.grid.getTileAtIndex(i);
        if (gridTile.tileBaseObj) {
          var wall = new Wall(this.game, gridTile.x, gridTile.y, null, gridTile);
          gridTile.tileBaseObj.addObject(wall, CONST.TILE.OBJECT.WALL);
          gridTile.tileBaseObj.z = 2;
        }
      }
      else if (this.mapFile.objects[i] === CONST.TILE.OBJECT.START) {        
        var gridTile = this.grid.getTileAtIndex(i);
        if (gridTile.tileBaseObj) {
          var startObj = new FloorObject(this.game, 'gridSampleStartPoint', gridTile, null);
          gridTile.tileBaseObj.addObject(wall, CONST.TILE.OBJECT.START);
          gridTile.tileBaseObj.z = 1;
          
        }
      }
      else if (this.mapFile.objects[i] === CONST.TILE.OBJECT.END) {        
        var gridTile = this.grid.getTileAtIndex(i);
        if (gridTile.tileBaseObj) {
          var startObj = new FloorObject(this.game, 'gridSampleEndPoint', gridTile, null);
          gridTile.tileBaseObj.addObject(wall, CONST.TILE.OBJECT.END);
          gridTile.tileBaseObj.z = 1;
        }
      }
    }

    this.player = new Player(this.game, 'prohyasIdle', this.playerStartTile);
    this.player.z = 3;
    this.main.add(this.player);
  }
}

With the above code, the player doesn't actually spawn on top of anything. It's firmly behind everything.

Changing the sort('y', Phaser.Group.SORT_ASCENDING) into sort by Z, or by ascending or descending, doesn't really change anything.

I've tried moving the this.main.add(this.player) from SpawnManager into the main Gameplay class - no dice.

About the only thing that seemed to improve it was to outright remove the sort function, which leaves me with a player that does spawn on the tiles, but is located behind every other object.

Is there anything else I can try?

Link to comment
Share on other sites

I haven't read all your code, but some points that may help you figure out what's going on:

In Phaser 2 all objects (including groups) are in a display list, which is a tree-like structure. Items at the bottom of the tree render on the top. Anything higher up gets rendered further and further back. In short, the order in which you create elements matters.

Groups maintain their own internal display lists. Using 'sort' on a Group only impacts the children of that Group. It makes no difference to any other element elsewhere on the display list.

Directly changing the z property of a Group child has no effect. It's an internal property, used by the internal sorting. To move the position of something in a Group you can either sort it, or use the methods like 'moveUp', 'moveDown', etc.

If you want your player to appear above the Grid, then it needs creating after the Grid, or you need to move it above the Grid in the World (which again is its own display list).

If you want to have enemies or other objects appear above the Grid, but below the Player, then again, you need to plan your structure such that you create the Grid first, then the items, then your player. Or you need to use the Group methods to move these things around (because the World is a Group too)

You cannot interweave your Player between the Grid elements unless it is part of the Grid Group. In which case it will then always appear behind anything placed over the top of the Grid.

The fact you are tweening instead of setting the position makes no difference to your problem. It's one of planning more than anything - you need to plan out the 'layers' of your game, what will appear on each, and then divide them up. Perhaps you have 3 "master" Groups, Grid, Objects and Player, created in that order, so no matter how big or small those Groups grow, they always stack correctly.

Link to comment
Share on other sites

Thank you so much for the reply. I think I see where I failed, and I'm quite embarrassed to say that instead of adding the sprite object itself to the the game group, I've been adding the empty sprite object container. My error is in fact visible in the code above. I've since corrected the code, and re-applied the sort('y', Phaser.Group.SORT_ASCENDING) code back, and took your advice and moved the floor tiles into a group of their own, one that is drawn earlier.

So now, I have two groups:

  1. Floor Group - This is unsorted, and spawned / drawn earlier than the Player/Object Group. This is used to contain the floor tiles, which by all accounts should be beneath the player at all times.
  2. Player/Object Group - This is the group that contains the player and the objects. The player is the last to be added to this group. The walls are also here, but as the player cannot travel to tiles with walls, and objects cannot spawn where there are already walls, how they stack against the player / objects of similar Y value is not my concern, for as long as they cover the player if the player's Y is lower than the wall's.

However, I do still have a question:

Ideally, in the Player/Object Group, the higher the object Y is (player included), the more "in-front" it will be right?

Moving my player into a tile with an object puts the player on top of that object (intended), unless the player moves in from a tile above, in which case, the player comes in behind the object.

From there, if I move a tile lower, and then move back up to the tile with the object, the player is on top again. I've  added a this.playerObjectGroup.moveUp(this.player); after every move, in hopes of curing this, but so far no luck.

Any suggestions?

Link to comment
Share on other sites

If you want the Group sorted on the Y axis instead, then you need to call Group.sort('y') because the default is the z value (i.e. the display list order) - the whole Group would need sorting though, not just the player, if he's supposed to be mixed into the Group at the right point on the y axis.

Remember that 'moveUp' and 'moveDown' etc will only work on the current Group - if the player isn't in the current Group, or the objects you're trying to get him to appear above are in a different Group, it would have any effect. Also, 'moveUp' only moves him to be above object, you probably need 'bringToTop' - all of these commands would be overridden if you sort on the y axis though.

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

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