[Fixed] Scaling a whole game x2 correctly

Hello!

Please consider these to pictures of my current WIP:
Unscaled:

http://www.javadaemon.com/pictures/scaling_problem.png

Scaled x2 in super render method:

http://www.javadaemon.com/pictures/scaling_problum.png

I’m using Slick2D by the way. The upper image shows the game, and it’s working as it should.
The second image, shows the result when I’m doing a g.scale(2,2); in the upper-most rendering method (the one in the “main” class).

I’m obviously missing something because that wasn’t quite the result I was looking for.

I’ve tried to multiply various different variables in the world-rendering, but it all just gave me wrong (though rahter interresting results).
Here it is, as it is when I am not trying to scale it:


@Override
	public void render(GameContainer container, Graphics g)
			throws SlickException {

		
		// Draw the current tile map, and the actors in it
		
		final TileMap map = model.getCurrentMap(); // Get the currently active (displayed) map from the game state
		final SpriteSheet graphics = RessourceManager.getRessourceManager().getGraphics(); // Get the sprite sheet

		Actor player = model.getPlayer();
		int px = player.getWorldX();
		int py = player.getWorldY();

		// Sets the offset of the viewport, depending on the players location and the fact that
		// the player is always the center of the viewport.
		int viewportOffsetX = CENTER_OFFSET_X-(px);
		int viewportOffsetY = CENTER_OFFSET_Y-(py);

		// Check boundaries for viewport (for cropping purposes - we do not want to loop through and draw tiles
		// which are outside the currently visible part of the map)
		int firstVisibleX = ((int)-viewportOffsetX)/RessourceManager.TILE_WIDTH;
		int lastVisibleX = Math.min(firstVisibleX+fitsInViewX+1, map.getWidth());
		
		int firstVisibleY = ((int)-viewportOffsetY)/RessourceManager.TILE_HEIGHT;
		int lastVisibleY = Math.min(firstVisibleY+fitsInViewY+1, map.getHeight());
		
		graphics.startUse(); // Until released by endUse, only graphics from this sprite sheet will be drawn, but they will be drawn faster.

		// Draw all visible tiles (with contents, if any)
		for (int x = firstVisibleX; x<lastVisibleX; x++)
		{
			int currentXOffset = x*RessourceManager.TILE_WIDTH;
			for (int y = firstVisibleY; y<lastVisibleY; y++)
			{
				int currentYOffset = y*RessourceManager.TILE_HEIGHT;
				if (x >= 0 && y >=0)
				{
                                        // Rendering the first layer - raw tiles
					Tile tile = map.getTile(x, y);
					if (tile != null)
					{
						// Using the image reference key from the tile's type, retrieve and draw the terrain image
						TILE_TYPE type = map.getTile(x, y).getType();
						RessourceManager.IMAGE image = type.getImage();
						Image imageToDraw = graphics.getSubImage(image.getSheetColumn(), image.getSheetRow());
						imageToDraw.drawEmbedded(viewportOffsetX+currentXOffset, viewportOffsetY+currentYOffset, RessourceManager.TILE_WIDTH, RessourceManager.TILE_HEIGHT); // Note that the drawEmbedded method is used to make use of the enhanced drawing speed.
					}
				}
			}
		}

                // Draw all characters
		for (int x = firstVisibleX; x<lastVisibleX; x++)
		{
			for (int y = firstVisibleY; y<lastVisibleY; y++)
			{
				if (x >= 0 && y >=0)
				{
                                        // Rendering second layer - characters
                                        Actor actor = model.getCharacterManager().getCharacter(x, y);
                                        if (actor != null)
                                        {
                                            Animation currentAnimation1 = actor.getCurrentAnimation();
                                            currentAnimation1.getCurrentFrame().drawEmbedded(viewportOffsetX+actor.getWorldX(),
                                                    viewportOffsetY+actor.getWorldY(), RessourceManager.TILE_WIDTH, RessourceManager.TILE_HEIGHT);
                                        }
				}
			}
		}
		
		// Draw the player's current animation
		Animation currentAnimation = player.getCurrentAnimation();
		currentAnimation.getCurrentFrame().drawEmbedded(viewportOffsetX+player.getWorldX(), viewportOffsetY+player.getWorldY(), RessourceManager.TILE_WIDTH, RessourceManager.TILE_HEIGHT);
		
		graphics.endUse(); // Release, so that graphics that are not in the sprite sheet can be used again.
	}

I think it’s pretty self-explainatory what it does, so I wont go into greater detail about that.

I have never scaled anything successfully, so if you could provide me with a little info on what I got to look out for, that would be really nice :slight_smile:

Thanks for the help guys!

Slick2D scales the screen from (0,0). Imagine the window as an image, double its width and height, then make sure the top left corner of the image is at the top left corner of the window. That’s how things are scaled. If you want to center it, you should add a call to translate somewhere in there.

Well, that makes sence. Though not enough to apply it :-\

Although I haven’t used Slick2D in a while
You should be able to do something like

g.translate(someVar1, someVar2) //(move it to "center it" around 0,0)
g.scale2D( 2.0, 2.0 ) //scale up 2.0 
g.translate( -someVar1, -someVar2)//move it back 

or something like that, I think.

I found this, see if you can refactor it to work with your game
http://slick.javaunlimited.net/viewtopic.php?t=755

Yeah as namrog showed, you should translate your rendering to 0,0, scale it, then translate it back to the original position.
Slick2D always scales from 0,0 so you should do this translate, scale, translate pattern everytime you want to scale one specific rendering. Then just set the the scale back at 1,1 when you’re done.

So by how much would I want to transform per scale?

The specific value would depend upon on how everything is setup,

I would recommend just setting up the transform to some keys on keyboard and try to manipulate it ingame and see around/about what type of numbers you need for your setup.

It should be independent(separate) from the scale value itself, it doesn’t need to be a dependent upon a particular scale.

Edit: My method didn’t worked on all setups so I snipped it away.

Alright, so I can now align it manually until I feel the screen is where it should be. But, I actually want to be able to calculate it based on a zoom-factor in-game. How much would I want to translate, per scale? It’s definately not the same amount.
Is there any way I can calculate this or something?

Try dividing amount of translate by scalefactor.

Yes I was wrong in my original statement, if you wanted a dynamic zoom then youd need a scaling factor

Id suggest trying something like

translateX = -screenWidth + screenWidth / scale; 

Or use whatever number you found in place of screenwidth to work

What type of numbers does it take when you align manually?

I just tried appying this:


g.scale(2,2);
g.translate(-800 +800/2, -600+600/2);

In the start of my render loop. It makes what’s supposed to be the middle of the screen, lie in the 0,0 coordinates.
So it moves 800/2, 600/2 to the upper left corner of the screen. I’ll try a manual align and post the results now.


g.translate(-800/2+800/2, -600/2+600/2);

Gives the exact opposite result. The middle is now @ the downer right corner.


g.translate(-800+1200/2, -600+900/2);

Appears to work, where my window width = 800 and height = 600.

Be aware translate(double,double) behaves differently to translate(int,int).

The former concatenates the Graphics context’s AffineTransformation with a translation matrix.
The latter just translates the origin of the current coordinate system (and in so doing creates a new coordinate system centered @ x/y)

For clean code (and your own sanity) I recommend you avoid using translate(int,int) as it becomes unintuitive when used in conjunction with the AffineTransforms. (rotate, translate, scale & shear)

To eliminate your apparent confusion, and improve your understanding I suggest you recode your program to use just get/setTransform.
Doing this will make the code dealing with the transformation concatenations much more verbose, and easier to understand.
It’ll also highlight to you when you are performing inverse transformations that run the risk of accruing rounding errors.
In short the transformation process should always be:
STORE, TRANSFORM, RENDER, RESTORE; not TRANSFORM, RENDER, INVERSE TRANSFORM

I’m a bit left off here, but you’re saying that translate(double, double) will benefit me when transforming right? I don’t get the store, transform, render, restore-praxis. What’s the store/restore states?

At slick there is Graphics.push() and Graphics.pop().
You put current transform to stack and then you can restore it later. Just like at assembly when storing registers.

Yes, applying the inverse transform can end up with rounding errors that will degrade your game over time. It also is an extra operation you don’t need.