thanks for the reply, but still jerking, is my code so wrong?
tested on a galaxy note 2, the garbage collector seems not be the problem (cause no log about it)
Is there some specifics implementations for ANDROID to have a smooth render?
Best regards
The Game Loop:
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
// The Main thread which contains the game loop. The thread must have access to the surface view and holder to trigger events every game tick.
public class GameLoopThread extends Thread
{
private static final String TAG = GameLoopThread.class.getSimpleName();
private SurfaceHolder surfaceHolder; // Surface holder that can access the physical surface
private GameView gameview; // The actual view that handles inputs and draws to the surface
private Canvas canvas;
final int TARGET_FPS = 40;
final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
private static boolean running; // flag to hold game state
int lastFpsTime;
int fps;
public GameLoopThread(SurfaceHolder surfaceHolder, GameView gameview)
{
super();
this.surfaceHolder = surfaceHolder;
this.gameview = gameview;
}
public static void setRunning(boolean runningstate)
{
running = runningstate;
}
@Override
public void run()
{
Log.d(TAG, "Starting game loop");
long lastLoopTime = System.nanoTime();
while (running) // keep looping round til the game ends
{
long now = System.nanoTime();
canvas = null;
try // try locking the canvas for exclusive pixel editing in the surface
{
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder)
{
// work out how long its been since the last update, this will be used to calculate how far the entities should move this loop
long updateLength = now - lastLoopTime;
lastLoopTime = now;
double delta = updateLength / ((double)OPTIMAL_TIME);
// update the frame counter
lastFpsTime += updateLength;
fps++;
// update our FPS counter if a second has passed since we last recorded
if (lastFpsTime >= 1000000000)
{
System.out.println("(FPS: "+fps+")");
lastFpsTime = 0;
fps = 0;
}
gameview.update(delta); // update game state
gameview.render(canvas); // render state to the screen draws the canvas on the panel
int sleeptime = (int)((lastLoopTime-System.nanoTime() + OPTIMAL_TIME)/1000000); // remember this is in ms, whereas our lastLoopTime etc. vars are in ns.
if(sleeptime>0)
{
try
{
Thread.sleep( sleeptime );
}
catch (InterruptedException e) {}
}
else
{
// WHAT TO DO HERE??? nothing ???
//Log.d("sleeptime" , String.valueOf(sleeptime));
}
}
}
finally
{
if (canvas != null) // in case of an exception the surface is not left in an inconsistent state
{
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
The Sprite class
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.Log;
public class SingleBlock {
private Context context;
// sprites
public ArrayList<Bitmap> bmp_sprite = new ArrayList<Bitmap>();
// data
public static double x = 0; // x position of the sprite
public static double y = 50; // y position of the sprite
public int speed = 10; // speed of the sprite
public int direction = -1; // -1 means moving to the left, and 1 means movingto the right
//________________________
// constructeur |
//________________________|
public SingleBlock(Context context)
{
this.context = context;
LoadSprite();
}
public void update(double delta)
{
// movethe sprite
if(direction == -1) { x-= speed*delta; }
if(direction == 1) {x+= speed*delta; }
//change direction
if(x<=0) { direction = 1; }
if(x>=1000) { direction = -1; }
}
//________________________
// Draw |
//________________________|
public void Draw(Canvas canvas)
{
canvas.drawBitmap(bmp_sprite.get(1), (int)x , (int)y , null);
}
//________________________
// LoadSprite |
//________________________|
public void LoadSprite()
{
for(int i = 0;i<= 2; i++)
{
AssetManager assetManager = context.getAssets();
InputStream inputStream = null;
try
{
inputStream = assetManager.open(ScreenConf.asset_sub_directory + "/" + "block" + String.valueOf(i) + ".png");
}
catch (IOException e) { e.printStackTrace(); }
Bitmap bmp_temp = BitmapFactory.decodeStream(inputStream);
bmp_sprite.add(bmp_temp);
}
}
}
And all other class:
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
// This is the main surface that handles the ontouch events and draws the image to the screen.
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = GameView.class.getSimpleName();
private GameLoopThread gameLoopThread;
public static Context context;
private static GameView gameview;
private static SingleBlock singleblock;
public GameView(Context context)
{
super(context);
this.context = context;
gameview = this;
getHolder().addCallback(this); // adding the callback (this) to the surface holder to intercept events
gameLoopThread = new GameLoopThread(getHolder(), this); // create the game loop thread
setFocusable(true); // make the GamePanel focusable so it can handle events
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
// at this point the surface is created and we can safely start the game loop
new ScreenConf(gameview);
StartNewGame();
GameLoopThread.setRunning(true);
gameLoopThread.start();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish this is a clean shutdown
boolean retry = true;
while (retry)
{
try
{
gameLoopThread.join();
retry = false;
}
catch (InterruptedException e)
{
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
public static void StartNewGame()
{
singleblock = new SingleBlock(context);
}
public void render(Canvas canvas)
{
if (canvas != null)
{
canvas.drawColor(Color.BLACK);
singleblock.Draw(canvas);
}
}
// This is the game update method. It iterates through all the objects and calls their update method if they have one or calls specific engine's update method./
public void update(double delta)
{
singleblock.update(delta);
}
}
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity
{
/** Called when the activity is first created. */
private static final String TAG = MainActivity.class.getSimpleName();
//##################################
// onCreate #
//##################################
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // requesting to turn the title OFF
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // making it full screen
setContentView(new GameView(this)); // set our GameView as the View
Log.d(TAG, "View added");
}
//##################################
// onResume #
//##################################
@Override
public void onResume()
{
Log.d(TAG, "onResume...");
super.onResume(); // Always call the superclass method first
}
//##################################
// onDestroy #
//##################################
@Override
protected void onDestroy()
{
Log.d(TAG, "Destroying...");
super.onDestroy();
}
//##################################
// onStop #
//##################################
@Override
protected void onStop()
{
Log.d(TAG, "Stopping...");
super.onStop();
}
}