I make this code
Timer.schedule(new Task(){
#Override
public void run() {
flyAway();
}
}, 12);
and when my game paused i do this in draw method (Actor)
Timer.instance().stop();
On resume game i do this:
Timer.instance().start();
It work, but when i pause on 2 sec and resume after 35 sec, function flyAway() executed immediately, not counting the remaining 10 seconds.
How i can paused delaySeconds. Thanks.
Sorry for my bad English.
FlyAvay code
//Муха улетает
public void flyAway(){
flyAway = 1;
targetFlyX = Gdx.graphics.getWidth() / 2;
targetFlyY = Gdx.graphics.getHeight() + 300;
this.setColor(1f, 1f, 1f, 0.2f);
this.addAction(sequence(moveTo(targetFlyX, targetFlyY, flySpeed), desctroyFlyAction));
}
Action desctroyFlyAction = new Action(){
public boolean act( float delta ) {
removeFly();
return true;
}
};
public void removeFly(){
rectBounds.set(1, 1, 0, 0);
this.remove();
}
I had this problem and I know this is late but maybe it'll help someone else. The problem is that when a Timer is created, it is given a time on the system's clock to execute rather than a specified amount of time until it executes. So when you call Timer.stop in your pause method, wait 35 seconds, then call Timer.start in the resume method, the time that task was supposed to be completed had already passed so it was executed right away.
My solution was just to save the current system's time on pause, then subtract it from the current time on resume, and add the difference as a delay to the Timer using Timer.delay() before calling Timer.start() again.
public void pause() {
timerDelay = TimeUtils.nanosToMillis(TimeUtils.nanoTime());
Timer.stop();
}
public void resume() {
Timer.delay(TimeUtils.nanosToMillis(TimeUtils.nanoTime()) - timerDelay);
Timer.start();
}
Related
I am trying to write a simple little thing that stops an audio file from being played when the user "interrupts" it by talking to the phone. I'm using the SoundMeter class that this person wrote to get the maxAmplitude of the microphone. I've set a threshold to 800 and want to test the return of the microphone amplitude against it constantly (every 50ms).
For some reason, it's hit or miss when I get the "interrupt" to show up and when the audio stops. I'd like for the interrupt to be showing the entire time the amplitude is above 800.
handler = new Handler();
final Runnable r = new Runnable() {
#Override
public void run() {
mic_amplitude.setText(""+mSoundMeter.getAmplitude());
if(mSoundMeter.getAmplitude() > threshold) {
Log.d("INTERRUPT", "INTERRUPT");
interrupt.setText("Interrupt");
mBackgroundAudio.pause(mp);
audioButton.setText("Resume Audio");
} else {
interrupt.setText("");
}
handler.postDelayed(this, 50);
}
};
handler.postDelayed(r, 50);
While viewing this, I'm able to see the amplitude at a steady 40-50 while there is no background noise and peaks of 1200-1500 when talking quietly. I'd like the interrupt to show anytime above 800, but it's currently only showing up intermittently.
Ok, I figured it out. I tested what my amplitude was by logging the amp along with the interrupt and I saw I was getting 0. I realized I had been testing on a new amplitude (other than the amp I was showing), so I assigned the amplitude to a variable and used the variable everywhere else.
here's my outcome:
handler = new Handler();
final Runnable r = new Runnable() {
#Override
public void run() {
mAmplitude = mSoundMeter.getAmplitude();
mic_amplitude.setText(""+mAmplitude);
if(mAmplitude > threshold) {
Log.d("INTERRUPT", "INTERRUPT " + mAmplitude);
interrupt.setText("Interrupt");
mBackgroundAudio.pause(mp);
audioButton.setText("Resume Audio");
} else {
interrupt.setText("");
}
handler.postDelayed(this, 100);
}
};
handler.postDelayed(r, 100);
I'm trying to update a label every millisecond in libGDX for a set amount of time.
However, sometimes the label suddenly stops without an error OR I receive a "String index out of range" error which then crashes my program. These are two separate issues.
Code:
Stage stage;
Timer timer = new Timer();
Label countUpLabel;
int countUp;
#Override
public void create () {
stage = new Stage(new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()));
//Setting up time label
BitmapFont font = new BitmapFont(Gdx.files.internal("04b_19__-32.fnt"));
LabelStyle labelStyle = new LabelStyle(font, Color.WHITE);
countUpLabel = new Label("Time:\n00000", labelStyle);
countUpLabel.setPosition(200,200);
countUpLabel.setAlignment(Align.center);
countUpLabel.setFontScale(3);
stage.addActor(countUpLabel);
//Setting up timer
timer.schedule(new TimerTask() {
#Override
public void run() {
if (countUp < 3000) {
countUp++;
countUpLabel.setText(String.format("Time:\n%d", countUp));
}else
timer.cancel();
}
}, 0, 1); //every millisecond run the timer
}
Thanks in advance.
Most of libGDX is not thread safe, unless explicitly stated that it is. Also, updating a label every millisecond, while it is only shown about every 16 milliseconds (the refresh rate of your typical display device), isn't a very good approach. So you might want to remove your timer and instead update the label when it is actually visible: in the render method:
float counter = 0f;
#Override
public void render() {
if (counter < 3) {
counter += Gdx.graphics.getDeltaTime();
countUpLabel.setText(String.format("Time:\n%01.22f", counter));
}
// call glClear, stage.act, stage.draw, etc.
}
essentially I want to run a method whenever the user goes from not moving their mouse at all, to moving it. I have no clue how to go about this.
custom listener:
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
public class CustomMouseListener implements MouseMotionListener, MouseListener{
//whatever other methods I have (irrelevant for the question)
public void mouseMoved(MouseEvent e){
//code goes here
//but right now it fires every time the mouse is moved, which
//is way too much, I only need one per move
}
}
Plain algorithm
0. Fire mouse listener for every second with (x=x1, y=y1)
1. Store (x,y) of mouse pointer;
2. If (x,y) == (x1,y1) for another 15(or whatever you consider as time interval) sec
3. make account of (x1,y1);
4. Else do nothing;
5. If(x1,y1) changed after 15 sec
6. call mouseMove();
If you're only interested in knowing two things, when the mouse started to move and when the mouse stopped moving, you could use a javax.swing.Timer to insert a delay between events, so that it will only be tiggered when the delay is reached...
public class CustomMouseListener implements MouseMotionListener, MouseListener{
private javax.swing.Timer moveTimer;
private boolean moving = false;
public CustomMouseListener() {
moveTimer = new javax.swing.Timer(25, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
moving = false;
// Method to be called when you want to
// to know when the mouse has stopped moving...
}
});
moveTimer.setRepeats(false);
}
//whatever other methods I have (irrelevant for the question)
public void mouseMoved(MouseEvent e){
if (moving || moveTimer.isRunning()) {
moveTimer.restart();
} else {
moving = true;
moveTimer.start();
// Method to call when you want to know when the mouse
// has started moving...
}
}
}
Basically, when 25 milliseconds pass without mouseMoved being called, the javax.swing.Timer will be triggered....You may want to play with the threshold a little...
One way to do it would be save the last time it moved. If current time - lastMovedTime > x then call your listeners or mouseStartedMoving() method
public class CustomMouseListener implements MouseMotionListener, MouseListener{
public final static long TIME_DIFFERNCE_FOR_IDLE = 800;//milliseconds
long lastMoveTime = -1;
public void mouseMoved(MouseEvent e){
long currentTime = System.currentTimeMillis();
long diff = lastMoveTime - currentTime ;
if(lastMoveTime == -1 || diff > TIME_DIFFERNCE_FOR_IDLE ){
lastMoveTime();
}
lastMoveTime = System.currentTimeMillis();
}
}
void lastMoveTime(){
//do what u need to when mouse starts mooving
}
Another Need to adda polling thread. Can make a default thread pool of size 1. ADd one task (Runnable) in the run method sleep for 1 second (or 2 or 800 milliseconds - depends what you define as pause between moves)
Anyway in your original code keep track of the current mouse position x,y and expose to the runnable.
The runnable keeps track of previous mouse position. Also have a state variable in Runnable that is an enum - {INITIAL, STATIONARY, MOVING}.
Initially its INITIAL, if you get mouse move position and its INITIAL it goes to MOVING
IF MOVING and for X ticks in the Runnable it does not move goes to STATIONARY. Again on Mouse move it goes to MOVING.
When it goes from INITIAL to MOVING OR STATIONARY to MOVING, can have listeners who are called or just a special method - mouseStartedMoving()
And do whatever there.
I'm new to both android and game development and have been trying to create a pong clone to get to grips with everything. I have a "PongView" class which extends SurfaceView and a "PongThread" which extends thread.
I have found a way to detect if the ball or "bomb" has gotten past the paddle and reached the wall behind it. I'm not sure if my approach is the best (opinions welcome) but it seems to work as i have a little toast message displaying game over if it happens. Now i want to set it up so that, as well as displaying game over, the view restarts (or whatever the more appropriate term is) so that the bomb is in the center of the screen again and the user can play another round.
Due to inexperience im not really sure how i should handle the thread and what methods to call in the view to achieve this.
I've had several stabs at trying to figure it out but i have no idea if i'm on the right track, it'll probably be pretty clear from my code that i don't know what i'm doing. (at the moment my thread stops and my surface is just stuck displaying the last frame). Any advice will be welcome!
PongView.java: (scroll down to the update method to get to the section i'm trying to figure out)
package biz.hireholly.pirateponggame;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Handler;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.widget.Toast;
public class PongView extends SurfaceView implements Callback{
/*GLOBALS*/
private static final String TAG = PongView.class.getSimpleName();
private PongThread pongThread;
private Bomb bomb;
private Paddle paddleA;
private int viewWidth;
private int viewHeight;
Handler handler;
boolean gameOver;
public PongView(Context context) {
super(context);
// sets current class as the handler for the events happening on the surface
getHolder().addCallback(this);
//instantiate thread, pass the current holder and view so that the thread can access them
pongThread = new PongThread(getHolder(), this);
//make the GameView focusable so it can handle events
setFocusable(true);
handler = new Handler();
}
//CURRENTLY WHERE IM INITIALISING SPRITES
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
//INITIALISE viewWidth and viewHeight here
//so that they can be passed as parameters
viewWidth = getWidth();
viewHeight = getHeight();
//NEW BOMB, INITIAL BITMAP
bomb = new Bomb(BitmapFactory.decodeResource(
getResources(), R.drawable.bombsprite),
getWidth() /2, getHeight() /2); //draws in middle
//bombs random starting direction
bomb.getSpeed().randomiseDirection();
//NEW PADDLE, INITIAL BITMAP
paddleA = new Paddle(BitmapFactory.decodeResource(
getResources(), R.drawable.paddlesprite),
getWidth() /2, getHeight() -(getHeight()/30) ); //middle bottom screen
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
pongThread = new PongThread(getHolder(), this); //needed if user exits and returns
pongThread.setRunning(true);
//.start() == PongThread.run() except PongThread does all the work
pongThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while(retry){
try{
//tells thread to shut down and waits for it to finish. Clean shutdown
pongThread.setRunning(false);
pongThread.join();
retry = false;
} catch(InterruptedException e){
//try again shutting down the thread
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event)
{
paddleA.onTouchEvents(event, viewWidth);
return true;
}
/**
* CHECKS FOR COLLISIONS, CALLS OBJECT UPDATE METHODS
*/
public void update(){
gameOver = false;
//CHECK IF NULL as objects aren't created till surface change
if(bomb != null && paddleA != null){
bomb.handlePaddleCollision(paddleA);
gameOver = bomb.handleWallCollision(viewWidth, viewHeight);
//object physics updates
bomb.update();
//paddleA.update();
}
//WHEN handleWallColision returns true, one player scored and its game over.
if( gameOver ){
//handler is needed as alerts and toasts can only be made in the ui thread,
handler.post(new Runnable(){
public void run(){
Toast.makeText(getContext(), "GAME OVER", Toast.LENGTH_LONG).show();
}
});
/*THIS IS WHERE I'M TRYING TO RESTART THE GAME*/
///////////////////////////////////////////////////////
//could maybe call surfaceDestroyed here instead?
boolean retry = true;
while(retry){
try{
//tells thread to shut down and waits for it to finish. Clean shutdown
pongThread.setRunning(false);
pongThread.join();
retry = false;
} catch(InterruptedException e){
//try again shutting down the thread
}
}
//copied from surfaceCreated
pongThread = new PongThread(getHolder(), this); //needed if user exits and returns
pongThread.setRunning(true);
//.start() == PongThread.run() except PongThread does all the work
pongThread.start();
////////////////////////////////////////////////////////
}
}
protected void render(Canvas canvas)
{
canvas.drawColor(Color.GREEN);
bomb.draw(canvas);
paddleA.draw(canvas);
}
}
PongThread.java:
package biz.hireholly.pirateponggame;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class PongThread extends Thread {
/*GLOBALS*/
private static final String TAG = PongThread.class.getSimpleName();
//desired frames per second
private final static int MAX_FPS = 30;
//maximum number of frames to be skipped if drawing took too long last cycle
private final static int MAX_FRAME_SKIPS = 5;
//cycle period (cycle = update,draw,if excess time sleep)
private final static int CYCLE_PERIOD = 1000 / MAX_FPS;
//Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
//the view that actually handles inputs and draws to the surface
private PongView pongView;
//flag to hold game state
private boolean running;
public void setRunning(boolean running){
this.running = running;
}
/**Takes PongView instance and surfaceHolder as parameters
* so that we can lock the surface when drawing.
* #param surfaceHolder
* #param pongView
*/
public PongThread(SurfaceHolder surfaceHolder, PongView pongView){
super();
this.surfaceHolder = surfaceHolder;
this.pongView = pongView;
}
#Override
public void run(){
//canvas is a surface bitmap onto which we can draw/edit its pixels
Canvas canvas;
Log.i(TAG, "Starting pong thread");
long beginTime =0; // time when cycle began
long timeDiff =0; // time it took for the cycle to execute
int sleepTime =0; // milliseconds to sleep (<0 if drawing behind schedule)
int framesSkipped =0; // number of frames skipped
while (running) {
canvas = null;
//try locking canvas, so only we can edit pixels on surface
try{
canvas = this.surfaceHolder.lockCanvas();
//sync so nothing else can modify while were using it
synchronized (surfaceHolder){
beginTime = System.currentTimeMillis();
framesSkipped = 0; //reset frame skips
//UPDATE game state
this.pongView.update();
//RENDER state to screen, draws the canvas on the view
this.pongView.render(canvas);
//calculate how long cycle took
timeDiff = System.currentTimeMillis() - beginTime;
//calculate potential sleep time
sleepTime = (int)(CYCLE_PERIOD - timeDiff);
//sleep for remaining cycle
if (sleepTime >0){
try{
//saves battery
Thread.sleep(sleepTime);
} catch (InterruptedException e){}
}
//if there's no leftover cycle time then we're running behind
while (sleepTime < 0
&& framesSkipped < MAX_FRAME_SKIPS){
//we need to catch up so we update without rendering
this.pongView.update();
sleepTime += CYCLE_PERIOD;
framesSkipped++;
}
}
} finally{
//in case of exception,
//surface is not left in an inconsistent state
if (canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);
}
}//end finally
}
}
}
If you want to "reboot" the thread so to speak, you have two options. Either start a fresh thread every time the game is supposed to restart or construct your initial thread to loop indefinitely and wait to be notified when it is able to run. Like so:
public void run() {
while(true) {
signal.wait();
// your previous run code here
}
}
In this scenario, you start the thread initially and then signal it to actually start with signal.notify() (signal must be an object visible to both threads). Then, when the game ends, the thread will return to the call to wait. Then simply notify it again in order to restart from the beginning.
I've been trying for a while to understand the Handlers thingy in order to pause a game for a few seconds.. no luck so far.
I was hoping if someone could walk me through or show me a complete method that can be adapted to my needs.
which are:
I have a pong like game, when the ball hit either sides of the screen, I want to pause for a few seconds to display the score, and then to resume the game.
Thanks!
if you are using it would be easier (and more efficient) to use a library like libgdx or andengine to do this but to do it in your current case i suggest a simple solution
on the update method use a bool to know if the ball is paused
update()
{
if(!isPaused)
{
animateBall();
}
...
}
how to pause the ball
pauseBall()
{
isPaused=true;
Timer t= new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
isPaused = false;
}
}, fewSeconds);
}