I wanted to make some water ripples in my android application so I used this javascript code http://jsfiddle.net/esteewhy/5Ht3b/6/ converted into C code like it is shown here Live Wallpaper Water Ripple Effect (by esteewhy).
I have a main activity, which calls the water ripples activity after clicking a button. The water ripples are working great with my mutable bitmap, the problem is when I clicked the "back" button of the android device, the system crashes throwing this error:
FATAL EXCEPTION: Thread-2556
java.lang.NullPointerException
at com.wheelly.whater.WaterView.onDraw (line 149)
at com.wheelly.whater.WaterView$GameThread.run (line 255)
I guess there is some problem handling the Thread or the activities themselves.
Here is the code of the Water Ripples:
public class WaterView extends SurfaceView implements SurfaceHolder.Callback {
GameThread thread;
public static Bitmap icon;
//Measure frames per second.
long now;
int framesCount=0;
int framesCountAvg=0;
long framesTimer=0;
Paint fpsPaint=new Paint();
//Frame speed
long timeNow;
long timePrev = 0;
long timePrevFrame = 0;
long timeDelta;
private int width = 480;
private int height = 800;
private short riprad = 6;
boolean flip;
private short[] ripplemap, last_map;
Bitmap ripple;
private static final String TAG = null;
private Rippler rippler;
public WaterView(Context context) {
super(context);
initialize();
}
void initialize() {
//CB
icon = BitmapFactory.decodeResource(getResources(), R.drawable.sand100);
icon = convertToMutable(icon);
//--
rippler = new NativeRippler();
reinitgGlobals();
fpsPaint.setTextSize(30);
//Set thread
getHolder().addCallback(this);
setFocusable(true);
}
void reinitgGlobals() {
int size = width * (height + 2) * 2;
ripplemap = new short[size];
last_map = new short[size];
Bitmap texture = createBackground(width, height); // this creates a MUTABLE bitmap
ripple = texture;
_td = new int[width * height];
texture.getPixels(_td, 0, width, 0, 0, width, height);
_rd = new int[width * height];
}
void randomizer() {
final Random rnd = new Random();
final Handler disHAndler = new Handler();
final Runnable disturbWater = new Runnable() {
#Override
public void run() {
disturb(rnd.nextInt(width), rnd.nextInt(height));
disHAndler.postDelayed(this, 7000);
}
};
disHAndler.post(disturbWater);
}
private static Bitmap createBackground(int width, int height) {
Canvas cb = new Canvas (icon);
cb.save();
cb.restore();
return icon;
}
private Bitmap convertToMutable(Bitmap bitmap) {
try {
File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
int width_mutable = bitmap.getWidth();
int height_mutable = bitmap.getHeight();
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width_mutable*height_mutable*4);
bitmap.copyPixelsToBuffer(map);
bitmap.recycle();
bitmap = Bitmap.createBitmap(width_mutable, height_mutable, Config.ARGB_8888);
map.position(0);
bitmap.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
} catch (FileNotFoundException e) {
Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
} catch (IOException e) {
Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
}
return bitmap;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
reinitgGlobals();
}
#Override
protected void onDraw(android.graphics.Canvas canvas) {
super.onDraw(canvas);
newframe();
canvas.drawBitmap(ripple, 0, 0, null);
//Measure frame rate (unit: frames per second).
now=System.currentTimeMillis();
canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
framesCount++;
if(now-framesTimer>1000) {
framesTimer=now;
framesCountAvg=framesCount;
framesCount=0;
}
}
/**
* Disturb water at specified point
*/
private void disturb(int dx, int dy) {
rippler.disturb(dx, dy, width, height, riprad, ripplemap, flip);
}
int[] _td;
int[] _rd;
/**
* Generates new ripples
*/
private void newframe() {
System.arraycopy(_td, 0, _rd, 0, width * height);
flip = !flip;
rippler.transformRipples(height, width, ripplemap, last_map, _td, _rd, flip);
ripple.setPixels(_rd, 0, width, 0, 0, width, height);
}
#Override
public synchronized boolean onTouchEvent(MotionEvent event) {
disturb((int)event.getX(), (int)event.getY());
return true;
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
thread = new GameThread(getHolder(), this);
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
class GameThread extends Thread {
private SurfaceHolder surfaceHolder;
private WaterView gameView;
private boolean run = false;
public GameThread(SurfaceHolder surfaceHolder, WaterView gameView) {
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
public void setRunning(boolean run) {
this.run = run;
}
public SurfaceHolder getSurfaceHolder() {
return surfaceHolder;
}
#Override
public void run() {
Canvas c;
while (run) {
c = null;
//limit frame rate to max 60fps
timeNow = System.currentTimeMillis();
timeDelta = timeNow - timePrevFrame;
if ( timeDelta < 16) {
try {
Thread.sleep(16 - timeDelta);
}
catch(InterruptedException e) {
}
}
timePrevFrame = System.currentTimeMillis();
try {
c = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//call methods to draw and process next fame
gameView.onDraw(c);
}
} finally {
if (c != null) {
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
}
I found the solution:
I had to interrupt the thread in the onPause method of the activity instead of doing it in the onStop method. Now it is just working fine.
Thanks a LOT to all of you for helping me with that problem.
you should shut down your working thread in the method Activity.onStop
#Override
protected void onStop() {
thread.interrupt();
super.onStop();
}
in the run method of GameThread check if thread was interrupted right before you call gameView.onDraw(c); like this:
if(isInterrupted()) {
break;
}
gameView.onDraw(c);
BTW, probably it would be better if you replace gameView.onDraw(c) with gameView.invalidate();
Don't call onDraw directly on a view, but rather call gameView.invalidate()
synchronized (surfaceHolder) {
//call methods to draw and process next fame
gameView.invalidate();
}
If it doesn't work, try using debugger with a breakpoint to see which variable is null exactly
Related
I am trying to set a sprite image as a background
and I didn't success to set the image size to screen size.
I'm trying this:
public class Game extends SurfaceView implements Runnable {
private SurfaceHolder holder;
private boolean isRunning = false;
private Thread gameThread;
private Sprite s;
private int screenWidth;
private int screenHeight;
Canvas canvas;
// private Sprite[] sprites;
private final static int MAX_FPS = 40; //desired fps
private final static int FRAME_PERIOD = 1000 / MAX_FPS; // the frame period
public Game(Context context) {
super(context);
holder = getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
screenWidth = width;
screenHeight = height;
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
//====== not working
// Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
// int sWidth = display.getWidth();
// int sHeight = display.getHeight();
//====== not working
// DisplayMetrics dm = new DisplayMetrics();
// ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
// int sWidth = dm.widthPixels;
// int sHeight = dm.heightPixels;
//====== not working
// screenHeight=canvas.getHeight();
// screenWidth=canvas.getWidth();
s=new Sprite(0, 0, BitmapFactory.decodeResource(this.getResources(), R.mipmap.back));
}
/**
* Start or resume the game.
*/
public void resume() {
isRunning = true;
gameThread = new Thread(this);
gameThread.start();
}
/**
* Pause the game loop
*/
public void pause() {
isRunning = false;
boolean retry = true;
while (retry) {
try {
gameThread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
}
class Sprite {
int x;
int y;
int directionX = 1;
int directionY = 1;
int speed = 10;
int color = 0;
Bitmap image;
public Sprite(int x, int y) {
this.x = x;
this.y = y;
}
public Sprite(int x, int y, Bitmap image) {
this(x, y);
this.image = image;
}
public Sprite(int x, int y, Bitmap image, int color) {
this(x, y, image);
this.color = color;
}
}
protected void step()
{
//blablabla
}
protected void render(Canvas canvas) {
canvas.drawColor(Color.BLACK);
Paint p = new Paint();
canvas.drawBitmap(s.image,s.x,s.y,p);
}
#Override
public void run() {
while(isRunning) {
// We need to make sure that the surface is ready
if (! holder.getSurface().isValid()) {
continue;
}
long started = System.currentTimeMillis();
// update
step();
// draw
canvas = holder.lockCanvas();
if (canvas != null) {
render(canvas);
holder.unlockCanvasAndPost(canvas);
}
float deltaTime = (System.currentTimeMillis() - started);
int sleepTime = (int) (FRAME_PERIOD - deltaTime);
if (sleepTime > 0) {
try {
gameThread.sleep(sleepTime);
}
catch (InterruptedException e) {
}
}
while (sleepTime < 0) {
step();
sleepTime += FRAME_PERIOD;
}
}
}
}
This options doesn't work:
//====== not working
// Display display = ((Activity)context).getWindowManager().getDefaultDisplay();
// int sWidth = display.getWidth();
// int sHeight = display.getHeight();
//====== not working
// DisplayMetrics dm = new DisplayMetrics();
// ((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(dm);
// int sWidth = dm.widthPixels;
// int sHeight = dm.heightPixels;
//====== not working
// screenHeight=canvas.getHeight();
// screenWidth=canvas.getWidth();
So how I can get the canvas or current screen size?
And set the Sprite.image to full screen ?
Try this for size in pixels,
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Try this when you decode your bitmap.
final int width = context.getResources().getDisplayMetrics().widthPixels;
final int height = context.getResources().getDisplayMetrics().heightPixels;
BitmapFactory.Options options = new BitmapFactory.Options();
options.outHeight = height;
options.outWidth = width;
Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher, options)
I am trying to use 15 to 20 images to show on livewallpaper background one by one on a fix time ,but I am facing grow heap problem (Grow heap (frag case) to 8.987MB for 6220816-byte allocation) I have search everything but couldn't get through this
Any help pleas.
here is my code
public class LiveWallpaperService extends WallpaperService {
private float x;
private int y;
private int angle;
private int speed;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public Engine onCreateEngine() {
stopSelf();
return new MyWallpaperEngine();
}
class MyWallpaperEngine extends Engine {
private final Handler handle = new Handler();
private final Runnable drawRunner = new Runnable() {
#Override
public void run() {
draw();
}
};
private Boolean visble = true;
public Bitmap mBackgroundImage;
public String name;
int[] mImagesArray;
private int mImagesArrayIndex = 0;
MyWallpaperEngine() {
mImagesArray = new int[] { R.drawable.love_1_mini,
R.drawable.love_2_mini, R.drawable.love_3_mini,
R.drawable.love_4_mini, R.drawable.love_5_mini,
R.drawable.love_6_mini, R.drawable.love_7_mini,
R.drawable.love_8_mini, R.drawable.love_9_mini,
R.drawable.love_10_mini,
R.drawable.love_11_mini, R.drawable.love_12_mini,
R.drawable.love_13_mini, R.drawable.love_14_mini,
R.drawable.love_15_mini, R.drawable.love_16_mini,
R.drawable.love_17_mini, R.drawable.love_18_mini,
R.drawable.love_19_mini, R.drawable.love_20_mini };
if (mImagesArrayIndex == 0) {
x = -330; // initialize x position
y = 0; // initialize y position
} else {
x = -330; // initialize x position
y = 0; // initialize y position
}
}
#Override
public Bundle onCommand(String action, int x, int y, int z,
Bundle extras, boolean resultRequested) {
return super.onCommand(action, x, y, z, extras, resultRequested);
}
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
#Override
public void onOffsetsChanged(float xOffset, float yOffset,
float xOffsetStep, float yOffsetStep, int xPixelOffset,
int yPixelOffset) {
draw();
}
#Override
public void onSurfaceCreated(SurfaceHolder holder) {
super.onSurfaceCreated(holder);
this.visble = false;
}
#Override
public void onTouchEvent(MotionEvent event) {
}
#Override
public void onVisibilityChanged(boolean visible) {
this.visble = visible;
if (visible) {
handle.post(drawRunner);
} else {
handle.removeCallbacks(drawRunner);
}
super.onVisibilityChanged(visible);
}
private void incrementCounter() {
mImagesArrayIndex++;
if (mImagesArrayIndex >= mImagesArray.length) {
mImagesArrayIndex = 0;
}
}
#SuppressWarnings("deprecation")
void draw() {
int count = mImagesArray[mImagesArrayIndex];
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
int scale = 2;
Resources res = getResources();
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inPreferredConfig = Bitmap.Config.RGB_565;
o2.inSampleSize = scale;
o2.inPurgeable = true;
o2.inInputShareable = true;
Bitmap image = BitmapFactory.decodeResource(res, count, o2);
Calendar mCalendar = Calendar.getInstance();
int h = mCalendar.get(Calendar.HOUR);
int m = mCalendar.get(Calendar.MINUTE);
int s = mCalendar.get(Calendar.SECOND);
if (s == 10) {
mImagesArrayIndex = 0;
}
try {
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
c = holder.lockCanvas();
c.drawColor(Color.RED);
if (c != null) {
int a = 0;
int counting = 0;
if (counting == a) {
a++;
counting++;
c.drawBitmap(image, x, y, paint);
int widthhhh = c.getWidth();
x = (float) (x + 10.10);
if (x > widthhhh + 400) {
image.recycle();
image = null;
incrementCounter();
}
}
paint.setColor(Color.RED);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} finally {
try {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
handle.removeCallbacks(drawRunner);
if (visble) {
handle.postDelayed(drawRunner, 10);
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
}
}
Bitmap image = BitmapFactory.decodeResource(getResources(), mImagesArray[mImagesArrayIndex]);
When your done with it call image.recycle() (see javadoc).
In your draw() method, you have this code:
Bitmap image = BitmapFactory.decodeResource(getResources(),
mImagesArray[mImagesArrayIndex]);
int count = mImagesArray[mImagesArrayIndex];
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
int scale = 4;
Resources res = getResources();
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inPreferredConfig = Bitmap.Config.RGB_565;
o2.inSampleSize = scale;
o2.inPurgeable = true;
o2.inInputShareable = true;
image = BitmapFactory.decodeResource(res, count, o2);
Notice the first line calls BitmapFactory.decodeResource() and assigns the result to variable image and then later, the last line again calls BitmapFactory.decodeResource() and assigns the result to variable image. You never called recycle() on the first decoded image, so this is giving you a memory leak.
guys. I'm playing around with making my very first Android game, but stumbled into a problem. The framerate seems to have random lag spikes. If I comment the background(s) out the framerate gets much smoother. I've looked around SO and can't find anything to solve my problems. I have a feeling it has something to do with allocating a specific amount of time every time I draw, but I don't know how to properly implement such a feature. Any suggestions? Btw, tryed hardware ac, anti etc.
This is the class that starts the surfaceview :
package com.example.glassrunner;
Imports Here
public class Game extends Activity
{
MySurfaceView mySurfaceView;
public SoundPool spool;
private int soundID;
int length=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
mySurfaceView = new MySurfaceView(this);
setContentView(mySurfaceView);
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
mySurfaceView.onResumeMySurfaceView();
}
#Override
protected void onPause()
{
// TODO Auto-generated method stub
super.onPause();
mySurfaceView.onPauseMySurfaceView();
}
#Override
protected void onDestroy()
{
super.onDestroy();
mySurfaceView = null;
}
}
This is the surfaceview class :
package com.example.glassrunner;
Imports here
public class MySurfaceView extends SurfaceView implements Runnable
{
public static boolean gameOver = false;
SurfaceHolder surfaceHolder;
Thread thread = null;
public Integer score=0;
public SoundPool spool;
private int soundID;
int length=0;
public static MediaPlayer mp;
volatile boolean running = false;
int Yposition = 450;
int Xposition = 50;
Paint textPaint;
long mLastTime;
Bitmap background;
Bitmap background2;
Bitmap lines;
Bitmap runSprite;
Bitmap box;
Paint bitmapPaint ;
Paint textPaint2;
Bitmap scaledBackground ;
Bitmap scaledBackground2 ;
Bitmap scaledLines ;
Bitmap scaledBox;
Canvas canvas;
Paint paint;
int SpX=0;
int SpY=0;
Bitmap[][] sprite;
/** Variables for the counter */
int frameSamplesCollected = 0;
int frameSampleTime = 0;
int fps = 0;
int speed = 5;
Toast GameOverToast;
Context context;
MediaPlayer mMediaPlayer;
public MySurfaceView(Context context)
{
super(context);
this.context = context;
// TODO Auto-generated constructor stub
surfaceHolder = getHolder();
surfaceHolder.setFormat(PixelFormat.RGB_565);
CharSequence text = "Game Over!";
int duration = Toast.LENGTH_SHORT;
GameOverToast = Toast.makeText(context, text, duration);
spool = new SoundPool(10, AudioManager.STREAM_MUSIC, 0);
soundID = spool.load(context, R.raw.jump, 1);
mp = MediaPlayer.create(context, R.raw.saturdaymorningfunk);
initialization();
}
public void initialization()
{
mp.setLooping(true);
mp.start();
Options options = new Options();
options.inSampleSize = 1/4;
options.inPreferredConfig = Bitmap.Config.RGB_565;
background=BitmapFactory.decodeResource(getResources(),R.drawable.background,options);
lines=BitmapFactory.decodeResource(getResources(),R.drawable.lines);// getting the png from drawable folder
background2=BitmapFactory.decodeResource(getResources(),R.drawable.background2,options);
runSprite=BitmapFactory.decodeResource(getResources(),R.drawable.runsprite);
box=BitmapFactory.decodeResource(getResources(),R.drawable.box);
bitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // tool for painting on the canvas
bitmapPaint.setAntiAlias(true);
bitmapPaint.setFilterBitmap(true);
textPaint = new Paint();
textPaint.setColor(Color.RED);
textPaint.setTextSize(32);
textPaint2 = new Paint();
textPaint2.setColor(Color.BLUE);
textPaint2.setTextSize(50);
scaledBackground = Bitmap.createScaledBitmap(background, 2560, 500, true);
scaledBackground2 = Bitmap.createScaledBitmap(background2, 2560, 400, true);
scaledLines = Bitmap.createScaledBitmap(lines, 2560, 30, true);
runSprite = Bitmap.createScaledBitmap(runSprite, 1400, 1000, true);
scaledBox = Bitmap.createScaledBitmap(box, 100, 100, true);
sprite = new Bitmap[4][7];
for(int row=0;row<=3;row++)
{
for(int col=0;col<=6;col++)
{
sprite[row][col] = Bitmap.createBitmap(runSprite, SpX, SpY, 200, 250);
SpX+=200;
}
SpX=0;
SpY+=250;
}
}
public void onResumeMySurfaceView()
{
mp.seekTo(length);
mp.start();
running = true;
thread = new Thread(this);
thread.start();
}
public void onPauseMySurfaceView()
{
mp.pause();
length=mp.getCurrentPosition();
boolean retry = true;
running = false;
while(retry){
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void onDestroyMySurfaceView()
{
mp.stop();
running = false;
thread = null;
thread.stop();
}
private void fps()
{
long now = System.currentTimeMillis();
if (mLastTime != 0)
{
//Time difference between now and last time we were here
int time = (int) (now - mLastTime);
frameSampleTime += time;
frameSamplesCollected++;
//After 10 frames
if (frameSamplesCollected == 10)
{
//Update the fps variable
fps = (int) (10000 / frameSampleTime);
//Reset the sampletime + frames collected
frameSampleTime = 0;
frameSamplesCollected = 0;
}
}
mLastTime = now;
}
public boolean pressDown = false;
public long pressTime;
public boolean onTouchEvent(MotionEvent event)
{
if (event != null)
{
if (event.getAction() == MotionEvent.ACTION_DOWN)
{ if(Yposition == orgPos)
{
spool.play(soundID, 15, 15, 1, 0, 1f);
pressDown = true;
pressTime = System.currentTimeMillis();
}
}else if (event.getAction() == MotionEvent.ACTION_UP)
{
pressDown = false;
}
}
return true;
}
int x=0;
int y=100;
int x2=0;
int y2=20;
int row=0;
int col=0;
int limit = 100;
int orgPos = 450;
int Xbox = 1280;
int Ybox = 580;
Random r = new Random();
int RBox;
public static String Fscore;
boolean onTop = false;
long now;
long start;
long stop;
long time ;
int spritePosition = 0 ;
int spriteSize;
#Override
public void run()
{
while(running)
{
canvas = null;
if(surfaceHolder.getSurface().isValid())
{
canvas = surfaceHolder.lockCanvas();
fps(); // fps
// Update screen parameters
update();
draw();
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
public void update()
{
if(score<500)
{
speed = 7;
}
else if(score%500 == 0)
{
speed = 7 + (score / 500);
}
if(col==6)
{
row++;
col=0;
}
if(row==4)
{
row=0;
}
score++;
Fscore = score.toString();
if(x>-1280)
{
x-=speed;
}else if(x<=-1280)
{
x=0;
}
if(x2>-1280)
{
x2-=5;
}else if(x2<=-1280)
{
x2=-0;
}
RBox = r.nextInt(999)+1280;
if(Xbox > -100)
{
Xbox-=speed;
}else if(Xbox<=-100)
{
Xbox=RBox;
}
if( (Xposition + 200 == Xbox +40 )&&(Yposition + 250 > Ybox+20)||( Xposition+200<=Xbox+70)&&( Xposition+200>=Xbox+20)&&(Yposition + 250 > Ybox+30) ) // collision
{
GameOverToast.show();
running = false;
spool.release();
mp.release();
Looper.prepare();
Intent database = new Intent(context, MainHighscore.class);
database.putExtra("score", Fscore);
context.startActivity(database);
onDestroyMySurfaceView();
}
now = System.currentTimeMillis();
if(( now - pressTime) <= 600)
{
if(Yposition > limit)
{
Yposition -= 10;
}
}
onTop = false;
if((now - pressTime) >= 600 && (now - pressTime) <= 1200)
{
if(!(Yposition == orgPos))
{
if(Yposition+250 >= Ybox && Xposition+200>=Xbox+70 && Xposition <= Xbox+40)
{
onTop=true;
Yposition = 340;
}else
{
Yposition += 10;
}
}
}
if((now - pressTime) >= 1200)
{
if(Yposition < 450) Yposition +=10;
else Yposition = 450;
}
}
public void draw()
{
canvas.drawColor(Color.WHITE);
//canvas.drawBitmap(scaledBackground, x2,y2, bitmapPaint);
canvas.drawBitmap(scaledBackground2, x,y, bitmapPaint);
canvas.drawBitmap(scaledLines, x,650, bitmapPaint);
canvas.drawText(Fscore, 1050, 50, textPaint2);
canvas.drawText(fps + " fps", getWidth() / 2, getHeight() / 2, textPaint);
canvas.drawBitmap(sprite[row][col],Xposition,Yposition,bitmapPaint );
canvas.drawBitmap(scaledBox,Xbox,Ybox,bitmapPaint);
col++;
}
}
I think your problem might be actually the moving part. Your just drawing too much stuff, and the surfaceView is not meant for that.
Hi in my application I have an explosion animation that comes up extremely often. when creating an explosion I load 3 images from resources then once the explosion animation is over I recycle those 3 images. I am constantly doing this and have noticed framerate drops. Is their a better way of doing this like static bitmaps or something?
I once made an application, a simple canvas on which bombs exploded.
I used a tiled bitmap composed of each step of the explosion and drew only a part of it, which changes often in order to create the animation effect.
It updates steps automatically following currentTimestamp
So this is the explosion class:
public class ExplosionAnimated {
private static final String TAG = ExplosionAnimated.class.getSimpleName();
private Bitmap mBitmap;
private Rect mSourceRect;
private int mFrameCountX;
private int mFrameCountY;
private int mCurrentFrame;
private long mFrameTicker;
private int mFramePeriod;
private int mSpriteWidth;
private int mSpriteHeight;
private int mX;
private int mY;
private boolean mFinished = false;
public ExplosionAnimated(Bitmap pBitmap, int pX, int pY,
int pFrameCountX, int pFrameCountY, int pFps) {
this.mBitmap = pBitmap;
this.mX = pX;
this.mY = pY;
this.mCurrentFrame = 0;
this.mFrameCountX = pFrameCountX;
this.mFrameCountY = pFrameCountY;
this.mSpriteWidth = pBitmap.getWidth() / pFrameCountX;
this.mSpriteHeight = pBitmap.getHeight() / pFrameCountY;
this.mSourceRect = new Rect(0, 0, this.mSpriteWidth, this.mSpriteHeight);
this.mFramePeriod = 1000 / pFps;
this.mFrameTicker = 0l;
}
public void update(long gameTime) {
if (gameTime > this.mFrameTicker + this.mFramePeriod) {
this.mFrameTicker = gameTime;
this.mCurrentFrame++;
if (this.mCurrentFrame >= this.mFramePeriod) {
this.mCurrentFrame = 0;
this.mFinished = true;
}
}
if (!this.mFinished) {
this.mSourceRect.left = this.mCurrentFrame * this.mSpriteWidth;
this.mSourceRect.right = this.mSourceRect.left + this.mSpriteWidth;
}
}
public void draw(Canvas canvas) {
Rect destRect = new Rect(this.mX, this.mY,
this.mX + this.mSpriteWidth,
this.mY + this.mSpriteHeight);
canvas.drawBitmap(this.mBitmap, this.mSourceRect, destRect, null);
}
public boolean isFinished() {
return this.mFinished;
}
}
These are methods from an object (Bomb.java for exemple) that starts explosion and draw it:
public void explode(Context pContext, Canvas pCanvas) {
this.mState = State.EXPLODING;
this.mExplosion = new ExplosionAnimated(this.mExplosionBitmap,
(int) this.mX, (int) this.mY, 7, 3, 7);
}
public void doDraw(Canvas pCanvas) {
if (this.mState == State.EXPLODING) {
if (this.mExplosion.isFinished()) {
this.mState = State.EXPLODED;
} else {
this.mExplosion.update(System.currentTimeMillis());
this.mExplosion.draw(pCanvas);
}
} else {
pCanvas.drawBitmap(this.mBombBitmap, this.mX, this.mY, null);
}
}
I used a Thread and a SurfaceView to continually draw the bomb (or the explosion), giving just the currentTimestamp to update the explosion.
I hope it helps, and if you need I can show and explain more code
I'm learning android and java and am trying to learn how to make a live wallpaper
I am using the aquarium tutorial, I put it all in one file instead of spreading it but what I am trying to do is get one scale number for everything, I want to get the height of the screen divide that by the background image height (which will always be bigger), this should give me a ratio to use for scaling everything, I tried this in a surfaceView code and it worked perfect, I tried it in livewallpaper and nada.
In the surfaceView test I use onSizeChange and got the height that way and it worked no problems.
This is what I did for the livewallpaper one
This is what I put for the onSurfaceChange
public int screenWidth;
public float rescaler;
public float totalHeight = theTemplate._backgroundImage.getHeight();
#Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//add rescale and width
screenWidth = width;
rescaler = (float) (height / totalHeight);
super.onSurfaceChanged(holder, format, width, height);
}
The background image comes from an inner class named TheTemplate
This is what I did there
The variables
private SpriteAnimationActivityEngine theTest;
private Bitmap theBackgroundImage;
private float theScaler = theTest.rescaler;
then I try and rescale it using theScaler
public void initialize(Context context, SurfaceHolder surfaceHolder) {
this.spriteThread = new SpriteThread(this);
this._surfaceHolder = surfaceHolder;
this._sprites = new ArrayList<Renderable>();
this._context = context;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this._backgroundImage = BitmapFactory.decodeResource(context.getResources(),ca.samsstuff.testonepagewallpaper.R.drawable.sky, options);
this.theBackgroundImage = Bitmap.createScaledBitmap(_backgroundImage, (int) (theScaler * _backgroundImage.getWidth()), (int) (theScaler * _backgroundImage.getHeight()), true);
this.addSprites();
}
Then it passes it to draw the background
private void renderBackGround(Canvas canvas) {
canvas.drawBitmap(this.theBackgroundImage, 0, 0, null);
}
Which sends it to draw
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._sprites) {
renderable.render(canvas);
}
}
I keep getting errors and don't know what I am doing wrong. Like I said I am learning both android and java but this method worked in another test I did but I got the screen height from onSizeChange can I get the height by using onSurfaceChange?
Any help would be appreciated
Thanks in advance
sam
EDIT
I also tried to rescale within the theTemplate class just to see if it would work having everything within its own class and still having issues, I used the DisplayMetrics to get the screen height this time.
This might work if I can get it going properly.
Here is this attempt
public class TheTemplate {
private SpriteThread spriteThread;
private SurfaceHolder _surfaceHolder;
private ArrayList<Renderable> _sprites;
public Bitmap _backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.sky);;
private Context _context;
// add rescale stuff
private Bitmap theBackgroundImage;
private float theScaler = initFrameParams() / _backgroundImage.getHeight();
private Bitmap oneBackImage = Bitmap.createScaledBitmap(_backgroundImage, (int) (theScaler * _backgroundImage.getWidth()), (int) (theScaler * _backgroundImage.getHeight()), true);
int initFrameParams()
{
//get the screen height to use to rescale everything
DisplayMetrics metrics = new DisplayMetrics();
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
display.getMetrics(metrics);
int screenHeight = display.getHeight();
return screenHeight;
}
public void render(){
Canvas canvas = null;
try{
canvas = this._surfaceHolder.lockCanvas(null);
synchronized (this._surfaceHolder) {
this.onDraw(canvas);
}
}finally{
if(canvas != null){
this._surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._sprites) {
renderable.render(canvas);
}
};
public void start(){
this.spriteThread.switchOn();
}
public void stop(){
boolean retry = true;
this.spriteThread.switchOff();
while (retry) {
try {
this.spriteThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
public int getLeft() {
return 0;
}
public int getRight() {
return this.theBackgroundImage.getWidth();
}
public void initialize(Context context, SurfaceHolder surfaceHolder) {
this.spriteThread = new SpriteThread(this);
this._surfaceHolder = surfaceHolder;
this._sprites = new ArrayList<Renderable>();
this._context = context;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this.theBackgroundImage = oneBackImage;
this.addSprites();
}
private void addSprites() {
Point startPoint = new Point(100, 100);
this._sprites.add(new SpriteOne(this._context, this, startPoint, 90));
Point startPoint1 = new Point(100, 300);
this._sprites.add(new SpriteOne(this._context, this, startPoint1, 50));
Point startPoint2 = new Point(200, 200);
this._sprites.add(new SpriteOne(this._context, this, startPoint2, 15));
}
private void renderBackGround(Canvas canvas)
{
canvas.drawBitmap(this.theBackgroundImage, 0, 0, null);
}
}
As I stated before any help would be appreciated.
Thanks again in advance
Sam
Edit SOLVED Here is an answer I came up with, The rescale code is where the comments are
Hope this helps someone out.
public class TheTemplate {
private SpriteThread spriteThread;
private SurfaceHolder _surfaceHolder;
private ArrayList<Renderable> _sprites;
public Bitmap _backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.sky);;
private Context _context;
// add rescale stuff
//private SpriteAnimationActivityEngine theTest;
private Bitmap theBackgroundImage;
private float totalHeight = _backgroundImage.getHeight();
private int screenSized = initFrameParams();
private float theScaler = (float) (screenSized / totalHeight);
private Bitmap oneBackImage = Bitmap.createScaledBitmap(_backgroundImage, (int) (theScaler * _backgroundImage.getWidth()), (int) (theScaler * _backgroundImage.getHeight()), true);
int initFrameParams()
{
DisplayMetrics metrics = new DisplayMetrics();
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
display.getMetrics(metrics);
int screenHeight = display.getHeight();
return screenHeight;
}
public void render(){
Canvas canvas = null;
try{
canvas = this._surfaceHolder.lockCanvas(null);
synchronized (this._surfaceHolder) {
this.onDraw(canvas);
}
}finally{
if(canvas != null){
this._surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._sprites) {
renderable.render(canvas);
}
};
public void start(){
this.spriteThread.switchOn();
}
public void stop(){
boolean retry = true;
this.spriteThread.switchOff();
while (retry) {
try {
this.spriteThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
public int getLeft() {
return 0;
}
public int getRight() {
return this.theBackgroundImage.getWidth();
}
public void initialize(Context context, SurfaceHolder surfaceHolder) {
this.spriteThread = new SpriteThread(this);
this._surfaceHolder = surfaceHolder;
this._sprites = new ArrayList<Renderable>();
this._context = context;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this.theBackgroundImage = oneBackImage;
this.addSprites();
}
private void addSprites() {
Point startPoint = new Point(100, 100);
this._sprites.add(new SpriteOne(this._context, this, startPoint, 90));
Point startPoint1 = new Point(100, 300);
this._sprites.add(new SpriteOne(this._context, this, startPoint1, 50));
Point startPoint2 = new Point(200, 200);
this._sprites.add(new SpriteOne(this._context, this, startPoint2, 15));
}
private void renderBackGround(Canvas canvas)
{
canvas.drawBitmap(this.theBackgroundImage, 0, 0, null);
}
}
I added the answer to the original post, hope this helps someone.
Sam
Sorry about that folks, new to this method of using a forum
Here is the answer, I added comments in the code where I made the changes for scaling images.
This method can also be used for positioning also.
Here is the answer.
public class TheTemplate {
private SpriteThread spriteThread;
private SurfaceHolder _surfaceHolder;
private ArrayList<Renderable> _sprites;
public Bitmap _backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.sky);;
private Context _context;
// add rescale stuff
//private SpriteAnimationActivityEngine theTest;
private Bitmap theBackgroundImage;
private float totalHeight = _backgroundImage.getHeight();
private int screenSized = initFrameParams();
private float theScaler = (float) (screenSized / totalHeight);
private Bitmap oneBackImage = Bitmap.createScaledBitmap(_backgroundImage, (int) (theScaler * _backgroundImage.getWidth()), (int) (theScaler * _backgroundImage.getHeight()), true);
int initFrameParams()
{
DisplayMetrics metrics = new DisplayMetrics();
Display display = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay();
display.getMetrics(metrics);
int screenHeight = display.getHeight();
return screenHeight;
}
public void render(){
Canvas canvas = null;
try{
canvas = this._surfaceHolder.lockCanvas(null);
synchronized (this._surfaceHolder) {
this.onDraw(canvas);
}
}finally{
if(canvas != null){
this._surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
protected void onDraw(Canvas canvas) {
this.renderBackGround(canvas);
for (Renderable renderable : this._sprites) {
renderable.render(canvas);
}
};
public void start(){
this.spriteThread.switchOn();
}
public void stop(){
boolean retry = true;
this.spriteThread.switchOff();
while (retry) {
try {
this.spriteThread.join();
retry = false;
} catch (InterruptedException e) {
// we will try it again and again...
}
}
}
public int getLeft() {
return 0;
}
public int getRight() {
return this.theBackgroundImage.getWidth();
}
public void initialize(Context context, SurfaceHolder surfaceHolder) {
this.spriteThread = new SpriteThread(this);
this._surfaceHolder = surfaceHolder;
this._sprites = new ArrayList<Renderable>();
this._context = context;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
this.theBackgroundImage = oneBackImage;
this.addSprites();
}
private void addSprites() {
Point startPoint = new Point(100, 100);
this._sprites.add(new SpriteOne(this._context, this, startPoint, 90));
Point startPoint1 = new Point(100, 300);
this._sprites.add(new SpriteOne(this._context, this, startPoint1, 50));
Point startPoint2 = new Point(200, 200);
this._sprites.add(new SpriteOne(this._context, this, startPoint2, 15));
}
private void renderBackGround(Canvas canvas)
{
canvas.drawBitmap(this.theBackgroundImage, 0, 0, null);
}
}