Pixi Tile Wizard Demo (1)


The introduction

This is in learning pixi tiling wizard when the association of a parallax rolling collision detection demo and encountered a problem.


directory

  1. Tile elves

    1.1 Creation Methods

    1.2 the difference between

    1.3 offset value

    1.4 Texture offset code

  2. Parallax scrolling

    2.1 Texture accuracy deviation

  3. Collision detection

  4. demo

  5. conclusion


1. Tiling sprites

Jump back to directory

1.1 Creation Methods

Create a pixi tile Sprite

/ / the first
new PIXI.extras.TilingSprite(texture, width, height);

/ / the second
new PIXI.extras.TilingSprite.from(source, width, height);

Copy the code

1.2 the difference between

The first:

Texture by PIXI. Loader. Shared. Get photo texture in resources.

let bgSpr = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['bg'].texture, app.renderer.width, app.renderer.height);
Copy the code

The second:

Source can be imported directly through the URL path.

let bgSpr = new PIXI.extras.TilingSprite.from('./img/bg.jpg', app.renderer.width, app.renderer.height);
Copy the code

Note that widht height means that the tiled range is not set. Default is 100px.


1.3 offset value

tilePosition:

Tileposition. set(x, y) differs from positions. Set (x, y) in that the former moves the tile Sprite texture and the latter moves the tile Sprite position.

Specific usage:

Update tilePosition.x value with ticker game loop.

app.ticker.add(() = > {
  bgSpr.tilePosition.x -= -1;
});
Copy the code


1.4 Texture offset code

let app = new PIXI.Application({widht: app.renderer.width, height: app.renderer.height});

document.body.appendChild(app.view);

PIXI.Loader.shared.add('bg'.'./img/bg.jpg');

PIXI.Loader.shared.load(() = > {
  setup();
});

function setup () {
  let bgSpr = new PIXI.extras,TilingSprite(PIXI.Loader.shared.resources['bg'].texture, app.renderer.width, app.renderer.height);
  
  app.stage.addChild(bgSpr);
  
  app.ticker.add(() = > {
  	bgSpr.tilePosition.x -= 1;
	});
};
Copy the code

Effect drawing (random material) :



2, parallax rolling

Jump back to directory

Tiling sprites are commonly used to create seamless scrolling backgrounds. Above we implemented the offset of tiling sprites. What is parallax scrolling?

Parallax scrolling is a three-dimensional motion effect created by moving multiple layers of background at different speeds. For example, in a Super Mario Game, the movement of the character’s location and the movement of the background sky is a parallax scroll.

This means you need two tile sprites and offset them at different speeds throughout the game loop.

let prospectSpr = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['prospect'].texture, app.renderer.width, 437);

app.stage.addChild(prospectSpr);

app.ticker.add(() = > {
  prospectSpr.tilePosition.x -= 3;
})
Copy the code

Effect:

A parallax scrolling is achieved, with the speed of the background layer being 1 and the speed of the foreground layer being 3.


2.1 Texture accuracy deviation

In the process of learning, I also encountered a pit, that is, every time I offset a cycle, the clarity of the Sprite map will become a little bit worse, and after multiple cycles, the Sprite map will become a Mosaic:

Note: this does not happen on browser emulation mobile, but only on mobile.

At first I thought there was a problem with the game loop method, but after changing the plugin, the above situation still appeared in the test results. I also found a similar situation in Google:

Probably the problem is that tile sprites have accuracy issues over time.

Solutions are also proposed:

prospectSpr.tilePosition.x %= PIXI.Loader.shared.resources['prospect'].texture.width;
Copy the code

The ticker code is as follows:

app.ticker.add(() = > {
  prospectSpr.tilePosition.x -= 3;
  prospectSpr.tilePosition.x %= PIXI.Loader.shared.resources['prospect'].texture.width;
})
Copy the code


3. Collision detection

Jump back to directory

The principle of collision detection in web pages is to calculate whether two rectangles collide by x and y coordinates, that is, to determine whether they overlap.

The collision detection method can be found in the PIXI tutorial. There is also a handy plugin called bump.js, which is very simple to use:

b.hit(sprite1, sprite2); // Returns a Boolean type, true for collision.B.h it (sprite1, sprite2,true); // The third argument is true and does not overlap on collisions.B.h it (sprite1, sprite2,true.true); // The fourth argument is true and bounces the first Sprite on collision
Copy the code

For a full tutorial, go to: Bump.js Tutorial.

Knowing the principle, it is actually very simple to do, as long as the center point of the wizard is set, and the x and y axes of the two sprites are judged to overlap. The following is a relatively simple collision detection that I have implemented:

function bump (spr1, spr2) {
  spr1.anchor.set(0.5.1); // Set the Sprite center position
  spr2.anchor.set(0.5.1);
  
  if (spr1.x - spr2.x < spr2.width && spr1.x - spr2.x > -spr2.width) {
    // Meet the x position condition
    return spr1.y - spr2.y === 0? true : false; // Return if y conditions are met
  } else {
    return false; }}Copy the code

Then you just have to determine whether the return is true or false.

New code:

PIXI.Loader.shared
  .add('role'.'./img/sprite1_0.png')
  .add('monster'.'./img/blob.png');let role = new PIXI.Sprite(PIXI.Loader.shared.resources['role'].texture);
let monster = new PIXI.Sprite(PIXI.Loader.shared.resources['monster'].texture);
let isBump = null;

role.anchor.set(0.5.1);
monster.anchor.set(0.5.1);

role.scale.set(1.5.1.5);
monster.scale.set(3.3);

role.position.set(300, app.renderer.height - 180);
monster.position.set(1500, app.renderer.height - 180);

app.stage.addChild(role, monster);

app.ticker.add(() = > {
  monster.x -= 3;
  bump(role, monster) && console.log('collision');
});

function bump (spr1, spr2) {
  spr1.anchor.set(0.5.1); // Set the Sprite center position
  spr2.anchor.set(0.5.1);
  
  if (spr1.x - spr2.x < spr2.width && spr1.x - spr2.x > -spr2.width) {
    // Meet the x position condition
    return spr1.y - spr2.y === 0? true : false; // Return if y conditions are met
  } else {
    return false; }}Copy the code

Effect:



4, the demo

Jump back to directory

The above main function is almost, in addition to the character action and interaction so a similar parkour demo came out, the following code also uses a plug-in for replacing the character action picture smoothie. Js tutorial to go

function setup () {
  / / the background
  let bgSpr = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['bg'].texture, app.renderer.width, app.renderer.height);
  / / outlook
  let prospectSpr = new PIXI.extras.TilingSprite(PIXI.Loader.shared.resources['prospect'].texture, 1600.437);
  / / character
  let role = new PIXI.Sprite(PIXI.Loader.shared.resources['role'].texture);
  / / the monster
  let monster = new PIXI.Sprite(PIXI.Loader.shared.resources['monster'].texture);

  let roleSmoothie = null; // Character animation
  let monsterSmoothie = null; // Monster animation
  let prospectSmoothie = null; // Foreground animation
  let isBump = null; // Jump state

  let roleSprGoIndex = 0; // walk action picture subscript
  let roleSprRunIndex = 0; // Run action picture subscript
  let roleSprJumpIndex = 0; // runout is used to subscript the image
  let roleSprInverIndex = 0; // reverse action picture subscript

  let prospectSpeed = 3; // Foreground speed

  let isAction = true; // Action state
  
  / / center
  prospectSpr.anchor.set(0.1);
  role.anchor.set(0.5.1);
  monster.anchor.set(0.5.1);

  // Scale
  role.scale.set(1.5.1.5);
  monster.scale.set(3.3);

  / / position
  role.position.set(300, app.renderer.height - 180);
  monster.position.set(1500, app.renderer.height - 180);
  prospectSpr.y = app.renderer.height;

  // Add to the stage
  app.stage.addChild(bgSpr, prospectSpr, role, monster);
  
  / / translation
  function translate (spr, num) {
    spr.tilePosition.x -= num;
    spr.tilePosition.x %= PIXI.Loader.shared.resources['prospect'].texture.width;
  };

  // The monster moves
  function monsterTranslate (spr, num, x) {
    spr.position.x -= num;
    spr.position.x < -x && (spr.position.x = 1600);
  };
 
  / /.
  function go () {
    role.texture = PIXI.Loader.shared.resources[config.go[roleSprGoIndex]].texture;
    roleSprGoIndex < 6? roleSprGoIndex++ : roleSprGoIndex = 0;
  };

  / / run
  function run () {
    role.texture = PIXI.Loader.shared.resources[config.run[roleSprRunIndex]].texture;
    if (roleSprRunIndex < 6) {
      roleSprRunIndex++;
    } else {
      roleSprRunIndex = 0;
      roleSmoothie.update = go.bind(this); }}/ / jump
  function jump () {
    role.texture = PIXI.Loader.shared.resources[config.jump[roleSprJumpIndex]].texture;
    if (roleSprJumpIndex < 5) {
      roleSprJumpIndex++;
      role.position.y -= 30;
      isAction = false;
    } else {
      roleSprJumpIndex = 0;
      role.position.y = 570;
      isAction = true;
      roleSmoothie.update = go.bind(this); }}/ / the
  function inverted (num) {
    role.texture = PIXI.Loader.shared.resources[config.inverted[roleSprInverIndex]].texture;
    if (roleSprInverIndex < num) {
      roleSprInverIndex++;
      isAction = false;
    } else if (num === 6) {
      isAction = true;
    } else {
      roleSprInverIndex = 0;
      isAction = true;
      roleSmoothie.update = go.bind(this); }}// The character moves
  roleSmoothie = new Smoothie({
    engine: PIXI,
    renderer: app.renderer,
    root: app.stage,
    fps: 8.update: go.bind(this)}); roleSmoothie.start();// The monster moves
  monsterSmoothie = new Smoothie({
    engine: PIXI,
    renderer: app.renderer,
    root: app.stage,
    update: monsterTranslate.bind(this, monster, 7.100)}); monsterSmoothie.start();// Foreground move
  prospectSmoothie = new Smoothie({
    engine: PIXI,
    renderer: app.renderer,
    root: app.stage,
    update: translate.bind(this, prospectSpr, 3)}); prospectSmoothie.start();// Keyboard press event
  $(document).keydown((e) = > {
    if(! isAction)return;
    e.keyCode === 38 && (roleSmoothie.update = jump.bind(this), prospectSmoothie.update = translate.bind(this, prospectSpr, 4));
    e.keyCode === 39 && (roleSmoothie.update = run.bind(this), prospectSmoothie.update = translate.bind(this, prospectSpr, 6));
  });

  // The keyboard is lifted
  $(document).keyup((e) = > {
    prospectSmoothie.update = translate.bind(this, prospectSpr, 3);
  });
  
  app.ticker.add(() = > {
    bgSpr.tilePosition.x -= 1;
    bgSpr.tilePosition.x %= PIXI.Loader.shared.resources['bg'].texture.width;
    bump(role, monster) && (roleSmoothie.update = inverted.bind(this.3));
  });
  
  / / collision
  function bump (spr1, spr2) {
    spr1.anchor.set(0.5.1);
    spr2.anchor.set(0.5.1);
    if (spr1.x - spr2.x < spr2.width && spr1.x - spr2.x > -spr2.width) {
      return spr1.y - spr2.y === 0? true : false;
    } else {
      return false; }}; }Copy the code

Project link: Demo


5, summary

Learning knowledge still needs to be done through practice, from practice we can see that there are many problems in the text tutorial, such as tile sprites will have accuracy problems over time, I did not know this problem will occur before the demo and it will not appear on the PC browser. Making a demo by learning a knowledge point can not only reinforce that knowledge point, but also extend it to other knowledge points, such as collision detection, Sprite texture switch, game loop, etc. Once you have a preliminary demo, you can add other features, such as health, scores, scene changes, overhead pillars and obstacles, and you have a simple little game.