I'm new at Java. I'm writing a simple game like Flappy Bird. I like the add menu screen before starting the game, like
-Start Game -Credits maybe options (maybe), something like that.
Here is my basic code for oncreate:
#Override
public void create () {
batch = new SpriteBatch();
background = new Texture("background.png");
smurf = new Texture("smurf.png");
gargamel1 = new Texture("gargamel.png");
gargamel2 = new Texture("gargamel.png");
gargamel3 = new Texture("gargamel.png");
distance = Gdx.graphics.getWidth() / 2;
random = new Random();
smurfX = Gdx.graphics.getWidth() / 2 - smurf.getHeight() / 2;
smurfY = Gdx.graphics.getHeight() / 3;
shapeRenderer = new ShapeRenderer();
smurfCircle = new Circle();
enemyCircles = new Circle[numberOfEnemies];
enemyCircles2 = new Circle[numberOfEnemies];
enemyCircles3 = new Circle[numberOfEnemies];
font = new BitmapFont();
font.setColor(Color.WHITE);
font.getData().setScale(5);
font2 = new BitmapFont();
font2.setColor(Color.WHITE);
font2.getData().setScale(8);
for (int i = 0; i<numberOfEnemies; i++){
enemyOffSet[i] = (random.nextFloat() - 0.5f) * (Gdx.graphics.getHeight() - 200);
enemyOffSet2[i] = (random.nextFloat() - 0.5f) * (Gdx.graphics.getHeight() - 200);
enemyOffSet3[i] = (random.nextFloat() - 0.5f) * (Gdx.graphics.getHeight() - 200);
enemyX[i] = Gdx.graphics.getWidth() - gargamel1.getWidth() / 2 + i * distance;
enemyCircles[i] = new Circle();
enemyCircles2[i] = new Circle();
enemyCircles3[i] = new Circle();
sound = Gdx.audio.newSound(Gdx.files.internal("lose.ogg"));
}
}
To have several Screens you must extend Game in your Main class.
Default when you create a libGdx project with the "gdx-setup.jar" the Main class extends ApplicationAdapter.
public class MultipleScreen extends Game {
Now you have a method: setScreen(Screen). With this method you can change Screens.
Here is a template which I use often for my projects. It also dispose the old screen:
public class MultipleScreen extends Game {
#Override
public void create () {
}
public void changeScreen(Screen newScreen){
Screen oldScreen = getScreen();
setScreen(newScreen);
//Dispose the old screen to release resources
if(oldScreen != null)
oldScreen.dispose();
}
#Override
public void render () {
//Clear the Screen
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Render the current Screen
super.render();
}
#Override
public void dispose () {
//Dispose Assets which you used in the whole Game
}
}
Here I created a new method: changeScreen(Screen). This method uses the setScreen method of the Game to set the screen and additional dispose the old one.
Now to make an example we need two Screens:
public class MenuScreen implements Screen {
//We need MultipleScreen class to change the screen
private MultipleScreen game;
public MenuScreen(MultipleScreen game) {
this.game = game;
}
#Override
public void show() {
}
private float f = 0;
#Override
public void render(float delta) {
//A simple Timer which changes the screen after 10 seconds
f += delta;
if(f > 10000){
//Here you can add for example also the Level to the GameScreen which level the player choose
//game.changeScreen(new GameScreen(game, levelNumber));
game.changeScreen(new GameScreen(game));
}
}
...
}
And a GameScreen which simple displays the badlogic image:
public class GameScreen implements Screen {
private MultipleScreen game;
private SpriteBatch batch;
private Texture img;
public GameScreen(MultipleScreen game) {
this.game = game;
}
#Override
public void show() {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
#Override
public void render(float delta) {
//Here we don't need to add Gdx.gl.glClear...
//because we already have this in our MultipleScreen which does this for every subScreen
batch.begin();
batch.draw(img, 0, 0);
batch.end();
}
...
#Override
public void dispose() {
//This things will be disposed if we change to another Screen to save resources
batch.dispose();
img.dispose();
}
Now the last thing we must do is to set a Screen if our game Starts. So in the create() method of our Main class (in this case MultipleScreen) we add:
#Override
public void create () {
changeScreen(new MenuScreen(this));
}
Now if the game is over we will go back to MenuScreen. But instead of creating a new MenuScreen everyTime we can add a backToMenu() method in our Main class:
private MenuScreen menuScreen;
#Override
public void create () {
menuScreen = new MenuScreen(this);
backToMenu();
}
public void backToMenu(){
setScreen(menuScreen);
}
In our GameScreen we now can simple call: game.backToMenu(); to change back to MenuScreen without creating a new one.
Important
If we call setScreen() the show() method will be called and not the Constructor of the class.
So every Asset you dispose in the classes dispose() method you must create in the show() method not in the Constructor else you will have no Assets if you show a Screen a second time.
A hope this little guide will help you.
If you need more help you can find some tutorials in the Internet where they change Screen:
https://www.gamedevelopment.blog/full-libgdx-game-tutorial-project-setup/
Related
this is my first question to ask here so i am sry beforehand that i do something wrong here.
I am currently trying to develop my first own game in libgdx.
So for the start i have 4 classes.
FlipX a class that extends from Game.
StartMenu a class that implements from Screen.
GameScreen a class that implements from Screen.
Button a class that extens from Sprite.
I hope i can show you some code here, but let me first explain.
When starting the code the Class FlipX, where i have my SpriteBatch to draw, set the Screen to the StartMenu Class. There i can click on 2 Buttons which are drawn to the SpriteBatch from FlipX.
These Buttons came from the Button Class which i created to store multiple Textures to one so called Button. This Button Class extends from Sprite.
The PlayBtn will set the Screen to the GameScreen class when i click on it.
The Problem then occurs when the GameScreen Class draws another Button on the same batch after the Screen is set to GameScreen.
It is the Line from GameScreen in the drawBatch() function where i draw jumpBtn.draw(game.batch).
When i do this i get the following error.
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.badlogic.gdx.graphics.g2d.SpriteBatch.switchTexture(SpriteBatch.java:1067)
at com.badlogic.gdx.graphics.g2d.SpriteBatch.draw(SpriteBatch.java:558)
at com.badlogic.gdx.graphics.g2d.Sprite.draw(Sprite.java:580)
at de.geecogames.flipx.Screens.GameScreen.drawBatch(GameScreen.java:88)
at de.geecogames.flipx.Screens.GameScreen.render(GameScreen.java:63)
at com.badlogic.gdx.Game.render(Game.java:46)
at de.geecogames.flipx.FlipX.render(FlipX.java:26)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:232)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:127)
This Error does not occur when i replace the Button with a simple Sprite, which is odd because the Button Class is just a class that extends Sprite.
private Sprite jumpBtn = new Sprite(new Texture("game/UpDef.png"));
public class FlipX extends Game {
public SpriteBatch batch;
//Filemanager to load Assets from an Asset Manager
public FileManager manager;
StartMenu startMenu;
#Override
public void create () {
manager= new FileManager();
startMenu=new StartMenu(this);
batch = new SpriteBatch();
setScreen(startMenu);
}
#Override
public void render () {
super.render();
}
#Override
public void dispose () {
batch.dispose();
}
public FileManager getFileManager(){
return manager;
}
}
public class StartMenu implements Screen {
private final FlipX game;
private final FileManager fileM;
private final OrthographicCamera camera;
private final FitViewport viewport;
private Sprite img = new Sprite(new Texture("badlogic.jpg"));
private final Button playBtn, exitBtn;
private final Sprite backGround;
private boolean playPressed, exitPressed;
public StartMenu(FlipX game) {
this.game = game;
//Get Manager and load Assets
this.fileM = game.getFileManager();
fileM.loadAssetsMenu();
//Testlabel
Label testlabel = new Label(String.format("test"), new Label.LabelStyle(new BitmapFont(), Color.BLACK));
//Get the Assets from Manager
playBtn = new Button(fileM.getTexture(fileM.playBtnDefault), fileM.getTexture(fileM.playBtnHover), fileM.getTexture(fileM.playBtnClick));
exitBtn = new Button(fileM.getTexture(fileM.exitBtnDefault), fileM.getTexture(fileM.exitBtnHover), fileM.getTexture(fileM.exitBtnClick));
backGround = fileM.getSprite(fileM.backgroundMenu);
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
viewport = new FitViewport(Z.V_WIDTH, Z.V_HEIGHT, camera);
camera.position.set(viewport.getWorldWidth() / 2, viewport.getWorldHeight() / 2, 0);
//Btn Position
playBtn.setPosition(Z.V_WIDTH / 2 - playBtn.getWidth() / 2, Z.V_HEIGHT / 2 - playBtn.getHeight() / 2);
exitBtn.setPosition(Z.V_WIDTH / 2 - exitBtn.getWidth() / 2, Z.V_HEIGHT / 2 - exitBtn.getHeight() / 2 - playBtn.getHeight() - playBtn.getHeight() / 2);
img.setPosition(0, 0);
}
#Override
public void render(float delta) {
update(delta);
clearColor();
drawBatch();
}
private void update(float dt) {
handleInput();
cameraUpdates();
}
private void clearColor() {
Gdx.gl.glClearColor(0, 0.5f, 0.9f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void drawBatch() {
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
backGround.draw(game.batch);
img.draw(game.batch);
//Buttons
playBtn.draw(game.batch);
exitBtn.draw(game.batch);
game.batch.end();
}
private void handleInput() {
Vector3 realCoords = viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
if (Gdx.input.isKeyPressed(Input.Keys.K)) {
float addX = 2;
float addY = 0;
img.setPosition(img.getX() + addX, img.getY() + addY);
}
if (Gdx.input.isKeyPressed(Input.Keys.UP)) {
camera.position.y = camera.position.y + 1;
} else if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {
camera.position.y = camera.position.y - 1;
}
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {
camera.position.x = camera.position.x - 1;
} else if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {
camera.position.x = camera.position.x + 1;
}
if (Gdx.input.isKeyPressed(Input.Keys.PAGE_UP)) {
camera.zoom = camera.zoom + 0.1f;
} else if (Gdx.input.isKeyPressed(Input.Keys.PAGE_DOWN)) {
camera.zoom = camera.zoom - 0.1f;
}
//btn test
if (BasicFunctions.isInside(realCoords.x, realCoords.y, playBtn)) {
if (Gdx.input.justTouched()) {
playBtn.setClick();
playPressed = true;
fileM.playSound(fileM.playBtnSound);
} else {
if (playPressed) {
game.setScreen(new GameScreen(game));
}
playBtn.setHover();
}
} else
playBtn.setNormal();
if (BasicFunctions.isInside(realCoords.x, realCoords.y, exitBtn)) {
if (Gdx.input.isTouched()) {
exitBtn.setClick();
exitPressed = true;
} else {
if (exitPressed)
Gdx.app.exit();
exitBtn.setHover();
}
} else
exitBtn.setNormal();
}
private void cameraUpdates() {
camera.update();
}
}
public class GameScreen implements Screen {
private final FlipX game;
private final FileManager fileM;
private final OrthographicCamera camera;
private final FitViewport viewport;
private final Button jumpBtn;
private final Sprite background;
public GameScreen(FlipX game){
this.game=game;
this.fileM=game.getFileManager();
fileM.loadAssetsGame();
camera=new OrthographicCamera(Gdx.graphics.getWidth(),Gdx.graphics.getHeight());
viewport = new FitViewport(Z.V_WIDTH,Z.V_HEIGHT,camera);
jumpBtn = new Button(new Texture("game/UpDef.png"),new Texture("game/UpHov.png"),new Texture("game/UpClick.png"));
jumpBtn.setPosition(2,2);
background = fileM.getSprite(fileM.backgroundMenu);
}
#Override
public void show() {
}
#Override
public void render(float delta) {
update(delta);
clearColor();
drawBatch();
}
private void update(float dt){
handleInput(dt);
}
private void clearColor() {
Gdx.gl.glClearColor(1, 0.5f, 0.9f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void drawBatch(){
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
background.draw(game.batch);
//Bug here?
jumpBtn.draw(game.batch);
game.batch.end();
}
private void handleInput(float dt){
Vector3 realCoords = viewport.unproject(new Vector3(Gdx.input.getX(), Gdx.input.getY(), 0));
if(Gdx.input.isTouched()){
if(BasicFunctions.isInside(realCoords.x,realCoords.y,jumpBtn)){
EzLog.logging("X " + jumpBtn.getX() + " Y " + jumpBtn.getY());
}
}
}
public class Button extends Sprite {
private final Texture normal, hover, click;
private float stateTimer;
public Button(Texture normal, Texture hover, Texture click){
this.normal=normal;
this.hover=hover;
this.click=click;
stateTimer=0;
setSize();
}
private void setSize(){
float height,width;
width=normal.getWidth();
height=normal.getHeight();
setBounds(0,0,width*3,height*3);
}
public void setNormal(){
setRegion(normal);
}
public void setHover(){
setRegion(hover);
}
public void setClick(){
setRegion(click);
}
}
After hours and hours i found the solution.
The Problem is that my Class Button can save up to 3 textures.
To draw one of the textures, we can use the draw function of it because Button extends from Sprite. To show it on the SpriteBatch it just needs to set the texture to the region.
setRegion("TextureHere.png")
So everytime i draw it with my playBtn.draw(game.batch) it will show this texture at the position i gave him before with setPosition(x,y)
But i never actually do "setRegion()" in the Constructor, so it will try to draw something which isn't there. And this is the thing why it says NullPointerException. I just got confused of the error because of the functions switchTexture from SpriteBatch.
In the Class StartMenu it coincidentally just calls the "setRegion()" because it is always called in the else part from the condition in "handleInput" with the function "setNormal()" from the Button Class.
Solution i know has is the following:
I added the setNormal() function in the Constructor so the first texture will always be set to region, if i can say it like this, and this works.
public class Button extends Sprite {
private final Texture normal, hover, click;
public Button(Texture normal, Texture hover, Texture click){
this.normal=normal;
this.hover=hover;
this.click=click;
setSize();
setNormal();
}
private void setSize(){
float height,width;
width=normal.getWidth();
height=normal.getHeight();
setBounds(0,0,width*3,height*3);
}
public void setNormal(){
setRegion(normal);
}
public void setHover(){
setRegion(hover);
}
public void setClick(){
setRegion(click);
}
Need to rotate the big pixmap(from gallery) in my game.
I use the following code for this.
For example:
public class MyGdxGame extends ApplicationAdapter {
private int w = 720, h = 1280;
private Stage stage;
private Image bigImg, button, rotationIndicator;
private Pixmap pixmap0;
private Pixmap pixmap90;
private Texture textureBigImg;
private String buttonPath = "badlogic.jpg", bigImgPath = "DSC_0006.JPG"; //DSC_0006.JPG: 5504x3096
private Thread thread;
#Override
public void create () {
stage = new Stage(new FillViewport(w, h));
button = new Image(new Texture(buttonPath));
textureBigImg = new Texture(bigImgPath);
bigImg = new Image(textureBigImg);
rotationIndicator = new Image(new Texture(buttonPath));
rotationIndicator.setPosition(720-256,0);
button.setPosition(0,0);
bigImg.setBounds( 0,256, w, w*3096/5504f);
pixmap0 = new Pixmap(Gdx.files.internal(bigImgPath));
thread = new Thread(new Runnable() {
#Override
public void run() {
rotation();
}
});
button.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
super.clicked(event, x, y);
stage.addActor(rotationIndicator);
rotation(); //rotationIndicator draw only after the turn process.
//thread.start(); //I get a black screen.
}
});
stage.addActor(button);
stage.addActor(bigImg);
Gdx.input.setInputProcessor(stage);
}
private void rotation() {
pixmap90 = new Pixmap(pixmap0.getHeight(), pixmap0.getWidth(), pixmap0.getFormat());
for (int y = 0, a = 0; y<pixmap0.getWidth(); ++y, ++a) {
for (int x = 0, b = pixmap0.getHeight(); x<pixmap0.getHeight(); ++x, --b) {
pixmap90.drawPixel(x, y, pixmap0.getPixel(a, b));
}
}
textureBigImg.dispose();
textureBigImg = new Texture(pixmap90);
textureBigImg.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
bigImg.setDrawable(new TextureRegionDrawable(new TextureRegion(textureBigImg)));
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.draw();
}
#Override
public void dispose () {
pixmap0.dispose();
pixmap90.dispose();
textureBigImg.dispose();
}
}
The problem is that the big picture turns too long. From this freezing program.
I tried to do it in a separate thread, but I get a black screen.
I tried to display the turn indicator, but it is drawn only after the turn process.
Now I am trying to reduce pixmap, but on weak devices there is still freezing.
You cannot create new Texture objects or manipulate them in separate threads because they are OpenGL objects. Your rotate() method should send a runnable back to the GL thread to load the Texture after you're done creating the rotated pixmap.
So take all the code after your for loop in rotate() and wrap it in a Runnable. Call Gdx.app.postRunnable().
so I am doing what is suppose to be a simple game but I think I might be complicating things. I have a camera for the GameScreen and a viewPort which follow the position of the player and when it reaches to points on the sides, the camera stops following the player and stays in one point.
This by itself works fine, but then I wanted to add the pause menu and some other features in the game, creating a hud class with its own camera and viewport as well as a Stage and a shapeRenderer.
The problem comes when I create the instance of this hud inside my gameScreen, the camera that I am looking while I am playing looks like is the hudCam, which does not follow the player and basically does not let me see the player when it reaches the edges of the screen.
This is my GameScreen Class:
public class GameScreen implements Screen {
WowInvasion game;
ScrollingBackground background;
private OrthographicCamera gameCam;
private Viewport gameViewPort;
/*
Basically I wanna keep the same sprites running while in the menu, playing and till dead
therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
the game has a background for the menu and there's no need for running a second screen.
*/
public static final int MAIN_MENU = 0;
public static final int GAME = 1;
private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN
//STAGES
private GameStage gameStage; //game ui
private menuStage mainMenu; //Main menu of the game
private Hud hud;
//Resources
private TextureAtlas atlas; //for the textures most
private Skin skin; //for the styles and fonts
//Sprites
private Player player;
//Shapes
private float progressPower; //for the power to build up
private final float POWER_CHARGED = 1000; //limit to get power
private final float DECREASING_POWER = 20; //limit to get power
public GameScreen(WowInvasion game){
this.game = game;
gameCam = new OrthographicCamera();
gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
progressPower = 0f;
game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
game.wowAssetManager.loadSkins(); //load the needed skins
game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
}
#Override
public void show() {
game.batch.setProjectionMatrix(gameCam.combined);
background = new ScrollingBackground();
atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
gameStage = new GameStage(gameViewPort, game.batch, skin, game);
hud = new Hud(game.batch, skin);
player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);
switch(state){
case MAIN_MENU:
Gdx.input.setInputProcessor(mainMenu);
break;
case GAME:
background.setFixedSpeed(false); //does not work in here
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(state == GAME) {
background.setFixedSpeed(false);
player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
//really cheap way to charge power with velocity
if(progressPower != POWER_CHARGED) {
progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
progressPower -= DECREASING_POWER;
}
else
progressPower = POWER_CHARGED / 4;
}
mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
game.batch.begin();
background.updateAndRender(delta, game.batch); //updating scrolling background
player.draw(game.batch);
game.batch.end();
mainMenu.draw(); //draw the menu stage
gameStage.draw(); //draw the ui stage for the game
hud.getStage().draw();
hud.renderRotateMeter();
updateCamera(0, WowInvasion.WIDTH);
System.out.println(player.getPosition().x);
}
public void updateCamera(float startX, float endX){
Vector3 position = gameCam.position;
//linear interpolation : a + (b - a) * lerp
//b = player position
//a = current camera position
//lerp = interpolation factor
position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;
//making the camera stay when the player gets to close to the sides
if(position.x < startX) {
position.x = startX;
}
if(position.x > endX){
position.x = endX;
}
gameCam.position.set(position);
gameCam.update();
}
#Override
public void resize(int width, int height) {
gameViewPort.update(width, height);
//hud.getViewport().update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
mainMenu.dispose();
gameStage.dispose();
game.dispose();
hud.dispose();
}
public static void setState(int state) {
GameScreen.state = state;
}
}
And this is my HUD:
public class Hud implements Disposable{
private Stage stage;
private Viewport viewport;
Button buttonPause, buttonResume;
private OrthographicCamera hudCam;
private ShapeRenderer sp; //like a batch for shapes
public Hud(SpriteBatch sb, Skin skin){
hudCam = new OrthographicCamera();
hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
stage = new Stage(viewport, sb);
sp = new ShapeRenderer();
Table table = new Table();
table.top();
//this makes the table the size of the stage
table.setFillParent(true);
buttonPause = new Button(skin, "pause");
buttonPause.setTransform(true);
buttonPause.addListener(new ClickListener(){ //listener to handle event
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
buttonResume = new Button(skin, "resume");
buttonResume.setTransform(true);
buttonResume.setScale(0.5f);
buttonResume.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
buttonResume.setVisible(false);
}
});
table.add(buttonPause);
table.row();
table.add(buttonResume);
stage.addActor(table);
}
public void renderRotateMeter(){
sp.setProjectionMatrix(hudCam.combined);
sp.begin(ShapeRenderer.ShapeType.Filled);
sp.setColor(Color.YELLOW);
sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
sp.end();
}
public Viewport getViewport() {
return viewport;
}
public Stage getStage() {
return stage;
}
#Override
public void dispose() {
stage.dispose();
sp.dispose();
}
}
thanks in advance!
EDIT
so I tried passing the gameCam has a parameter to the hud and instead of making a new OrthographicCamera I used that one has the hudCamara as well and well, the movement with the player is perfect except now the thins from the Hud do not move at all..
It looks like you only set projectionMatrix to only HUD camera as seen in
sp.setProjectionMatrix(hudCam.combined);
Try to set it the same to other stuff outside of the HUD class prior to draw call too.
Another thing to keep in mind is that, when you involve using multiple Viewport and Camera in the game as most of the time it will be 1 Viewport matching with 1 Camera and work with another set as in your case. In draw call, you need to call apply() or apply(true) of Viewport class too to tell the system that you will draw based on which viewport thus in turn it will adhere to screen coordinate that was set up by viewport's attaching camera.
So assume you have 2 objects that needed to be called in different viewport consecutively, do it like the following code. The methods call is correct according to libgdx API but variable names are fictional.
// draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
sb.setProjectionMatrix(cameraA.combined);
viewportA.apply();
objA.draw();
// draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
sb.setProjectionMatrix(cameraB.combined);
viewportB.apply(true); // send in true as for HUD, we always want to center the screen
objB.draw();
In summary, 2 things to keep in mind when drawing objects that use multiple of camera and viewport in consecutive draw call.
Set projection matrix to either SpriteBatch or ShapeRenderer.
Call apply() or apply(true) of Viewport class to let it know you work with this viewport.
Hello I am currently programming a LibGDX game based on physics with a ball that must stay on a platform, I have set up the physics components for the ball, but when I try updating the sprite for the ball based on the physic body's position it is giving me a null pointer exception.
I have spent 1 whole day trying to fix the problem through researching and looking at other peoples' code but cannot find my error. Thank you for your time and any input that you can give. Below I have typed the code from my Render class, Ball GameObject class, Asset class, and Exception.
Ball GameObject Class:
public class Ball {
public static World physicsWorld;
public static BodyDef ballBodyDef;
public static Body ballBody;
public static CircleShape ballShape;
public static FixtureDef ballFixtureDef;
public static Fixture ballFixture;
public Ball() {
physicsWorld = new World(new Vector2(0, -9.8f), true);
ballBodyDef = new BodyDef();
ballBodyDef.position.set(Assets.ballSprite.getX(),
Assets.ballSprite.getY());
ballBodyDef.type = BodyDef.BodyType.DynamicBody;
ballBody = physicsWorld.createBody(ballBodyDef);
ballShape = new CircleShape();
ballShape.setRadius(6f);
ballFixtureDef = new FixtureDef();
ballFixtureDef.density = 1.0f;
ballFixtureDef.friction = 0.2f;
ballFixtureDef.restitution = 0.4f;
ballFixture = ballBody.createFixture(ballFixtureDef);
}
public void dispose() {
physicsWorld.dispose();
ballShape.dispose();
}
public BodyDef getBallBodyDef() {
return ballBodyDef;
}
public void setBallBodyDef(BodyDef ballBodyDef) {
this.ballBodyDef = ballBodyDef;
}
public Body getBallBody() {
return ballBody;
}
public void setBallBody(Body ballBody) {
this.ballBody = ballBody;
}
public CircleShape getBallShape() {
return ballShape;
}
public void setBallShape(CircleShape ballShape) {
this.ballShape = ballShape;
}
public FixtureDef getBallFixtureDef() {
return ballFixtureDef;
}
public void setBallFixtureDef(FixtureDef ballFixtureDef) {
this.ballFixtureDef = ballFixtureDef;
}
public Fixture getBallFixture() {
return ballFixture;
}
public void setBallFixture(Fixture ballFixture) {
this.ballFixture = ballFixture;
}
}
Render Class:
public class GameRenderer {
private GameWorld myWorld;
private OrthographicCamera cam;
int width = Gdx.graphics.getWidth();
int height = Gdx.graphics.getHeight();
private SpriteBatch batch;
public GameRenderer(GameWorld world, int gameHeight, int midPointY) {
myWorld = world;
cam = new OrthographicCamera();
cam.setToOrtho(true, width, height);
batch = new SpriteBatch();
batch.setProjectionMatrix(cam.combined);
}
public void render(float delta) {
Gdx.app.log("GameRenderer", "render");
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Ball.physicsWorld.step(Gdx.graphics.getDeltaTime(), 6, 2);
Assets.ballSprite.setPosition(Ball.ballBody.getPosition().x,
Ball.ballBody.getPosition().y);
batch.begin();
batch.draw(Assets.skySprite, 0, 0, 1920, 1080);
batch.draw(Assets.ballSprite, Assets.ballSprite.getX(),
Assets.ballSprite.getY());
batch.draw(Assets.platformSprite, Assets.platformSprite.getX(),
Assets.platformSprite.getY());
batch.end();
}
}
Lastly my Assets Class:
public class Assets {
public static Texture skyTexture;
public static Sprite skySprite;
public static Texture ballTexture;
public static Sprite ballSprite;
public static Texture platformTexture;
public static Sprite platformSprite;
public static Button rightTiltButton;
public static TextureAtlas rightButtonAtlas;
public static TextButtonStyle rightButtonStyle;
public static Skin rightButtonSkin;
public static void Load() {
skyTexture = new Texture(Gdx.files.internal("Assets/skybackground.png"));
skyTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
skySprite = new Sprite(skyTexture);
skySprite.flip(false, true);
ballTexture = new Texture(
Gdx.files.internal("Assets/ballcharacter.png"));
ballTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
ballSprite = new Sprite(ballTexture);
ballSprite.flip(false, true);
ballSprite.setPosition(
Gdx.graphics.getWidth() / 2 - ballSprite.getWidth()/2,
Gdx.graphics.getHeight() / 2 - ballSprite.getHeight()-4);
platformTexture = new Texture(Gdx.files.internal("Assets/platform.png"));
platformTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
platformSprite = new Sprite(platformTexture);
platformSprite.flip(false, true);
platformSprite.setPosition(
Gdx.graphics.getWidth() / 2 - platformSprite.getWidth()/2,
Gdx.graphics.getHeight() / 2 - platformSprite.getHeight()/2);
rightButtonSkin = new Skin();
rightButtonAtlas = new TextureAtlas(
Gdx.files.internal("Assets/right_tilt_button.pack"));
rightButtonSkin.addRegions(rightButtonAtlas);
rightButtonStyle = new TextButtonStyle();
rightButtonStyle.up = rightButtonSkin.getDrawable("right_tilt_button");
rightButtonStyle.up = rightButtonSkin
.getDrawable("right_tilt_button_pressed");
rightButtonStyle.up = rightButtonSkin.getDrawable("right_tilt_button");
rightButtonStyle.over = rightButtonSkin
.getDrawable("right_tilt_button_pressed");
rightButtonStyle.down = rightButtonSkin
.getDrawable("right_tilt_button_pressed");
rightTiltButton = new Button(rightButtonStyle);
}
public static void dispose() {
skyTexture.dispose();
rightButtonAtlas.dispose();
rightButtonSkin.dispose();
ballTexture.dispose();
}
}
NullPointerException:
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.manumade.tiltr.tiltrhelpers.GameRenderer.render (GameRenderer.java:36)
at com.manumade.tiltr.screens.GameScreen.render(GameScreen.java:39)
at com.badlogic.gdx.Game.render(Game.java:46)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:208)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:114)
Looking at your code and stack trace there seems to be a problem on one of this lines:
batch.draw(Assets.skySprite, 0, 0, 1920, 1080);
batch.draw(Assets.ballSprite, Assets.ballSprite.getX(),
Assets.ballSprite.getY());
batch.draw(Assets.platformSprite, Assets.platformSprite.getX(),
Assets.platformSprite.getY());
From the stack trace that you provided we can see that the error is happening on line 36:
at com.manumade.tiltr.tiltrhelpers.GameRenderer.render (GameRenderer.java:36)
Look at your GameRenderer class and see what object gets called on that line. When you receive a NullPointerException it means that you are trying to call a method or acess a variable on a object that does not point to anything (The object might never be created.)
I would assume that either:
The SpriteBatch is NULL
Assets.skySprite is NULL
Assets.ballSprite is NULL
Assets.platformerSprite is NULL
So look at the way those object get instantiated. Also make sure that you are actually initializing them before performing anything on those objects. Your render() method might be called before you actually initialize the Sprite objects in your Assets class.
It looks like a problem with drawing one of your sprites (based on the exception line number). Try replacing the sprites you have used with other ones (obviously it will look silly), and see if any of them work.
Once you know which sprites work and which ones don't, you can try and narrow down what might be different about them. I have had similar problems caused by using non-power of two textures, for example.
I see that you call:
Assets.ballSprite.setPosition(Ball.ballBody.getPosition().x,
Ball.ballBody.getPosition().y);
batch.begin();
batch.draw(Assets.skySprite, 0, 0, 1920, 1080);
batch.draw(Assets.ballSprite,
Assets.ballSprite.getX(),
Assets.ballSprite.getY());
batch.draw(Assets.platformSprite,
Assets.platformSprite.getX(),
Assets.platformSprite.getY());
batch.end();
Assets.skySprite,
Assets.ballSprite,
Assets.platformSprite
but I can not see that you call Assets.load ();
before using the above why is null, because they are not initialized only declared or I think that's the error.
try calling Assets.Load (); before that used in render method, for example inside of the your public GameRenderer(...); method.
could try to read about it AssetsManager :
https://github.com/libgdx/libgdx/wiki/Managing-your-assets
and create a loading of assets before entering the playing area.(but it's just an idea).
could look at, www some tutorial about it AssetManager.
P.S: which is the line: (GameRenderer.java:36),
sure that the staticas variables used for example in Ball, are initialized before being used in render.
I just started learning libGDX, and was following the example from http://steigert.blogspot.in/2012/02/2-libgdx-tutorial-game-screens.html (the second link on libGdx help page).
As of now, I just display a logo of 512x512. There is nothing else happening in the application but when I run the application in desktop mode,I get a FPS of 15-16. When I remove the image, I get 60fps for the blank screen. For android its even worse, I get 3-4 fps in Galaxy SL - GT-i9003 (Temple Run runs on playable speed on the device).
My laptop plays World of Warcraft without any hiccups in high quality so its baffling that such a small app would only achieve 15fps.
public class SplashScreen extends AbstractScreen {
private Texture splashTexture;
private TextureRegion splashTextureRegion;
public SplashScreen(MyGDXGame game){
super(game);
}
#Override
public void show()
{
super.show();
// load the splash image and create the texture region
splashTexture = new Texture("splash.png");
// we set the linear texture filter to improve the stretching
splashTexture.setFilter( TextureFilter.Linear, TextureFilter.Linear );
// in the image atlas, our splash image begins at (0,0) at the
// upper-left corner and has a dimension of 512x301
splashTextureRegion = new TextureRegion( splashTexture, 0, 0, 512, 382 );
}
#Override
public void render(float delta ) {
super.render( delta );
// we use the SpriteBatch to draw 2D textures (it is defined in our base
// class: AbstractScreen)
batch.begin();
// we tell the batch to draw the region starting at (0,0) of the
// lower-left corner with the size of the screen
batch.draw( splashTextureRegion, 0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight() );
// the end method does the drawing
batch.end();
}
#Override
public void dispose()
{
super.dispose();
splashTexture.dispose();
}
}
Here is the relevant section of AbstractScreen class:
public class AbstractScreen implements Screen {
protected final MyGDXGame game;
protected final BitmapFont font;
protected final SpriteBatch batch;
public AbstractScreen(MyGDXGame game ){
this.game = game;
this.font = new BitmapFont();
this.batch = new SpriteBatch();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor( 0f, 0f, 0f, 1f );
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
}
...
}
And the desktop app:
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "First Game";
cfg.useGL20 = false;
cfg.width = 800;
cfg.height = 480;
new LwjglApplication(new MyGDXGame(), cfg);
}
MyGDXGame is as follows:
public class MyGDXGame extends Game {
private FPSLogger fpsLogger;
public SplashScreen getSplashScreen() {
return new SplashScreen( this );
}
#Override
public void create() {
fpsLogger = new FPSLogger();
}
#Override
public void dispose() {
super.dispose();
}
#Override
public void render() {
super.render();
setScreen(getSplashScreen());
fpsLogger.log();
}
#Override
public void resize(int width, int height) {
super.resize(width, height);
}
#Override
public void pause() {
super.pause();
}
#Override
public void resume() {
super.resume();
}
I have read so many good things about libGdx, so this issue seems baffling to me. What am I doing wrong to get such a low frame rate?
I think setScreen(getSplashScreen()); in your render() method might be the problem. Move it to the create() method of MyGDXGame.
Right now you are switching the screen in every single frame and recreate a SpriteBatch every single time which is a very heavy object (see my comment to your question).