I have a UI bar and a game board rectangle bellow it.
I want the UI elements to maintain their original size, regardless of screen resolution. This can be achieved via a ScreenWievport (as per suggested here), but that completely messes up my game board.
What I have right now works, but the UI elements resize with the window.
My code:
MyGame.java (entry point)
import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.utils.viewport.FitViewport;
public final class MyGame extends Game {
private int screenWidth;
private int screenHeight;
private ShapeRenderer renderer;
public MyGame(int screenWidth, int screenHeight) {
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
}
#Override
public void create() {
renderer = new ShapeRenderer();
this.setScreen(
new MyGameScreen(
// new FitViewport(screenWidth, screenHeight)
new FitViewport(640, 480),
renderer
)
);
}
}
MyGameScreen.java (main application Screen)
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.EventListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import com.badlogic.gdx.utils.viewport.Viewport;
public final class MyGameScreen extends ScreenAdapter {
/** Starting sprouts slider minimum value. */
private static final int SLIDER_MIN = 2;
/** Starting sprouts slider maximum value. */
private static final int SLIDER_MAX = 7;
/** Starting sprouts slider step value. */
private static final int SLIDER_STEP = 1;
/** The spacing between the elements in a toolbar. */
private static final int TOOLBAR_CELL_SPACING = 10;
/** The padding of the toolbar row. */
private static final int TOOLBAR_PADDING = 10;
/** The spacing between the rows of the root layout. */
private static final float ROOT_ROW_SPACING = 5;
/** The padding of the root layout. */
private static final float ROOT_PADDING = 10;
private final Viewport viewport;
private final ShapeRenderer renderer;
private Stage stage;
public MyGameScreen(Viewport viewport, ShapeRenderer renderer) {
this.viewport = viewport;
this.renderer = renderer;
}
#Override
public void show() {
stage = new Stage(viewport);
Skin skin =
new Skin(
Gdx.files.internal("default/skin/uiskin.json")
);
final Label sliderLabel = new Label(""+SLIDER_MIN, skin);
sliderLabel.setColor(Color.BLACK);
final Slider slider =
new Slider(
SLIDER_MIN,
SLIDER_MAX,
SLIDER_STEP,
false,
skin
);
slider.addListener(new EventListener() {
#Override
public boolean handle(final Event event) {
sliderLabel.setText("" + (int) slider.getValue());
return true;
}
});
slider.setValue(SLIDER_MIN);
TextButton resetButton =
new TextButton(
"New game",
skin
);
Table toolbar = new Table().pad(TOOLBAR_PADDING);
toolbar.left().add(resetButton).space(TOOLBAR_CELL_SPACING);
toolbar.add(sliderLabel).space(TOOLBAR_CELL_SPACING);
toolbar.add(slider);
toolbar.setHeight(resetButton.getPrefHeight() + 2 * TOOLBAR_PADDING);
Rectangle gameBounds =
new Rectangle(
ROOT_PADDING,
ROOT_PADDING,
stage.getViewport().getWorldWidth() - 2 * ROOT_PADDING,
stage.getViewport().getWorldHeight()
- 2 * ROOT_PADDING
- toolbar.getHeight()
- ROOT_ROW_SPACING
);
Actor gameBoard = new MyGameBoard(new MyGameState(gameBounds), renderer);
gameBoard.setBounds(
gameBounds.getX(),
gameBounds.getY(),
gameBounds.getWidth(),
gameBounds.getHeight()
);
VerticalGroup rootLayout =
new VerticalGroup()
.center()
.space(ROOT_ROW_SPACING)
.pad(ROOT_PADDING);
rootLayout.setFillParent(true);
rootLayout.grow().addActor(toolbar);
rootLayout.addActor(gameBoard);
stage.addActor(rootLayout);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.getViewport().apply();
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
}
MyGameBoard.java
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;
public final class MyGameBoard extends Actor {
private static final float BORDER_THICKNESS = 5f;
private static final Color DEFAULT_BACKGROUND_COLOR =
new Color(204 / 255f, 229 / 255f, 1, 1);
private final MyGameState gameState;
private final ShapeRenderer renderer;
public MyGameBoard(
final MyGameState gameState,
final ShapeRenderer renderer
) {
this.gameState = gameState;
this.renderer = renderer;
}
#Override
public void draw(final Batch batch, final float parentAlpha) {
batch.end();
Gdx.gl.glLineWidth(BORDER_THICKNESS);
renderer.setProjectionMatrix(batch.getProjectionMatrix());
renderer.begin(ShapeType.Line);
renderer.setColor(DEFAULT_BACKGROUND_COLOR);
renderer.rect(getX(), getY(), getWidth(), getHeight());
// draw my game board state (a single point in the middle of the board)
renderer.circle(
gameState.gameBounds.width / 2,
gameState.gameBounds.height / 2,
BORDER_THICKNESS
);
renderer.end();
batch.begin();
}
}
MyGameState.java (sample game board state)
import com.badlogic.gdx.math.Rectangle;
public class MyGameState {
public Rectangle gameBounds;
public MyGameState(Rectangle gameBounds) {
this.gameBounds = gameBounds;
}
}
I've looked at the following similar questions:
libgdx resolution/density scaling assets
Libgdx have two different views drawn with own coordinate systems?
Scale libgdx UI for mobile?
but I simply wasn't able to piece the information provided in each of those questions together into a working solution for my problem.
Related
i want to create a jump of my player that is smooth it goes up then down
its a 2D side scroller like Mario
iv'e tried wait and make jumps slow by using a lot of steps and i cant figure it out
player control class:
package com.mygdx.game;
import java.lang.Thread.State;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
#SuppressWarnings("unused")
public class PlayerController {
static int speed = 1;
public static int jump = 0;
static void keyInput() {
jump++;
if (Gdx.input.isKeyPressed(Keys.A)) {
main.playerX += speed;
main.backgroundSpriteX += speed;
}
if (Gdx.input.isKeyPressed(Keys.D)) {
main.playerX -= speed;
main.backgroundSpriteX -= speed;
}
if (Gdx.input.isKeyPressed(Keys.W)) {
//this is where i want to be able to jump
}
}
static void Controller() {
main.player = new Sprite(main.texture);
main.playerX = (main.canvisWidth * 0);
main.playerY = (main.canvisHeight * 0); //can be 0
}
}
main class:
package com.mygdx.game;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class main implements ApplicationListener {
public static final int backgroundSpriteY = 0;
public static final int backgroundSprite2Y = 0;
public static int canvisWidth = 800;
public static int canvisHeight = 480;
public static int backgroundSpriteX = 0;
public static Texture texture;
public static int backgroundSprite2X = -canvisWidth;
public static Sprite player;
public static int playerX;
public static int playerY;
static SpriteBatch spriteBatch;
static int Jumpframes = 0;
private double playerSize = .4;
public void create() {
WorldObjects.shapeRender.setAutoShapeType(true);
spriteBatch = new SpriteBatch();
texture = new Texture(Gdx.files.internal("imageedit_3_3813241913.png"));
PlayerController.Controller();
WorldSetup.start();
player.setSize((float) (player.getWidth() * playerSize), (float) (player.getHeight() * playerSize));
}
public void render() {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
PlayerController.keyInput();
WorldController.Scroll();
spriteBatch.begin();
spriteBatch.draw(WorldSetup.backgroundTexture, backgroundSpriteX, backgroundSpriteY);
spriteBatch.draw(WorldSetup.backgroundTexture2, backgroundSprite2X, backgroundSprite2Y);
spriteBatch.draw(texture, playerX, playerY, player.getWidth(), player.getHeight());
spriteBatch.end();
WorldSetup.WorldRender();
//Jumpframes++;
}
public void resize(int width, int height) {
}
public void pause() {
}
public void resume() {
}
public void dispose() {
}
}
i want to have a slow jump like a Mario sort of jump and i cant seem to create a slow/smooth jump
I’d like 20 frame jump going up 30 pixels starting fast and slowing down
I think what you need is a 2D Physics Engine for Games, The the popular one is Box2D
lots of tutorials on this, like Brent Aureli's works, to jump your character you just apply force on it like
player.b2body.applyForceToCenter(0, 80f, true);
hope this helps
You can maybe get away from recording the start jump position like this... instead of using the buttons to directly alter their position, use the buttons to alter their velocity then use their current position plus xvelocity and yvelocity to calculate next position. when they press jump they get a increased y and every frame you reduce that until they land on a platform then set it back to 0.
i'm kind of new with Java graphics, i'm trying to create a simple crossroad GUI interface with 4 traffic lights on it, when using the following classes that I have created - I get a window with a large grey rectangle on it (I assume that since I didn't allocate a traffic lights in the center it has been filled with the default grey background), how do I control the size of the center of the JFrame?
This is what i'm looking to acheive:
This is what i'm getting:
This is the JFrame class.
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 400;
private static final int HEIGHT_OF_WINDOW = 400;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
this.setVisible(true);
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(tLightW, BorderLayout.WEST);
this.add(tLightN, BorderLayout.NORTH);
this.add(tLightE, BorderLayout.EAST);
this.add(tLightS, BorderLayout.SOUTH);
}
}
This is the Jpanel class.
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 100;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
this.setSize(CAR_DIAMETER, 120);
this.setBackground(Color.BLACK);
status = 0;
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(this.getX(), this.getY(), CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(this.getX(), this.getY()+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(this.getX(), this.getY()+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
System.out.println(this.getX()+" "+this.getY());
}
}
EDIT:
Following Hovercraft Full Of Eels' advise, here are my new classes:
import java.awt.*;
import javax.swing.*;
public class CrossroadInterface extends JFrame /*implements IAppInterface*/ {
private static final int WIDTH_OF_WINDOW = 900;
private static final int HEIGHT_OF_WINDOW = 900;
//Panels
TrafficLight tLightW, tLightC, tLightE, tLightS, tLightN;
//Other
public CrossroadInterface() {
super("My Crossroad");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW);
setLayout(new GridLayout(3,3));
createInterface();
}
public void createInterface () {
tLightW = new TrafficLight();
tLightE = new TrafficLight();
tLightS = new TrafficLight();
tLightN = new TrafficLight();
this.add(new JPanel());
this.add(tLightW);
this.add(new JPanel());
this.add(tLightN);
this.add(new JPanel());
this.add(tLightE);
this.add(new JPanel());
this.add(tLightS);
this.setVisible(true);
}
}
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TrafficLight extends JPanel {
private final Color offRed = new Color(128, 0, 0);
private final Color offGreen = new Color(0, 96, 0);
private static final int CAR_DIAMETER = 50;
private static final int PERSON_HEIGHT = 50;
private static final int PERSON_WIDTH = 50;
private int status;
public TrafficLight() {
super();
status = 0;
this.setPreferredSize(new Dimension(CAR_DIAMETER,2*CAR_DIAMETER+2*PERSON_HEIGHT));
this.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(offRed);
g.fillOval(100, 50, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offGreen);
g.fillOval(100, 50+CAR_DIAMETER, CAR_DIAMETER, CAR_DIAMETER);
g.setColor(offRed);
g.fillRect(100, 50+CAR_DIAMETER+PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
g.setColor(offGreen);
g.fillRect(100, 50+CAR_DIAMETER+2*PERSON_HEIGHT, PERSON_WIDTH, PERSON_HEIGHT);
//drawIlluminatedLights(g);
}
}
Your problem is not that the center of the JFrame is too large, but rather it's because the size of your surrounding JPanels are too small. Understand that most Swing layout managers respect a components preferred size, and use this to set the size of the component. Your other problems include
using getX() and getY() to place your drawings. These values give the location of the JPanel within its container, but that won't help you place your drawing since when you draw within the JPanel the drawing's location is placed relative to the location of the pixel within the JPanel not its container, so using these methods will mess you up.
Calling the JFrame's setVisible(true) before adding all components. This risks not displaying all components.
Making your TrafficLight class extend JPanel. You're far better off using a single JPanel to do all the drawing and have your TrafficLight class not extend from any Swing component but rather be a logical class. Give it a public void draw(Graphics2D g2) method that you can call within the drawing JPanel's paintComponent method.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class CrossRoads2 extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 100;
List<TrafficLight2> lights = new ArrayList<>();
public CrossRoads2() {
// create a timer to randomly change traffic light state
// and start it
new Timer(TIMER_DELAY, new TimerListener()).start();
// create 4 TrafficLight2 objects and place them at 4
// compass locations, and add to lights ArrayList
int x = (PREF_W - TrafficLight2.getWidth()) / 2;
int y = 0;
lights.add(new TrafficLight2(x, y));
x = 0;
y = (PREF_H - TrafficLight2.getHeight()) / 2;
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth());
lights.add(new TrafficLight2(x, y));
x = (PREF_W - TrafficLight2.getWidth()) / 2;
y = (PREF_H - TrafficLight2.getHeight());
lights.add(new TrafficLight2(x, y));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// cast g into a Graphics2 object
Graphics2D g2 = (Graphics2D) g;
// for smooth rendering
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the ArrayList, calling the draw method on each light
for (TrafficLight2 light : lights) {
light.draw(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
// give our JPanel a decent size
return new Dimension(PREF_W, PREF_H);
}
// ActionListener that randomly changes the LightState of each traffic light
private class TimerListener implements ActionListener {
private Random random = new Random();
#Override
public void actionPerformed(ActionEvent e) {
for (TrafficLight2 light : lights) {
// random number 0 to 2
int randomIndex = random.nextInt(LightState.values().length);
// get one of the LightStates using the index above
LightState lightState = LightState.values()[randomIndex];
// set our light to this state
light.setLightState(lightState);
}
repaint();
}
}
private static void createAndShowGui() {
CrossRoads2 mainPanel = new CrossRoads2();
JFrame frame = new JFrame("Cross Roads");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class TrafficLight2 {
private static final int ELLIPSE_W = 40;
private static final int GAP = 4;
private int x;
private int y;
private LightState lightState = LightState.RED; // what color is bright
// map to hold our 3 ellipses, each one corresponding to a LightState
private Map<LightState, Shape> lightMap = new EnumMap<>(LightState.class);
public TrafficLight2(int x, int y) {
// create 3 ellipses, one each for RED, YELLOW, GREEN
// place each one below the previous
// associate each one with one of our RED, YELLOW, or GREEN LightStates
// putting the Ellipse into the map with the light state as key
this.x = x;
this.y = y;
int tempX = x + GAP;
int tempY = y + GAP;
lightMap.put(LightState.RED, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.YELLOW, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
tempY += ELLIPSE_W + GAP;
lightMap.put(LightState.GREEN, new Ellipse2D.Double(tempX, tempY, ELLIPSE_W, ELLIPSE_W));
}
// called by JPanel's paintComponent
public void draw(Graphics2D g2) {
// iterate through the 3 LightStates
for (LightState ltSt : LightState.values()) {
// if the ltSt in the for loop is this traffic light's LightState
// then the display color should be bright
Color c = ltSt == lightState ? ltSt.getColor() :
// other wise the display color should be very dark
ltSt.getColor().darker().darker().darker();
g2.setColor(c);
g2.fill(lightMap.get(ltSt)); // fill the oval with color
g2.setColor(Color.BLACK);
g2.draw(lightMap.get(ltSt)); // draw a black border
}
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public LightState getLightState() {
return lightState;
}
public void setLightState(LightState lightState) {
this.lightState = lightState;
}
// static method for the width of our traffic lights
public static int getWidth() {
return 2 * GAP + ELLIPSE_W;
}
// static method for the height of our traffic lights
public static int getHeight() {
return 4 * GAP + 3 * ELLIPSE_W;
}
}
// enum that encapsulates the 3 possible states of the traffic light
enum LightState {
RED("Red", Color.RED), YELLOW("Yellow", Color.YELLOW), GREEN("Green", Color.GREEN);
private LightState(String text, Color color) {
this.text = text;
this.color = color;
}
private String text;
private Color color;
public String getText() {
return text;
}
public Color getColor() {
return color;
}
}
So I am programming a game, and implemented preferences to save/load some simple data for sound/high score, nothing too much. The problem is that when I load preferences for the first time on a physical phone (samsung g. s3) it takes about 15sec and on s5 it takes about 5-8secs and in that time it shows black screen even though I have set the loading screen to be shown first, before preferences are used.
In my main application code I set screen to loadingScreen
loadingScreen = new com.package.game.Screens.LoadingScreen(this);
mainScreen = new com.package.game.Screens.MainScreen(this);
gameScreen = new com.package.game.Screens.GameScreen(this);
settingsScreen = new com.package.game.Screens.SettingsScreen(this);
this.setScreen(loadingScreen);
and then in my loading screen I access preferences:
#Override
public void show() {
this.progress_assets = 0f;
this.progress_assets = 0f;
font_loading = app.initFont(50, 1, 255, 255, 255, 1);
queueAssets();
loading_db();
}
private void loading_db() {
if(database.get_first_time()){
//do some introduction for first time run
Gdx.app.log("db","first time worked");
database.set_first_time();
progress_db=1f;
}else{
Gdx.app.log("db","first time is set false");
progress_db=1f;
}
}
and my preferences class:
package com.package.game.Engine;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.package.game.Application;
public class Database {
private Preferences preferences;
//database
private Preferences pref_settings;
private Preferences pref_score;
private Preferences pref_unlocks;
public Database(){
pref_settings = Gdx.app.getPreferences("com.package.game.settings");
pref_score = Gdx.app.getPreferences("com.package.game.score");
pref_unlocks = Gdx.app.getPreferences("com.package.game.unlocks");
}
//checking if first time run
public boolean get_first_time(){
return pref_settings.getBoolean("First_run",true);
}
public void set_first_time(){
pref_settings.putBoolean("First_run",false);
for(int i=0;i<7;i++){
pref_unlocks.putBoolean("unlock_"+i,true);
}
pref_unlocks.flush();
pref_settings.flush();
}
//settings
public boolean getSound(){
return pref_settings.getBoolean("sound_on",true);
}
public void setSound(boolean sound){
pref_settings.putBoolean("sound_on",sound);
pref_settings.flush();
}
//High Score Mode:
//Classic
public int getScore_classic(int place){
return pref_score.getInteger("score_classic_"+place,0);//default value 0 so we could place new score if we havent reached it
}
public void setScore_classic(int place,int scored){
pref_score.putInteger("score_classic_"+place,scored);
pref_score.flush();
}
//Recipe
public int getScoreRec(int place){
return pref_score.getInteger("score_recipe_"+place,0);//default value 0 so we could place new score if we havent reached it
}
public void setScoreRec(int place,int scored){
pref_score.putInteger("score_recipe_"+place,scored);
pref_score.flush();
}
//food unlocks
public boolean getFoodUnlock(int unlockID){
return pref_unlocks.getBoolean("unlock_"+unlockID,false);
}
public void setFoodUnlock(int unlockID,boolean state){
pref_unlocks.putBoolean("unlock_"+unlockID,state);
pref_unlocks.flush();
}
}
I'm not sure if creating 3 pref. files is good, but i would like to know how to show screen before pref. file being created/loaded.
Edit: Adding my main code
package com.mindutis.game;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.mindutis.game.Screens.LoadingScreen;
public class Application extends Game {
public static int V_WIDTH ;
public static int V_HEIGHT;
public OrthographicCamera camera;
public SpriteBatch batch;
public AssetManager assets;
public com.mindutis.game.Screens.LoadingScreen loadingScreen;
public com.mindutis.game.Screens.SplashScreen splashScreen;
public com.mindutis.game.Screens.MainScreen mainScreen;
public com.mindutis.game.Screens.GameScreen gameScreen;
public com.mindutis.game.Screens.SettingsScreen settingsScreen;
public BitmapFont f_game_name;
public String t_game_name;
public String[] font_type = new String[2];
private boolean firstFrame = true;
private boolean loading = true;
#Override
public void create() {
assets = new AssetManager();
camera = new OrthographicCamera();
V_WIDTH= Gdx.graphics.getWidth();
V_HEIGHT = Gdx.graphics.getHeight();
camera.setToOrtho(false, V_WIDTH, V_HEIGHT);
batch = new SpriteBatch();
//Global var.
//font initializer
font_type[0] = "fonts/comics_bold.ttf";
font_type[1] = "fonts/vdj.ttf";
//game name
t_game_name = " Catch a\nSandwich";
f_game_name = initFont(70, 0, 255, 255, 255, 1);
loadingScreen = new com.mindutis.game.Screens.LoadingScreen(this);
splashScreen = new com.mindutis.game.Screens.SplashScreen(this);
mainScreen = new com.mindutis.game.Screens.MainScreen(this);
gameScreen = new com.mindutis.game.Screens.GameScreen(this);
settingsScreen = new com.mindutis.game.Screens.SettingsScreen(this);
// this.setScreen(loadingScreen);
}
//int size=font size, int x = font type
public BitmapFont initFont(int size, int type, float red, float green, float blue, float alpha) {
BitmapFont font;
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal(font_type[type]));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = size;
font = generator.generateFont(parameter);
font.setColor(red / 255f, green / 255f, blue / 255f, alpha);
return font;
}
#Override
public void render() {
if (firstFrame){
Gdx.gl.glClearColor(.2f, .67f, .88f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
f_game_name.draw(batch, t_game_name, camera.viewportWidth / 2 - 150, camera.viewportHeight - 100);
batch.end();
firstFrame = false;
} else {
if(loading){
loading=false;
loadEverything();
}
// Notice you don't actually render anything so what you previously drew stays on the screen.
}
super.render();
}
private void loadEverything(){
// load your font, assets, prefs, etc.
this.setScreen(loadingScreen);
// setScreen(loadingScreen);
}
#Override
public void dispose() {
batch.dispose();
assets.dispose();
f_game_name.dispose();
loadingScreen.dispose();
splashScreen.dispose();
mainScreen.dispose();
gameScreen.dispose();
settingsScreen.dispose();
}
}
Loading screen class:
package com.mindutis.game.Screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.FitViewport;
import com.mindutis.game.Application;
import com.mindutis.game.Engine.Database;
public class LoadingScreen implements Screen {
private final Application app;
private Database database;
private BitmapFont font_loading;
private boolean load;
private ShapeRenderer shapeRenderer;
private Stage stage;
private String t_loading;
private float progress_assets,progress_db;
private boolean firstFrame = true;
private boolean loading = true;
public LoadingScreen(Application app) {
this.app = app;
database = new Database();
this.stage = new Stage(new FitViewport(com.mindutis.game.Application.V_WIDTH, com.mindutis.game.Application.V_HEIGHT, app.camera));
this.shapeRenderer = new ShapeRenderer();
t_loading = "Loading...";
load=false;
}
private void queueAssets() {
//good food
//bread
app.assets.load("img/Food/breadtop.png", Texture.class);
app.assets.load("img/Food/breadbot.png", Texture.class);
//rest of the food
app.assets.load("img/Food/food_sheet.png", Texture.class);
//misc
app.assets.load("img/Misc/coin.png", Texture.class);
app.assets.load("img/Misc/soon.png", Texture.class);
app.assets.load("img/Misc/shoptriangle.png", Texture.class);
//buttons
app.assets.load("img/Buttons/button.png", Texture.class);
app.assets.load("img/Buttons/soundBT.png", Texture.class);
app.assets.load("img/Buttons/btX.png", Texture.class);
app.assets.load("img/Buttons/btShop1.png", Texture.class);
//human
app.assets.load("img/Human/human.png", Texture.class);
app.assets.load("img/Human/human1.png", Texture.class);
app.assets.load("img/Human/human2.png", Texture.class);
}
#Override
public void show() {
this.progress_assets = 0f;
this.progress_assets = 0f;
font_loading = app.initFont(50, 1, 255, 255, 255, 1);
queueAssets();
loading_db();
}
private void loading_db() {
if(database.get_first_time()){
//do some introduction for first time run
Gdx.app.log("db","first time worked");
database.set_first_time();
progress_db=1f;
}else{
Gdx.app.log("db","first time is set false");
progress_db=1f;
}
}
#Override
public void render(float delta) {
if (firstFrame){
Gdx.gl.glClearColor(.2f, .67f, .88f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
app.batch.begin();
app.f_game_name.draw(app.batch, app.t_game_name, app.camera.viewportWidth / 2 - 150, app.camera.viewportHeight - 100);
app.batch.end();
firstFrame = false;
} else {
if(loading){
loading=false;}
//input
Gdx.input.setCatchBackKey(true);
Gdx.input.setInputProcessor(new InputAdapter() {
#Override
public boolean keyDown(int keycode) {
if (keycode == Input.Keys.BACK) {
// Do nothing
}
return false;
}
});
Gdx.gl.glClearColor(.2f, .67f, .88f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
update(delta);
app.batch.begin();
font_loading.draw(app.batch, t_loading, app.camera.viewportWidth / 2 - 150, 123);
app.f_game_name.draw(app.batch, app.t_game_name, app.camera.viewportWidth / 2 - 150, app.camera.viewportHeight - 100);
app.batch.end();
shapeRenderer.begin(ShapeRenderer.ShapeType.Filled);
shapeRenderer.setColor(Color.WHITE);
shapeRenderer.rect(32, 50, app.camera.viewportWidth - 64, 16);
shapeRenderer.setColor(Color.GREEN);
shapeRenderer.rect(32, 50, (progress_assets+progress_db)/2 * (app.camera.viewportWidth - 64), 16);
shapeRenderer.end();
}
// Notice you don't actually render anything so what you previously drew stays on the screen.
}
private void update(float delta) {
progress_assets = MathUtils.lerp(progress_assets, app.assets.getProgress(), .1f);
if (app.assets.update() && (progress_assets+progress_db)/2 >= app.assets.getProgress() - .01f) {
app.setScreen(app.mainScreen);
}
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, false);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
shapeRenderer.dispose();
font_loading.dispose();
stage.dispose();
}
}
You can quickly load a single texture to show while everything else loads. Load and show the texture the first time render() is called. Then the second time render() is called, load your stuff, dispose the texture, and switch screens.
public class LoadingScreen extends Screen {
private boolean firstFrame = true;
private Texture texture;
private ExtendViewport viewport;
private MyGame game;
private SpriteBatch batch;
private static final float LOAD_IMAGE_WIDTH = 480, LOAD_IMAGE_HEIGHT = 600;
// Use whatever the loading image dimensions are.
public LoadingScreen (MyGame game, SpriteBatch batch){
this.game = game;
this.batch = batch;
viewport = new ExtendViewport(LOAD_IMAGE_WIDTH, LOAD_IMAGE_HEIGHT);
}
public void resize (int width, int height) {
viewport.update(width, height, false);
viewport.getCamera().position.set(LOAD_IMAGE_WIDTH / 2, LOAD_IMAGE_HEIGHT / 2, 0);
viewport.getCamera().update();
}
public void show (){} // do nothing
public void render (float delta) {
if (firstFrame){
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
viewport.apply();
batch.setProjectionMatrix(viewport.getCamera().combined);
batch.setBlendingDisabled(true);
batch.begin();
batch.draw(texture, 0, 0, LOAD_IMAGE_WIDTH, LOAD_IMAGE_HEIGHT);
batch.end();
firstFrame = false;
} else {
loadEverything();
// Notice you don't actually render anything so what you previously drew stays on the screen.
}
}
private void loadEverything(){
// load your font, assets, prefs, etc.
texture.dispose();
main.setGameScreen();
}
}
I added in a custom font Aller_light.ttf and when I try to run the program with the font drawn with no errors the screen is black, so I'm not sure if its rendering somewhere else off screen or its not rendering at all.
If you could give your 2 cents that would be of great value to me.
EDIT
Now it's saying i have a nullPointerExeption
Exception in thread "LWJGL Application" java.lang.NullPointerException
at com.mygdx.finis.states.MenuState.draw(MenuState.java:43)
at com.mygdx.finis.states.GameStateManager.draw(GameStateManager.java:31)
at com.mygdx.finis.Main.render(Main.java:36)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication.mainLoop(LwjglApplication.java:215)
at com.badlogic.gdx.backends.lwjgl.LwjglApplication$1.run(LwjglApplication.java:120)
This is what should be happening:
MenuState Class
package com.mygdx.finis.states;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.mygdx.finis.Assets;
import com.mygdx.finis.Main;
public class MenuState extends GameState{
private SpriteBatch sb;
private BitmapFont font;
private int currentItem;
private String[] menuItems;
protected MenuState(GameStateManager gsm) {
super(gsm);
}
public void init() {
sb = new SpriteBatch();
FreeTypeFontGenerator gen = new FreeTypeFontGenerator(
Gdx.files.internal(null));
font = gen.generateFont(20);
menuItems = new String[]{
"Play"
};
}
public void update(float dt) {
handleInput();
}
public void draw() {
sb.setProjectionMatrix(Main.cam.combined);//*** This is where the null pointer starts *********
sb.begin();
font.draw(sb, "Quack", Main.WIDTH / 2, Main.HEIGHT / 2);//*** This does not work *******************
sb.end();
}
public void handleInput() {
}
public void dispose() {
}
}
Main Class
package com.mygdx.finis;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL30;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.mygdx.finis.states.GameStateManager;
public class Main implements ApplicationListener{
public static int WIDTH;
public static int HEIGHT;
public static OrthographicCamera cam;
private GameStateManager gsm;
public void create(){
WIDTH = Gdx.graphics.getWidth();
HEIGHT = Gdx.graphics.getHeight();
cam = new OrthographicCamera(WIDTH, HEIGHT);
cam.translate(WIDTH / 2, HEIGHT / 2);
cam.update();
gsm = new GameStateManager();
}
public void render(){
Gdx.gl.glClearColor(0.1f, 0.1f, 0.1f, 1);
Gdx.gl.glClear(GL30.GL_COLOR_BUFFER_BIT);
gsm.update(Gdx.graphics.getDeltaTime());
gsm.draw();//****** THIS IS WHERE THE ISSUE ENDS ******
}
public void resize(int width, int height){}
public void pause(){}
public void resume(){}
public void dispose(){}
}
It seems most likely that your SpriteBatch sb is null. Are you absolutely sure init() is being called?
The only other alternative is that Main.cam is null.
It has to be one of those.
Incidentally, this looks odd to me...
Gdx.files.internal(null));
Why are you doing that?
I recently wanted to create an animated background in JavaFX, similar to the Swing example seen here. I used a Canvas on which to draw, as shown in Working with the Canvas API, and an AnimationTimer for the drawing loop, as shown in Animation Basics. Unfortunately, I'm not sure how to resize the Canvas automatically as the enclosing Stage is resized. What is a good approach?
A similar question is examined in How to make canvas Resizable in javaFX?, but the accepted answer there lacks the binding illustrated in the accepted answer here.
In the example below, the static nested class CanvasPane wraps an instance of Canvas in a Pane and overrides layoutChildren() to make the canvas dimensions match the enclosing Pane. Note that Canvas returns false from isResizable(), so "the parent cannot resize it during layout," and Pane "does not perform layout beyond resizing resizable children to their preferred sizes." The width and height used to construct the canvas become its initial size. A similar approach is used in the Ensemble particle simulation, FireworksApp, to scale a background image while retaining its aspect ratio.
As an aside, note the difference from using fully saturated colors compared to the original. These related examples illustrate placing controls atop the animated background.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/a/31761362/230513
* #see https://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
CanvasPane canvasPane = new CanvasPane(WIDTH, HEIGHT);
canvas = canvasPane.getCanvas();
BorderPane root = new BorderPane(canvasPane);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
private static class CanvasPane extends Pane {
private final Canvas canvas;
public CanvasPane(double width, double height) {
canvas = new Canvas(width, height);
getChildren().add(canvas);
}
public Canvas getCanvas() {
return canvas;
}
#Override
protected void layoutChildren() {
super.layoutChildren();
final double x = snappedLeftInset();
final double y = snappedTopInset();
// Java 9 - snapSize is deprecated, use snapSizeX() and snapSizeY() accordingly
final double w = snapSize(getWidth()) - x - snappedRightInset();
final double h = snapSize(getHeight()) - y - snappedBottomInset();
canvas.setLayoutX(x);
canvas.setLayoutY(y);
canvas.setWidth(w);
canvas.setHeight(h);
}
}
public static void main(String[] args) {
launch(args);
}
}
I combined both prior solutions ( #trashgod and #clataq's ) by putting the canvas in a Pane and binding it to it:
private static class CanvasPane extends Pane {
final Canvas canvas;
CanvasPane(double width, double height) {
setWidth(width);
setHeight(height);
canvas = new Canvas(width, height);
getChildren().add(canvas);
canvas.widthProperty().bind(this.widthProperty());
canvas.heightProperty().bind(this.heightProperty());
}
}
Couldn't you do this with a Binding as well? The following seems to produce the same results without having to add the derived class.
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.CheckBox;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
/**
* #see http://stackoverflow.com/a/31761362/230513
* #see http://stackoverflow.com/a/8616169/230513
*/
public class Baubles extends Application {
private static final int MAX = 64;
private static final double WIDTH = 640;
private static final double HEIGHT = 480;
private static final Random RND = new Random();
private final Queue<Bauble> queue = new LinkedList<>();
private Canvas canvas;
#Override
public void start(Stage stage) {
canvas = new Canvas(WIDTH, HEIGHT);
BorderPane root = new BorderPane(canvas);
CheckBox cb = new CheckBox("Animate");
cb.setSelected(true);
root.setBottom(cb);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
// Create bindings for resizing.
DoubleBinding heightBinding = root.heightProperty()
.subtract(root.bottomProperty().getValue().getBoundsInParent().getHeight());
canvas.widthProperty().bind(root.widthProperty());
canvas.heightProperty().bind(heightBinding);
for (int i = 0; i < MAX; i++) {
queue.add(randomBauble());
}
AnimationTimer loop = new AnimationTimer() {
#Override
public void handle(long now) {
GraphicsContext g = canvas.getGraphicsContext2D();
g.setFill(Color.BLACK);
g.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
for (Bauble b : queue) {
g.setFill(b.c);
g.fillOval(b.x, b.y, b.d, b.d);
}
queue.add(randomBauble());
queue.remove();
}
};
loop.start();
cb.selectedProperty().addListener((Observable o) -> {
if (cb.isSelected()) {
loop.start();
} else {
loop.stop();
}
});
}
private static class Bauble {
private final double x, y, d;
private final Color c;
public Bauble(double x, double y, double r, Color c) {
this.x = x - r;
this.y = y - r;
this.d = 2 * r;
this.c = c;
}
}
private Bauble randomBauble() {
double x = RND.nextDouble() * canvas.getWidth();
double y = RND.nextDouble() * canvas.getHeight();
double r = RND.nextDouble() * MAX + MAX / 2;
Color c = Color.hsb(RND.nextDouble() * 360, 1, 1, 0.75);
return new Bauble(x, y, r, c);
}
public static void main(String[] args) {
launch(args);
}
}