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.
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'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.
I want to change background image of a menu every x-amount of seconds. I'm using libGDX scene2D.ui for making the menu. The TestScreen class extends the AbstractScreen which is a abstract class that implements libGDX's Screen class.
Problem: After I load the Image to the stage through a Table object on a stack, changing the image reference to a different image does nothing. Stage.draw() gives no cares as if it made a copy of my original image. I would like to keep the background as Image class and render through stage.draw().
To further complicate things, if I do change the image to another in render() method, then image.setVisible(false) also stops working.
public class TestScreen extends AbstractScreen {
private Stage stage;
private Image background;
private boolean ChangeBackground = true;
private final float refreshTime = 2.0f; // refresh to new image every 2 seconds.
private float counter = refreshTime;
public TestScreen(Game game) {
super(game);
}
#Override
public void render(float deltaTime) {
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
if(ChangeBackground){
counter -= deltaTime;
if(counter < 0){
counter = refreshTime;
// Assets class has the 12 images loaded as "Image" objects already.
// I simple want to change the reference to other (already loaded in memory images) ...
// and make stage render the new image.
background = Assets.instance.wallpapers[(int) (Math.random()*12)]; // The image should change.
//background.setVisible(false);
}
}
stage.act(deltaTime);
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.setViewport(Constants.VIEWPORT_GUI_WIDTH, Constants.VIEWPORT_GUI_HEIGHT, false);
}
#Override
public void show() {
stage = new Stage();
Gdx.input.setInputProcessor(stage);
makeStage();
}
#Override
public void hide() {
stage.dispose();
}
#Override
public void pause() {
}
// Builds the Background later and adds it to a stage through a stack.
// This is how it's done in my game. I made this test bench to demonstrate.
private void makeStage() {
Table BackGroundLayer = new Table();
background = Assets.instance.wallpapers[(int) (Math.random()*12)];
BackGroundLayer.add(background);
Stack layers = new Stack();
layers.setSize(800, 480);
layers.add(BackGroundLayer);
stage.clear();
stage.addActor(layers);
}
}
Image is a subclass of Actor. The main difference is, that Image has a Drawable inside. This Drawable gets drawn if you call stage.draw(), which calls the draw() of Image. Instead of changing Image you can change the Drawable by using setDrawable(Drawable param);.
What is a Drawable? It is any class, which implements the Drawable interface, for example the TextureRegionDrawable. If you are using TextureRegions you can use this constructor: TextureRegionDrawable(TextureRegion region);. Maybe it would be better to store your background images in a Drawable Array so you don't have to call a construcor each time you set a new Drawable. Example code:
TextureRegionDrawable[] images = new TextureRegionDrawable[12];
for (int i = 0; i<12; i++) {
images[i] = new TextureRegionDrawable(Assets.instance.textureRegions[i]);
}
Then in your render:
if(changeBackground) {
counter -= delta;
if (counter < 0) {
counter = refreshtime
background.setDrawable(images[(int)(Math.random()*12)]);
}
}
This should work
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().