Skip to main content
GameDev.net gamedev.net
🔒 Locked

How to reverse platform generation?

Started by Steffi Sep 30, 2018 at 6:27 AM 6 replies 1.5k views
Original Post
Steffi
Steffi

I am creating an endless runner, please see my code here: https://codepen.io/clownhead/pen/BqyqOm

At the moment when the left arrow key is pressed the player moves left, the camera follows him so he does not go off screen and the ground tiles below the player are destroyed as they move out of the world bounds on the right and are recreated just before they move into the world bounds on the left. This allows him to keep running forever without running out of ground and works perfectly, but with one problem: I want the player to be running to the right of the game, NOT to the left. Please note I do not need the player to be able to move both ways. I can make the camera follow the player to the right instead of the left by doing the following: I remove the negative from -this in


this.world.setBounds(-this.player.xChange, 0, ...);

so it becomes


this.world.setBounds(this.player.xChange, 0, ...);

Now when the right arrow key is pressed the camera follows the player as he runs right so he does not go off screen, but the ground tiles do not regenerate and he runs out of ground. So basically I need to reverse the direction in which the ground is regenerating but I can't figure out how to do it. As everything is working to the left, I am sure it's a few simple settings, perhaps some negatives that need to be made positive? I have tried all that I can think of but I can't get it to work. I think the key to solving this might be in these lines:


this.platforms.forEachAlive( function( elem ) {
      this.platformXMin = Math.min( this.platformXMin, elem.x );
      if( elem.x > this.camera.x + this.game.width ) {
        elem.kill();
        this.platformsCreateOne( this.platformXMin - 70, this.world.height - 50, 50 );
      }
    }, this );

I have tried changing all sorts of settings there, but I can't seem to get it to work. I am new to Phaser and still learning Javascript. Is anyone able to see an obvious solution? I'd be so grateful. Thanks in advance for any help!

Endurion
Endurion

Shouldn't


if ( elem.x > this.camera.x + this.game.width )

be changed to eliminate platforms only when they're out on the left side?
Also, the same, for platformsCreateOne, that should probably create the platforms outside the right border, not the left.

Currently you seem to create platforms outside the left border, and try to kill them, when they're out on the right.


Seriously, that should be pretty easy to find if you'd use a debugger. I've used Phaser with Typescript in Visual Studio and it was quite nice to use the debugger.

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>
Steffi
Steffi

Thank you Endurion for your response. I have managed to figure it out. Indeed it was partly the camera settings that needed changing. I have only been learning programming for a few months so I am sure the solution is obvious to most but I will post the code here in case it helps someone trying to create something similar:



var Jumper = function() {};
Jumper.Play = function() {};

Jumper.Play.prototype = {

  preload: function() {
    this.load.image( 'hero', 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/836/dude.png' );
    this.load.image( 'pixel', 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/836/pixel_1.png' );
  },

  create: function() {
    // background color
    this.stage.backgroundColor = '#6bf';

    // scaling
    this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    this.scale.maxWidth = this.game.width;
    this.scale.maxHeight = this.game.height;
    this.scale.pageAlignHorizontally = true;
    this.scale.pageAlignVertically = true;
    this.scale.setScreenSize( true );

    // physics
    this.physics.startSystem( Phaser.Physics.ARCADE );

    // camera and platform tracking vars
    this.cameraXMin = 0;
    this.platformXMin = 0;

    // create platforms
    this.platformsCreate();

    // create hero
    this.heroCreate();

    // cursor controls
    this.cursor = this.input.keyboard.createCursorKeys();
  },

  update: function() {
    // this is where the main magic happens
    // the x offset and the width of the world are adjusted
    // to match the furthest point the hero has reached
    this.world.setBounds(-this.hero.xChange, 0, this.world.width + this.hero.xChange, this.game.height);

    // the built in camera follow methods won't work for our needs, we create a custom one
    
    this.cameraXMin = Math.max( this.cameraXMin, this.hero.x - this.game.width + 130 );
    this.camera.x = this.cameraXMin;

    // hero collisions and movement
    this.physics.arcade.collide( this.hero, this.platforms );
    this.heroMove();

     // these are pooled so they are very performant
    this.platforms.forEachAlive( function( elem ) {
      this.platformXMin = Math.max(this.platformXMin, elem.x );
      if( elem.x < this.camera.x-70) { 
        elem.kill();
        this.platformsCreateOne(this.platformXMin + 70, this.world.height - 50, 50 );
        console.log("element killed");
      }
    }, this );
    
  },

  shutdown: function() {
    // reset everything, or the world will be messed up
    this.world.setBounds( 0, 0, this.game.width, this.game.height );
    this.cursor = null;
    this.hero.destroy();
    this.hero = null;
    this.platforms.destroy();
    this.platforms = null;
  },

  platformsCreate: function() {
    // platform basic setup
    this.platforms = this.add.group();
    this.platforms.enableBody = true;
    this.platforms.createMultiple( 10, 'pixel' );

    // create a batch of platforms that start to move across the level
    for( var i = 0; i < 9; i++ ) {
      this.platformsCreateOne( this.world.width - 450 + 70 * i, this.world.height - 50, 50);
    }
  },

  platformsCreateOne: function( x, y, width ) {
    // this is a helper function since writing all of this out can get verbose elsewhere
    var platform = this.platforms.getFirstDead();
    platform.reset( x, y );
    platform.scale.x = width;
    platform.scale.y = 16;
    platform.body.immovable = true;
    return platform;
  },

  heroCreate: function() {
    // basic hero setup
    this.hero = game.add.sprite( this.world.centerX, this.world.height - 69, 'hero' );
    this.hero.anchor.set( 0.5 );
    
    // track where the hero started and how much the distance has changed from that point
    this.hero.xOrig = this.hero.x;
    this.hero.xChange = 0;

    // hero collision setup
    // disable all collisions except for down
    this.physics.arcade.enable( this.hero );
    this.hero.body.gravity.y = 500;
    this.hero.body.checkCollision.up = false;
    this.hero.body.checkCollision.left = false;
    this.hero.body.checkCollision.right = false;
  },

  heroMove: function() {
    // handle the left and right movement of the hero
    if( this.cursor.left.isDown ) {
      this.hero.body.velocity.x = -400;
    } else if( this.cursor.right.isDown ) {
      this.hero.body.velocity.x = 400;
    } else {
      this.hero.body.velocity.x = 0;
    }

    // handle hero jumping
    if( this.cursor.up.isDown && this.hero.body.touching.down ) {
      this.hero.body.velocity.y = -350;
    } 
    
    // wrap world coordinated so that you can warp from top to bottom
    //this.world.wrap( this.hero, this.hero.width / 2, false );

    // track the maximum amount that the hero has travelled
    this.hero.xChange = Math.max( this.hero.xChange, Math.abs( this.hero.x - this.hero.xOrig ) );
  
  }
}

var game = new Phaser.Game( 500, 300, Phaser.CANVAS, '' );
game.state.add( 'Play', Jumper.Play );
game.state.start( 'Play' );


Shaarigan
Shaarigan

Just a remark, when creating an Endless Runner, you should never move the player instead move the world arround the player. The problem here is as when reaching the end of world, you'll get undefined behavior of your code otherwise. This occures due to numerical overflow.

A normal 32 bit Integer can handle arround +/- 4 billions so when reaching 4 billions and one, a byte swap will happen (unless Javascript causes some kind of error here) and you will end on the other side of the world at - 4 billions.

This is why you would normally change that and instead let the player stay unmoved while anything else arround him moves

Steffi
Steffi

Thank you so much Shaarigan for your input. In addition to what you have explained I am actually fast realising that trying to move the player through an infinite world is too problematic when it comes to adding additional sprites and assets. Despite all the hours I have put into trying to solve this, having him stationary and moving the world around him is really the only answer. All part of the learning curve I guess!

Shaarigan
Shaarigan

Youre welcome, every developer is learning new things every day so don't worry! :)

When I did an Endless Runner, I used a concurent array of meshes (in Unity for mobile platform) where each mesh was a tile of the world that is generated by certain rules on demand. Those tiles have certain size so that it wouldn't grab under player's feet when going out of scope while there are enougth tiles generated during runtime that there are always 3 tiles more than player could see far and last tile is still behind the player until he reaches the center of the next one.

If you generate those tiles on demand then you could theoretically define the world you have in some kind of meta format, process these defines and add anything to it as read if you don't want to have completely random tiles. My game was some kind of Minecraft meets Super Mario Run so I have had predefined levels with multiple optional routes to take (depending on the powerup grabbed)

Steffi
Steffi

Thanks for sharing this really useful info Shaarigan, it's always great to be encouraged and to hear ideas from other developers! ?

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.