Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by lazer

I've been working on a game with procedurally generated levels and as a makeshift solution instead of using the collision map I set up collision entities that spawn on the fly as the player moves. Their size is adjusted to match the player's position, screen size, and they're set up to not spawn when a pit entity comes up (so that the player could fall in).

However, I think this is a really messy way of doing this. In addition I'm getting lots of motion jitter. I'm not sure if this has anything to do with the player moving on top of these entities instead of a regular collision map (any thoughts?).

Anyway, I think that this is possible to do with the collision map - I just don't know how. There are no height changes - the only variation has to be the solid collision tiles becoming one way pass-through tiles when a pit entity comes up - the rest of the time they're just repeating as the player moves in one direction. Could anyone please point me in the right direction here?

Thanks!

1 decade ago by StuartTresadern

Does this help , it creates a random scrolling single tile with collision map.

You may want to create a bigger scroll buffer and even look at drawing larger images on a seperate canvas. This example is only using a single tile buffer.

ig.module(
    'game.main'
)
    .requires(
    'impact.game',
    'impact.font',
    'game.entities.player',
    'impact.debug.debug'

)
    .defines(function () {

        MyGame = ig.Game.extend({

            scrollMap:null,
            viewSize:{x:0, y:0},
            tileSize:16,

            init:function () {

                // Bind keys
                ig.input.bind(ig.KEY.LEFT_ARROW, 'left');
                ig.input.bind(ig.KEY.RIGHT_ARROW, 'right');
                ig.input.bind(ig.KEY.UP_ARROW, 'up');
                ig.input.bind(ig.KEY.DOWN_ARROW, 'down');

                //Get the number of tiles displayed on the screen.
                //Add one extra row to act as a scroll buffer
                this.viewSize.x = Math.floor(ig.system.width / this.tileSize) + 1;
                this.viewSize.y = Math.floor(ig.system.height / this.tileSize);

                // Generate blank array for the display map & collision Map
                var data = new Array(this.viewSize.y);
                var cdata = new Array(this.viewSize.y);

                for (var iY = 0; iY < this.viewSize.y; iY++) {
                    data[iY] = new Array(this.viewSize.x);
                    cdata[iY] = new Array(this.viewSize.x);
                    for (var iX = 0; iX < this.viewSize.x; iX++) {

                        data[iY][iX] = 0;
                        cdata[iY][iX] = 0;
                    }
                }

                // Background Map   ( for this example the tile size needs to be the same for both
                // the collision Map and the background map.
                this.scrollMap = new ig.BackgroundMap(this.tileSize, data, 'media/pipemaze.png');
                this.scrollMap.setScreenPos(0, 0);
                // Collision Map
                ig.game.collisionMap = new ig.CollisionMap(this.tileSize, cdata);
            },

            update:function () {

                this.scrollMap.scroll.x += (ig.system.tick * 100);

                if (this.scrollMap.scroll.x >= this.tileSize) {

                    // some fractional error that may cause judder..
                    this.scrollMap.scroll.x = (this.scrollMap.scroll.x % this.tileSize);

                    // Remove first element in array
                    this.scrollMap.data[10].shift();
                    ig.game.collisionMap.data[10].shift();
                    // Push the new tile to the end of the array  .. some random generation!
                    if (Math.floor(Math.random() * 2) != 0) {
                        this.scrollMap.data[10].push(1);
                        ig.game.collisionMap.data[10].push(1);

                    }

                    this.scrollMap.data[10].push(0);
                    ig.game.collisionMap.data[10].push(0);

                }

                this.parent();

            },

            draw:function () {

                this.parent();
                this.scrollMap.draw();

            }
        });



        ig.main('#canvas', MyGame, 60, 320, 240, 1);

    });

1 decade ago by lazer

Thanks, Stuart, I'll try this out tonight. The only thing is - does this background behavior match what would usually happen with a regular level? Eg player vel.x is set to something, the camera is told to follow the player, and the background repeats after it (this is what I use now)? Or is this a totally different mechanic (eg background tiles being added and removed driving the appearance of movement rather than the player's velocity). The background and collision map stuff is the hardest for me to grasp, so I'm having some trouble getting my head around it >.<

Thanks again!

1 decade ago by StuartTresadern

Well sort of; for example if we had a normal level made in the editor of say 200 x 100 tiles and we had a screen display area (camera view) of 100 x 100 tiles we can move the camera view at whatever speed we want to the right up to another 100 tiles. If we forget about the player for a bit as this is simply a reference point for the camera we could simply scroll the tile map in our main update:
this.scrollMap.scroll.x += (ig.system.tick * 100);

Of course you could simply move the view screen as it does in the camera class instead of using scroll:
ig.game.screen.x += myspeed;

Now the tile map you want does not exist because you are generating it so we need to create a 2d array to store the level data. Note this is stored in Y, X order. Generally this needs to be big enough to fill the camera view area so for this example 100 x 100. If we simply create this map with some graphic tile and update it using either of the methods above the map will get pushed out of the camera view to the left.
So we need some kind of off screen (out of camera view) buffer that we can generate before we need to draw it on the screen. Keeping the numbers easy we will use a tile size of 10 x 10 this means we need ten tiles to fill the camera view from left to right (we don’t care about vertical tiles as you are not moving them, if you are the idea is the same). If we use a single tile buffer as in the above example we need to generate the background map to be 1 tile wider that the visible area.
this.viewSize.x = Math.floor(ig.system.width / this.tileSize) + 1;
                this.viewSize.y = Math.floor(ig.system.height / this.tileSize);

                // Generate blank array for the display map & collision Map
                var data = new Array(this.viewSize.y);
                var cdata = new Array(this.viewSize.y);

                for (var iY = 0; iY < this.viewSize.y; iY++) {
                    data[iY] = new Array(this.viewSize.x);
                    cdata[iY] = new Array(this.viewSize.x);
                    for (var iX = 0; iX < this.viewSize.x; iX++) {

                        data[iY][iX] = 0;
                        cdata[iY][iX] = 0;
                    }
                }

Each time the update routine executes it scrolls the visible area to the left by whatever speed has been specified in the scroll.x until the screen has moved approx. one whole tile to the left (10 pixels in this case) at this point we have no more tiles on the right of the map so the scroll.x position is moved back to 0 (the example needs some cleaning up as this is a fractional move at the moment and will cause some kind of judder, I would take a look in the engine code as Dominic must have resolved this somehow already).Currently this example uses shift() and push(x) to remove the first tile from the map row and add a new tile to the end. This gives us the new display data required as we start the next scroll.

I have no idea of your player movement requirements but if the player is static in the middle of the screen on the x axis (we will leave y alone and let the normal gravity / velocity do its job) instead of moving the player which uses a camera to position the camera view we can move the screen based on the same calculations you would get from your player entity. If you take a look in the entity code you will see getNewVelocity which we can use to pass onto the scroll.x .
Add a few properties to the above example:
scrollvelx:0,
scrollaccel:0,
scrollmaxvelx:10,

Replace the update code:
            update:function () {
                if (ig.input.state('right'))
                {
                    this.scrollvelx = this.scrollvelx < 0 ? 0 : this.scrollvelx;
                    this.scrollaccel= 2;
                }
                if (ig.input.state('left'))
                {
                    this.scrollvelx = 0; // stop dead
                }
                this.scrollMap.scroll.x += this.scrollvelx;//(ig.system.tick * this.scrollvelx);
                if (this.scrollMap.scroll.x >= this.tileSize) {
                    // some fractional error that may cause judder..
                    this.scrollMap.scroll.x = (this.scrollMap.scroll.x % this.tileSize);
                    // Remove first element in array
                    this.scrollMap.data[10].shift();
                    ig.game.collisionMap.data[10].shift();
                    // Push the new tile to the end of the array  .. some random generation!
                    if (Math.floor(Math.random() * 2) != 0) {
                        this.scrollMap.data[10].push(1);
                        ig.game.collisionMap.data[10].push(1);
                    }
                    this.scrollMap.data[10].push(0);
                    ig.game.collisionMap.data[10].push(0);
                }
                this.scrollvelx = this.getNewVelocity(this.scrollvelx,this.scrollaccel,0,this.scrollmaxvelx);
                this.parent();
                this.scrollaccel=0;
            },

This is a bit hacky but you should get the idea, we move the screen to the right in the same way we would move a player entity. Just bind and check the jump key as normal.
The above assumes that the player does not move on the x axis but of course you could create a trap area in the same way the camera class does and take the values into account or modify the camera class to do the same work as above.

I would look a generating as much of the map upfront as possible before entering the game loop, if the map backgrounds are not going to change much once generated then it would be faster and of course you can pre render the chunks.

But if you are generating the map based on what happens in the game then you will have to take a similar approach to the above example. If you can increase the scroll buffer and the tile size you will probably get smoother results.

I’m still newish to JavaScript and the HTML5 Canvas and of course Impact so some of the more experienced member’s may have other ideas but this is similar to how I have done the same thing on other engines.

1 decade ago by lazer

Thank you so much for the awesome and in-depth explanation. I'll go through all this when I'm home tonight and give it a try :)
Page 1 of 1
« first « previous next › last »