I'm making a game for android using libgdx and I want the game look good (with the same proportion) on different screens of smartphones, but not achievement. In a device the picture looks normal and another is very small.
I used viewports and OrthographicCamera but I don't see good results. Maybe what I'm doing wrong.
Currently I have this code (excerpt):
public class PlayScreen extends BaseScreen {
private Stage stage;
private FaceActor faceActor64;
private Texture faceTexture64;
private int sw;
private int sh;
public PlayScreen(MainGame game) {
super(game);
faceTexture64 = new Texture("images/all64.png");
}
#Override
public void show() {
sw = Gdx.app.getGraphics().getWidth();
sh = Gdx.app.getGraphics().getHeight();
stage = new Stage(new FitViewport(sw, sh));
faceActor64 = new FaceActor(faceTexture64);
faceActor64.setBounds(150, 150, 64, 64);
stage.addActor(faceActor64);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void hide() {
stage.dispose();
}
#Override
public void dispose() {
faceTexture64.dispose();
}
}
I'm using an image of 64px.
Result in a smartphone of 480x800.
Result in a smartphone of 1080x1920.
Any idea how to fix it?
What you are doing there is this:
For a 720x1280 device you are setting the view of your game world to 720x1280 and for a 1080x1920 device you are setting it to 1080x1920. You assets most likely do not change in size so the more pixels a device has the more it will show of your game world. I always tell people to forget about pixels, unless they want a pixel perfect game and that is what you are creating if you do not resize your assets, a pixel perfect game world.
If you think about it, your game does not even need to know about the device. It just needs to know how much of your game world to render in that FitViewport. So let's say I have a tile game and my tiles have a size of 1x1 units. If I would want to show 16 tiles vertically and 9 horizontally I would setup my FitViewport as new FitViewport(9, 16). This would fill up the screen on most devices since they often have a aspect ratio of 16:9.
The short explanation is, pass only constants into the Viewport constructor, not the actual screen dimensions. Pick dimensions that you want to work with, and the viewport will stretch them to fit the actual screen.
Also, if you don't want black bars ("letterboxing / pillarboxing"), use ExtendViewport instead of FitViewport.
Related
I'm working on a simple game and I have problem with setting the map size. I am using OrthographicCamera. I want the map to be visible. How should I set the proper size of viewport new OrthographicCamera(viewport_width, viewport_height)?
Currently I'm passing some value and when I'm drawing elements on the map I don't see them because they are out of bound, if I make the width and height enormous I can see them. I want to have the whole map visible and draw everything on the sight of the user. I'm not sure where I'm making a mistake
You map is in square size and most of devices are in rectangular size so you need to keep your map in center of screen, either you're using portrait or landscape mode.
public class TileTest extends ApplicationAdapter {
ExtendViewport extendViewport;
OrthogonalTiledMapRenderer mapRenderer;
OrthographicCamera camera;
float worldWidth,worldHeight;
#Override
public void create() {
float tileWidth=64,tileHeight=64;
float mapWidth=20,mapHeight=20;
worldWidth=tileWidth*mapWidth;
worldHeight=tileHeight*mapHeight;
camera=new OrthographicCamera();
extendViewport =new ExtendViewport(worldWidth,worldHeight,camera);
TmxMapLoader mapLoader=new TmxMapLoader();
TiledMap map=mapLoader.load("square.tmx");
mapRenderer=new OrthogonalTiledMapRenderer(map);
}
#Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
mapRenderer.setView(camera);
mapRenderer.render();
}
#Override
public void dispose() {
mapRenderer.dispose();
}
#Override
public void resize(int width, int height) {
extendViewport.update(width,height,false);
extendViewport.getCamera().position.set(worldWidth/2,worldHeight/2,0);
extendViewport.getCamera().update();
}
}
Output is :
I am currently working on a StartMenu for my very first android game and there are 3 Problems that I am currently encountering:
1) the stage doesn't fit perfectly into the screen. I already tried to pass the different viewport types (stretch-, fit, extendviewport) to the stage Constructor, but none of them really worked out as I intended. I also tried different parameters for the viewport (e.g. numbers like 480, 800 but also Gdx.graphics.getWidth()), but I am unable to make the Menu resolution independent (tried on s3 mini and on Huawei P8), it either turns out too small or too big.
2) the settings buttons is not centered within the table cell.
3) the buttons change size. when pressed, they fill the cell in which they are in, how do I disable this feature? (i defined the button skin with a JSON-file, by the way, but I doubt this is the cause)
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
#Override
public void show() {
stage = new Stage(new ExtendViewport(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT));
Gdx.input.setInputProcessor(stage);
rebuildStage();
}
private void rebuildStage() {
skinStartMenu = new Skin(
Gdx.files.internal(Constants.SKIN_Start_Menu_UI),
new TextureAtlas(Constants.Start_Menu_Texture_Atlas));
Table layerControls = buildControlsLayer();
stage.clear();
Stack stack = new Stack();
stage.addActor(stack);
stack.setSize(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
stack.add(layerControls);
}
private Table buildControlsLayer() {
Table layer = new Table();
// + Play Button
btnMenuPlay = new Button(skinStartMenu, "play");
layer.add(btnMenuPlay);
btnMenuPlay.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
onPlayClicked();
}
});
layer.row();
// + Options Button
btnMenuOptions = new Button(skinStartMenu, "options");
layer.add(btnMenuOptions);
btnMenuOptions.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
onOptionsClicked();
}
});
if (debugEnabled) layer.debug();
return layer;
}
No Button Pressed;
Button Pressed
Edit: I solved the first problem by resizing the cells of the table, however i still do not have a workaround for problem 2) and 3).
Okay, I found the root of my problems. stupid me exported the png-files of the button in different sizes. thats why the buttons got taller when pressed. i thought this was a feature of the button-widget, which was enabled by default x). The problem with the stage size was solved, by simply changing the size of the table-cells in which the buttons where contained via " layer.add(btnMenuPlay).size(273, 150);".
The StartMenu looks like this now:
I have taken some code from a book on LibGDX, and adapted it for my own project, unfortunately, I have got a little stuck.
I have a class called MenuScreen (see below) which of course displays the Menu Screen. It is basically the same as the original code, just adapted for the Images I am using. In the project that was in the book, the Background image used for the Menu displayed correctly, filling an 800x480 Window on the Desktop, and filling the Screen of my Nexus 7.
With my project, the Image displays fine on the Desktop, but much smaller on the Nexus 7. I have a recent build for this project, and I understand that there has been some changes regarding the Viewport. I assume it is displaying now at actual size on the screen, but it is in fact now a lot smaller than is should be anyway... I have a 2012 N7, which has a resolution of 1280 x 800, but is displaying at about 550 x 300, even though the image itself is 800 x 480.
Could someone please point me in the right direction to get this image displaying correctly ?
public class MenuScreen extends AbstractGameScreen {
private static final String TAG = MenuScreen.class.getName();
private Stage stage;
private Skin demoSkin;
private Skin skinLibGdx;
private Viewport viewport;
private Image imgBackground;
// options
private Window winOptions;
private TextButton btnWinOptSave;
private TextButton btnWinOptCancel;
private TextButton optionsButton;
private TextButton startButton;
private Slider numBlocks;
private CheckBox chkShowFpsCounter;
public MenuScreen (Game game)
{
super(game);
}
#Override
public void render(float deltaTime) {
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(deltaTime);
stage.draw();
Table.drawDebug(stage);
}
#Override
public void resize(int width, int height) {
stage.getViewport().update((int)Constants.VIEWPORT_GUI_WIDTH, (int)Constants.VIEWPORT_GUI_HEIGHT, false);
}
#Override
public void show() {
stage = new Stage();
rebuildStage();
}
private void rebuildStage() {
demoSkin = new Skin(Gdx.files.internal(Constants.SKIN_PHYSICSDEMO_UI), new TextureAtlas(Constants.TEXTURE_ATLAS_UI));
skinLibGdx = new Skin(Gdx.files.internal(Constants.SKIN_LIBGDX_UI), new TextureAtlas(Constants.TEXTURE_ATLAS_LIBGDX_UI));
// build all layers
Table layerBackground = buildBackgroundLayer();
Table layerControls = buildControlsLayer();
Table layerOptionsWindow = buildOptionsWindowLayer();
// assemble stage for menu screen
stage.clear();
Stack stack = new Stack();
stage.addActor(stack);
stack.setSize(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT);
stack.add(layerBackground);
stack.add(layerControls);
//stage.addActor(layerOptionsWindow);
}
private Table buildOptionsWindowLayer() {
winOptions = new Window("Options", skinLibGdx);
// Make options window slightly transparent
winOptions.setColor(1, 1, 1, 0.8f);
// Hide options window by default
winOptions.setVisible(false);
//showOptionsWindow(false, false);
// Let TableLayout recalculate widget sizes and positions
winOptions.pack();
// Move options window to bottom right corner
winOptions.setPosition(Constants.VIEWPORT_GUI_WIDTH - winOptions.getWidth() - 50, 50);
return winOptions;
}
private void showOptionsWindow (boolean visible, boolean animated) {
float alphaTo = visible ? 0.8f : 0.0f;
float duration = animated ? 1.0f : 0.0f;
Touchable touchEnabled = visible ? Touchable.enabled : Touchable.disabled;
winOptions.addAction(sequence(touchable(touchEnabled), alpha(alphaTo, duration)));
}
private Table buildControlsLayer() {
Table layer = new Table();
startButton = new TextButton("Start Simulation", skinLibGdx);
optionsButton = new TextButton("Options", skinLibGdx);
layer.add(startButton);
layer.row();
layer.add(optionsButton);
return layer;
}
private Table buildBackgroundLayer() {
Table layer = new Table();
imgBackground = new Image(demoSkin, "background");
layer.add(imgBackground);
return layer;
}
#Override
public void hide() {
stage.dispose();
skinLibGdx.dispose();
demoSkin.dispose();
}
#Override
public void pause() {
// TODO Auto-generated method stub
}
#Override
public InputProcessor getInputProcessor() {
return stage;
}
}
You need to set size of the background image separately.
imgBackground.setSize(imageWidth, imageHeight);
Since you are changing Viewport size in resize method, for a screen with higher resolution, image will become even smaller. So image size must be set to be large enough.
You can calculate required image size by multiplying original image size by scaling factor (ratio of actual resolution and assumed resolution).
Good luck.
Sorry, I can't describe my problem clearly. I re-describe it:
I am making an android game with libgdx, and for now I finish a demo:
It has only one game view which is implemented by GameScreen class which is a subclass of Screen in libgdx:
public class LaohuGame extends Game {
public static final int FRUIT_NUM = 24;
public static final String WORK_DIR = "Desktop/assets/";
#Override
public void create() {
GameScreen gameScreen = new GameScreen(this);
setScreen(gameScreen);
}
public class GameScreen extends LaohuScreen {
private final Stage stage;
public static int START_X = 11;
public static int START_Y = 199;
public GameScreen(Game game) {
super(game);
stage = new Stage(GAME_WIDTH,GAME_HEIGHT,true);
//add actors to the stage
}
public class LaohuScreen implements Screen{
int GAME_WIDTH;
int GAME_HEIGHT;
protected Game game;
public LaohuScreen(Game game) {
this.game = game;
GAME_WIDTH = Gdx.graphics.getWidth();
GAME_HEIGHT = Gdx.graphics.getHeight();
}
The GAME_WIDTH,GAME_HEIGHT is equal to the width,height of phone screen, so the game view fill the whole phone screen.
Now I go on developing my game. I wanna have 4 game view in my game. Each game view implemented by a subclass of Screen in libgdx. I think the completed game should look like this pic:
A button bar is added to the bottom of the phone screen. When I click one of the 4 buttons, the game view switched, but the button bar didn't switch. For example, if player clicks the "Rank" button, game set the view to Rank View(implemented by Rank screen) which shows players' scores, and the buttons still visible:
I can think of 2 ways to keep the button bar visible:
Combine button bar and game view into a libgdx's Screen. I think it is a waste because I need to draw 4 buttons in every screen, totally 16 buttons in 4 Screens.
Split phone screen into 2 areas; game view area and button bar area. Game view area implemented by libgdx's Screen and I can switch game view by setScreen method. The problem is that libgdx's Screen seems always fills the whole phone screen; I can't set the screen's width and height. I tried using resize(int width, int height) method to cut down the height of screen to leave a blank space for button bar, but it doesn't work.
Code:
public class LaohuGame extends Game {
public static final int FRUIT_NUM = 24;
public static final String WORK_DIR = "Desktop/assets/";
#Override
public void create() {
GameScreen gameScreen = new GameScreen(this);
setScreen(gameScreen);
}
#Override
public void setScreen(Screen screen) {
super.setScreen(screen);
this.getScreen().resize(Gdx.graphics.getWidth(), Gdx.graphics.getHeight() * 5 / 10);
}
#Override
public void resize(int width, int height) {
if (getScreen() != null) getScreen().resize(width, height * 5 / 10);
}
}
Any suggestions are appreciated.
All libgdx things assume that the width and height always are equal to the "window" size. On a desktop machine this might be less than the whole screensize, but it will still fill the whole render area. On a phone it will most likely always be the phones display size.
Setting it to something smaller will not help you, because it will not use only a part of rendering area. Instead you will get weirdly stretched results.
Don't alter the values you get from the resize method. Instead go with your first solution and render the buttons on the bottom part of your game screens.
You don't need to duplicate any code here. Implement something like a "ButtonBarGUI" which uses a Stage and the scene2d.ui stuff to render four buttons on the bottom of the screen and then just reuse this helper class to render the button bar on any screen via Stage.draw().
It is possible to render a view at a low resolution, and then scale it up to fit the actual size of your view using the setFixedSize() method of a SurfaceHolder. However, the scaling is done with some kind of interpolation, causing everything to blur.
Is there any method for changing the method of interpolation to nearest neighbour or just turning it off?
Here is an example of what I mean, Made with a 4x4 surface in a fullscreen-view:
Left image: This is how I want the result to look (here achieved by drawing a nonfiltered bitmap)
Right image: This is what happens when the 4x4 canvas is scaled to fullscreen.
Sample code for generating the right image if anyone's interested:
public class ScaleView extends SurfaceView implements SurfaceHolder.Callback {
private final static float[] points = {0,0, 2,0, 4,0, 1,1, 3,1, 0,2, 2,2, 4,2, 1,3, 3,3};
private Paint white;
public ScaleView(Context context) {
super(context);
white = new Paint();
white.setColor(0xFFFFFFFF);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height){
Canvas c = holder.lockCanvas();
try{
c.drawPoints(points, white);
}finally{
holder.unlockCanvasAndPost(c);
}
}
#Override
public void surfaceCreated(SurfaceHolder holder){
holder.setFixedSize(4, 4);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){}
}
Note: The square pattern above is just an example of the effect I want to avoid. I am looking for a general solution that can be applied to any view (with a surfaceholder) with any content.
Sorry, you can't control this. It is not defined what kind of scaling will be done on these surfaces -- it depends on the hardware and how it is configured. Also, this kind of extreme scaling really should be avoided, since in some cases hardware can't do it so you will end up in slower paths. (For example if surfaces are being put into overlays, many hardware overlay engines can't do that kind of extreme scaling.)