Android How to handle Touchevent in OpenGL Thread efficiently? - android

I handle my onTouchEvent like this:
public boolean onTouchEvent(MotionEvent event)
{
queueEvent(new Runnable(){
public void run(){
mRenderer.onTouchEvent(event);
}
});
return true;
}
I have to queue the call into my GL Thread because I want to update most of the stuff only if the user touches and game related stuff changes.
However the queueEvent solution seems to be a bit slow. It takes 10-15ms between the onTouchEvent at the MainActivity and the onTouchEvent at my Renderer. Is it possible to achieve lower delays without passing the onTouchEvent directly?

If you think about it this way, 10-15 ms means that you will be able to draw the next frame from the current user input. At 60fps (android default), it's 16 ms between each frame. It has to synchronize the input with the renderer and it cannot interfere at any time. Of course, you could change values inside the renderer in a synchronized way, but still, it can be only drawn in the next frame, which might take about that much time too.

Related

Android runOnUiThread and performance

My Android App is an Open GL ES 2.0 App. For one particular scene, I am overlaying a few textViews on top of my GL Surfaceview along with the some OpenGL textured quads.
I need one of my textViews to 'flash' - I'm targeting Gingerbread, therefore, I can't use animations, so I've created a method which does this:
public void flashText(){
if(myText.getVisibility()==View.VISIBLE)
myText.setVisibility(View.GONE);
else
myText.setVisibility(View.VISIBLE);
}
Then, from my OpenGL thread, I do the following:
void updateLogic(
if (System.currentTimeMillis()>(flashTimer+250)){
flashTimer=System.currentTimeMillis();
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
activity.flashText();
}
});
}
}
The above method (updateLogic) is called 60 times a second. The timer is set to 250ms, so I get a 'flashing' animation 4 times a second, - or 4 times a second, FlashText is called via runOnUiThread.
This does work, however, it affects the animation of my openGL objects enough for it to be a problem.
My question is, is there a better way to do this? (because the method I'm using is clearly not efficient enough).

Speed up bitmap animation in custom View

In my custom view i have 1 animation that i need to run at demand (on tile click). Currently i am using this method:
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//check what tile for clicked
getHandler().removeCallbacks(explosionThread);
getHandler().post(explosionThread);
}
}
break;
}
return super.onTouchEvent(event);
}
So i am calling (or sending to view thread, to be specific) a Runnable that calls it self until it comes to an end of an image...
private Runnable explosionThread=new Runnable(){
#Override
public void run() {
invalidate();
if(expCount<15){
getHandler().postDelayed(this, 10);
}
}
};
In my onDraw() method i implemented logic to go threw bitmap and draw it on screen ( using cnavas.drawBitmap(bitmap,srcRect,destRect,paint)....
Now, I want to avoid using SurfaceView (i have only 1 animation and View uses less resources).
I think the animation is slow because onDraw needs to draw whole screen each time invalidate() is called witch can be slow ( drawing 64 tiles with png images). I know that there is an overload of invalidate method, invalidate(Rect dirty) but i don't really know how to use it. If u think that that is the answer please write how to avoid drawing whole onDraw method ( or what method can I overwrite that is used by invalidate(Rect) method, if there is any).
If you have any other better way to speed up animation post it plz.
Thanks in advance....
That's right. One of the way to speed up rendering through canvas is to use invalidate(Rect). Rect passed to invalidate method defines area which will be redrawn. Your onDraw will be called after invalidate with clipping region being set up on canvas. So all your "drawBitmap" will be clipped by the rect.
for running the animation are using a .gif file or you are using a sequence of images run on a thread to show as an animation ?

Android OpenGL gameloop outside onDrawFrame

I've got a problem with creating gameloop for my first game. I've read a lot about it but still can't figure it out. It's based on OpenGL so I've used onDrawFrame as a game loop and it works fine on my phone. Problem is that onDrawFrame is refresh time depends on hardware so it runs way too fast on some devices. So what I want is adding a separate game loop that will refresh itself at constant period of time on all smartphones. (and onDrawFrame will only take care of graphics as it should)
As for now I have:
myGameRenderer class with all openGl stuff an onDrawFrame
myGLSurfaceView that supports touch events
myGameActivity
onDrawFrame activates myGameUpdate function that controls changing positions of all objects in game depending on info from myGLSurfaceView
I've tried with creating new Runnable but it doesn't seem to work, I can't figure out how to start that runnable and where i should place it (I've tried to place it in myGameRenderer class, but it didn't seem to work, nothing was moving:
private final Runnable mUpdateDisplay = new Runnable() {
#Override
public void run() {
update();
}};
private void update() {
//some update stuff blablabla
//some update stuff blablabla
mHandler.postDelayed(mUpdateDisplay,40); //to refresh at 25 fps
}
but I guess I don't get the idea of it - I mean I create this runnable.
I've tried to place it in onCreateSurface to start it but no effect.
So - is the generall idea ok? And how to start the loop? Where to place it? Or should I use any other way?
Ok it was simple - I was just missing r.run();
But as allways there's something. Now it works as i wanted - I mean frames doesn't depend on hardware, but everything is not as smooth as it was - and part of objects in 3d are flickering. Seems like some objects visibly are drawn faster, some later and it looks ugly.
So what am I doing wrong? Is there a better way?

Android game scrolling background

I'm just trying to figure out the best approach for running a scolling background on an android device. The method I have so far.... its pretty laggy. I use threads, which I believe is not the best bet for android platforms
#Override
public void run() {
// Game Loop
while(runningThread){
//Scroll background down
bgY += 1;
try {
this.postInvalidate();
t.sleep(10);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
where postinvalidate in the onDraw function simply pushings the background image down
canvas.drawBitmap(backgroundImage, bgX, bgY, null);
Thanks in advance
UPDATE
I've identified the problem. And it is the fact that my player updates the same rate as the background scrolls (making it look choppy). from top to bottom. This is because both get drawn in the same function. I'm not really sure how to tackle this and would be grateful for any help. i.e so that player movement is handled separately from the map scrolling
Also how can I control the speed at which onDraw(canvas) get called?
Thanks in advance.
However, I have patched together a different run loop for anyone having the same problem. This is partially from the jetboy example on google.
Below is my inner class in my surfaceview
class MapThread extends Thread{
private Map map;
private SurfaceHolder holder;
private boolean run = false;
public MapThread(Map map, SurfaceHolder holder){
this.holder = holder;
this.map = map;
setRunning(true);
}
public void setRunning(boolean run){
this.run = run;
}
#Override
public void run(){
while(run){
try{
Canvas c = null;
try {
c = holder.lockCanvas(null);
synchronized (holder) {
map.onDraw(c);
}
} finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
}
}
}
SOLUTION
https://gamedev.stackexchange.com/questions/8127/android-game-scrolling-background
Use the SurfaceView implementation draw on the screen. It allows you more control of what to draw and when.
The SurfaceView is a special subclass of View that offers a dedicated drawing surface within the View hierarchy. The aim is to offer this drawing surface to an application's secondary thread, so that the application isn't required to wait until the system's View hierarchy is ready to draw.
The basic design is to have a surfaceview that draws continuously in a while loop. Then add an if-statement whose condition is to be true if a timer thread tells you its time to draw. Say, every 30ms, draw the bitmap. This will give you about 33 fps.
Now you may also have another timer thread that tells you when to update the the bgX or bgY values. Say at every 60ms, it will set a boolean updateFlag = true; Then in your main thread, you have an if-statement check this flag, set it to false, and update your bgX and bgY values. By accurately controlling the timer and the bgX/bgY increments, you should be able to produce smooth animations.
It would be a good idea to look at the LunarLander source code provided by Google.
One thing to keep in mind is that sleep is very inaccurate. To work around this, you can keep track of exactly how much time passed during the sleep and update how much you move things accordingly.
Its not clear from you code, but you need to make sure that all of your UI updates happen in the UI thread.
You do need to do your timing outside of the UI thread, because otherwise the UI will never update. There are other methods of timing, like using a Handler that can be a little bit cleaner, but I think the overhead on them might be a bit much for what you are trying to do. I think a simple thread has the least amount of overhead.
I am using this method on the second level of my SpaceQuestAlpha game. This makes a seemless scroll.
I used the 2 lines below to set original position.
moony=0;
moon2y=-(heighty);
Then these lines increment both versions of the background image. One starts at 0 and one starts at negative screen height. Every time one of the images goes below the bottom of the screen it is moved up twice the height to move it back into position. I am using surface view with no latency issues.
moony+=5;
moon2y+=5;
if(moon2y>=heighty) {moon2y=moon2y-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moon2y, null);
if(moony>=heighty){moony=moony-(heighty*2);}
canvas.drawBitmap(lavabackground, 0, moony, null);

Android: Touch seriously slowing my application

I've been raking my brains on this one for a while.
when I'm running my application (opengl game) eveyrthing goes fine but when I touch the screen my application slows down quite seriously (not noticeable on powerful phones like the nexus one, but on the htc magic it gets quite annoying).
I did a trace and found out that the touch events seem to be handled in a different thread and even if it doesn't take so much processing time I think androids ability to switch between threads is not so good...
What is the best way to handle touch when speed is an issue ?
Currently I'm using :
in the GLSurfaceView
#Override
public boolean onTouchEvent(MotionEvent event) {
GameHandler.onTouchEvent(event);
return true;
}
Any ideas are welcome
I have a feeling the bug report below may be relevant. Sadly in seems its only going to be fixed in gingerbread.
Bug report
Have just seen that SO prefers details in the answers in case links vanish etc. the above refers to a google accepted bug in Adnroid 2.1 targetted for a fix in the gingerbread release.
Issue 7836: system_server consumes excessive CPU processing touch events
I can't vouch for this myself, but my research has shown that touching the screen fires so many events that it floods the events queue and therefore lags the CPU taking your resources.
Try putting: -
try {
Thread.sleep(16);
} catch (InterruptedException e) {} //ignore
before any returns that you have in the onTouch method (usually there's only the one at the end - but just making sure). I know sleep is usually a very bad thing to do, but its not in the UI thread so it should be ok. Sleep 16 should cap the FPS to 60.
Don't put heavy computation to your onTouchEvent(). The OnTouchEvent can be fired tens or hundred of times per second per finger, you should defer the heavy computation to the other part of your game (e.g. the physics engine or the graphic engine). In particular avoid drawing in your onTouchEvent.
You should preferrably use onClickEvent or other less intensive mouse event and only use onTouchEvent when you really need to track the motion of the touch.
#Jason:
This was going to be a comment, but it grew too big and it is a different [better] answer to the one I just gave.
I have changed my implementation using this method as described here
http://obviam.net/index.php/the-android-game-loop/
By employing the method described above means you should not need to sleep the OnTouch events.
Also don't forget to protect your game loop thread as mentioned here http://wonton-games.blogspot.com/2010/06/lunar-lander-resume-game-workaround.html
Also also keep in mind that Chris Pruett when writing Replica Island said he used 2 threads, one for the update() and the other for render() - both will have to be protected.
Chris does sleep his OnTouch with 16 milliseconds (60 fps) to cut back the events - so I would say its best to experiment if you still need to cut the events down - you need only touch the screen whilst stuff is happening to see if it lags and thus subsequently speeds up when let go.
For my needs currently I am using the same thread as I haven't progressed into OpenGL, I still use the canvas. But when I go OpenGL, it will be 2 threads, and each will be a complete class of its own.
Finally, I don't time my sprites by counting frames, I've wrapped them with a timer. I'll share my Class and show you how I call it. Bear in mind I am still new to Java so apologies for poor code.
package com.yourname.yourapplication;
//Used for doing something after a set time
public class TimeDo {
private int mRepeat = 0; //Stores the last wait period for the reset() later
private long mTime = 0; //The goal time of when its due
private boolean mFlagged = false; //Stop them getting a second true on a subsequent check
public TimeDo(int milliseconds) {
reset(milliseconds);
}
public TimeDo() {
this(0);
}
public void reset(int milliseconds) {
mRepeat = milliseconds;
mTime = System.currentTimeMillis() + milliseconds;
mFlagged = mRepeat==0; //ignore if zero
}
public void reset() { //Set it back to the delay used last time
reset(mRepeat);
}
public boolean check() {
if (mFlagged) //Assert: shouldn't really happen
return false;
mFlagged = System.currentTimeMillis() > mTime;
return mFlagged;
}
public boolean checkAndReset() {
if (check()) {
reset();
return true;
}
return false; //note mFlagged could be true here, so don't use it
}
}
And it is implemented thusly: -
public class Gem {
private TimeDo mMoveGem = new TimeDo(100); //move 10 times a second, 100ms
private int mX = 0;
private int mY = 0;
private int mMoveX = 3;
private int mMoveY = 4;
.
.
.
public void update() {
if (mMoveGem.checkAndReset()) {
mX += mMoveX;
mY += mMoveY;
.
.
}
}
public void render(Canvas canvas) {
//etc etc
.
.
}
}
Hope any of that helps!
Sorry if you have to rewrite lots of your application - I did.
Edit: That TimeDo class is not a postDelayed runnable, like an automatic alarm. If you don't check it no "event" will fire. You could get excited and have it create a runnable and pass it a callback method that resides within your class (think of an OnClick method) - but defeats the purpose of running an exclusive time-able update() thread that updates all your components.

Categories

Resources