Android - Can't draw same size square on all devices - java

I have an application running on android on which I have to draw a grid. I would like the cells of the grid to be of the same size on all devices, and I've found this method to do that:
float scale = getResources().getDisplayMetrics().density;
float SIZE = DESIRED_DP_VALUE * scale + 0.5f;
DESIRED_DP_VALUE is a value I set for the cells dimension.
I've tried the app on two smartphones and a tablet: the tablet and one of the smartphones have the cells of the same size, while the other smartphone doesn't.
This is the method I use to draw the grid:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
int count=0;
while(count<=row){
float coordinate=count*SIZE;
canvas.drawLine(coordinate,0,coordinate,column*SIZE,whitePaint);
count++;
}
count=0;
while(count<=column){
float coordinate=count*SIZE;
canvas.drawLine(0,coordinate,row*SIZE,coordinate,whitePaint);
count++;
}
}
The grid is draw correctly on each device.
Where could the problem be?

I think you are mixing some concepts.
The size in pixels is that, how many dots are counting per with or height.
Every device has its own screen size measured in inches and the amount of pixels or dots able to draw in width and height.
The relationship between dimension and pixels is the density.
So, what do you want to do?
Draw an square which shows the same physical size on the screen or a square with the same amount of pixels?
If you want to draw based on inches, take it that way.
For drawing an one inch square, for a device with a density of 300 pixels per inch you need 300 pixels.
If the device is 150 pixels per inch, you must draw a 150 pixels square.
So, you need to keep fixed the size in inches, not the desired DP.

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.

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 sprite dimension meters or pixels?

I have been reading "Learning Libgdx Game development". I tried the below snippet:
// First the camera object is created with viewport of 5 X 5.
OrthographicCamera camera = new OrthographicCamera(5, 5);
I have a texture having a dimension of 32 pixels by 32 pixels. I form a sprite out of this
Sprite spr = new Sprite(texture);
// I set the size of Spr as
spr.setSize(1,1);
According to the book the dimensions above are meters and not pixels.
What I don't understand is how is mapping from meters to pixels happening on the screen? When I draw the sprite on the screen the size is not even half a meter let alone 1.
Also, the size of the underlying texture is 32 X 32 pixels. WHen I resize, the size of my sprites also changes.
Then, what would be the dimensions of spr.setPosition(x, y)? Will they be meters or pixels?
The library uses pixels for dimensions like texture size, and meters for in-game units.
setPosition will move an object in game units. When you move an object X game units, the number of pixels changes based on the camera's projection matrix amongst other settings.
If you think about it, it wouldn't make sense to move in pixels. If camera A is zoomed in more than cameraB moving X pixels in the view of each camera would require moving two different amounts.
Edit: Sorry, I made some assumptions in your understanding above, partially misunderstood the question, and frankly used the misleading wording. The key is that the convention of meters for units is not built-in, it's one that you enforce because the ratio of one pixel to one meter in Box2D wouldn't make sense. The wording I used implied that internally setPosition cares about meters, but you should be doing the scaling yourself. Often times the ratio I see in libgdx is 30 pixels = 1 meter.

Android positioning in pixels works but shouldn't

I've been positioning things (bitmaps, text) on a surface view using pixels. For example, to centre something I would take half of the width and height of the rectangle representing the screen. Screen width and height are being returned as expected. My phone (480*800) is reporting the available screen as 442*800, with density 1.5, and running on a 240*320 emulator the available screen is reported as 221*320, density 0.75. All as expected.
But these values, surely, are pixels, and so surely to centre something I should first multiply by the density factor before halving. The strange thing is everything is centred perfectly, on both the low res emulator and my high res phone. This doesn't make sense to me.
On both screens, in order to get text to appear the same, I set the size using a density scaled value, as I'd expect, and this works.
In the manifest I've declared support for all screen sizes (although I was seeing the same 'correct' behaviour before I did this as well)
Why are the pixel values 'working' without me adjusting them for density? I'm now very confused.
A pixel is a pixel is a pixel!
It is the lowest common denominator of screens and sizes.
Density is how many pixels are packed into a unit of measure, typically an inch. on a physical display.
For example, taking a physical width of 5 inches, with a resolution of 500x1000 yields a density of 200dpi (1000 pixels in 5 inches). A physical width of 4 inches with the same resolution yields a density of 250dpi (1000 pixels in 4 inches).
x=500 (half the width) is the centre of the screen on both and is independent of density. Indeed, half the width in pixels is the centre of the screen for any size screen at any density.

How to convert pixels to dp?

I am trying to create a simple android app in Java but I have hit a bump in the road.
I have an Array List that contains many Bitmaps and I am drawing them to the canvas but I am using the measurement of pixels and that doesn't come out right on most devices.
So is there any way to find out the size of my Graphic in dp and scale it to the screen density/size with dp and position it with dp or dip.
My Graphics ( Squares ) :
Graphic size in ldpi = 50px
Graphic size in mdpi = 75px
Graphic size in hdpi = 100px
Everyting else in the code is fine apart from it comes out looking weird?
Any help would be appreciated.
James.
From the android developer doc:
A virtual pixel unit that you should use when defining UI layout, to express layout dimensions or position in a density-independent way.
The density-independent pixel is equivalent to one physical pixel on a 160 dpi screen, which is the baseline density assumed by the system for a "medium" density screen. At runtime, the system transparently handles any scaling of the dp units, as necessary, based on the actual density of the screen in use. The conversion of dp units to screen pixels is simple: px = dp * (dpi / 160). For example, on a 240 dpi screen, 1 dp equals 1.5 physical pixels. You should always use dp units when defining your application's UI, to ensure proper display of your UI on screens with different densities
see this for more info.
/**
* Convert dp in pixels
* #param dp
* #return
*/
public int getPx(int dp){
float scale = getResources().getDisplayMetrics().density;
return((int) (dp * scale + 0.5f));
}

Categories