Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic collision layer #152

Open
murilopolese opened this issue Dec 21, 2014 · 7 comments
Open

Dynamic collision layer #152

murilopolese opened this issue Dec 21, 2014 · 7 comments

Comments

@murilopolese
Copy link

Is there a good way to append chunks of data to the current collision layer on a scene?
The main reason is to be able to create dynamic "infinite" games and specially to create the level on the run.
I did the following changes on file quintus_2d.js:208`:

load: function(dataAsset) {
  var data;
  if( dataAsset instanceof Array ) {
    data = dataAsset;
  } else {
    var fileParts = dataAsset.split("."),
        fileExt = fileParts[fileParts.length-1].toLowerCase();

    if (fileExt === "json") {
      data = Q._isString(dataAsset) ?  Q.asset(dataAsset) : dataAsset;
    }
    else {
      throw "file type not supported";
    }
  }

So I could keep an array with my level and concat new chunks as the character moves. Here's the demo:
http://murilopolese.com.br/sandbox/fabulous-runner/

I'd love to understand what would be the best way to do that or if this is the way to go.

Cheers,
Murilo

@cykod
Copy link
Owner

cykod commented Dec 22, 2014

I'd suggest instead of this plan of attack (expanding the data on 1 collision layer), you instead add additional collisionLayers offset in position - otherwise the 1 collision layer will get extremely large and unwieldly. This way you could in theory remove the old collision layers and free up memory (this would require some custom code to clean up the scene._collisionLayers array when you remove a collision layer.

@JaredSartin
Copy link

Necroing... @cykod - how do you suggest offsetting? putting an x position on the tileLayer doesn't allow it to render properly.

@R4wizard
Copy link

R4wizard commented Jan 7, 2016

I'm having a similar problem. I am also trying to stream in chunks of levels. I have also tried setting the x position on the tileLayer, with varying results. I've come to the conclusion that if you change the 'x' of the tileLayer, you move the collision perfectly but then the tiles are rendered at 'x*2'. I can't see a clean way to resolve this as I am unfamilar with the code, but I'll keep digging.

@R4wizard
Copy link

R4wizard commented Jan 7, 2016

After a tiny bit more digging, I think I found the problem:
https://github.com/cykod/Quintus/blob/master/lib/quintus_2d.js#L395
The 'p.x' and 'p.y' are added here. If I remove this addition by doing:

        tileLayer.drawBlock = function(ctx, blockX, blockY) {
            var p = tileLayer2.p,
                startX = Math.floor(blockX * p.blockW),
                startY = Math.floor(blockY * p.blockH);

            if(!tileLayer.blocks[blockY] || !tileLayer.blocks[blockY][blockX]) {
                tileLayer.prerenderBlock(blockX,blockY);
            }

            if(tileLayer.blocks[blockY] && tileLayer.blocks[blockY][blockX]) {
                ctx.drawImage(tileLayer.blocks[blockY][blockX],startX,startY);
            }
        };

Then everything renders correctly, and the collision appears to work.
I haven't created a push request as I don't know if my changes will affect the code in other ways.

@JaredSartin
Copy link

I ended up modifying the collision layer, then calling .setDimensions(); and ._generateCollisionObjects(); on the layer object. My code is messy and from 6 months ago - so I can try to find the key points for you...

@R4wizard
Copy link

R4wizard commented Jan 7, 2016

My current solution seems to function okay, but please do post any information you have. It may all blow up in my face! (and you never know who might find this ticket next and want info).

Thanks!

@JaredSartin
Copy link

shitty old code below. What this does is news up a map, with a start room, some empty rooms, and then a goal room. A room is merely and X by Y sized section of the tiles/collision layer. There are helper methods that get called at any point to change "rooms" in the map. See changeRoom - you see it create a tilemap from the given map name with var tileLayer = new Q.TileLayer..., then it finds the room offset on the main tilemap and modifies the data at that point (the while loop following the tile layer creation). We then create a new tile layer from the current tile layer's tiles... THIS is the crappy point. You can't just simply tell the tile layer to update and have it work.

The reason we create a new tile layer had to do with 2 things - the cruft left on the old tile layer, attempting to use old data for collisions with no way to reset it AND the stage collision layer not accepting a reset on the collision layer. What we end up doing is cloning the modified tile layer and re-assigning the cloned layer to the stage as the collision layer.

Yes, brainf*ck. Also, could possibly be done better. I put comments on key points in the code below:

  var mapLayer = new Q.TileLayer({ dataAsset: 'Entrance.json', sheet: 'terrain', tileW: tileSize, tileH: tileSize }); // initial map creation
  var empty = new Q.TileLayer({ dataAsset: 'Empty.json', sheet: 'terrain', tileW: tileSize, tileH: tileSize });
  var goal = new Q.TileLayer({ dataAsset: 'Goal.json', sheet: 'terrain', tileW: tileSize, tileH: tileSize });
  stage.collisionLayer(mapLayer); // collision layer assignment

  // helper method to add tiles to the map layer
  var addTiles = function(tileLayer) {
    var len = mapLayer.p.tiles.length;
    while(len--) {
      mapLayer.p.tiles[len] = mapLayer.p.tiles[len].concat(tileLayer.p.tiles[len]); // Modify current tile/collision layer
    }
  };

  // helper method to change portions of the map
  // called at many points in the game when parts of the map are swapped out.
  var changeRoom = function(roomIdx, mapName) {
    if(roomIdx > totalRooms - 2 || roomIdx < 1) throw "NO! ROOM NUMBER NOT IN RANGE [1," + totalRooms - 2 + "]";
    var len = mapLayer.p.tiles.length;
    var roomOffset = roomWidth * roomIdx;
    var tileLayer = new Q.TileLayer({ dataAsset: mapName + '.json', sheet: 'terrain', tileW: tileSize, tileH: tileSize });
    while(len--) {
      var colCounter = roomWidth;
      while(colCounter--) {
        mapLayer.p.tiles[len][colCounter+roomOffset] = tileLayer.p.tiles[len][colCounter]; // Modify current tile/collision layer
      }
    }
    mapLayer = new Q.TileLayer({ tiles: mapLayer.p.tiles, sheet: 'terrain', tileW: tileSize, tileH: tileSize }); // clone the current layer... see replaceCollisionLayer
    replaceCollisionLayer(mapLayer);
  };

  // We replace the current collision layer with a cloned version of the modified original.
  // The modified original could not be reset properly to tell the stage that is has changed.
  // Also, the way the current layer was referenced, I believe it wouldn't allow a re-assign.
  var replaceCollisionLayer = function(newLayer) {
    stage.collisionLayer(newLayer);
    stage._collisionLayers.shift().destroy(); // This is because we ADDED a new collision layer on the line before. This removes the old layer.
  }

  // All the crap below is setup code - but useful to get bearings on the rest of the code.
  var i = totalRooms - 2;
  while(i--) {
    addTiles(empty);
  }

  var i = totalRooms - 2;
  while(i--) {
    changeRoom(i+1, "Empty");
  }
  addTiles(goal);
  mapLayer.setDimensions();
  mapLayer._generateCollisionObjects();

  stage.centerOn(stage.options.startX, stage.options.startY);
  stage.viewport.scale = stage.options.zoom;
  stage.follow(rogue, {x: stage.options.followX, y: stage.options.followY});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants