To use a SurfaceView for drawing a 2D game in Android, I use this in the main activity's onCreate():
setContentView(new GameView(this));
Which is a reference to this class:
public class GameView extends SurfaceView implements SurfaceHolder.Callback
Additionally, I have a thread with its run() function:
public void run() {
Canvas c;
while (_run) {
c = null;
try {
c = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
_panel.updatePhysics();
_panel.onDraw(c);
}
}
finally { // when exception is thrown above we may not leave the surface in an inconsistent state
if (c != null) {
_surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
In updatePhysics() I do some calculations. They are more complex than this simple example, of course, but work the same way:
public void updatePhysics() {
GraphicObject.Coordinates coord;
GraphicObject.Speed speed;
for (GraphicObject graphic : _allElements) {
coord = graphic.getCoordinates();
speed = graphic.getSpeed();
coord.setX(coord.getX() + speed.getX());
coord.setY(coord.getY() + speed.getY());
...
}
}
And in onDraw(), I draw everything to the canvas:
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(BITMAP, xPos, yPos, null);
...
}
This works fine - everything. And when I tested it on my device, it looked pretty good. But when I gave it to someone else and he did a test game, the objects were moving much faster! Why is this so? Because the thread calls updatePhysics() as often as possible which means that fast devices call this function more often?
How can I prevent this and make the game equally fast on all devices? Something like this?
private long lastRun = System.currentTimeMillis();
public void updatePhysics() {
long millisPassed = System.currentTimeMillis()-lastRun;
...
float newCoord = (coord.getX() + speed.getX()) * millisPassed / 33;
coord.setX(newCoord);
...
}
Thanks for your help!
If you can, use the time directly to calculate all your physics. That would usually work best.
If you have no way to calculate based on time because what you are doing that is just step based and you know that generating the next step does not take much time then you have another option.
You create two threads. The first one advances the state at a fixed rate (and you have to be sure that this works on slow devices at that rate too). The second one takes the current state is sees and draws that. Now the second thread can be as slow as it wants because it simply skips some states (or draws the same state several times if it is faster).
Small example below has one thread that advances some state object and replaces the reference each time so the consuming thread does not need to worry that it's state object gets modified
class GameState {
private int state = 0;
public GameState advanceState() {
GameState result = new GameState();
result.state = this.state + 1;
return result;
}
}
class SurfaceViewImplementation extends SurfaceView {
// the current state
volatile GameState mState = new GameState();
void somewhere() {
Thread fastProducer = new Thread(new Runnable() {
private static final long MAX_WAIT = 1000 / 60;
#Override
public void run() {
while (!Thread.interrupted()) {
long timeBefore = SystemClock.currentThreadTimeMillis();
GameState newState = mState.advanceState();
mState = newState;
long timeAfter = SystemClock.currentThreadTimeMillis();
long timeSpent = timeAfter - timeBefore;
SystemClock.sleep(Math.max(0, MAX_WAIT - timeSpent));
}
}
});
fastProducer.start();
Thread slowConsumer = new Thread(new Runnable() {
#Override
public void run() {
while (!Thread.interrupted()) {
GameState currentState = mState;
longRunningDraw(currentState);
}
}
});
slowConsumer.start();
}
}
That will still fail to give you a speed independant result if the producing thread can't run at the desired rate.
I would save the time when I start rendering the frame(in your case is updatePhysics()), and then next time I would got to this point, I know how much time pass, if it's to fast you can use Thread.Sleep();
Related
I'm writing a simple Whack a Mole clone, and I've got my UI elements declared in a GridLayout in a layout.xml, then assigned to ImageView variables in an array programmatically. I've got a startGame() method that simply takes a random int, pulls it from the array and causes it to go visible for a second, then repeats. For some reason, when I put this code in a while() loop, it causes my UI to go blank as soon as it's launched.
I know it's the while() loop because I tried taking the code out of the while() loop, and it ran correctly (once), but turns everything white when placed in a while loop.
Here's the method causing the problem:
public void startGame() {
gameStarted = true;
while(gameStarted) {
randomInt = rand.nextInt(11);
mole[randomInt].setVisibility(View.VISIBLE);
handler.postDelayed(new Runnable() {
#Override
public void run() {
mole[randomInt].setVisibility(View.INVISIBLE);
}
}, 5000);
}
}
All the other relevant code is in onCreate, it's otherwise just a skeleton Activity subclass.
public class WAM_Activity extends Activity {
private ImageView[] mole = new ImageView[11];
private int[] moleId = {R.id.mole1, R.id.mole3, R.id.mole4, R.id.mole5, R.id.mole6, R.id.mole7, R.id.mole8, R.id.mole9, R.id.mole10, R.id.mole11, R.id.mole12};
private boolean gameStarted;
private int randomInt = 0;
private Random rand = new Random();
Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.wam_view_layout);
for (int i = 0; i < 11; i++) {
mole[i] = (ImageView) findViewById(moleId[i]);
mole[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//do stuff eventually
}
});
}
gameStarted = true;
startGame();
}
Any idea why this isn't working? I've been staring at it for hours and I'm quite stumped.
Android doesn't work that way, when onCreate is called, it need to be finished in order for the app to keep responding, I'm surprised you are not getting any "App not respopnding" error.
If you want to create a "game loop" you can simply by creating a new Thread and putting the while in there.
Activity's lifecycle must be executed without blocking them for the app to operate correctly, for more info check here.
Do you know about threads? if you want i can post an example of how to do that with threads but it might be long and if you don't know what a Thread is it will be too confusing for you.
Edit: Ok I'll make an example of a Thread
When I create my games I usually have only one Activity that the only thing it does is creating a custom SurfaceView and nothing else.
public class GameActivity extends Activity
{
//This is a custom class that extends SurfaceView - I will write it below
private GameSurface game;
#Override
protected void onCreate(Bundle bundle)
{
super.onCreate(bundle);
//Create a new instance of the game
game = new GameSurface(this);
//Set the View of the game to the activity
setContentView(game);
}
}
You can also add extra stuff like onSaveInstanceState to save game data and restore them later but I don't want to add them now so the code looks simple.
This class was very simple, let's move on to our SurfaceView. The reason I picked a SurfaceView to do that it's because it is made to allow custom graphics to be drawn on it - exactly what we want on a video game. I will try to make the class as simple as possible:
/*SurfaceHolder.Callback will run some functions in our class when
our surface is completed - at that point we can initialize data
that have to do with the View's width/height.
I don't know if you've noticed that on a View's onCreate()
when you call getWidth() or getHeight() you get 0, that's because
the surface is not initialized yet, this is a way to fix that.
Also we need a Runnable to run the Thread inside this class,
no need to make more classes and make it more complicated*/
public class GameSurface extends SurfaceView
implements SurfaceHolder.Callback, Runnable
{
//This is our thread - we need the "running" variable to be
//able to stop the Thread manually, this will go inside our "while" loop
private Thread thread;
private boolean running;
//Right here you can add more variables that draw graphics
//For example you can create a new class that has a function that
//takes Canvas as a parameter and draws stuff into it, I will add
//a Rect in this case which is a class already made by android
//but you can create your own class that draws images or more
//complicated stuff
private Rect myRect;
//Rect needs a paint to give it color
private Paint myPaint;
//Constructor
public GameSurface(Context context)
{
super(context);
//This is the callback to let us know when surface is completed
getHolder().addCallback(this);
}
//When a class implements SurfaceHolder.Callback you are forced to
//create three functions "surfaceCreated", "surfaceChanged" and
//"surfaceDestroyed" these are called when the surface is created,
//when some settings are changed (like the orientation) and when
//it is about to be destroyed
#Override
public void surfaceCreated(Surface holder)
{
//Let's initialize our Rect, lets assume we want it to have 40
//pixels height and fill the screen's width
myRect = new Rect(0, 0, getWidth(), 40);
//Give color to the rect
myPaint = new Paint();
myPaint.setARGB(0, 255, 0, 0);
//In case you are not familiar with the Rect class, as
//parameters it gets Rect(left, top, right, bottom)
//Time to start our Thread - nothing much to explain here if
//you know how threads work, remember this class implements
//Runnable so the Thread's constructor gets "this" as parameter
running = true;
thread = new Thread(this);
thread.start();
}
//We won't use this one for now, but we are forced to type it
//Even if we leave it empty
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
//When the surface is destroyed we just want the Thread to
//terminate - we don't want threads running when our app is not visible!
#Override
public void surfaceDestroyed(SurfaceHolder holder)
//We will type this function later
{destroyThread();}
//Time for the interesting stuff! let's start with input
#Override
public boolean onTouchEvent(MotionEvent event)
{
//The logic is as follows: when our Rect is touched, we want
//it to become smaller
if (event.getAction() == MotionEvent.ACTION_DOWN)
{
if (myRect.contains((int) event.getX(), (int) event.getY())
{
myRect.right -= 5;
//Return true - we did something with the input
return true;
}
}
return super.onTouchEvent(event);
}
//This is our update, it will run once per frame
private void update()
{
//Let's assume we want our rect to move 1 pixel downwards
//on every frame
myRect.offset(0, 1);
}
//Now for our draw function
public void draw(Canvas canvas)
{
//Here we want to draw a background and our rect
canvas.drawARGB(0, 0, 0, 255);
canvas.drawRect(myRect, myPaint);
}
//The only thing left is our run() function for the Thread
#Override
public void run()
{
//Screen
Canvas canvas;
//Our game cycle (the famous while)
while(running)
{
//Count start time so we can calculate frames
int startTime = System.currentTimeMillis();
//Update our game
update();
//Empty screen so it can obtain new instance
canvas = null;
//Try locking the canvas for pixel editing on surface
try
{
//Try getting screen
canvas = getHolder().lockCanvas();
//Succeeded
if (canvas != null) synchronized (getHolder())
{
//Actual drawing - our draw function
draw(canvas);
}
} finally
{
//Draw changes
if (canvas != null) getHolder().unlockCanvasAndPost(canvas);
}
//End Frame - 1000/30 means 30 frames per second
int frameTime = System.currentTimeMillis() -startTime;
if (frameTime < 1000/30)
try { Thread.sleep(1000/30 -frameTime); } catch (InterruptedException e){}
}
}
//Last but not least, our function for closing the thread
private void destroyThread()
{
//Stop thread's loop
running = false;
//Try to join thread with UI thread
boolean retry = true;
while (retry)
{
try {thread.join(); retry = false;}
catch (InterruptedException e) {}
}
}
}
I may have made some minor mistakes (probably with case sensitive letters) so feel free to correct these, I wrote the code at once so I didn't have time to test it, it should work flawlessly though.
If you have any more questions, need more explanation or something is not working right let me know!
My project is to create a flip card game in Android like http://partyhatmy.blogspot.kr/2012/08/angry-bird-matching-card-game.html. The differences are that there will be 3 x 4 total 12 cards on the screen, and the game will have a timer. So if the timer expires or if the user finds all pairs, the new stage begins.
My problem is that I do know how to implement this using SurfaceView, but since all cards are at fixed positions, I think it might be possible to implement the game using layouts in xml, but I don't know how. Is there any starting point resource available on the web?
Edition 1
My Code is like this: I just first want to print the remaining time to one TextView to the screen. The problem is that the screen is all black (without runOnUiThread() invocation, the activity draws the given layout activity_game flawlessly.
public class GameActivity extends Activity {
private TextView mTimerTextView;
private int mRemainingTime = 30;
#Override
protected void onCreate(Bundled savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_game);
mTimerTextView = (TextView)findViewById(R.id.remaining_time);
this.runOnUiThread(new Runnable() {
public void run() {
long lastSystemTime = 0;
mTimeTextView.setText(String.valueOf(mRemainingTime));
while (mRemainingTime > 0) {
if (lastSystemTime == 0) { // initial run
lastSystemTime = System.currentTimeMillis();
continue;
}
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - lastSystemTime;
lastSystemTime = currentTime;
if (elapsedTime > 1000) {
mRemainingTime--;
mTimeTextView.setText(String.valueOf(mRemainingTime));
}
// To avoid excessive loop
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}
});
}
}
see this link it will help you with flip animation
Displaying card flip animation on old android
or
http://www.techrepublic.com/blog/software-engineer/use-androids-scale-animation-to-simulate-a-3d-flip/
use this coode to do changes in ui thread
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
//do ui changes here
}
});
I have a Pong game that I wrote in Java and I'm trying to port it to Android. (It's my first Android game ;).
In Java I used a Timer object which basically updated the Game values (ball/paddles position) and then redrew the screen.
I'm trying to implement the same functionality in Android but I'm getting a number of errors.
My program consists of a PongView class which is the visual part of the game, and a PongDriverActivity class which uses PongView for its view. If I have a looping thread invalidating PongView I get an error because the thread can't touch a view spawned on another thread.
I assume I need to do some sort of AsyncTask, but I'm not sure how to loop that.
Any suggestions on what would be the best way to implement this?
There's many ways to do this. Android does support normal Java Thread objects, and you can use those. You can search (SO) for android game loop and probably find lots of examples. But, if you'd like to try AsyncTask, here's a sample implementation:
public class PongView extends View {
public PongView(Context context) {
super(context);
}
public void setBallPosition(int x, int y) {
// relocate the ball graphic
}
}
Then, your Activity:
public class PongDriverActivity extends Activity implements OnSeekBarChangeListener {
private enum GameSpeed { SLOW, MEDIUM, FAST };
private PongView mGameView;
private PongDriverTask mWorker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGameView = new PongView(this);
mWorker = new PongDriverTask(GameSpeed.MEDIUM);
// if you need to pass some data to the worker:
mWorker.execute("one", "two", "three");
// else, declare the AsyncTask's first generic param as Void, and do:
//mWorker.execute();
setContentView(mGameView);
}
// you would connect this up to a button, to allow the user to stop
public void onStopClicked(View sender) {
if (mWorker.isRunning()) {
mWorker.stopRunning();
}
}
// you could connect this up to a slider, to allow the user to control the paddle
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// you may need to convert progress to a y-coordinate
mWorker.setPaddlePosition(progress);
}
And, then (maybe inside PongDriverActivity.java, an inner class):
private class PongDriverTask extends AsyncTask<String, Integer, Integer> {
private int mDelay;
private boolean mRunning = true;
private int mPaddleY = 0;
public PongDriverTask(GameSpeed speed) {
if (speed == GameSpeed.SLOW) {
mDelay = 100;
} else if (speed == GameSpeed.MEDIUM) {
mDelay = 50;
} else if (speed == GameSpeed.FAST) {
mDelay = 10;
}
}
public synchronized boolean isRunning() {
return mRunning;
}
public synchronized void stopRunning() {
mRunning = false;
}
public synchronized void setPaddlePosition(int y) {
mPaddleY = 0;
}
private synchronized int getPaddlePosition() {
return mPaddleY;
}
#Override
protected void onPostExecute(Integer score) {
super.onPostExecute(score);
// here you could show a UI that shows the final score
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mGameView.setBallPosition(0, 0);
// here you could throw up some UI that shows just
// before the game really starts
}
#Override
protected void onProgressUpdate(Integer... params) {
super.onProgressUpdate(params);
// retrieve updated game coordinates from params
mGameView.setBallPosition(params[0], params[1]); // x,y
}
#Override
protected Integer doInBackground(String... args) {
// If you have some information to pass to the background worker,
// it would be passed in args[0], args[1], etc.
// It doesn't have to be String data. you could change the generic
// to take something other than String as the first param, in which
// case, doInBackground() would take a variable length list of that
// other data type.
int ballX = 0;
int ballY = 0;
int score = 0;
while (isRunning()) {
// use your game engine to recalculate the ball position
// on this background thread
int paddleY = getPaddlePosition();
ballX += 1;
ballY += 2;
score++;
// now, update the UI, which must happen on the UI thread
publishProgress(ballX, ballY);
try {
Thread.sleep(mDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return score;
}
}
The task's doInBackground() method will be run on a background thread, so you must not directly modify the UI from there. But, all the other AsyncTask methods I override above are called on the UI thread, so it's safe to perform UI work in any of them.
This code assumes the paddle is controlled by a seek bar, which you'd make your Activity a change listener for. Lots of other ways to do it, too.
I just implemented a crude game stop mechanism, which you could hook up to a button. If you want to allow the user to pause and resume, you can search for implementations to pause/resume threads, which should be applicable here, too. For example:
How to Pause and Resume a Thread in Java from another Thread
More Reading ...
See this writeup for some discussion of game loop speeds ...
and maybe take a look at this discussion, too
I'm developing a 2d Game using Canvas/Surfaceview and have a problem with thread.
So what I want to accomplish is something in the background that is for example:
for each second SpawnEnemy() or ticking seconds or attacking.
I tried Thread.wait but that just cause pain and make my game 2fps.
here is my gameLoop:
import android.graphics.Canvas;
public class GameLoopThread extends Thread {
static final long FPS = 20;
private GameView view;
private boolean running = false;
public GameLoopThread(GameView view) {
this.view = view;
}
public void setRunning(boolean run) {
running = run;
}
#Override
public void run() {
long ticksPS = 1000 / FPS;
long startTime;
long sleepTime;
while (running) {
Canvas c = null;
startTime = System.currentTimeMillis();
try {
c = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
view.onDraw(c);
}
} finally {
if (c != null) {
view.getHolder().unlockCanvasAndPost(c);
}
}
sleepTime = ticksPS - (System.currentTimeMillis() - startTime);
try {
if (sleepTime > 0)
sleep(sleepTime);
else
sleep(10);
} catch (Exception e) {}
}
}
}
So I want something that is ticking in the background (seconds) that doesn't thread.wait.
You should make your thread run the game normally about 60fps, see this example: How can I use the animation framework inside the canvas?
If you want for each second to do something then you either count frames in onDraw(), on each 60th frame do it, or if you need a greater precision then in each onDraw() check the system time and do something when a second has elapsed.
You should also consider using two methods one for drawing onDraw() and another for the game logic which are called from your thread.
you could use AsyncTask for something to run in background
I based my game off of the lunar lander demo, although heavily modified, and I can get around 40-50fps but the problem is it fluctuates between 40-50fps so much that it causes the moving graphics to jitter! Its very annoying and makes my game look really shitty when in fact its running at a good frame rate.
I tried setting the thread priority higher but that just made it worse... now it will fluctuate between 40-60fps...
I was thinking of limiting the FPS to about 30 so that it will be constant. Is this a good idea and does anyone else have experience or a different solution?
Thanks!
This is my run loop
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
if(mMode == STATE_RUNNING){
updatePhysics();
}
doDraw(c);
}
} finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
private void updatePhysics() {
now = android.os.SystemClock.uptimeMillis();
elapsed = (now - mLastTime) / 1000.0;
posistionY += elapsed * speed;
mLastTime = now;
}
Don't base your game's logic (object movement, etc.) updating rate on the framerate. In other words, put your drawing and logic updating code in two separate components/threads. This way your game logic is completely independent from your framerate.
Logic updating should be based on how much time has passed since the last update (let's call it delta). Therefore, if you have an object moving at 1px/millisecond, then during each update your object should do something like this:
public void update(int delta) {
this.x += this.speed * delta;
}
So now even if your FPS lags, it won't affect your object's movement speed, since the delta will just be larger, making the object move farther to compensate (there are complications in some cases, but that's the gist of it).
And this is one way of calculating delta within your logic updating object (running in some thread loop):
private long lastUpdateTime;
private long currentTime;
public void update() {
currentTime = System.currentTimeMillis();
int delta = (int) (currentTime - lastUpdateTime);
lastUpdateTime = currentTime;
myGameObject.update(delta); // This would call something like the update method above.
}
Hope that helps! Please ask if you have any other questions; I've been making Android games myself. :)
Sample code:
Copy these two snippets (1 activity and 1 view) and run the code. The result should be a white dot smoothly falling down your screen, no matter what your FPS is. The code looks kinda complicated and long, but it's actually quite simple; the comments should explain everything.
This activity class isn't too important. You can ignore most of the code in it.
public class TestActivity extends Activity {
private TestView view;
public void onCreate(Bundle savedInstanceState) {
// These lines just add the view we're using.
super.onCreate(savedInstanceState);
setContentView(R.layout.randomimage);
RelativeLayout rl = (RelativeLayout) findViewById(R.id.relative_layout);
view = new TestView(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
10000, 10000);
rl.addView(view, params);
// This starts our view's logic thread
view.startMyLogicThread();
}
public void onPause() {
super.onPause();
// When our activity pauses, we want our view to stop updating its logic.
// This prevents your application from running in the background, which eats up the battery.
view.setActive(false);
}
}
This class is where the exciting stuff is!
public class TestView extends View {
// Of course, this stuff should be in its own object, but just for this example..
private float position; // Where our dot is
private float velocity; // How fast the dot's moving
private Paint p; // Used during onDraw()
private boolean active; // If our logic is still active
public TestView(Context context) {
super(context);
// Set some initial arbitrary values
position = 10f;
velocity = .05f;
p = new Paint();
p.setColor(Color.WHITE);
active = true;
}
// We draw everything here. This is by default in its own thread (the UI thread).
// Let's just call this thread THREAD_A.
public void onDraw(Canvas c) {
c.drawCircle(150, position, 1, p);
}
// This just updates our position based on a delta that's given.
public void update(int delta) {
position += delta * velocity;
postInvalidate(); // Tells our view to redraw itself, since our position changed.
}
// The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
new Thread() {
public void run() {
// Store the current time values.
long time1 = System.currentTimeMillis();
long time2;
// Once active is false, this loop (and thread) terminates.
while (active) {
try {
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
time2 = System.currentTimeMillis(); // Get current time
int delta = (int) (time2 - time1); // Calculate how long it's been since last update
update(delta); // Call update with our delta
time1 = time2; // Update our time variables.
}
}
}.start(); // Start THREAD_B
}
// Method that's called by the activity
public void setActive(boolean active) {
this.active = active;
}
}
I am thinking there might be, not really something wrong with some of the above code, but rather an inefficiency. I am talking about this code...
// The important part!
// This starts another thread (let's call this THREAD_B). THREAD_B will run completely
// independent from THREAD_A (above); therefore, FPS changes will not affect how
// our velocity increases our position.
public void startMyLogicThread() {
new Thread() {
public void run() {
// Store the current time values.
long time1 = System.currentTimeMillis();
long time2;
// Once active is false, this loop (and thread) terminates.
while (active) {
try {
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
time2 = System.currentTimeMillis(); // Get current time
int delta = (int) (time2 - time1); // Calculate how long it's been since last update
update(delta); // Call update with our delta
time1 = time2; // Update our time variables.
}
}
}.start(); // Start THREAD_B
}
Specifically, I am thinking about the following lines...
// This is your target delta. 25ms = 40fps
Thread.sleep(25);
It seems to me that just having the thread hang out doing nothing is a waste of valuable processing time, when in fact what you want to be doing is performing the updates, then, if the updates have taken less time than the 25 millis, then sleep the thread for the difference of what was used during the update and 25 millis (or whatever your chosen frame rate is). In this way the update will happen while the current frame is being rendered, and will be completed so the next frame update uses the updated values.
The only problem I can think of here is that some kind of syncronization will need to occur so that the current frame render does not use partially updated values. Perhaps update into a new instance of your set of values, and then make the new instance the current instance just before rendering.
I think I remember reading something in a graphics book about the goal being to perform as many updates as you can while staying within your desired frame rate, then, and only them, perform a screen update.
This of course will require one thread to drive the updates - if you use a SurfaceView, the render is controlled by this thread when you lock the canvas (in theory, according to my understanding anyway).
So, in code, it would be more like...
// Calculate next render time
nextRender = System.currentTimeInMillis() + 25;
while (System.currentTimeInMillis() < nextRender)
{
// All objects must be updated here
update();
// I could see maintaining a pointer to the next object to be updated,
// such that you update as many objects as you can before the next render, and
// then continue the update from where you left off in the next render...
}
// Perform a render (if using a surface view)
c = lockCanvas() blah, blah...
// Paint and unlock
// If using a standard view
postInvalidate();
Good luck and any feedback from anyone using this would surely help us all learn something...
rpbarbati
I think it's about Garbage collector
I would use SurfaceView instead of View if your game is action heavy. If you don't need to update the GUI rapidly then View is fine but for 2D games it's always better to use SurfaceView.
I have a similar issue, the jitter makes large object moves look uneven. Even though the "speed" is the same, different lengths of steps make the movements look jumpy.
Broody - You say a SurfaceView is beter, however, this is not true after Android 3.0 as the View is HW accelerated but the canvas returned by .lockCanvas is not.
Steven - Yes, this is likely causing poroblems, but is easy to detect.
/Jacob