Search code examples
javaandroidsharedpreferencesgame-loop

Where should I save data using Shared Preferences in the game loop in Android?


Well trying to program a simple game in Android, I cam across the problem of saving data. I went the route of using simple Key Value pairs using Shared Preferences, mostly because I'm only saving 10 - 15 simple Integers. But I cant save it in the main Activity. because I cant seem to call it on Surface Created (In the Game Panel). Also I tried having the function of saving data in the Game Panel but it cant be created in a static method. Which the Game Panel is. So it seems I don't know where to put it.

Here is the GamePanel code:

public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {

    private MainThread thread;
    private UserObject player = new UserObject();
    private MainActivity activity = new MainActivity();
    private loadImage background;
    private loadImage house;
    private loadImage constructionWorker;
    private loadImage houseUpgrade;
    private loadImage bob;
    private loadImage workGroup;
    private loadImage bank;
    private loadImage factory;
    private loadImage superTapping;
    private loadImage unlockStore;
    private loadImage store;
    int screenWidth = this.getResources().getDisplayMetrics().widthPixels;
    int screenHeight = this.getResources().getDisplayMetrics().heightPixels;

    Bitmap buttonVars = BitmapFactory.decodeResource(getResources(), R.drawable.house_upgrade);
    int buttonWidth = buttonVars.getWidth();
    int buttonHeight = buttonVars.getHeight();

    Bitmap houseVars = BitmapFactory.decodeResource(getResources(), R.drawable.house_upgrade);
    int houseWidth = houseVars.getWidth();
    int houseHeight = houseVars.getHeight();

    public GamePanel(Context context) {
        super(context);

        //add the callback to the surface holder to intercept events
        getHolder().addCallback(this);

        //make gamePanel focusable so it can handle events
        setFocusable(true);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        activity.saveData();
        boolean retry = true;
        while (retry) {
            try {
                thread.setRunning(false);
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        //we can safely start the game loop
        activity.getData()
        thread = new MainThread(getHolder(), this);
        thread.setRunning(true);
        thread.start();

        //Create Bitmaps
        background = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.blue_background), 0, 0);
        house = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.crap_shack), ((screenWidth / 2) - houseWidth), (houseHeight * 2));
        houseUpgrade = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.house_upgrade), 0, (screenHeight - buttonHeight));
        constructionWorker = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.construction_worker), ((screenWidth / 2) - (buttonWidth / 2)), (screenHeight - buttonHeight));
        bob = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.bob), (screenWidth - buttonWidth), (screenHeight - buttonHeight));
        workGroup = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.work_group), 0, (screenHeight - (buttonHeight * 3)));
        bank = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.bank), ((screenWidth / 2) - (buttonWidth / 2)), (screenHeight - (buttonHeight * 3)));
        factory = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.factory), (screenWidth - buttonWidth), (screenHeight - (buttonHeight * 3)));
        superTapping = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.super_tapping), 0, (screenHeight - (buttonHeight * 5)));
        unlockStore = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.unlock_store), ((screenWidth / 2) - (buttonWidth / 2)), (screenHeight - (buttonHeight * 5)));
        store = new loadImage(BitmapFactory.decodeResource(getResources(), R.drawable.store), (screenWidth - buttonWidth), (screenHeight - (buttonHeight * 5)));

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            //handle house
            house.handleActionDown((int) event.getX(), (int) event.getY());
            if (house.touched) {
                player.money += player.MPC;
                house.setTouched(false);
            }

            //handle houseUpgrade
            houseUpgrade.handleActionDown((int) event.getX(), (int) event.getY());
            if (houseUpgrade.touched && player.money >= player.houseUpgradeCost) {
                player.money -= player.houseUpgradeCost;
                player.MPC += 1;
                player.houseUpgradeCost += 250;
                houseUpgrade.setTouched(false);
            }

            //handle ConstructionWorker
            constructionWorker.handleActionDown((int) event.getX(), (int) event.getY());
            if (constructionWorker.touched && player.money >= player.constructionWorkerCost) {
                player.money -= player.constructionWorkerCost;
                player.MPS += 1;
                player.constructionWorkerCost += 25;
                constructionWorker.setTouched(false);
            }

            //handle bob
            bob.handleActionDown((int) event.getX(), (int) event.getY());
            if (bob.touched && player.money >= player.bobCost) {
                player.money -= player.bobCost;
                player.MPS += 5;
                player.bobCost += 200;
                bob.setTouched(false);
            }

            //handle workGroup
            workGroup.handleActionDown((int) event.getX(), (int) event.getY());
            if (workGroup.touched && player.money >= player.workGroupCost) {
                player.money -= player.workGroupCost;
                player.MPS += 50;
                player.workGroupCost += 1250;
                workGroup.setTouched(false);
            }

            //handle bank
            bank.handleActionDown((int) event.getX(), (int) event.getY());
            if (bank.touched && player.money >= player.bankCost) {
                player.money -= player.bankCost;
                player.MPS += 250;
                player.bankCost += 5000;
                bank.setTouched(false);
            }

            //handle Factory
            factory.handleActionDown((int) event.getX(), (int) event.getY());
            if (factory.touched && player.money >= player.factoryCost) {
                player.money -= player.factoryCost;
                player.MPS += 1000;
                player.factoryCost += 25000;
                factory.setTouched(false);
            }

            //handle superTapping
            superTapping.handleActionDown((int) event.getX(), (int) event.getY());
            if (superTapping.touched && player.money >= player.superTappingCost) {
                player.money -= player.superTappingCost;
                player.MPC += 25;
                player.superTappingCost += 7500;
                superTapping.setTouched(false);
            }

            //handle unlockStore
            unlockStore.handleActionDown((int) event.getX(), (int) event.getY());
            if (unlockStore.touched && player.money >= player.unlockStoreCost && !player.storeUnlocked) {
                player.money -= player.unlockStoreCost;
                player.storeUnlocked = true;
                unlockStore.setTouched(false);
            }

            //handle store
            store.handleActionDown((int) event.getX(), (int) event.getY());
            if (store.touched && player.storeUnlocked) {

            }
        }

        if (event.getAction() == MotionEvent.ACTION_UP) {
            house.setTouched(false);
            houseUpgrade.setTouched(false);
            constructionWorker.setTouched(false);
            bob.setTouched(false);
            workGroup.setTouched(false);
            bank.setTouched(false);
            factory.setTouched(false);
            superTapping.setTouched(false);
            unlockStore.setTouched(false);
        }

        return super.onTouchEvent(event);
    }

    public void draw(Canvas canvas) {

        //Draws to canvas
        background.draw(canvas);
        house.draw(canvas);
        houseUpgrade.draw(canvas);
        constructionWorker.draw(canvas);
        bob.draw(canvas);
        workGroup.draw(canvas);
        bank.draw(canvas);
        factory.draw(canvas);
        superTapping.draw(canvas);
        unlockStore.draw(canvas);
        if (player.storeUnlocked) {
            store.draw(canvas);
        }

        writeText(canvas, "Money: $" + player.money, Paint.Align.CENTER, Color.BLACK, 25, (screenWidth / 2), 50);
        writeText(canvas, "MPS: " + player.MPS, Paint.Align.CENTER, Color.BLACK, 25, (screenWidth / 2), 75);
        writeText(canvas, "MPC: " + player.MPC, Paint.Align.CENTER, Color.BLACK, 25, (screenWidth / 2), 100);
        writeText(canvas, "Cost: $" + player.houseUpgradeCost, null, Color.BLACK, 15, 0, (screenHeight - buttonHeight));
        writeText(canvas, "Cost: $" + player.constructionWorkerCost, Paint.Align.CENTER, Color.BLACK, 15, (screenWidth / 2), (screenHeight - buttonHeight));
        writeText(canvas, "Cost: $" + player.bobCost, Paint.Align.LEFT, Color.BLACK, 15, screenWidth, (screenHeight - buttonHeight));
        writeText(canvas, "Cost: $" + player.workGroupCost, null, Color.BLACK, 15, 0, ((screenHeight - (buttonHeight * 3))));
        writeText(canvas, "Cost: $" + player.bankCost, Paint.Align.CENTER, Color.BLACK, 15, (screenWidth / 2), ((screenHeight - (buttonHeight * 3))));
        writeText(canvas, "Cost: $" + player.factoryCost, Paint.Align.LEFT, Color.BLACK, 15, (screenWidth), ((screenHeight - (buttonHeight * 3))));
        writeText(canvas, "Cost: $" + player.superTappingCost, null, Color.BLACK, 15, 0, ((screenHeight - (buttonHeight * 5))));
        writeText(canvas, "Cost: $" + player.unlockStoreCost, Paint.Align.CENTER, Color.BLACK, 15, (screenWidth / 2), ((screenHeight - (buttonHeight * 5))));
    }

    private void writeText(Canvas canvas, String text, Paint.Align align, int color, int textSize, int x, int y) {

        //Sets default align if null
        if (align == null) {
            align = Paint.Align.RIGHT;
        }

        //Sets paint using values
        Paint paint = new Paint();
        paint.setTextAlign(align);
        paint.setColor(color);
        paint.setTextSize(textSize);

        canvas.drawText(text, x, y, paint);

    }
}

Here is the actual Main Activity where I'm trying to save the Data.

public class MainActivity extends Activity {

    private UserObject player = new UserObject();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Sets window settings FULLSCREEN, NO TITLE, NO ACTION BAR.
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(new GamePanel(this));
    }

    public void saveData() {
        SharedPreferences sharedPreferences = getSharedPreferences("houseTapper_dataSaveFile", MODE_PRIVATE);
        SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
        sharedPreferencesEditor.putInt("money", player.money);
        sharedPreferencesEditor.putInt("MPS", player.MPS);
        sharedPreferencesEditor.putInt("MPC", player.MPC);
        sharedPreferencesEditor.putInt("houseUpgradeCost", player.houseUpgradeCost);
        sharedPreferencesEditor.putInt("constructionWorkerCost", player.constructionWorkerCost);
        sharedPreferencesEditor.putInt("bobCost", player.bobCost);
        sharedPreferencesEditor.putInt("workGroupCost", player.workGroupCost);
        sharedPreferencesEditor.putInt("bankCost", player.bankCost);
        sharedPreferencesEditor.putInt("factoryCost", player.factoryCost);
        sharedPreferencesEditor.putInt("superTappingCost", player.superTappingCost);
        sharedPreferencesEditor.putBoolean("storeUnlocked", player.storeUnlocked);
        sharedPreferencesEditor.apply();
    }

    public void getData() {
        SharedPreferences sharedPreferences = getSharedPreferences("houseTapper_dataSaveFile", MODE_PRIVATE);
        player.money = sharedPreferences.getInt("money", 0);
        player.MPS = sharedPreferences.getInt("MPS", 0);
        player.MPC = sharedPreferences.getInt("MPC", 0);
        player.houseUpgradeCost = sharedPreferences.getInt("houseUpgradeCost", 0);
        player.constructionWorkerCost = sharedPreferences.getInt("constructionWorkerCost", 0);
        player.bobCost = sharedPreferences.getInt("bobCost", 0);
        player.workGroupCost = sharedPreferences.getInt("workGroupCost", 0);
        player.bankCost = sharedPreferences.getInt("bankCost", 0);
        player.factoryCost = sharedPreferences.getInt("factoryCost", 0);
        player.superTappingCost = sharedPreferences.getInt("superTappingCost", 0);
        player.storeUnlocked = sharedPreferences.getBoolean("storeUnlocked", false);
    }
}

Finnaly here is the mainThread

public class MainThread extends Thread {

    private UserObject player = new UserObject();
    private int FPS = 30;
    private SurfaceHolder surfaceHolder;
    private GamePanel gamePanel;
    private boolean running;
    public static Canvas canvas;
    //Game variables

    public MainThread(SurfaceHolder surfaceHolder, GamePanel gamePanel) {
        super();
        this.surfaceHolder = surfaceHolder;
        this.gamePanel = gamePanel;
    }

    @Override
    public void run() {
        long startTime;
        long timeMillis;
        long waitTime;
        long targetTime = 1000/FPS;
        int frames = 0;

        while(running) {
            startTime = System.nanoTime();
            canvas = null;

            if (frames == 30) {
                frames = 0;
                player.money += player.MPS;
            }

            //try locking the canvas for pixel editing
            try {
                canvas = this.surfaceHolder.lockCanvas();
                synchronized (surfaceHolder) {
                    this.gamePanel.draw(canvas);
                }
            } catch (Exception e) {
            }
            finally{
                if(canvas!=null)
                {
                    try {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                    catch(Exception e){e.printStackTrace();}
                }
            }

            timeMillis = (System.nanoTime() - startTime) / 1000000;
            waitTime = targetTime-timeMillis;
            frames++;

            try {
                this.sleep(waitTime);
            } catch(Exception e) {}
        }
    }
    public void setRunning(boolean b)
    {
        running=b;
    }
}

I left out the LoadImage and UserObject classes. Because they are unimportant.


Solution

  • What has saving/loading got to do with an activity? Nothing, you probably just started with it there, but it has no reason to stay there.

    So separate Persistence out to own class, then you can call anywhere you have a Context:

    public final class Persistence {
        private final SharedPreferences sharedPreferences;
    
        public Persistence(Context context) {
            sharedPreferences = context.getSharedPreferences("houseTapper_dataSaveFile", MODE_PRIVATE);
        }
    
        public void saveData(Player player) {
            SharedPreferences.Editor sharedPreferencesEditor = sharedPreferences.edit();
            sharedPreferencesEditor.putInt("money", player.money);
            sharedPreferencesEditor.putInt("MPS", player.MPS);
            sharedPreferencesEditor.putInt("MPC", player.MPC);
            // etc...
            sharedPreferencesEditor.apply();
        }
    
        public void getData(Player player) {
            player.money = sharedPreferences.getInt("money", 0);
            player.MPS = sharedPreferences.getInt("MPS", 0);
            player.MPC = sharedPreferences.getInt("MPC", 0);
            // etc...
        }    
    }
    

    Usage:

    public class GamePanel extends SurfaceView implements SurfaceHolder.Callback {
         private final Persistence persistence = new Persistence(getContext());
    
         @Override
         public void surfaceCreated(SurfaceHolder holder) {
            //we can safely start the game loop
            persistence.getData(player);
    
    
    }