简体   繁体   中英

LibGDX Android Preferences - How to save data to app

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

How do apps save data

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM