Reputation: 315
I've done some research and what I've found is many articles about how to store data like levels, high score, options etc. in an app (LibGDX). But none of those solutions have worked for me. I read the LibGDX description of the class Preferences and searched StackOverflow for an answer of how to use it.
All I want to do is to save the level of the app so the user doesn't have to start over when entering the app again
Preferences (LibGDX) - https://github.com/libgdx/libgdx/wiki/Preferences
But when I entered the code that libGDX showed me nothing did happen. When using Preferences is it enough to enter this code or do I need to do something else too?
// LibGDX - create()
Preferences prefs = Gdx.app.getPreferences("My Preferences");
// LibGDX - render()
prefs.putString("name", "Donald Duck");
String name = prefs.getString("name", "No name stored");
prefs.putBoolean("soundOn", true);
prefs.putInteger("highscore", 10);
// bulk update your preferences
prefs.flush();
After that is done I have to get the data that are written to the file called "My Preferences" in this case and get it from there? However, where do I find this file in Android since the LibGDX shows a path for Windows.
%UserProfile%/.prefs/My Preferences
By the way, do I need to create a file called "My Preferences" before using this class or will Preferences create the file itself?
Does the code that LibGDX provides be put in a specific order? I know that I have to create a Preferences variable first and then create values to be stored in the file. But other than that? Am I using wrong LibGDX methods to put the code into? (create(), render()).
I tried to just write the code
Preferences pref = Gdx.app.getPreferences("somefile");
pref.putString("name", "Menyo");
pref.putInteger("level", 20);
from a link just to see if it worked. But it doesn't seem like it does. Am I missing something in the code? Or is it a file I'm missing that needs to be created?
Research links:
Using LibGDX with Android Preferences
Android libgdx preferences not working
http://www.badlogicgames.com/forum/viewtopic.php?f=11&t=6365#p32981
Android libgdx preferences not working
EDIT:
I got an explanation of what I asked but I need more information. In my LibGDX app I have the method render and in that method I have menu(), game(). And I am not supposed to call the Preferences in the render method? Where should them be called since they only will be called once if they're in the create() method right?
I thought that I could call the Preferences (Prefs) methods when exiting the app to save the level data. But it didn't work, the app still goes back to level one. So I'm deciding to post my code here so that everyone can see what I'm doing wrong.
(You can use CTRL + F to find the variable "level" and "prefs" everywhere in the code)
package com.game.whatstheanswer;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputProcessor;
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.g2d.GlyphLayout;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
private SpriteBatch batch;
private GAME STATE;
private BitmapFont font;
private BitmapFont fontTextAdjustment;
private GlyphLayout layout;
private String message;
private Texture logo;
private Texture questionmarks;
private Texture keyboard;
private Texture btnReset;
private Texture btnMenu;
@Override
public void create () {
batch = new SpriteBatch();
// Configure the Android keyboard
Gdx.input.setInputProcessor(this);
isAndroidKeyboardShowing = true;
// Menu logo
logo = new Texture("logo.png");
questionmarks = new Texture("questionmarks.png");
//level = 1;
STATE = GAME.MENU;
btnPlay = new Texture("play.png");
btnInfo = new Texture("info.png");
btnQuit = new Texture("quit.png");
// The game over menu
btnMenu = new Texture("menu.png");
btnReset = new Texture("reset.png");
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("Amethysta.ttf"));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 66;
font = generator.generateFont(parameter); // font size 66 pixels
parameter.size = 28;
fontTextAdjustment = generator.generateFont(parameter);
generator.dispose(); // don't forget to dispose to avoid memory leaks!
layout = new GlyphLayout();
keyboard = new Texture("keyboard/keyboard.png");
userInput = "";
message = "";
/*
prefs = Gdx.app.getPreferences("com.game.whatstheanswer.settings");
String name = prefs.getString("Level", "0");*/
prefs = new Prefs();
}
private Prefs prefs;
//private static Preferences prefs;
//private String letter;
private String userInput;
private String answer;
private int level;
@Override
public void render () {
Gdx.gl.glClearColor(255, 255, 255, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
//STATE = GAME.OVER;
switch (STATE) {
case MENU: menu(); break;
case PLAY:
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
game();
break;
case PAUSE: break;
case RESUME: break;
case OVER: gameComplete(); break;
}
batch.end();
}
private Texture btnPlay, btnInfo, btnQuit;
private void menu() {
// Draw a colorful background
batch.draw(logo, Gdx.graphics.getWidth()/2 - logo.getWidth()/2, Gdx.graphics.getHeight() - logo.getHeight());
batch.draw(questionmarks, Gdx.graphics.getWidth()/2 - logo.getWidth()/2, Gdx.graphics.getHeight() - logo.getHeight() - 460);
batch.draw(btnPlay, Gdx.graphics.getWidth() / 2 - btnPlay.getWidth() / 2, 600);
batch.draw(btnInfo, Gdx.graphics.getWidth() / 2 - btnInfo.getWidth() / 2, 400);
batch.draw(btnQuit, Gdx.graphics.getWidth() / 2 - btnQuit.getWidth() / 2, 200);
// Play button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2) + btnPlay.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 600 - btnPlay.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 600) {
STATE = GAME.PLAY;
isAndroidKeyboardShowing = true;
System.out.println("Main Menu: Play");
}
// Info button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2) + btnPlay.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 400 - btnPlay.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 400) {
System.out.println("Main Menu: Info");
}
// Quit button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnPlay.getWidth()/2) + btnPlay.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 200 - btnPlay.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 200) {
level = prefs.getLevel();
Gdx.app.exit();
System.out.println("Main Menu: Quit");
}
}
/*
public void increaseLevel() {
prefs.putString("Level", String.valueOf(level));
prefs.flush();
Gdx.app.log("level", level+"");
}*/
private void game() {
// Set the color of the text
font.setColor(Color.WHITE);
level = prefs.getLevel();
/*
font.draw(batch, "MENU", 120, Gdx.graphics.getHeight() - 80);
font.draw(batch, "CLEAR", 420, Gdx.graphics.getHeight() - 80);
font.draw(batch, "SOLVE", 740, Gdx.graphics.getHeight() - 80);*/
font.draw(batch, "MENU", Gdx.graphics.getWidth()/7, Gdx.graphics.getHeight() - 80);
font.draw(batch, "CLEAR", (((7/2)*Gdx.graphics.getWidth()/7)), Gdx.graphics.getHeight() - 80);
font.draw(batch, "SOLVE", 5*Gdx.graphics.getWidth()/7, Gdx.graphics.getHeight() - 80);
System.out.println(userInput);
layout.setText(font, "Question " + level);
font.draw(batch, "Question " + level, Gdx.graphics.getWidth()/2 - layout.width/2, 5*Gdx.graphics.getHeight()/6);
switch (level) {
default: break;
// my cases are in here, removed them to shortned the code
case 1: break;
case 2: break;
...
case n: break;
}
// The user input
layout.setText(font, userInput);
font.draw(batch, userInput, Gdx.graphics.getWidth() / 2 - layout.width / 2, 880);
//layout.setText(font, letter = "Q");
drawnKeyboard();
inputKeyboard();
ShapeRenderer shapeRenderer = new ShapeRenderer();
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
shapeRenderer.setColor(com.badlogic.gdx.graphics.Color.WHITE);
// Horizontal line, user input above it
shapeRenderer.rectLine(110, 800, Gdx.graphics.getWidth() - 110, 800, 4); // shapeRenderer.rectLine(120, 800, 940, 800, 4);
shapeRenderer.end();
}
// Questions on one line
private void msgOneLine(String message, String answer) {
this.message = message;
this.answer = answer;
layout.setText(font, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width/2, 1200);
}
private void msgTwoLines(String msg1, String msg2, String answer) {
this.message = msg1;
this.answer = answer;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200);
message = msg2;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 50);
}
private void msgThreeLines(String msg1, String msg2, String msg3, String answer) {
this.message = msg1;
this.answer = answer;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200);
message = msg2;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 50);
message = msg3;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 120);
}
private void msgFourLines(String msg1, String msg2, String msg3, String msg4, String answer) {
this.message = msg1;
this.answer = answer;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200);
message = msg2;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 50);
message = msg3;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 120);
message = msg4;
layout.setText(fontTextAdjustment, message);
font.draw(batch, message, (Gdx.graphics.getWidth() / 2) - layout.width - 80, 1200 - layout.height - 190);
}
private void drawnKeyboard() {
batch.draw(keyboard, 440, 400);
}
private boolean isAndroidKeyboardShowing;
private void inputKeyboard() {
if (Gdx.input.justTouched()) {
System.out.println("(" + Gdx.input.getX() + ", " + Gdx.input.getY() + ")");
// ####################### BOTTOM BUTTONS #######################
// Menu button
if (Gdx.input.getX() > 76 && Gdx.input.getX() < 350 && Gdx.input.getY() > 40 && Gdx.input.getY() < 116) {
STATE = GAME.MENU;
isAndroidKeyboardShowing = false;
}
// Clear button
if (Gdx.input.getX() > 350 && Gdx.input.getX() < 676 && Gdx.input.getY() > 40 && Gdx.input.getY() < 116) {
userInput = "";
}
// Solve button
if (Gdx.input.getX() > 676 && Gdx.input.getX() < 976 && Gdx.input.getY() > 40 && Gdx.input.getY() < 116) {
System.out.println("SOLVE");
solve();
}
}
// Android keyboard
Gdx.input.setOnscreenKeyboardVisible(isAndroidKeyboardShowing);
}
// The solve algorithm
private void solve() {
if (userInput.equalsIgnoreCase(answer)) {
//level++;
prefs.increaseLevel();
userInput = "";
}
userInput = "";
}
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
// Numbers
case Input.Keys.NUM_0: userInput += '0'; break;
case Input.Keys.NUM_1: userInput += '1'; break;
case Input.Keys.NUM_2: userInput += '2'; break;
case Input.Keys.NUM_3: userInput += '3'; break;
case Input.Keys.NUM_4: userInput += '4'; break;
case Input.Keys.NUM_5: userInput += '5'; break;
case Input.Keys.NUM_6: userInput += '6'; break;
case Input.Keys.NUM_7: userInput += '7'; break;
case Input.Keys.NUM_8: userInput += '8'; break;
case Input.Keys.NUM_9: userInput += '9'; break;
// All english letters
case Input.Keys.A: userInput += 'A'; break;
case Input.Keys.B: userInput += 'B'; break;
case Input.Keys.C: userInput += 'C'; break;
case Input.Keys.D: userInput += 'D'; break;
case Input.Keys.E: userInput += 'E'; break;
case Input.Keys.F: userInput += 'F'; break;
case Input.Keys.G: userInput += 'G'; break;
case Input.Keys.H: userInput += 'H'; break;
case Input.Keys.I: userInput += 'I'; break;
case Input.Keys.J: userInput += 'J'; break;
case Input.Keys.K: userInput += 'K'; break;
case Input.Keys.L: userInput += 'L'; break;
case Input.Keys.M: userInput += 'M'; break;
case Input.Keys.N: userInput += 'N'; break;
case Input.Keys.O: userInput += 'O'; break;
case Input.Keys.P: userInput += 'P'; break;
case Input.Keys.Q: userInput += 'Q'; break;
case Input.Keys.R: userInput += 'R'; break;
case Input.Keys.S: userInput += 'S'; break;
case Input.Keys.T: userInput += 'T'; break;
case Input.Keys.U: userInput += 'U'; break;
case Input.Keys.V: userInput += 'V'; break;
case Input.Keys.W: userInput += 'W'; break;
case Input.Keys.X: userInput += 'X'; break;
case Input.Keys.Y: userInput += 'Y'; break;
case Input.Keys.Z: userInput += 'Z'; break;
// Special keys
case Input.Keys.ENTER: solve(); break;
case Input.Keys.BACKSPACE:
if (userInput.length() > 0)
userInput = userInput.substring(0, userInput.length()-1);
break;
}
return true;
}
private void gameComplete() {
font.setColor(Color.BLACK);
layout.setText(font, "Play again? Reset?");
font.draw(batch, "Play again? Reset?", Gdx.graphics.getWidth()/2 - layout.width/2, Gdx.graphics.getHeight() - 400);
batch.draw(btnMenu, Gdx.graphics.getWidth() / 2 - btnMenu.getWidth() / 2, 600);
batch.draw(btnReset, Gdx.graphics.getWidth() / 2 - btnReset.getWidth() / 2, 400);
batch.draw(btnQuit, Gdx.graphics.getWidth() / 2 - btnQuit.getWidth() / 2, 200);
// Menu button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnMenu.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnMenu.getWidth()/2) + btnMenu.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 600 - btnMenu.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 600) {
STATE = GAME.MENU;
System.out.println("Game Over Menu: Menu");
}
// Reset button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnReset.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnReset.getWidth()/2) + btnReset.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 400 - btnReset.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 400) {
level = 1;
System.out.println("Game Over Menu: Reset");
}
// Quit button
if (Gdx.input.justTouched() &&
Gdx.input.getX() >= Gdx.graphics.getWidth()/2 - btnQuit.getWidth()/2 &&
Gdx.input.getX() <= (Gdx.graphics.getWidth()/2 - btnQuit.getWidth()/2) + btnQuit.getWidth() &&
Gdx.input.getY() >= Gdx.graphics.getHeight() - 200 - btnQuit.getHeight() &&
Gdx.input.getY() <= Gdx.graphics.getHeight() - 200) {
Gdx.app.exit();
System.out.println("Game Over Menu: Quit");
}
}
// Usual input
@Override
public boolean keyUp(int keycode) {
return false;
}
@Override
public boolean keyTyped(char character) {
return false;
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
return false;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
@Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
@Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
@Override
public boolean scrolled(int amount) {
return false;
}
}
EDIT Sorry man, I have tried to implement the code you gave me but am still doing something wrong. All of my methods game(), menu(), gameComplete() are in the render() method between batch.begin() and batch.end(). I need to save the level when closing the app.
But how can I check that something actually happened so that I know that my preferences are created? Besides, if isn't recommended to call preferences in the render() method where should I call them? The create() method only calls preferences once and will therefore not save the level.
Upvotes: 3
Views: 4298
Reputation: 20140
No need to create My Preferences
, it create itself. Preference related work not should be done in render
method.
On Android, the system's [SharedPreferences][1] class is used. This means preferences will survive app updates, but are deleted when the app is uninstalled. SharedPreferences store private primitive data in key-value pairs.
public class TestGame2 extends Game {
public Prefs prefs;
@Override
public void create() {
prefs=new Prefs();
System.out.printf("Current Sound Status"+prefs.hasSound());
// I need to to change sound Status
prefs.setSound(false);
//Now sound is off
}
public void playSound(){
if(prefs.hasSound()) {
Sound sound=Gdx.audio.newSound("....");
sound.play();
}
}
public void startGame(){
//what is my previous highest saved game level, last time
int level=prefs.getLevel();
}
public void levelCompleted(){
// wow last Level completed so now i need to increase level
prefs.increaseLevel();
}
}
And Prefs is :
public class Prefs {
private Preferences pref ;
private boolean hasSound;
private int completedLevel;
public Prefs(){
pref = Gdx.app.getPreferences("My Preferences");
hasSound = pref.getBoolean("hasSound",true);
completedLevel=pref.getInteger("level",0);
}
public void setSound(boolean hasSound){
this.hasSound=hasSound;
pref.putBoolean("hasSound",hasSound);
pref.flush();
}
public boolean hasSound(){
return hasSound;
}
//should be called once when we need to increase my level
public void increaseLevel(){
completedLevel++;
pref.putInteger("level",completedLevel);
pref.flush();
}
public int getLevel(){
return completedLevel;
}
}
EDIT
Use a counter for the same.
boolean isDataSaved;
private void gameComplete() { // your method
if(!isDataSaved){
levelCompleted();
isDataSaved=true;
}
font.setColor(Color.BLACK);
layout.setText(font, "Play again? Reset?");
}
private void menu() {
isDataSaved=false;
}
And you're already using code of startGame()
in your game
method.
Upvotes: 3