I have a:
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (event)
{
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(event.getAction() == MotionEvent.ACTION_DOWN){
isDown = true;
}else if(event.getAction() == MotionEvent.ACTION_MOVE){
isDown = true;
}else if(event.getAction() == MotionEvent.ACTION_UP){
isDown = false;
}
return true;
}
}
Then in my MainGame thread I use setCharacterPosition();
public void setCharacterPosition(){
if(isDown){
CharacterX += 32;
}
}
but this make my Character to run toooo quickly so i tried to add:
Thread.sleep(500);
Because i only want my character to increase with 32 every half a sencond.
IT works but bring my FPS down to 2-3.
How do i do it right?
//Simon
I know little about Java or Android, but this seems like a wrong way to do it.
What you're after is setting up a Game Loop (a loop that continuously runs and updates the game logic, frame by frame).
This loop is executed on timed intervals (usually 60 frames per second, or maybe less on a mobile platform).
On each frame, you can scale the game object's movements based on the time elapsed since last frame.
This gives a scaled movement, irrelevant to the clock speed of the device you're using.
Read more abot game loops in these fine resources:
Simple Java Android Game Loop
Game Loop
Android Game Loop
Android UI is based on Single Thread Model, so do not block UI Thread. Your onTouchEvent executes in UI Thread...when you call Thread.sleep you are making your UI thread to sleep for that specific period of time.
Hope this help. Ref: Painless Threading
Related
I'm searching how to manage game framerate on mobiles devices, here is my problem:
On a computer with got something like that:
void main()
{
while(game.isRunning())
{
event.handle(eventInfos);
game.update(dt);
graphic.render();
}
}
On a mobile device with got something like that:
void update()
{
game.update(dt);
}
void render()
{
game.render(dt);
}
void event()
{
game.handle(eventInfos);
}
When I search on internet, I found something like that everytime:
-> GameLoop
-> Fix your time step
I'm using IOS (With GLKViewController) and Android (with the NDK), and I've that rendering method is call from another thread
Thanks for your help!
I findthe GameLoop link you have posted is very straight forward, he runs a thread which in its turn check for right time to call update() in these lines
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
// update without rendering
this.gamePanel.update();
// add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
I am making a board game. The board doesn't ever move, but pieces on top of it sometimes do depending on user interaction. There are also UI elements which may update periodically.
Right now the way I set it up is by overwriting the onDraw() method of a SurfaceView subclass. I have a drawing thread that constantly calls postInvalidate() in a while loop:
class PanelThread extends Thread
{
//...
long sleepTime = 0;
long nextGameTick = System.currentTimeMillis();
#Override
public void run()
{
Canvas c;
while (_run)
{ // When setRunning(false) occurs, _run is
c = null; // set to false and loop ends, stopping thread
try
{
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder)
{
// Insert methods to modify positions of items in onDraw()
_panel.postInvalidate();
}
} finally
{
if (c != null)
{
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
nextGameTick += MILLISECONDS_PER_FRAME;
sleepTime = nextGameTick - System.currentTimeMillis();
if(sleepTime >= 0)
{
try
{
sleep(sleepTime, 0);
} catch (InterruptedException e)
{
continue;
}
}
else
{
//we're behind, oh well.
System.out.println("behind!");
nextGameTick = System.currentTimeMillis();
}
}
}
This is not efficient and is taking a lot of CPU. Is there a easy way to get android to only update when something changes?
You have the right idea, but it needs a bit of refinement.
You definitely do not want to loop as fast as the CPU can handle it though.
You should be sleeping your Thread in every loop for a little while. You most certainly do not need to do everything in your loop every millisecond.
I found this guide to FPS control to be incredible helpful in designing a game loop.
This Android-specific game loop guide also provides a lot of great sample code and an in-depth explanation.
Hi everyone This is my first post so please be gentle. Even though my android final is complete and all i still find it hard to not continue to adjust my program and add to it just for the fun of it. what it is is a simple program that when you touch the screen the character on the screen laughs one of two ways with matching vibration. It works nicely but when my teacher (who was in a foul mood at the time) went to test it he pretty much button mashed the thing which made it que up every button press and we had to sit through like 15 laughter fits before we could do anything more with the phone. What i want to do is just have one touch event count until the first event is complete. what it is is a simple on touch event with a couple nested if statements.
public boolean onTouch(final View v, MotionEvent m)
{
v.setBackgroundResource(R.drawable.laughing);//changes the image to the laughing monkey
if (m.getAction() == MotionEvent.ACTION_DOWN)
{
if (timesTouched != I) //checks to see if the touch amount is not equal to the random number
{
if(laughter != 0)
{
sp.play(laughter, 1, 1, 1, 0, 1);//plays the loaded sound when the screen is pressed
//vib.vibrate(900);
}
Time = 900;
timesTouched++;
intDelay = 1;
if(vibon == 1)
{
vib.vibrate(Time);
}
}
else if (timesTouched == I)//checks to see if the touch amount is the same as the random number
{
if(laughter != 0)
{
sp.play(laughFit, 1, 1, 1, 0, 1);//plays the loaded sound when the screen is pressed
}
Time = 6000;
timesTouched = 0;
intDelay = 1;
if(vibon == 1)
{
vib.vibrate(laugh, -1);
}
}
}
else if((m.getAction() == MotionEvent.ACTION_UP) && (intDelay == 1))
{
try {
Thread.sleep(Time);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
v.setBackgroundResource(R.drawable.normal) ;//returns the image to the normal looking monkey
intDelay = 0;
}
return true;
}
}
the sleep timer is there to prevent the background image from going back to the default before the laughter is over. I did attempt to get my teacher to help but he is just a speedy replacement and had never even touched an android device before starting to teach at the beginning of this year. please any help you can provide is greatly appreciated because so far i have had to pretty much teach myself this stuff with the help of Google searches.
Thanks a Bunch!
If this were me I would probably move the sound playing to a separate thread then use messages to notify that thread when to start and stop the sounds. In this way the button can send the message immediately and not be blocked by any subsequent actions. The side-effect should be that every button press stops the previous laughter and starts a new one (or just queues up another sound byte over the previous one effectively combining sounds). You could contrarily disable the button until the sound is finished but I do not believe that would be a good user experience - they would expect to be able to mash and mash to their hearts content!
Check out http://developer.android.com/resources/articles/painless-threading.html for more guidance on how to approach such a thing.
I am developing an Android game and have a strange issue where occasionally the game will become unresponsive for a long period of time before springing back into life. As far as I can tell, this pause, if it occurs at all, only happens when the game starts up. Once running normally the game seems to behave itself.
After some investigation, it appears that the onTouchEvent callback is becoming blocked trying to acquire a lock on the synchronization object it shares with the game thread. Meanwhile the game thread is running normally, and not holding onto a lock on the synchronization object for any long period of time.
It was my understanding that the synchronized block in doTouchEvent would acquire a lock on mSyncObject as soon as that lock had been released by the game thread. But in some cases, it appears that the game thread is able to acquire and release a lock several hundred times before doTouchEvent is eventually able to acquire its lock.
There is no other code that uses the same object for synchronization.
I've copied the relevant bits of code below. From what I can gather, it's not doing anything out of the ordinary, so I am somewhat baffled by the strange behaviour that I am seeing.
Would appreciate any help on this one. Thanks in advance!
class GameThread extends Thread {
// ...some methods and members omitted...
private volatile int mFrameCount = 0;
private Object mSyncObject = new Object();
#Override
public void run() {
while (!mShutDown) {
Canvas canvas = null;
long timestampA = System.currentTimeMillis();
try {
synchronized (mSurfaceHolder) {
canvas = mSurfaceHolder.lockCanvas(null);
// Synchronized on our object...
synchronized (mSyncObject) {
long now = System.currentTimeMillis();
if ((now > mLastTime) && !mPaused) {
double timestep = (double) (now - mLastTime) / 1000.0;
mGame.update(timestep);
}
mLastTime = now;
if (canvas != null) {
mGame.draw(canvas);
}
}
}
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
// have tried inserting a sleep() here, but it didn’t help
++mFrameCount;
}
}
// Called from the UI thread
public boolean doTouchEvent(MotionEvent event) {
boolean result = false;
// Synchronized on our object...
// The game loop in run() acquires and releases a lock
// on this object on every frame, so would expect to be
// blocked here for no longer than one frame.
// However, on occasions have been blocked here for over
// 2000 iterations of the game loop.
int frameCount = mFrameCount;
synchronized (mSyncObject) {
int framesWaited = mFrameCount - frameCount;
if (framesWaited > 1) {
Log.i("Block", "doTouchEvent waited " + framesWaited + " frames for lock");
}
if (!(mPaused || mShutDown)) {
result = mGame.doTouchEvent(event);
}
}
return result;
}
}
It would appear that this issue is down to the fact that synchronization offers no guarantee of fairness when it comes to the order in which waiting threads will acquire a lock. In fact, a thread can be kept waiting indefinitely for a lock if another cpu-intensive thread is repeatedly acquiring and releasing locks.
See this thread discussing exactly the same issue...
http://groups.google.com/group/android-developers/browse_frm/thread/ffe76e4a433c8675/f424fb7dc3baeb10
...and here for an example of a thread-safe but synchronization-free solution.
http://blog.tomgibara.com/post/208684592/avoiding-starvation
Before stumbling upon the above, I changed my code to pass motion events between threads using a ConcurrentLinkedQueue, which also seemed effective in eliminating the stalls.
.. You are acquiring 3 different locks on every loop of your game loop! You HAVE to think of a better design in which you need no locks. This will bring your game performance to a crawl.
having some trouble with a Thread (CanvasThread) that is intermittently pausing at random points within my application. Everything else in the app continues to function as necessary, it's simply this thread that randomly blocks out for some reason and doesn't draw anything new to the screen. I noticed that Surface.lockCanvasNative() seems to be the last function called before the block, and the first one returned after. In a pattern as such:
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 26,560 msec ____
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 40,471 msec ____|
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 40,629 msec ____
Surface.lockCanvasNative (Landroid/graphics/Rect)Landroid/graphics/Canvas; # 54,516 msec ____|
This is evident with the traceview below:
I have been using the CanvasThread.run() below if it helps:
#Override
public void run() {
boolean tellRendererSurfaceChanged = true;
/*
* This is our main activity thread's loop, we go until
* asked to quit.
*/
while (!mDone) {
/*
* Update the asynchronous state (window size)
*/
int w;
int h;
synchronized (this) {
// If the user has set a runnable to run in this thread,
// execute it and record the amount of time it takes to
// run.
if (mEvent != null) {
mEvent.run();
}
if(needToWait()) {
while (needToWait()) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
if (mDone) {
break;
}
tellRendererSurfaceChanged = mSizeChanged;
w = mWidth;
h = mHeight;
mSizeChanged = false;
}
if (tellRendererSurfaceChanged) {
mRenderer.sizeChanged(w, h);
tellRendererSurfaceChanged = false;
}
if ((w > 0) && (h > 0)) {
// Get ready to draw.
// We record both lockCanvas() and unlockCanvasAndPost()
// as part of "page flip" time because either may block
// until the previous frame is complete.
Canvas canvas = mSurfaceHolder.lockCanvas();
if (canvas != null) {
// Draw a frame!
mRenderer.drawFrame(canvas);
mSurfaceHolder.unlockCanvasAndPost(canvas);
//CanvasTestActivity._isAsyncGoTime = true;
}
else{
Log.v("CanvasSurfaceView.CanvasThread", "canvas == null");
}
}
}
}
Just let me know if I can provide any other useful information. I'm simply looking for clues as to why my thread might be blocking at this point? Thanks for any help in advance!
I've since narrowed the block down to mSurfaceHolder.unlockCanvasAndPost(canvas); I inserted a log before and after this call and the one after is not logged after app is frozen; but the log before is last logged event on this thread. It's not pausing or using a null canvas either, because I threw in logs for those instances as well; which are not logged even once until app is done.
I'm not sure if this could be the reason, but under SurfaceHolder.lockCanvas(), it warns that,
If you call this repeatedly when the
Surface is not ready (before
Callback.surfaceCreated or after
Callback.surfaceDestroyed), your calls
will be throttled to a slow rate in
order to avoid consuming CPU.
If null is not returned, this function
internally holds a lock until the
corresponding
unlockCanvasAndPost(Canvas) call,
preventing SurfaceView from creating,
destroying, or modifying the surface
while it is being drawn. This can be
more convenient than accessing the
Surface directly, as you do not need
to do special synchronization with a
drawing thread in
Callback.surfaceDestroyed.
I'm not sure what the threshold is when the CPU starts throttling. How many threads are refreshing the canvas?
btw,
if(needToWait()) {
while (needToWait()) {
is redundant
I have since figured out my problem. I'm not sure why but because I had accidentally forgot to fully comment out an earlier asyncTask(), thus had two doing roughly the same tasks and obviously struggling to do so with the same variables and such. Thanks for your pointers, but simply another careless mistake on my part I guess.