My game works fine on desktop, but when I load it on an android device or the emulator I get a black screen. The game is working; I can hear the sounds and click the UI buttons. When testing on my android phone, I can turn the screen off and on again, and then the game renders fine. I would appreciate an explanation of what I'm doing wrong.
public class TestProject extends Game {
GameScreen gameScreen;
EndScreen endScreen;
MenuScreen menuScreen;
#Override
public void create () {
Gdx.app.log("TestProject", "created");
AssetLoader.load();
gameScreen = new GameScreen(this);
endScreen = new EndScreen(this);
menuScreen = new MenuScreen(this);
setScreen(menuScreen);
}
#Override
public void dispose() {
super.dispose();
AssetLoader.dispose();
}
}
public class MenuScreen implements Screen{
private SpriteBatch spriteBatch;
final static float GAME_WIDTH = 200;
final static float GAME_HEIGHT = 100;
public static final float UNIT_SCALE = 1/8f;
static float aspectRatio;
private OrthographicCamera cam;
Viewport viewport;
Stage stage;
TestProject project;
public MenuScreen(TestProject theProject){
project = new TestProject();
this.project = theProject;
aspectRatio = (float)Gdx.graphics.getHeight() / (float)Gdx.graphics.getWidth();
spriteBatch = new SpriteBatch();
stage = new Stage();
Gdx.input.setInputProcessor(stage);
cam = new OrthographicCamera();
viewport = new ExtendViewport(GAME_WIDTH * aspectRatio, GAME_HEIGHT, cam);
viewport.apply();
cam.setToOrtho(false, GAME_WIDTH, GAME_HEIGHT);
cam.position.set(GAME_WIDTH / 2, GAME_HEIGHT / 2, 0);
stage.addActor(AssetLoader.playButton);
}
#Override
public void render(float delta){
//Clear screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
spriteBatch.setProjectionMatrix(cam.combined);
AssetLoader.font.getData().setScale(.25f, -.25f);
AssetLoader.playButton.setPosition((Gdx.graphics.getWidth() / 2) - AssetLoader.playButton.getWidth() / 2,
(Gdx.graphics.getHeight() / 3) - AssetLoader.playButton.getHeight() / 2);
spriteBatch.begin();
spriteBatch.draw(AssetLoader.menuBG, 0, 0, 200, 100);
AssetLoader.font.draw(spriteBatch, "Shark Run", 55, 85);
spriteBatch.end();
stage.draw();
}
#Override
public void show() {
AssetLoader.playButton.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
project.setScreen(project.gameScreen);
}
});
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
}
Related
I have made my layout in scene 2d and implemented it. But the sizing is the issue. It comes in different alignment for my android phone and on desktop.
Although I think that the solution is to use viewport but I don't know how to correctly implement it.
The way I am implementing it gives me a white screen
#Override
public void show() {
width=GameScreen.getGameWidth();
height=GameScreen.getGameHeight();
camera = new OrthographicCamera();
camera.setToOrtho(true,width ,height);
stage = new Stage(new FillViewport(width, height,camera));
parameter.size = 40;
parameter.color=Color.WHITE;
parameter.flip=false;
font12 = generator.generateFont(parameter); // font size 12 pixels
generator.dispose();
TextureAtlas atlas= new TextureAtlas(Gdx.files.internal("data/uiAtlas.atlas"));
skin = new Skin(Gdx.files.internal("data/uiskin.json"));
skin2= new Skin(Gdx.files.internal("data/achievementSkin.json"),atlas);
Gdx.input.setInputProcessor(stage);
mainTable = new Table();
levelTable = new Table();
stage.addActor(mainTable);
mainTable.setFillParent(true);
scrollPane = new ScrollPane(levelTable, skin);
scrollPane.setFlickScroll(true);
scrollPane.setFadeScrollBars(true);
labelStyle=new LabelStyle();
labelStyle.font=font12;
makeLevelMenu();
}
public void makeLevelMenu() {
for (int i = 0; i < 10; i++) {
columnTable.add(new Table());
// Image
button.add(new ImageButton(skin2, "playButton"));
columnTable.get(i).add(button.get(i)).colspan(2).padBottom(100)
.padLeft(100).padRight(100).padTop(100);
columnTable.get(i).row();
// Enemy Label
enemyLabel.add(new Label("Enemy " + (i + 1), labelStyle));
columnTable.get(i).add(enemyLabel.get(i)).left().padBottom(300)
.padTop(100);
columnTable.get(i).row();
// Battle Style
battleStyle.add(new Label("Battle Style " + (i + 1), labelStyle));
columnTable.get(i).add(battleStyle.get(i)).left().padBottom(100);
levelTable.add(columnTable.get(i)).expand();
}
levelTable.debug();
// achievementsTable.setFillParent(true);
for (int i = 0; i < button.size(); i++) {
final int k = i;
button.get(k).addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
System.out.println("Level " + (k + 1) + "touchDown");
return true;
}
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
}
});
}
scrollPane.setScrollingDisabled(false, false);
mainTable.add(scrollPane).expand().fill();
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Gdx.gl20.glClearColor(1, 1, 1, 1);
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
LibGDX indeed offers Vieworts to help you with different resolutions. Personally I mostly use FitViewport, it keep the aspect ratio and "black" boxes the rest. There are a number of other viewports such as StretchViewport which does exactly what you expect.
You can easily test what fits your game if you setup everything correctly. The following code is straight from www.gamesfromscratch.com. And they describe the behavior of the less obvious viewports.
public class ViewportDemo extends ApplicationAdapter {
SpriteBatch batch;
Sprite aspectRatios;
OrthographicCamera camera;
Viewport viewport;
#Override
public void create () {
batch = new SpriteBatch();
aspectRatios = new Sprite(new Texture(Gdx.files.internal("Aspect.jpg")));
aspectRatios.setPosition(0,0);
aspectRatios.setSize(100,100);
camera = new OrthographicCamera();
viewport = new FillViewport(100,100,camera);
viewport.apply();
camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
}
#Override
public void render () {
camera.update();
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.setProjectionMatrix(camera.combined);
batch.begin();
aspectRatios.draw(batch);
batch.end();
}
#Override
public void dispose(){
aspectRatios.getTexture().dispose();
}
#Override
public void resize(int width, int height){
viewport.update(width,height);
camera.position.set(camera.viewportWidth/2,camera.viewportHeight/2,0);
}
You could do this yourself. Create any image, stick it to the stage and play with the different viewports and screen size. Also forget working with pixels since a pixel is a hardware thing.
Edit
The above implementation from www.gamesfromscratch.com does not show how to use the implementation in a stage. However this is as simple as it gets.
stage = new Stage(viewport, batch);
Now the stage will use the viewport and batch you provided. Now simply add stuff to the stage and draw it like you would do normally.
The following is my code that creates a very basic menu screen using a stage and a fitviewport. You do need your own atlas and skin to make this work, check my answer here on SO to learn more about that.
public class MenuScreen implements Screen{
private SpriteBatch batch;
protected Stage stage;
private Viewport viewport;
private OrthographicCamera camera;
private TextureAtlas atlas;
protected Skin skin;
public MenuScreen()
{
atlas = new TextureAtlas("atlas.txt");
skin = new Skin(Gdx.files.internal("skin.json"), atlas);
batch = new SpriteBatch();
camera = new OrthographicCamera();
viewport = new FitViewport(RealmOfConflict.WorldWidth, RealmOfConflict.WorldHeight, camera);
viewport.apply();
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
stage = new Stage(viewport, batch);
//Stage should controll input:
Gdx.input.setInputProcessor(stage);
}
#Override
public void show() {
//Create Table
Table mainTable = new Table();
//Set table to fill stage
mainTable.setFillParent(true);
//Set alignment of contents in the table.
mainTable.top();
//Create buttons
TextButton playButton = new TextButton("Play", skin);
TextButton optionsButton = new TextButton("Options", skin);
TextButton exitButton = new TextButton("Exit", skin);
//Add listeners to buttons
playButton.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
((Game)Gdx.app.getApplicationListener()).setScreen(new PlayScreen());
}
});
exitButton.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
Gdx.app.exit();
}
});
//Add buttons to table
mainTable.add(playButton);
mainTable.row();
mainTable.add(optionsButton);
mainTable.row();
mainTable.add(exitButton);
//Add table to stage
stage.addActor(mainTable);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(.1f, .12f, .16f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width, height);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
skin.dispose();
atlas.dispose();
}
}
The problem is in the viewport's camera. You are creating your own camera and don't setting its position and not updateing it.
If there is no reason to create your own camera just lean on default stage camera. Instead of:
camera = new OrthographicCamera();
camera.setToOrtho(true,width ,height);
stage = new Stage(new FillViewport(width, height,camera));
use
//no camera defining
stage = new Stage(new FillViewport(width, height));
and then normal flow of your app
I have a project on libgdx and I'm using a splash screen. The problem is, every time I block my phone on main menu and unlock it again, game shows splash screen again and I'd like it not to "restart" the game but stay where it is
EDIT: to avoid publishing all unnecessary code, I'll explain how I made it:
simple class extending game calls setScreen to splash screen, then after 3 seconds splash screen calls main menu, and I'd like the splash screen not to appear again unless the game is DESTROYED and opened again
code:
public MainMenuScreen(final TowerConquest gam) {
game = gam;
}
#Override
public void show() {
stage = new Stage(new FillViewport(800, 480));
Gdx.input.setInputProcessor(stage);
//((OrthographicCamera)stage.getCamera()).setToOrtho(false, 800, 480);
atlas = new TextureAtlas("skins/userInterface.pack");
skin = new Skin(atlas);
table = new Table(skin);
table.setBounds(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
white = new BitmapFont(Gdx.files.internal("font/white.fnt"), false);
black = new BitmapFont(Gdx.files.internal("font/black.fnt"), false);
TextButtonStyle textButtonStyle = new TextButtonStyle();
textButtonStyle.up = skin.getDrawable("normalbutton");
textButtonStyle.down = skin.getDrawable("pressedbutton");
textButtonStyle.disabled = skin.getDrawable("dissabledbutton");
textButtonStyle.pressedOffsetX = 1;
textButtonStyle.pressedOffsetY = -1;
textButtonStyle.font = black;
buttonPlay = new TextButton("PLAY", textButtonStyle);
buttonPlay.pad(20);
buttonPlay.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
game.setScreen(new testGame(game));
//Gdx.input.vibrate(150);
dispose();
}
});
buttonExit = new TextButton("EXIT", textButtonStyle);
buttonExit.pad(20);
buttonExit.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
dispose();
Gdx.app.exit();
}
});
LabelStyle headingStyle = new LabelStyle(white, Color.WHITE);
heading = new Label("Tower Conquest", headingStyle);
table.add(heading);
table.getCell(heading).padBottom(50);
table.row();
table.add(buttonPlay);
table.getCell(buttonPlay).spaceBottom(15);
table.row();
table.add(buttonExit);
table.debug();
table.setOrigin(table.getWidth()/2, table.getHeight()/2);
table.setFillParent(true);
stage.addActor(table);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height) {
my resizes...
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
all disposes..
}
I found the answer, add this on manifest file
android:configChanges="keyboard|keyboardHidden|screenSize|orientation">
it is because when the screen gets locked it changes orientation to portrait so it restarts the app, with that option on, it won't restart the application
I am trying to display a TextField on the screen. It works perfectly when the desktop version is run but when I run the android version the textfield is duplicated and the background is messed up.
screenshots
Code of Screen class:
public class SetupScreen implements Screen
{
private Stage stage;
private Table table;
private TextField paymentField;
#Override
public void show()
{
stage = new Stage();
Gdx.input.setInputProcessor(stage);
table = new Table();
table.setFillParent(true);
stage.addActor(table);
Pixmap bgPixmap = new Pixmap(1, 1, Format.RGBA8888);
bgPixmap.setColor(Color.GRAY);
bgPixmap.fill();
Texture bgTexture = new Texture(bgPixmap);
Pixmap cursorPixmap = new Pixmap(1, 1, Format.RGBA8888);
cursorPixmap.setColor(Color.WHITE);
cursorPixmap.fill();
Texture cursorTexture = new Texture(cursorPixmap);
Pixmap selPixmap = new Pixmap(1, 1, Format.RGBA8888);
selPixmap.setColor(Color.BLUE);
selPixmap.fill();
Texture selTexture = new Texture(selPixmap);
TextFieldStyle textFieldStyle = new TextFieldStyle();
textFieldStyle.background = new SpriteDrawable(new Sprite(bgTexture));
textFieldStyle.cursor = new SpriteDrawable(new Sprite(cursorTexture));
textFieldStyle.selection = new SpriteDrawable(new Sprite(selTexture));
textFieldStyle.font = new BitmapFont();
textFieldStyle.fontColor = Color.WHITE;
paymentField = new TextField("", textFieldStyle);
paymentField.setMessageText("Pay Rate");
table.add(paymentField);
}
#Override
public void render(float delta)
{
stage.act(delta);
stage.draw();
}
#Override
public void resize(int width, int height)
{
stage.getViewport().update(width, height);
}
#Override
public void pause()
{
}
#Override
public void resume()
{
}
#Override
public void hide()
{
}
#Override
public void dispose()
{
stage.dispose();
}
}
Your screen is completely messed up because you do not clear the screen at the beginning of each frame.
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
stage.act(delta);
stage.draw();
}
Another thing to note for UI is that you should center your camera. Otherwise the bottom-left corner of your UI will be in the middle of your screen.
You do that with the last parameter of the Viewport.update() method.
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
So, this is too hard for me to explain this in short, so the title looks so creepy. Well, I am writing a small android application and i have like three screens: the main-menu one, and two in-game screens. I can switch between the last two ones with a switch button. The problem is, that when i switch back to the last screen I've been to, all its acting stuff starts again from the very beginning. I have also seen a similar question here, but i did not work for me(( So, is there any way to keep the screen rendering, while I am using the other one?
Thank you!
The first screen:
public class InGameScreen implements Screen {
TinyBattles game;
public static OrthographicCamera camera;
public static Button switchButton1;
public InGameScreen(TinyBattles game) {
this.game = game;
}
#Override
public void show() {
camera = new OrthographicCamera(Global.VIEWPORT_WIDTH, Global.VIEWPORT_WIDTH*Global.actualHeight/Global.actualWidth);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
switchButton1 = new Button();
switchButton1.create(10, 800, 150, 150);
Assets.loadSwitchButton();
}
#Override
public void render(float delta) {
Global.cleanScreen();
touchHandler(camera, Global.touch);
switchButton1.act();
switchButton1.render(Assets.buttonSwitchStateUpSprite, Assets.buttonSwitchStateDownSprite);
Global.debugRender();
localDebugRender();
leaveScreen();
Global.batch.getProjectionMatrix().setToOrtho2D(0, 0, Global.actualWidth, Global.actualHeight);
Global.batch.setProjectionMatrix(camera.combined);
Global.shapeRenderer.setProjectionMatrix(camera.combined);
}
public void localDebugRender(){
switchButton1.debugRender();
}
public void touchHandler(OrthographicCamera camera, Vector3 touch){
if (Gdx.input.isTouched()){
touch.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touch);
Global.finger.bounds.x = touch.x - 64;
Global.finger.bounds.y = touch.y - 64;
}else{
Global.finger.bounds.x = 10000;
Global.finger.bounds.y = 10000;
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
public void leaveScreen(){
if (switchButton1.isPressed == true){
game.setScreen(TinyBattles.inGameVirtualScreen);
}
}
}
The second screen:
public class InGameVirtualScreen implements Screen {
TinyBattles game;
public static World world;
public static OrthographicCamera camera;
public static Button switchButton2;
public static Balls balls;
public InGameVirtualScreen(TinyBattles game) {
this.game = game;
}
#Override
public void show() {
world = new World(new Vector2(0, 0), true);
camera = new OrthographicCamera(Global.VIEWPORT_WIDTH, Global.VIEWPORT_WIDTH*Global.actualHeight/Global.actualWidth);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
camera.update();
switchButton2 = new Button();
switchButton2.create(10, 800, 150, 150);
balls = new Balls();
Assets.loadBallSprites();
balls.create();
balls.setup();
}
#Override
public void render(float delta) {
Global.tweenManager.update(1 / 60f);
Global.cleanScreen();
world.step(1 / 60f, 10, 10);
touchHandler(camera, Global.touch);
switchButton2.act();
balls.act();
renderLayerOne();
renderInterfaceLayer();
renderDebugLayer();
leaveScreen();
Global.batch.getProjectionMatrix().setToOrtho2D(0, 0, Global.actualWidth, Global.actualHeight);
Global.batch.setProjectionMatrix(camera.combined);
Global.shapeRenderer.setProjectionMatrix(camera.combined);
}
public void renderLayerOne(){
balls.render();
}
public void renderInterfaceLayer(){
switchButton2.render(Assets.buttonSwitchStateUpSprite, Assets.buttonSwitchStateDownSprite);
}
public void renderDebugLayer(){
Global.debugRender();
localDebugRender();
}
public void localDebugRender(){
switchButton2.debugRender();
}
public void touchHandler(OrthographicCamera camera, Vector3 touch){
if (Gdx.input.isTouched()){
touch.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touch);
Global.finger.bounds.x = touch.x - 64;
Global.finger.bounds.y = touch.y - 64;
}else{
Global.finger.bounds.x = 10000;
Global.finger.bounds.y = 10000;
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
public void leaveScreen(){
if (switchButton2.isPressed){
game.setScreen(TinyBattles.inGameScreen);
}
}
}
There is a leaveScreen() method at the bottom of the each class
You should move all your code in each screen from show() to the constructor. show() is called every time the screen becomes the active screen, so you don't want to do initialization there.
By the way, you should never keep static references to anything that implements Disposable, such as SpriteBatch, or anything that keeps a reference to something that implements Disposable, such as Button or Stage. If you do, when the user backs out of your game activity and reopens it, there will be memory leaks and incorrect textures, etc.
Its been a while since I used libgdx, but it seems you are creating a new screen on each leaveScreen function.
See here for a better approach, where when you create the Game class you create the screens as well:
public class MyGame extends Game {
MainMenuScreen mainMenuScreen;
AnotherScreen anotherScreen;
#Override
public void create() {
mainMenuScreen = new MainMenuScreen(this);
anotherScreen = new AnotherScreen(this);
setScreen(mainMenuScreen);
}
}
Then, you change the screens like this:
public void leaveScreen(){
if (switchButton2.isPressed){
game.setScreen(game.anotherScreen);
}
}
As I said it's been a while, but give it a shot :)
For some reason, my android phone will not load a table even though I have it being drawn. It shows up on the desktop version, but not on the android version. You can see my adding it in the render() method in the if statement. It loads perfectly on my desktop. This is only showing the code I used for the table. The desktop version and phone version has the same camera size. Everything else appears on my phone except for this table.
public class game implements Screen {
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
tManager.update(delta);
backBatch.begin();
splash.draw(backBatch);
backBatch.end();
stage.act(delta);
stage.draw();
world.step(TIMESTEP, VITERATIONS, PITERATIONS);
table2 = new Table(skin);
table2.setBounds(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
int numContacts = world.getContactCount();
if (numContacts > 0) {
table2.add(heading3).spaceBottom(35).row();
table2.add(replayButton).spaceBottom(15).row();
table2.add(menuButton);
movement.x = -sSpeed;
movement.y = 10;
movement.rotate(720);
stage.addActor(table2);
Gdx.input.setInputProcessor(stage);
}
debugRenderer.render(world, camera.combined);
}
public void resize(int width, int height) {
camera.viewportWidth = 450;
camera.viewportHeight = 250;
camera.update();
stage.setViewport(450, 250, true);
stage.getCamera().translate(-stage.getGutterWidth(), -stage.getGutterHeight(), 0);
if (table != null){
table.setSize(450, 250);
}
}
#Override
public void show()
{
stage = new Stage();
world = new World(new Vector2(0, 0), true);
debugRenderer = new Box2DDebugRenderer();
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
//replay button
replayButton = new TextButton("REPLAY", skin);
replayButton.addListener(new ClickListener() {
public void clicked(InputEvent event, float x, float y)
{
((Game) Gdx.app.getApplicationListener()).setScreen(new game());
}
});
replayButton.pad(15);
//main menu button
menuButton = new TextButton("MAIN MENU", skin);
menuButton.addListener(new ClickListener() {
public void clicked(InputEvent event, float x, float y)
{
((Game) Gdx.app.getApplicationListener()).setScreen(new MainMenu());
}
});
menuButton.pad(10);
//game over
heading3 = new Label("game over", skin);
heading3.setFontScale(2);
}
private void createCollisionListener() {
//collisions in world
world.setContactListener(new ContactListener() {
public void beginContact(Contact contact) {
}
public void endContact(Contact contact) {
}
public void preSolve(Contact contact, Manifold oldManifold) {
}
public void postSolve(Contact contact, ContactImpulse impulse) {
}
});
}