Timer task not being performed accurately - android

I am trying to perform basic task of rotating a canvas 20 times a
second using timer but it doesn't seem to be working properly and its
lagging. for example, if I rotate rectangle 0.3 degrees per 50 ms it
should rotate 6 degree in on second, but that is not the case. It
really slow in rotation. Here is my sample code:
//Code for update task
class UpdateTimeTask extends TimerTask {
public void run() {
hndView.post(new Runnable() {
public void run() {
hndView.invalidate(); //this code invalidates custom view that calls onDraw to draw rotated hand
}
});
}
}
//Code for onDraw method of custom view that needs to be update
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
//ang is angle to rotate and inc is float value of 0.3 degree to be incremented
ang = ang + inc;
if (ang >= 360) ang = ang - 360;
canvas.rotate(ang, canvas.getWidth()/2, canvas.getHeight()/2);
canvas.drawRect((canvas.getWidth()/2 - 2), (canvas.getHeight()/2 - 125), (canvas.getWidth()/2 + 2), (canvas.getHeight()/2 + 10), mTextPaint);
canvas.restore();
}
//code to schedule task
Timer timer = new Timer();
UpdateTimeTask tt = new UpdateTimeTask();
timer.schedule(tt, 0, 50);
Can anyone please tell me what am I doing wrong here? Should I used
different approach to perform this task? Because its hard to believe
that you cannot have simple smooth rotation of rectangle 20 times in
one second.

Timer/TimerTask are not supposed to be precise and are not supposed to be used for this purpose. Follow the recipes for 2D game development, such as the LunarLander sample that is included in your SDK. Or, you could just search StackOverflow and find all sorts of useful posts on the subject.

I believe you are not using a SurfaceView.
Drawing like that to a canvas was meant for controls and not fast rendering(read >10fps)
If you want performance you need either to use a SurfaceView where you'll average a 25-30 fps or opengl
Please read : http://developer.android.com/guide/topics/graphics/index.html

The number of calls to invalidate need not equal the number of calls to onDraw. If your timer ends up running twice successively before the UI thread gets a chance to run, then the double invalidate will end up only causing a single rotation. Consider putting in debug code to validate the number of calls to those two methods.

Related

Doing position calculation in onDraw it's faster than in game loop thread... why?

I'm developing a simple game which uses normal android views, not openGL or other apis, simply uses views and moves them on the scren. I have a game loop which calls to AsteroidManager.updateAsteroidsPositions() which iterates in all the screen asteroids calculating it's possitions.
After that, the thread, calls to a AsteroidManager.invalidateAsteroids() method using runOnUiThread() method, because in Android you need to manipulate views on the main thread. AsteroidManager.invalidateAsteroids() method simply iterates all the asteroids and set's x,y positions to the view and calls invalidate().
The problem is that I disscovered that it gives a much more smooth and faster behaviour if you put the logic of calculatePositions inside the onDraw method of the view. Doing that, the logic of calculating possitions is not being done in the game loop thread... its being done in the main UI thread!!
How is that possible? It is breaking all the game development logic... about doing the position calculations on Game Loop thread instead of other places like main thread or onDraws...
This the slower original code:
AsteroidManager class:
public void updateAsteroidsPositions(){
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).updatePosition();
}
}
public void invalidateAsteroids() {
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).invalidate();
}
}
Asteroid Class:
public void updatePosition(){
currentScale = (Float) scaleX.getAnimatedValue();
factor = currentScale/MAX_SCALE;
//adding a minimum of factor, because with too low factor the movement is not realistic
if (factor < 0.250f)
factor = 0.250f;
x = x-((float)GameState.getInstance().getJoyX()*factor);
y = y-((float)GameState.getInstance().getJoyY()*factor);
}
public void invalidate(){
view.setX(x);
view.setY(y);
view.invalidate();
}
this is the trick done in Asteroid class which does the behaviour of the game smooth and faster:
Asteroid Class:
public Asteroid(Bitmap bitmap, Context context) {
view = new ImageView(context){
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
currentScale = (Float) scaleX.getAnimatedValue();
factor = currentScale/MAX_SCALE;
//adding a minimum of factor, because with too low factor the movement is not realistic
if (factor < 0.250f)
factor = 0.250f;
x = x-((float)GameState.getInstance().getJoyX()*factor);
y = y-((float)GameState.getInstance().getJoyY()*factor);
view.setX(x);
view.setY(y);
}
};
view.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
view.setImageBitmap(bitmap);
}
public void updatePosition(){
}
public void invalidate(){
view.invalidate();
}
If you have too many items in onScreenAsteroids list it takes some time to execute updatePosition() for each of them. Try to use single method for them:
public void updateAndInvalidateAsteroidsPositions(){
for (int i = 0; i<onScreenAsteroids.size(); i++){
onScreenAsteroids.get(i).updatePosition();
onScreenAsteroids.get(i).invalidate();
}
}
Not all games need game loop. Thread switching has its own cost.
Game Loop separates game state from rendering. Ideally the game loop has the responsibility to processes all the onscreen objects in the game and objects have the responsibility to draw itself in its place. This way we have central place to react to events(mouse click, user touch etc) and update view positions and views have the responsibility to draw themselves at updated position.
For eg consider that we have 10 moving asteroids on screen and we are updating them in onDraw(), now two of them collide, but asteroid1 does not know position of asteroid2, so how will they detect collision? By game logic the game loop knows position of all 10 asteroids, it can detect collision. If don't care about messy code, then collision can be detected in onDraw also. But consider following...
If two are colliding , then we need to check if some other asteroid is near by collision region, if so then how much impact? Mess increases linearly...
After collision we decide to show collision graphic effects. Mess increases exponentially....
Asteroids collided, game state = 'EXPLOSIONS IN THE SKY', user gets a call, game goes to background, game state is to be saved, but our asteroids are master of their own destiny, now we need to provide every asteroid's state to our activity and save it in onPause(). Its all mess now...
User returns after few hours, we can't welcome them directly with 'EXPLOSIONS IN THE SKY', need to rewind to the state where asteroids are about to collide and then show BANG-BANG... Mess goes ALL HELL BREAK LOOSE.....
Views are slaves and they should not be empowered.
Where to display view, its dimens? -> comes from outside.
What to draw in view? -> comes from outside/ can have little say here.
How to animate view? -> comes from outside.
Coming to your particular case, you are using both versions differently, in onDraw() case you are directly invalidating asteroid's (first one is drawn instantly) whereas in game loop case you are first computing all asteroid's position and then invalidating, I don't know how many asteroids you have but if they are significant number, then this coupled with thread switching costs, may trick you to believe onDraw() is faster.

SurfaceView horrible performance

So I am basically creating a 2D physics engine I want to use for other projects in the future.
The only problem here is the performance of my Surface View.
I get a constant 60 logic-updates per second, but only 6 frames per second no matter what I do.
If I call the draw() method in my calculating thread, I get 6 fps.
If I create a whole new thread which is only drawing, without any delay in the loop
#Override
public void run() {
while(running) {
if(holder.getSurface().isValid()) {
canvas = holder.lockCanvas();
canvas.drawColor(Color.DKGRAY);
for (GameObject object : objects) {object.draw(canvas);}
holder.unlockCanvasAndPost(canvas);
}
gs.totalFrames++;
}
}
(gs = Game Surface) I get 6 fps. The draw() function of my game object is pretty simple:
public void draw(Canvas canvas) {
canvas.drawCircle((float) x, (float) y, 10, paint);
}
Why is the performance so bad? (I only have 5 game objects, so that's 5 circles, which should not be a performance problem at all!)
I also tried a runnable posting itself delayed, but that was bad, too.
Calling the draw() function in the GameSurface from the thread instead of doing the drawing directly in it, didn't change anything. A tiny delay didn't improve it either.
Do I somehow use the SurfaceView in a way it's not supposed to be used in?
What is the problem?

Using Android timers to implement graphical movement

I'm using opengl with android. I am just playing around and trying to learn some stuff and I've decided to make a simple game where there are falling triangles and you have to tap them to "collect" them (Don't steal my idea! xD).
I am using an Android Timer like this:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
float[] p1Temp = mTriangle.getP1();
float[] p2Temp = mTriangle.getP2();
float[] p3Temp = mTriangle.getP3();
mTriangle.changeCoords(new float[] {p1Temp[0], p1Temp[1] - 0.01f, p1Temp[2],
p2Temp[0], p2Temp[1] - 0.01f, p2Temp[2],
p3Temp[0], p3Temp[1] - 0.01f, p3Temp[2]});
if (mTriangle.getP1()[1] <= -1.0f ||
mTriangle.getP2()[1] <= -1.0f ||
mTriangle.getP3()[1] <= -1.0f) {
t.cancel();
}
}
}, 0, 40);
So basically what this code is doing is this: there is a timer, and every 40 milliseconds, the y coordinate of every vertex of the falling triangle is decremented. This process stops when it hits the bottom of the screen (i.e. hit the ground).
My question is this, I'm new to using openGL in android, is this the correct way to handle "movement" of objects etc? Or are there methods I'm supposed to use to implement animation/movement.
The most common approach I have seen is somewhat different. It's more typical to update the animation while preparing to render each frame, and base the update on the amount of time that has passed since the last frame.
Since distance is velocity multiplied by time, you do this by assigning a velocity vector to each of your objects. Then when it's time to update the animation, you take the time difference since the last update, and the increment you apply to your positions is the time difference multiplied by the velocity. The velocity is constant as long as you just use a linear motion, but can also change over time for more complex animations, e.g. due to gravity, collision with other objects, etc.
If you're using OpenGL on Android, you're probably using a GLSurfaceView for your rendering. By default, the GLSurfaceView will already invoke your rendering function continuously, up to 60 fps if your rendering can keep up with the display refresh rate.
What you roughly do is keep the time when the last frame was rendered as a member variable in your GLSurfaceView.Renderer implementation. Then each time onDraw() is called, you get the new time, and subtract the last frame time from this time to get the time increment. Then you store away the new time in your member variable, multiply the time increment by the velocity, and add the result to your positions.
After the positions are updated, you render your objects as you normally would.
To give you the outline, the following is a slightly adapted piece of (pseudo-)code I copied from my answer to a similar question (Android timing in OpenGL ES thread is not monotonic):
public void onDrawFrame(GL10 gl) {
currentTime = SystemClock.elapsedRealtime()
deltaTime = currentTime - lastFrameTime
lastFrameTime = currentTime
update animation based on deltaTime
draw frame
}
Where lastFrameTime is a member variable, currentTime a local variable.

Objects not moving at the same speed, when Thread sleeps for a millisec

I am currently trying to move some rectangle objects (displayed as bitmaps on my surfaceview).
They should all move with the same speed, therefor my code looks like this:
new Thread (new Runnable()
{
#Override
public void run()
{
while(true)
{
newTime = System.currentTimeMillis();
frameTime = newTime - currentTime;
currentTime = newTime;
physics(frameTime);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}).start();
i move my rectangle objects in the physics method based on the frametime parameter.
So my problem is the following: with the code i just posted all my rectangles move at the same speed, but the graphics are lagging. The lag disappears as soon as i remove the Thread.sleep(1), but then my rectangle objects wont move with the same speed anymore (some rectangles move faster than others).
any ideas?
Edit:
the physics and movedown methods are just normal methods in my classes, both are not in any threads or something. they are only getting called from my Thread i posted above
public void physics(double delta)
{
for(int i=0; i<=5; i++)
{
rectangles[i].moveDown(delta);
}
}
public void moveDown(double delta)
{
setY((double) (getY() + ((sH)*(delta/1000))));
//sH is the screen height
}
Edit2:
Graphics code
while(true)
{
if(!ourHolder.getSurface().isValid())
{
continue;
}
Canvas canvas = ourHolder.lockCanvas();
synchronized(ourHolder) {
graphics(canvas);
// in this method all the drawings happen
// e.g. canvas.drawBitmap
}
ourHolder.unlockCanvasAndPost(canvas);
}
It is difficult to analyse given the code you posted, since it's unclear what physics(frameTime); is actually doing. It seems like each moving rectangle is updated in its own Thread. Then the result you get has to be expected because one thread or the other will be called more often than the others depending on thread scheduling. Instead, use one single thread to control your simulation (e.g. update your frameTime and provide it to the other threads). However, IMHO you have to rethink your architecture.
It's hard to tell from your posted code what the problem could be, but one thing I notice is that you could experience some clock drift. See: Does time jump in android devices?.
Although, it looks like you are moving the rectangles using a fixed offset, so even if there were clock drift, I would expect them to jump around but by the same amount. So I agree with Axel that there appears to be some thread interaction going on.
How are you drawing to the screen? If you have another thread processing the graphics, the two threads could get out of sync and you'll need to ensure you are locking things properly. If you are using the deltas to update the location on the screen in your paint method, maybe instead use an absolute position that you update in your physics() method and draw based on that. It seems you probably need to rethink some of the architecture.

view.invalidate() not working to redraw imageview

Ok guys, this may sound dumb, but I have been banging my head against the keyboard for some time now trying to figure out why this will not refresh. the basics: I have a little sample app that i am testing to see if i can rotate an image around a point a X amount of degrees, and show it one degree at a time to make a smooth animation. So I have a great sample i found that works great with a slider bar, basically setting the images rotation to a point on the slider bar, great! but.... when i try and create a for loop with a random number and use my for variable updating the image along the way every degree... it does nothing... and all i get is the updated image at the end... but when i drag my finger on he slider bar the graphic is updated instant as me spinning it... I cant figure out what i am doing wrong here... here is the code with the slider... i don't have my piece that creates the random number and draws it but essentially i did it behind a button click
essentially if you look at this piece i did the same behind a button again but it doesnt do it "real time". i called view.invalidate() and view.postinvalidate() to try to force it but no go...
#Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
curRotate = (float)progress;
drawMatrix();
}
private void drawMatrix(){
Matrix matrix = new Matrix();
matrix.postScale(curScale, curScale);
matrix.postRotate(curRotate);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
myImageView.setImageBitmap(resizedBitmap);
}
I think what you did was something like:
for (int degrees = 0 ; degrees < maxRotation ; i++) {
// perform the rotation by matrix
myImageView.invalidate();
}
This wouldn't work because invalidate() only schedules a redraw in the main thread event queue. This means that the redraw will be performed only when the current code has all been executed (in this case, the for cycle).
For a simple rotation a Tween Animation would be better suited. For more advanced stuff (like game animations) you might need to create a custom view or use SurfaceView.
Sounds like you're blocking the UI thread with your code to rotate the image.
I don't have any code to show you right now (reply back and when I'm home tonight I can post something that should help), but yuo will probably get better results placing your rotate code in an AsyncTask, see the Painless Threading area of the dev site for more info.
I was having the same problem, i used:
runOnUiThread(new Runnable() {
public void run() {
myImageView.setImageBitmap(image);
imageView.invalidate();
}
});

Categories

Resources