androidandroid-thread

How to obtain the expected order of actions in my game loop?


I don't fully understand what is going on behind the scene, and therefore, what I can do to correctly code this issue. I'm looking for an explanation that will lead me to figure it out myself. This is just a fun home based project(I'm not a student), where I'm coding a turn based app. However, the battle scenes are randomly calculated durations, rather than turn based, so my desire is as follows:

  1. Present initial battle count on screen for 2 seconds
  2. Calculate first skirmish
  3. Present updated battle count on screen for 2 seconds
  4. Calculate 2nd skirmish
  5. ...
  6. ...
  7. Present Victory or Defeat on screen

The problem I'm having is that the app is performing as follows currently:

  1. Present initial battle count on screen
  2. Calculate all skirmishes
  3. Page displays null for the number, since it's apparently already returned?

Code looks like this:

void fightBattle(){
    setContentView(R.layout.brigands);
    boolean winnerDetermined = false;
    while(!winnerDetermined) {
        boolean brigandsWon = brigandsWon(player, brigandCount);
        if(brigandsWon) {
            player.removeWarriors(2);
        }
        displayWarriors(player);
        if(brigandsWon){
            if(player.getWarriors() < 2){
                winnerDetermined = true;
            }
        }
        if(!brigandsWon) {
            brigandCount = brigandCount / 2;
        }
        displayBrigands();
        if(brigandCount == 0){
            winnerDetermined = true;
        }
    }
}

private void displayWarriors(Player player){
    final Player currentPlayer = player;
    new CountDownTimer(2000, 2000) {
        public void onTick(long millisUntilFinished) { }
        public void onFinish() {
            setContentView(R.layout.warriors);
            TextView warrior_count_tv = findViewById(R.id.warrior_count_tv);
            warrior_count_tv.setText(currentPlayer.getWarriors());
        }
    }.start();
}

private void displayBrigands(Player player){
    new CountDownTimer(2000, 2000) {
        public void onTick(long millisUntilFinished) { }
        public void onFinish() {
            setContentView(R.layout.brigands);
            TextView brigand_count_tv = findViewById(R.id.brigand_count_tv);
            brigand_count_tv.setText(Integer.toString(brigandCount));
        }
    }.start();
}

Ultimately, what I want to see is something like the below sudo-code:

displayPage1For2Seconds;
while(somethingIsTrue){
    calculateNumber;
    displayPage2For2Seconds;
    displayPage3for2Seconds;
}
displayPage4For2Seconds;

Solution

  • Calculate all skirmishes

    Your current code does this because the while loop doesn't actually stops to wait. The flow will be like this:

    enter while loop -> call displayWarriors() -> create CountDownTimer() to do something after 2 seconds -> return to while loop -> call displayBrigands() -> create CountDownTimer() to do something after 2 seconds -> return to while loop -> do the same until you exit while

    With this code you'll end up with a bunch of CountDownTimers that are created and executed at the same(almost) time so after two seconds they all try to set a view to some value(with an indefinite behavior like you mention it happens).

    There are several ways to do what you want. You could use a Thread for example:

    void fightBattle(){
        setContentView(R.layout.brigands);
        new Thread(new Runnable() {
             public void run() {
                 // I assume R.layout.brigands is the initial screen that you want to show for 2 seconds?!? In this case wait 2 seconds
                 TimeUnit.Seconds.sleep(2);    
                     boolean winnerDetermined = false;
        while(!winnerDetermined) {
            // ...  
            // from your code it seems you want to show this for 2 seconds?
            displayWarriors(player);
            TimeUnit.Seconds.sleep(2);     
            //...
            displayBrigands();
            // also show this for 2 seconds
            TimeUnit.Seconds.sleep(2);    
            // ... 
        }
             }
        }).start();
    }
    

    Then your display methods will be something like this:

    private void displayWarriors(Player player){
        // you need to wrap this code in a runOnUiThread() method(from the activity)
        // because we are on a background thread and we are changing views!
        final Player currentPlayer = player;
        setContentView(R.layout.warriors);
        TextView warrior_count_tv = findViewById(R.id.warrior_count_tv);
        warrior_count_tv.setText(currentPlayer.getWarriors()); 
    }
    

    Another approach would be to use a Handler and break your code in Runnables that you then schedule at appropriate times.