I have problem with drawing on android. I'm trying to create simple game that includes robots which will paint screen on different colors. Firstly I'm trying only paint screen without any robot image (I'm planing place it later on higher transparent view) but I encountered problem with flashing canvas.
So this is my render method. I can't use every time canvas.drawColor(Color.BLACK); before drawing because as I said robots need to paint screen with own color (Red and Blue). Other idea was to store every robot location and paint it on every "render" method, but it takes too much time and after few secounds game becoming really slow.
public void render(Canvas canvas) {
if(lastD1X!=-1 && lastD1Y!=-1){
canvas.drawLine(lastD1X, lastD1Y, droid.getX(), droid.getY(), drawPaint);
}
lastD1X = droid.getX();
lastD1Y = droid.getY();
if(lastD2X!=-1 && lastD2Y!=-1){
canvas.drawLine(lastD2X, lastD2Y, droid2.getX(), droid2.getY(), drawPaint2);
}
lastD2X = droid2.getX();
lastD2Y = droid2.getY();
}
This is my game loop:
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
Related
I'm writing my very first android game. The graphics are primitive, some rectangles moving through the screen. The game seems smooth but with occasional stutters, every 0.5 - 1.0 secs, and I can't figure out what's causing it. I've already eliminated all the GC calls inside my draw / update routines, enabled hw acceleration in my surfaceView, activity and in the manifest file.
This is how my game loop looks like currently (taken from some sort of tutorial):
public void run() {
while (!terminated) {
if (!gameEngine.isRunning())
continue;
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
long beginTime = SystemClock.elapsedRealtime();
int framesSkipped = 0;
gameEngine.update();
if(canvas != null)
gameEngine.draw(canvas);
long timeDiff = SystemClock.elapsedRealtime() - beginTime;
int sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException ignored) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
gameEngine.update();
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
} finally {
if (canvas != null)
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
If I log the value of timeDiff at every frame, it stays in the 5-15 range, so it doesn't indicate the stutters.
I'm using SystemClock.elapsedRealtime() instead of System.currentTimeMillis() because I've read that System.currentTimeMillis isn't accurate enough. (Or should I be using System.nanoTime() ?)
This isn't the first solution I tried using, and none of them have so far solved my issue. So I suspect the problem may lie somewhere else. I appreciate any help!
Im trying to start a new intent from my gameView using this method:
if(happy.getHP() <= 0){ //if my main-character dies
thread.setRunning(false);
Context context = getContext();
Intent intent = new Intent("nielsen.happy.activities.ENDSCREEN");
context.startActivity(intent);
}
If I do it without thread.setRunning(false);, the endScreen comes up but its buttons wont work, but if i stop my mainThread they do work.
Anyway, the problem with this code is that when my character dies, it freezes for 3-4 seconds, then the endscreen flickers for a sec, then the gameView flickers for a sec, THEN the endscreen comes up for real and the buttons work.
When I start an Activity from another activity, like when I press "Start Game" in my menu, I dont get this problem. I really dont understand what happens here that makes it lag like this.
adding my thread below:
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// initialise timing elements for stat gathering
initTimingElements();
long beginTime; // the time when the cycle begun
float time1 = System.currentTimeMillis();
float time2;
//long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
adding whole method below:
private void checkCollision(Canvas canvas) {
Rect h1 = happy.getBounds();
for (int i = 0; i < enemies.size(); i++) {
Rect e1 = enemies.get(i).getBounds();
if (h1.intersect(e1)){
if(enemies.get(i).getX() < controls.pointerPosition.x){
enemies.get(i).setX(-20);
}else if (enemies.get(i).getX() > controls.pointerPosition.x){
enemies.get(i).setX(20);
}else if(enemies.get(i).getY() < controls.pointerPosition.y){
enemies.get(i).setY(-20);
}else if(enemies.get(i).getY() > controls.pointerPosition.y){
enemies.get(i).setY(20);
}
if(enemies.get(i).getCooldown() <= 0){
happy.damageHP(10);
score.incScore(-10);
enemies.get(i).setCooldown();
}
}
if(happy.getHP() <= 0){
thread.setRunning(false);
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Context context = getContext();
Intent intent = new Intent("nielsen.happy.activities.ENDSCREEN");
context.startActivity(intent);
//end-screen !!!!!!!
}
if(enemies.get(i).getHP() <= 0){
enemies.get(i).death(canvas, enemies);
score.incScore(5);
break;
}
for (int j = 0; j < bullets.size(); j++) {
Rect b1 = bullets.get(j).getBounds();
if (b1.intersect(e1)) {
enemies.get(i).damageHP(5);
bullets.remove(j);
}
}
}
}
I would have to see what your thread is doing to be sure, but I'm pretty sure something like this is happening:
You call setRunning(false) which sets some flag in the thread class.
You start the new intent but the thread hasn't stopped yet because it hasn't gotten to the part where it checks the running flag
The thread (which hasn't paused yet) starts making changes to the gui, this confuses the android UI which thinks that view should be inactive
The thread gets to the point where it's waiting for setRunning(true), and everything is happy again.
If this is true, you can fix by waiting until the thread is blocked on setRunning(true), then creating the new intent.
I'm aware that there is alot of posts on this subject but non really helped me with my problem. Im fairly new to android programming and built my thread with the helt of a tutorial, the problem is that my emulator dosnt go over 15 fps and the program should run at 50 (FPS is initialized at 50). Here is the code from my thread:
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
/* Stuff for stats */
private DecimalFormat df = new DecimalFormat("0.##"); // 2 dp
// we'll be reading the stats every second
private final static int STAT_INTERVAL = 1000; // ms
// the average will be calculated by storing
// the last n FPSs
private final static int FPS_HISTORY_NR = 10;
// last time the status was stored
private long lastStatusStore = 0;
// the status time counter
private long statusIntervalTimer = 0l;
// number of frames skipped since the game started
private long totalFramesSkipped = 0l;
// number of frames skipped in a store cycle (1 sec)
private long framesSkippedPerStatCycle = 0l;
// number of rendered frames in an interval
private int frameCountPerStatCycle = 0;
private long totalFrameCount = 0l;
// the last FPS values
private double fpsStore[];
// the number of times the stat has been read
private long statsCount = 0;
// the average FPS since the game started
private double averageFps = 0.0;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private MainGamePanel gamePanel;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, MainGamePanel gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
// initialise timing elements for stat gathering
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check
// if in next frame
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
/**
* The statistics - it is called every cycle, it checks if time since last
* store is greater than the statistics gathering period (1 sec) and if so
* it calculates the FPS for the last period and stores it.
*
* It tracks the number of frames per period. The number of frames since the
* start of the period are summed up and the calculation takes part only if
* the next period and the frame count is reset to 0.
*/
private void storeStats() {
frameCountPerStatCycle++;
totalFrameCount++;
// assuming that the sleep works each call to storeStats
// happens at 1000/FPS so we just add it up
// statusIntervalTimer += FRAME_PERIOD;
// check the actual time
statusIntervalTimer += (System.currentTimeMillis() - statusIntervalTimer);
if (statusIntervalTimer >= lastStatusStore + STAT_INTERVAL) {
// calculate the actual frames pers status check interval
double actualFps = (double) (frameCountPerStatCycle / (STAT_INTERVAL / 1000));
// stores the latest fps in the array
fpsStore[(int) statsCount % FPS_HISTORY_NR] = actualFps;
// increase the number of times statistics was calculated
statsCount++;
double totalFps = 0.0;
// sum up the stored fps values
for (int i = 0; i < FPS_HISTORY_NR; i++) {
totalFps += fpsStore[i];
}
// obtain the average
if (statsCount < FPS_HISTORY_NR) {
// in case of the first 10 triggers
averageFps = totalFps / statsCount;
} else {
averageFps = totalFps / FPS_HISTORY_NR;
}
// saving the number of total frames skipped
totalFramesSkipped += framesSkippedPerStatCycle;
// resetting the counters after a status record (1 sec)
framesSkippedPerStatCycle = 0;
statusIntervalTimer = 0;
frameCountPerStatCycle = 0;
statusIntervalTimer = System.currentTimeMillis();
lastStatusStore = statusIntervalTimer;
// Log.d(TAG, "Average FPS:" + df.format(averageFps));
gamePanel.setAvgFps("FPS: " + df.format(averageFps));
}
}
private void initTimingElements() {
// initialise timing elements
fpsStore = new double[FPS_HISTORY_NR];
for (int i = 0; i < FPS_HISTORY_NR; i++) {
fpsStore[i] = 0.0;
}
Log.d(TAG + ".initTimingElements()",
"Timing elements for stats initialised");
}
}
All my drawing method has is this:
public void render(Canvas canvas) {
canvas.drawColor(Color.BLACK);
happy.draw(canvas);
// display fps
displayFps(canvas, avgFps);
There is some updates in there aswell but thats just to get my sprite moving. If any1 can see a problem or has any tips PLEASE let me know.
Thank you!
}
Do not rely on your emulator for performance benchmarking. While the emulator is good for checking the functionality and correctness of your app, it can take more (or less) time to do certain operations than a real device would. In perticular drawing operations seem to be longer on the emulator. I had a game run at 9-16 fps on the emulator while going 50+ on an old real device.
If your app/game still runs too slow on a real device you can profile your app (with TraceView for example) to see exactly what is taking up all your time and try to either optimize it or remove it (if possible).
Just as an extra tip: if possible try to have your surfaceView format be the same as all the resources that go on it, and try to prescale everything you can in advance (either at compile-time or at app startup/initialization phase), scaling and converting are expensive operations to do each frame.
If using Windows, you might try BlueStacks. Here are some of my results:
*****************************************************
ARM Emulator 2.4 GHz Core 2 Duo
Android Java OpenGL Benchmark 06-Mar-2013 21.17
--------- Frames Per Second --------
Triangles WireFrame Shaded Shaded+ Textured
9000+ 2.66 3.97 3.29 1.61
18000+ 1.48 1.84 1.72 0.88
36000+ 0.82 0.95 0.87 0.46
Screen Pixels 320 Wide 480 High
*****************************************************
BlueStacks Emulator on 3 GHz Phenom
Android Java OpenGL Benchmark 06-Mar-2013 21.25
--------- Frames Per Second --------
Triangles WireFrame Shaded Shaded+ Textured
9000+ 41.72 42.02 32.90 20.72
18000+ 22.11 22.09 19.28 11.58
36000+ 11.16 11.14 10.35 5.89
Screen Pixels 1024 Wide 600 High
*****************************************************
Samsung EXYNOS 5250 Dual 2.0 GHz Cortex-A15, Android 4.2.2
Mali-T604 Quad Core GPU
Android Java OpenGL Benchmark 09-Aug-2013 09.42
--------- Frames Per Second --------
Triangles WireFrame Shaded Shaded+ Textured
9000+ 39.13 41.52 32.19 27.25
18000+ 22.03 20.73 19.69 16.30
36000+ 12.24 12.23 10.75 8.68
Screen Pixels 1920 Wide 1032 High
i'm currently writing a android game, but now, i'm having some strange issues with the controls
if i have a framelayout with all my controls(the HP, points, game controls..etc) and put it on top of my surfaceview, my fps will never get above 20 fps; if i remove the framelayout, the game will hit 50+fps.
I'm suspecting that the system is rendering my surfaceview and framelayout's content all together in one thread, which explains why it's so slow
here's my game loop code
public void run() {
Canvas c;
initTimingElements();
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (_run) {
c = null;
//long start = System.currentTimeMillis();
try {
c = _panel.getHolder().lockCanvas(null); // this is the problem!!
synchronized(_panel.getHolder()) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
_panel.updatePhysics();
_panel.checkForHits();
_panel.checkForKill();
_panel.checkForMCHits();
//if i put stuff related to ui in the loop, it will slow the control down to a unblelieveable state
//_panel.checkNPCQt();
_panel.onDraw(c);
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
/*
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
*/
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
//_panel.updatePhysics(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
// for statistics
framesSkippedPerStatCycle += framesSkipped;
// calling the routine to store the gathered statistics
storeStats();
}
} 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) {
_panel.getHolder().unlockCanvasAndPost(c);
//Log.d(TAG, "unlock canvas");
}
}
i've pretty sure i got all my game logic and bitmaps are right; if i comment out all onDraw functions and everything else, i still got only 20-23 fps with framelayout there.
is there any alternatives? can create a separate thread for the UI draw? or am i just doing it wrong?
here is my XML state:
Solved, i accidentally messed up the framelayout, clean project is the answer
I am having trouble in constructing a basic game loop for my game. I am at the early phase and all I want to do is to move the the ball at a constant speed (for ex: 3) through the bottom of the screen. The position of the ball is updated in the main panel update method and it is drawed in the render part as expected. Nothing exceptional in the mechanism. The ball moves but it is definitely not smooth, the visual is quiet disturbing.. I measured the implementation times of these methods and the total implementation time of this couple is about 3-4 milliseconds. Under these circumstances, what is the suitable FPS? Are the given constant values suitable What is missing or wrong in my mechanism? Thanks a lot in advance. the code block in main thread.
private final static int MAX_FPS = 200;
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
private final static int MAX_FRAME_SKIPS = 5;
public void run() {
initTimingElements();
long beginTime;
long timeDiff;
int sleepTime;
int framesSkipped;
long beginTime2;
long diff1;
long diff2;
sleepTime = 0;
Canvas canvas;
Log.d(TAG, "Starting game loop");
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0;
this.gamePanel.update();
diff1=System.currentTimeMillis()-beginTime;
beginTime2=System.currentTimeMillis();
this.gamePanel.render(canvas);
diff2=System.currentTimeMillis()-beginTime2;
timeDiff = System.currentTimeMillis() - beginTime;
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
this.gamePanel.update();
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
if (framesSkipped > 0) {
Log.d(TAG, "Skipped:" + framesSkipped);
}
framesSkippedPerStatCycle += framesSkipped;
storeStats();
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
Try using a TimerTask that fires off every 60ms or so.
That's what I do until I stop being lazy enough to do a proper implementation, but it works well.
Just remember that run() will be executed in a different thread, so use runOnUIThread() to run whatever code you need.