Jump to content

Stretch/reduce a TilingSprite background for zoom in/out purpose


Tylox
 Share

Recommended Posts

Hello, here is the code :
 

//not in the loop :
this.canvas = document.getElementById("ctx"); 
this.canvas.width = 1920; //viewport of the player
this.canvas.height = 1080;
this.renderer = new PIXI.autoDetectRenderer(this.canvas.width, this.canvas.height, {
        view: this.canvas
    });
this.renderer.clearBeforeRender = false;
this.stage = new PIXI.Container();
this.mapImage = new PIXI.Texture.fromImage('map.png'); //dimension of the map png : 1024x1024

//in the loop :
//selfState has the position of the player (x,y) and his mass
var drawMap = function(selfState) {
    if (!this.map) {
        this.map = new PIXI.extras.TilingSprite(this.mapImage, this.renderer.width, this.renderer.height);
        this.stage.addChild(this.map);
    }

//center the player and move the map when the player move
    if (this.centerX === undefined) {
        this.centerX = selfState.x;
        this.centerY = selfState.y;
    } else {
        if (selfState.x !== this.centerX || selfState.y !== this.centerY) {
            this.map.tilePosition.x += this.centerX - selfState.x;
            this.map.tilePosition.y += this.centerY - selfState.y;
            this.centerX = selfState.x;
            this.centerY = selfState.y;
        }
    }
}

I would like that the viewport of the player increase with his mass
I tried : 

//in the loop
var resizeMass = function(mass) {
    scopeW = 1920 + mass * 2;
    scopeH = scopeW * 9 / 16;
    resizeCanvas(scopeW, scopeH);
};

var resizeCanvas = function(w, h) {
    //...
    //resize canvas
    //...
    this.renderer.resize(w, h);
    if (this.map) {
        this.map.width = w;
        this.map.height = h;
    }
}

 

But it gets really big and there is performance issue.
Do you have a solution to show to the player all the map game (5000x5000 or even 10000x10000) without performance issue ? 
I think that I have to reduce the TilingSprite width/height of the map and all the other Sprites to keep a viewport of 1920x1080. But how to do it efficiently for the TilingSprite ?

And for the Sprites, if there is no performance issue, I don't want to decrease the scale of the Sprite because I lose the quality of the original sprite (blurry). I want to "reduce" the width of the image to draw and not to reduce the img.width or img.height. (like drawImage from canvas2d http://www.w3schools.com/tags/canvas_drawimage.asp )

Link to comment
Share on other sites

Its ok to make TilingSprite width and height very big, it can handle it.

Your problem is that you are resizing canvas and it is VERY bad. You have to scale the stage: 

stage.position.set(renderer.width/2, renderer.height/2); //position it in center, that way it will be scaled there
stage.scale.set(2,2); //scale stuff ;)
stage.pivot.set(centerX, centerY); // point on map that you are viewing

//method number one: create big tilingsprite and forget about that you actually need only one part. videodriver will cull it for you ;)

map.width = 10000; //your true map size, big one, including all the space of your map
map.height = 10000;

map.tilePosition.set(0, 0); // you dont have to change tileposition in this method.

//method2: carefully make tilingsprite size the same as your view screen. I dont think that it is significally faster than method number one.

map.width = renderer.width/stage.scale; map.height = renderer.height/stage.scale;
map.position.set(centerX - map.width/2, centerY - map.height/2);
map.tilePosition.copy(map.position); // actually, may have change the sign of it: -x, -y, i dont know if we need that

The quality of the sprite can be retained by using mipmaps, which are enabled by default in pixi-v4 for power-of-two texture, like that one. But at the same time, pixi-v4 has some bugs that will lead to strange lines on tilingsprite border, so please wait while we fix it :)

If you are using pixi-v3, you have to add line "this.mapImage.baseTexture.mipmap=true;" and it will work fine.

Link to comment
Share on other sites

Thank you for your reply.

1) For method 1, I scaled the stage successfully but I can't find a way to keep the player at the center of the viewport... I've also :

player.sprite.position.x = this.canvas.width / 2;
this.map.position.x = this.canvas.width / 2 - selfState.x;
this.map.position.y = this.canvas.height / 2 - selfState.y;


Maybe I don't understand where to put your line 1 and 3 in my first post code, and which centerX/Y to choose.

2) mipmap is a new concept for me, in adding line "this.mapImage.baseTexture.mipmap=true;", do you mean that the image will be blurry as I see it ? Because in setting it to "false", I keep the quality of the sprite (no blur).

Link to comment
Share on other sites

stage.position.set(renderer.width/2, renderer.height/2); //position it in center, that way it will be scaled there
stage.scale.set(2,2); //scale stuff ;)
stage.addChild(player); // add player

// in the update:

cameraX = player.position.x;
cameraY = player.position.y;
stage.pivot.set(cameraX, cameraY); // point on map that you are viewing

PLEASE look at my comments again and think about how position/scale/pivot system works. I'm begging you to just stop and think.

Link to comment
Share on other sites

  • 4 months later...
On 8/13/2016 at 10:09 PM, ivan.popelyshev said:

Its ok to make TilingSprite width and height very big, it can handle it.

Your problem is that you are resizing canvas and it is VERY bad. You have to scale the stage: 


stage.position.set(renderer.width/2, renderer.height/2); //position it in center, that way it will be scaled there
stage.scale.set(2,2); //scale stuff ;)
stage.pivot.set(centerX, centerY); // point on map that you are viewing

//method number one: create big tilingsprite and forget about that you actually need only one part. videodriver will cull it for you ;)

map.width = 10000; //your true map size, big one, including all the space of your map
map.height = 10000;

map.tilePosition.set(0, 0); // you dont have to change tileposition in this method.

//method2: carefully make tilingsprite size the same as your view screen. I dont think that it is significally faster than method number one.

map.width = renderer.width/stage.scale; map.height = renderer.height/stage.scale;
map.position.set(centerX - map.width/2, centerY - map.height/2);
map.tilePosition.copy(map.position); // actually, may have change the sign of it: -x, -y, i dont know if we need that

The quality of the sprite can be retained by using mipmaps, which are enabled by default in pixi-v4 for power-of-two texture, like that one. But at the same time, pixi-v4 has some bugs that will lead to strange lines on tilingsprite border, so please wait while we fix it :)

If you are using pixi-v3, you have to add line "this.mapImage.baseTexture.mipmap=true;" and it will work fine.

I am following your method to make my stage scale to the center point of the screen. However I don't know how to position my sprites on the stage.

I am having a container as stage.

On top of that  stage there is a gameScene container. Then I put all my sprites on the gameScene. Something like this:

const stage = new Container();
stage.position.set(renderer.view.width / 2, renderer.view.height / 2);
stage.pivot.set(centerX, centerY);
const gameScene = new Container();

for (let i = 0; i < COUNT; i++) {
  //... generate some texture and assign to 
  let texture = ...
  let sprite = Sprite.from(texture);
  gameScene.addChild(sprite);
}

let player = Sprite.from(otherTexture);
gameScene.addChild(player);

stage.addChild(gameScene);

Then in the animate update:

if(timeToScale) {
  stage.scale.set(0.6); // zoom to make the map smaller.
}

However I am not sure which x and y is centerX and centerY. Is it the current position of my player sprite? I am totally confused. I know you are a very experienced PIXI developer. Please shed some light for me. Thanks in advance.

 

EDIT:

 

I have figured out the solution based on your code and the issue discussion here: https://github.com/pixijs/pixi.js/issues/190

It's really simple but just very confusing for beginner who comes across this scenario for the first time.

All I need to do is change to pivot point to center, where my player sprite locates. and in update loop change my player sprite's scale, as is:

const g = new PIXI.Graphics();
g.beginFill(0xff0000);
g.lineStyle(0);
g.drawCircle(0, 0, 30);
g.endFill();

// ...
stage.position.x = WIDTH / 2; // Shift stage position to center, WIDTH, HEIGHT is the renderer.view's size
stage.position.y = HEIGHT / 2;
stage.pivot.x = WIDTH / 2; // change pivot point to center
stage.pivot.y = HEIGHT / 2;

//...
const gameScene = new PIXI.Container();
gameScene.addChild(g);
stage.addChild(gameScene);

let size = 5; 

In the update loop:

g.position.x = Math.round(WIDTH / 2); // WIDTH, HEIGHT is the renderer.view's size
g.position.y = Math.round(HEIGHT / 2);
size++;
if (timeToScale) {
  	scale = scale < 1.8? 1 + 0.02 * (size / 100) : 1.8;
  	g.scale.set(scale);
}

So now my sprite can scale in the center of the screen.

I can also make the background zoom out at the same time. However the problem is, how can I make the scaling smoother? It's stuttering when scaling which is not very natural. I want a natural scaling.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

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