Slot, stop method called multiple times - android

I have a slot machine code where it rolls through random fruits with a maxCount for each slot. Once the maxCount is reached that slot will stop. However, sometimes the slot, stop method is called more than once. Any ideas?
TimerTask
TimerTask task = new TimerTask() {
#Override
public void run() {
runOnUiThread(new TimerTask() {
#Override
public void run() {
count++;
if (!slotOneFinished){
animate(randomSwitchCount(), slotOne, count);
}
if (!slotTwoFinished) {
animate(randomSwitchCount(), slotTwo,count);
}
if (!slotThreeFinished) {
animate(randomSwitchCount(), slotThree,count);
}
}
});
}
};
Stop function
public void stop(ImageSwitcher slot){
if (slot.equals(slotOne)){
slotOneFinished=true;
Toast.makeText(MainActivity.this, "1",Toast.LENGTH_SHORT).show();
}
if (slot.equals(slotTwo)){
slotTwoFinished=true;
Toast.makeText(MainActivity.this, "2",Toast.LENGTH_SHORT).show();
}
if (slot.equals(slotThree)){
slotThreeFinished=true;
Toast.makeText(MainActivity.this, "3",Toast.LENGTH_SHORT).show();
}
if (slotOneFinished&&slotTwoFinished&&slotThreeFinished){
executor.shutdown();
executor=null;
roll.setEnabled(true);
checkWin(getFruits());
slotOneFinished=false;
slotTwoFinished=false;
slotThreeFinished=false;
}
}
Start function
public void start() {
if(executor==null) {
count =0;
executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(task,10,200,TimeUnit.MILLISECONDS);
}
}
Animate function
public void animate(final int maxCount, final ImageSwitcher slot, int i) {
if (i<maxCount){
Animation in = AnimationUtils.loadAnimation(this, R.anim.new_slot_item_in);
Animation out = AnimationUtils.loadAnimation(this, R.anim.old_item_out);
slot.setInAnimation(in);
slot.setOutAnimation(out);
int fruit = randomFruit();
slot.setTag(fruit);
slot.setImageResource(fruit);
}else {
stop(slot);
}
}
Count is set to 0 at the beginning of the program and randomSwitchCount() returns a random number between 10 and 40. I am assuming that I do not have the correct order of running some code. I used the toast messages to highlight my problem during testing. If there is anything else which you see wrong with my functions & logic, I am all ears.
Thanks,
Pi Net

The timer was scheduled to run one more time. But isFinished was set to false for every slot just before the timer ran so the condition became true. Instead, I set them to false only when the roll button was finished so that the time was in sync with the win detection.

Related

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:
Present initial battle count on screen for 2 seconds
Calculate first skirmish
Present updated battle count on screen for 2 seconds
Calculate 2nd skirmish
...
...
Present Victory or Defeat on screen
The problem I'm having is that the app is performing as follows currently:
Present initial battle count on screen
Calculate all skirmishes
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;
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.

Condition never returning true

I have an if statement which checks if three slots are finished. If it is, the timer should stop, but the code is not running for some reason. I have seen a post similar to this If-condition never executes despite correct condition, however their solution solved nothing.
Here is my code:
Stop function
public void stop(ImageSwitcher slot){
slotOneFinished = (slot.equals(slotOne));
slotTwoFinished = (slot.equals(slotTwo));
slotThreeFinished = (slot.equals(slotThree));
if (slotOneFinished&&slotTwoFinished&&slotThreeFinished){
//not running
Toast.makeText(MainActivity.this, "Running",Toast.LENGTH_SHORT).show();
checkWin(getFruits());
timer.cancel();
timer = null;
}
}
Timer
private Timer timer;
TimerTask timerTask = new TimerTask() {
#Override
public void run() {
runOnUiThread(new TimerTask() {
#Override
public void run() {
if (!slotOneFinished){
animate(randomSwitchCount(), slotOne);
}
if (!slotTwoFinished) {
animate(randomSwitchCount(), slotTwo);
}
if (!slotThreeFinished) {
animate(randomSwitchCount(), slotThree);
}
}
});
}
};
Animate function
public void animate(final int maxCount, final ImageSwitcher slot) {
i++;
if (i<maxCount){
Animation in = AnimationUtils.loadAnimation(this, R.anim.new_slot_item_in);
Animation out = AnimationUtils.loadAnimation(this, R.anim.old_item_out);
slot.setInAnimation(in);
slot.setOutAnimation(out);
int fruit = randomFruit();
slot.setTag(fruit);
slot.setImageResource(fruit);
}else {
stop(slot);
}
}
Using == did nothing as well.
Thanks for your help,
PiNet
This condition can never be true, assuming that equals() is implemented in the canonical way, and slotOne, slotTwo, and slotThree are 3 distinct objects:
if (slotOneFinished&&slotTwoFinished&&slotThreeFinished)
Looks like you had a mistaken assumption about the scope of the variables. You can probably fix this by using a condition like this, instead:
if( slot == slotOne )
slotOneFinished = true;
...and so forth.
The Android Studio debugger is your friend.

How can I control a timer in android?

I want to make an application about mini game.
Detail : In 2 seconds you must to answer a question if you don't answer or the answer is wrong -> Game Over . But if your answer is true the Timer will reset become 0 and countdown again with diffirent question.
I have already seen many code about timer in website but I don't understand clearly about it :(
So I want to ask : How can i set up a timer run only 2 seconds and how can i reset it and continue with a new question ?
Please help me.
you can use CountDownTimer in android like this:
public class Myclass {
myTimer timer =new myTimer(2000,1000);
public void creatQuestion(){
timer.start();
//method you init question and show it to user
}
public void getUserAnswer(/*evry thing you expected*/)
{
//if answer is true call timer.start()
//else call timer.onFinish(); to run onfinish in timer
}
public class myTimer extends CountDownTimer {
public myTimer(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
#Override
public void onTick(long millisUntilFinished) {
// you can update ui here
}
#Override
public void onFinish() {
this.cancel();
//fire game over event
}
}
}
i hope it make you satisfy
I've done something similar using Thread/Runnable.
Thread t = new Thread(new Runnable() {
public void run() {
final long startTime = getTime();
final long maxEndTime = startTime + 2000L;
try {
while (shouldContinueWaiting()) {
if (getTime() > maxEndTime) {
throw new TimeoutException();
}
sleep();
}
} catch (InterruptedException e) {
handleInterrupt();
} catch (TimeoutException e) {
handleTimeout();
}
}
boolean shouldContinueWaiting() {
// Has the user already answered?
}
void handleInterrupt() {
// The user has answered. Dispose of this thread.
}
void handleTimeout() {
// User didn't answer in time
}
void sleep() throws InterruptedException {
Thread.sleep(SLEEP_DURATION_IN_MILLIS);
}
void getTime() {
return System.currentTimeMillis();
}
then you can start/restart the thread by:
t = new Thread(same as above...);
t.start();
and stop by:
t.interrupt();
We want to use the Timer class.
private Timer timer;
When you're ready for the timer to start counting -- let's say it's after you press a certain button -- do this to start it:
timer = new Timer();
timer.scheduleAtFixedRate(incrementTime(), 0, 100);
The first line is us creating a new Timer. Pretty standard. The second line, however, is the one I wanted you to see.
incrementTime() is a method that is called at the end of every "tick" of the clock. This method can be called whatever you want, but it has to return an instance of TimerTask. You could even make an anonymous interface if you want, but I prefer moving it off into its own section of code.
The 0 is our starting location. We start counting from here. Simple.
The 100 is how large a "tick" of the clock is (in milliseconds). Here, it's every 100 milliseconds, or every 1/10 of a second. I used this value at the time of writing this code because I was making a stopwatch application and I wanted my clock to change every 0.1 seconds.
As for your project, I'd suggest making the timer's task be your question switch method. Make it happen every 2000 milliseconds, or 2 seconds.
You can use a Handler.
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
//this will happen after 2000 ms
}
}, 2000);
Maybe this can help you:
Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
// FIRE GAME OVER
handler.postDelayed(this, 2000); // set time here to refresh textView
}
});
You can fire your game over after 2000 milliseconds.
If you get the question correct -> remove callback from handler and reset it when the next question starts.

Avoid loop causing overload

I am developing an android game where I have a loop (not a game loop). This loop reacts if one of my booleans is changed. The problem is that when the boolean is changed, it calls the void inside the loop untill the app crashes of an overload. The void I am calling is suronded with a try-catch and it has to be, unless I have to remake the rest of my game. The code for the loop is like so:
private void listenerVoid(){
int timeBetweenChecks = 100;
final Handler h = new Handler();
h.postDelayed(new Runnable(){
public void run(){
//Do something if myBool returns true, otherwise loop again
if (myBool==false){
listenerVoid();
} else {
//Check if the listener has reacted to a change before
if(firstStart!=false){
firstStart = false;
theTryCatchVoid();
Log.i("MyClass","Boolean is changed");
}
else{
//Already started
Log.i("MyClass","Already started");
}
}
}
}, timeBetweenChecks);
};
And my try-catch void is similar to this:
private void tryCatchVoid(){
try{
//Try to do something that soemtimes causes a crash
}
catch(Exception e){
//Do nothing
}
}
I can not increase the timeBetweenChecks time, because it will then cause a long waiting time in my game. How can I make the tryCatchVoid only be called once?

android set visibility of a button on timer

I have an app that shows a disclaimer at the beginning of the program. I want a button to remain invisible for a set amount of time, and then become visible.
I set up a thread that sleeps for 5 seconds, and then tries to make the button visible. However ,I get this error when I execute my code:
08-02 21:34:07.868: ERROR/AndroidRuntime(1401): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How can I count 5 seconds, and then make the button visible?
THanks.
Thread splashTread = new Thread() {
#Override
public void run() {
try {
int waited = 0;
while(_active && (!_ok2)) {
sleep(100);
if(_active) {
waited += 100;
if(waited >= _splashTime)
{
turnButtonOn();
}
}
}
} catch(InterruptedException e) {
// do nothing
} finally {
finish();
startActivity(new Intent("com.lba.mixer.Choose"));
}
};
splashTread.start();
public static void turnButtonOn() {
okButton.setVisibility(View.VISIBLE);
}
The problem is that you're not in the UI thread when you call okButton.setVisibility(View.VISIBLE);, since you create and run your own thread. What you have to do is get your button's handler and set the visibility through the UI thread that you get via the handler.
So instead of
okButton.setVisibility(View.VISIBLE)
you should do
okButton.getHandler().post(new Runnable() {
public void run() {
okButton.setVisibility(View.VISIBLE);
}
});
I found this to be a much simpler solution. Visibility on 7 second delay
continuebutton.setVisibility(View.INVISIBLE);
continuebutton.postDelayed(new Runnable() {
public void run() {
continuebutton.setVisibility(View.VISIBLE);
}
}, 7000);
I found this a Better solution to the problem
(button id = but_resend)
define handler
private Handler handler;
call function in extend class
showButtons();
define after class
private void showButtons() {
handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
((Button) findViewById(R.id.but_resend)).setVisibility(View.VISIBLE);
}
}, 20000); // produce 20 sec delay in button visibility
}
and keep in mind to hide the visibility in the.xml file by
android:visibility="invisible"

Categories

Resources