Libgdx - Tiledmap rendered tiles disappearing from screen - java

I'm creating a side-scrolling platformer and I'm using my own Tiled map. I'm rendering it using the OrthogonalTiledMapRenderer, but after adding background images I noticed that they disappear from the screen too soon. On the first picture you can see the background giant trees still being rendered, and in the TiledMap the first background tile horizontally ends exactly where the ladder starts, and then the same picture is added (so it's basically one image pasted multiple times on the level - second picture).
However, even before reaching the first image's ending point, it disappears, which looks like this:
Anybody can help with that? Here is the code for the rendering:
OrthogonalTiledMapRenderer mapRenderer = new OrthogonalTiledMapRenderer(map, 1 / Constants.PPM);
OrthographicCamera camera = new OrthographicCamera();
float width = Constants.VIEWPORT_WIDTH * camera.zoom * 2;
float height = Constants.VIEWPORT_HEIGHT * camera.zoom * 2;
mapRenderer.setView(camera.combined, cameraX, cameraY, width, height);
Gdx.gl.glClearColor(0x64 / 255.0f, 0x95 / 255.0f, 0xed / 255.0f, 0xff / 255.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.render();
The floats are updated with camera position.
cameraX = camera.position.x - camera.viewportWidth * camera.zoom;
cameraY = camera.position.y - camera.viewportHeight * camera.zoom;
Camera follows the player and is clamped to the borders of the map. Nothing too fancy, I also tried mapRenderer.setView(camera) with the same results.

It looks like this is caused by optimistic culling of tiles on the renderer side, which breaks when tiles are overly large.
One way to fix this would be to not use a single large tile for these backgrounds, but rather to add the background as a tileset using the same tile size as your map. You can still easily place the whole image at once by using click+drag to select all the tiles in this tileset.
Another option may be to place this image as a tile object on an Object Layer instead, but I don't know if libgdx will render those for you by default.

Creating separate tilesets for every background image and splitting them in 32x32 tiles fixed the issue.

You could temporarily boost the zoom of your camera so you always render an area larger than you see.
float zoom = camera.zoom;
camera.zoom += 1f;
//render map
camera.zoom = zoom;

Related

LibGdx - Scaling Sprites, Camera and Viewports

Hell All & thanks for reading,
I recently started working on an 2D Android/Desktop project and have become stuck trying to display my sprites in the way i want.
I have a background Sprite that is 144(w) by 160(h) that I want to be able to position other sprites onto the screen relative to points on the background sprite.
I think I understand that if I create a camera/viewport that is 144 x 160 I would be able to position my sprites on the background sprite using the co-ordinates based on the 144 x 160 of the background sprite. This will work across the different screen resolutions found on mobile devices but will stretch the background sprite despite experimenting with the different viewport types (FillViewport, FitViewport etc..).
What I want to achieve is to have my background sprite to maintain it ratio across different screen resolutions and to be able to place other sprites over the background sprite. The placing of sprite need to work across different resolutions.
Apologies if my explanation is confusing or makes no sense. I would add some image to help explain but I reputation to add any to the post. However I think the TLTR question is "What is the correct way to display sprites on multiple screen resolutions while keeping a correct ratios and scaling to the screen size and position of sprite in a way that works across multiple resolutions?"
Thank, All Questions Welcome
A FitViewport would do what you described (maintain aspect ratio), but you will have black bars on some devices. Based on the code you posted on the libgdx forum, I see that you forgot to update the viewport in the resize method, so it is not behaving as designed.
However, for a static camera game like what you described, I think the best solution would be to plan your game around a certain area that is always visible on any device, for example, the box from (0,0) to (144,160). Then use an ExtendViewport with width and height of 144 and 160. After you update the viewport in resize, you can move the camera to be centered on the rectangle like this:
private static final float GAME_WIDTH = 144;
private static final float GAME_HEIGHT = 160;
public void create(){
//...
viewport = new ExtendViewport(GAME_WIDTH, GAME_HEIGHT);
//...
}
public void resize(int width, int height){
viewport.update(width, height, false); //centering by putting true here would put (0,0) at bottom left of screen, but then the game rectangle would be off center
//manually center the center of your game box
Camera camera = viewport.getCamera();
camera.position.x = GAME_WIDTH /2;
camera.position.y = GAME_HEIGHT/2;
camera.update();
}
Now your 144x160 box is centered on the screen as it would be with FitViewport, but you are not locked into having black bars, because you can draw extra background outside the 144x160 area using whatever method you like.
In your case 144:160 is a wider portrait aspect ratio than any screen out there, so you wouldn't need to worry about ever filling in area to the sides of your game rectangle. The narrowest aspect ratio of any phone or tablet seems to be 9:16, so you can do the math to see how much extra background above and below the game rectangle should be drawn to avoid black showing through on any device.
In this case it works out to 48 units above and below the rectangle that you would want to fill in:
144 pixels wide at 9:16 would be 256 tall.
(256 - 160) / 2 = 48
EDIT: I see from your post on the libgdx forum that you want the game area stuck at the top of the screen and the remainder of the area to be used for game controls. In that case, I would change the resize method like this, since you want to have the game area's top edge aligned with the top edge of the screen. You can also calculate where the bottom of the controls area will be on the Y axis. (The top will be at Y=0.)
public void resize(int width, int height){
viewport.update(width, height, false);
//align game box's top edge to top of screen
Camera camera = viewport.getCamera();
camera.position.x = GAME_WIDTH /2;
camera.position.y = GAME_HEIGHT - viewport.getWorldHeight()/2;
camera.update();
controlsBottomY = GAME_HEIGHT - viewport.getWorldHeight();
}
I'm not sure how you plan to do your controls, but they would need to fit in the box (0, controlsBottomY) to (GAME_WIDTH, 0). Keep in mind that there are some phones with aspect ratios as small as 3:4 (although rare now). So with your 0.9 aspect ratio, on a 3:4 phone only the bottom 17% of the screen would be available for controls. Which might be fine if it's just a couple of buttons, but would probably be problematic if you have a virtual joystick.

Pan within large image borders with LibGdx

I am building a small testing game, basically where is waldo. I have a large image that I can pan around and look for Waldo, but I can't figure out how to keep the camera within the sprite borders (x, y). Right now you can pan past the image borders and on and on and on forever.
Relevant code:
sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
public boolean pan(float x, float y, float deltaX, float deltaY) {
camera.translate(-deltaX * PAN_SPEED, deltaY * PAN_SPEED);
camera.update();
return true;
}
there isn't much, I've tried quite a few things but the problem is I can't figure out how to get the distance I have panned, and I need that if I am going to put up a "border". Right now the sprite.getX() == -2200), and the camera viewport is only (480x800), so I am having a hard time working with the Image size and the viewport, and the distance that has been panned.
I've had to solve this before, but I did it very inelegantly. I basically just had a log printing out my current camera's position as I panned around. When I could see off the edge of the screen (to the blank GL wipe underneath), I wrote down the camera's position.
I ended up knowing that if the camera.getX() > sprite.getWidth() - someVal, the edge would on screen. So, I just added in a method that clipped down any X/Y val of a camera if it over shot these predefined bounds.
It's not a great answer, but it also allows you control.

libgdx using percentage for images

I'm making a game sort of like flappy bird, but it works on landscape mode, to make it fair so that people with bigger screens don't have an advantage I change my images to screen % for example the bird will be 10% of screen height, so it will be the same in all screens, the question is, is this a good idea or is there any other way?
You don't need to use percentages. If you are using libgdx, you can (and should) use a predefined viewport size, so the screen resolution is handled by the library and not you.
You can achieve this using a camera, setting a fixed height, and calculating the width:
HEIGHT = 10;
WIDTH = (Gdx.graphics.getWidth()/Gdx.graphics.getHeight())*HEIGHT;
camera = new OrthographicCamera();
camera.setToOrtho(false, WIDTH, HEIGHT);
this way, your height will be 10 units. And the width will be caculated by the screen x-y ratio.
the bird will be 10% of screen height
Then your bird will be 1 unit size (height/10).
If you want both sides to be fixed (so noone sees more in the width). Its even easier, but your images won't keep their ratio, they will be stretched in both axis.
HEIGHT = 10;
WIDTH = 15;
camera = new OrthographicCamera();
camera.setToOrtho(false, WIDTH, HEIGHT);

libgdx - combine a number of textures into one

is there a way to combine a number of textures into one texture with libgdx API?
For example, consider having these 3 textures:
Texture texture1 = new Texture("texture1.png");
Texture texture2 = new Texture("texture2.png");
Texture texture3 = new Texture("texture3.png");
The goal is to combine them into one usable texture, is there a way to do it?
I wanted to get the same result, but I did not find the previous instructions given to be of any help. Neither I was able to find any other clear solution for this issue, so here's what I managed to get working after I read a bit of api-docs and did some testing of my own.
Texture splashTexture = new Texture("texture1.png"); // Remember to dispose
splashTexture.getTextureData().prepare(); // The api-doc says this is needed
Pixmap splashpixmap = splashTexture.getTextureData().consumePixmap(); // Strange name, but gives the pixmap of the texture. Remember to dispose this also
Pixmap pixmap = new Pixmap(splashTexture.getWidth(), splashTexture.getHeight(), Format.RGBA8888); // Remember to dispose
// We want the center point coordinates of the image region as the circle origo is at the center and drawn by the radius
int x = (int) (splashTexture.getWidth() / 2f);
int y = (int) (splashTexture.getHeight() / 2f);
int radius = (int) (splashTexture.getWidth() / 2f - 5); // -5 just to leave a small margin in my picture
pixmap.setColor(Color.ORANGE);
pixmap.fillCircle(x, y, radius);
// Draws the texture on the background shape (orange circle)
pixmap.drawPixmap(splashpixmap, 0, 0);
// TADA! New combined texture
this.splashImage = new Texture(pixmap); // Not sure if needed, but may be needed to get disposed as well when it's no longer needed
// These are not needed anymore
pixmap.dispose();
splashpixmap.dispose();
splashTexture.dispose();
Then use the splashImage (which is a class variable of type Texture in my case) to render the combined image where you want it. The resulting image has the given background and the foreground is a png image which has transparent parts to be filled by the background color.
#Ville Myrskyneva's answer works, but it doesn't entirely show how to combine 2 or more textures. I made a Utility method that takes 2 textures as input and returns a combined texture. Note that the second texture will always be drawn on top of the first one.
public static Texture combineTextures(Texture texture1, Texture texture2) {
texture1.getTextureData().prepare();
Pixmap pixmap1 = texture1.getTextureData().consumePixmap();
texture2.getTextureData().prepare();
Pixmap pixmap2 = texture2.getTextureData().consumePixmap();
pixmap1.drawPixmap(pixmap2, 0, 0);
Texture textureResult = new Texture(pixmap1);
pixmap1.dispose();
pixmap2.dispose();
return textureResult;
}
You should use the TexturePacker for creating one big Texture from lots of small one. https://github.com/libgdx/libgdx/wiki/Texture-packer .
And here is GUI version (jar) whcih will help you to pack all your textures in one before putting them to your project: https://code.google.com/p/libgdx-texturepacker-gui/.
You should also remember that maximum size of one texture for most Android and IOS devices is: 2048x2048.
In case you want to use combined texture as atlas for performance benefits, you better use #Alon Zilberman answer.
But I think that your question is about another problem. If you want to get some custom texture from these three textures (e.g. merge them by drawing one at one), then your best solution is to draw to FrameBuffer. Here you can find nice example of how to do it https://stackoverflow.com/a/7632680/3802890

How to -accurately- measure size in pixels of text being drawn on a canvas by drawTextOnPath()

I'm using drawTextOnPath() to display some text on a Canvas and I need to know the dimensions of the text being drawn. I know this is not feasible for paths composed of multiple segments, curves, etc. but my path is a single segment which is perfectly horizontal. I am using Paint.getTextBounds() to get a Rect with the dimensions of the text I want to draw.
I use this rect to draw a bounding box around the text when I draw it at an arbitrary location.
Here's some simplified code that reflects what I am currently doing:
// to keep this example simple, always at origin (0,0)
public drawBoundedText(Canvas canvas, String text, Paint paint) {
Rect textDims = new Rect();
paint.getTextBounds(text,0, text.length(), textDims);
float hOffset = 0;
float vOffset = paint.getFontMetrics().descent; // vertically centers text
float startX = textDims.left; / 0
float startY = textDims.bottom;
float endX = textDims.right;
float endY = textDims.bottom;
path.moveTo(startX, startY);
path.lineTo(endX, endY);
path.close();
// draw the text
canvas.drawTextOnPath(text, path, 0, vOffset, paint);
// draw bounding box
canvas.drawRect(textDims, paint);
}
The results are -close- but not perfect. If I replace the second to last line with:
canvas.drawText(text, startX, startY - vOffset, paint);
Then it works perfectly. Usually there is a gap of 1-3 pixels on the right and bottom edges. The error seems to vary with font size as well. Any ideas? It's possible I'm doing everything right and the problem is with drawTextOnPath(); the text quality very visibly degrades when drawing along paths, even if the path is horizontal, likely because of the interpolation algorithm or whatever its using behind the scenes. I wouldnt be surprised to find out that the size jitter is also coming from there.
I've found a solution that works for my particular situation, although it doesn't really resolve the original question.
Since my particular use case only draws text along single segment straight paths, I was able to get rid of the paths all together and use Canvas.drawText() along with Canvas.translate() and canvas.rotate(). This got rid of the text size randomness and ugliness I was seeing when drawing along paths as well.

Categories