How do I use .equals properly for this? - android

So here is my code,
public class GameView extends SurfaceView {
private SurfaceHolder holder;
private GameLoopThread gameLoopThread;
private List<Sprite> sprites = new ArrayList<Sprite>();
private long lastClick;
public int d = 0;
public int color;
TextView tv;
public int score;
public GameView(Context context) {
super(context);
gameLoopThread = new GameLoopThread(this);
holder = getHolder();
holder.addCallback(new Callback() {
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
createSprites();
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format,
int arg2, int height) {
}
});
}
private void createSprites() {
int c = 10;
{
Random rnd = new Random();
color = Color.argb(255, rnd.nextInt(256), rnd.nextInt(256),
rnd.nextInt(256));
for (int b = 1; b <= c; b++) {
int random = (int) Math.ceil(Math.random() * 24);
if (random == 1) {
sprites.add(createSprite(R.drawable.bad1));
} else if (random == 2) {
sprites.add(createSprite(R.drawable.bad2));
} else if (random == 3) {
sprites.add(createSprite(R.drawable.bad3));
} else if (random == 4) {
sprites.add(createSprite(R.drawable.bad4));
} else if (random == 5) {
sprites.add(createSprite(R.drawable.bad5));
} else if (random == 6) {
sprites.add(createSprite(R.drawable.bad6));
} else if (random == 7) {
sprites.add(createSprite(R.drawable.bad7));
} else if (random == 8) {
sprites.add(createSprite(R.drawable.bad8));
} else if (random == 9) {
sprites.add(createSprite(R.drawable.bad9));
} else if (random == 10) {
sprites.add(createSprite(R.drawable.bad10));
} else if (random == 11) {
sprites.add(createSprite(R.drawable.bad11));
} else if (random == 12) {
sprites.add(createSprite(R.drawable.bad12));
} else if (random == 13) {
sprites.add(createSprite(R.drawable.bad13));
} else if (random == 14) {
sprites.add(createSprite(R.drawable.bad14));
} else if (random == 15) {
sprites.add(createSprite(R.drawable.bad15));
} else if (random == 16) {
sprites.add(createSprite(R.drawable.bad16));
} else if (random == 17) {
sprites.add(createSprite(R.drawable.bad17));
} else if (random == 18) {
sprites.add(createSprite(R.drawable.good1));
} else if (random == 19) {
sprites.add(createSprite(R.drawable.good2));
} else if (random == 20) {
sprites.add(createSprite(R.drawable.good3));
} else if (random == 21) {
sprites.add(createSprite(R.drawable.good4));
} else if (random == 22) {
sprites.add(createSprite(R.drawable.good5));
} else if (random == 23) {
sprites.add(createSprite(R.drawable.good6));
} else if (random == 24) {
sprites.add(createSprite(R.drawable.good7));
}
}
}
}
private Sprite createSprite(int resource) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
return new Sprite(this, bmp);
}
#SuppressLint({ "WrongCall", "DrawAllocation" })
#Override
public void onDraw(Canvas canvas) {
canvas.drawColor(color);
Paint paint = new Paint();
paint.setColor(Color.CYAN);
canvas.drawText("SCORE " + score, 10, 10, paint);
for (Sprite sprite : sprites) {
sprite.onDraw(canvas);
}
}
// this is the ontouch event to destroy the sprites and make the blood splat
// effect
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 200) {
lastClick = System.currentTimeMillis();
synchronized (getHolder()) {
float x = event.getX();
float y = event.getY();
for (int i = sprites.size() - 1; i >= 0; i--) {
Sprite sprite = sprites.get(i);
if (sprite.isCollition(x, y)) {
{
if ((sprites).equals (R.drawable.bad1))
score = score + 5;
else if ((sprites).equals(R.drawable.bad2))
score = score + 5;
else if ((sprites).equals(R.drawable.bad3))
score = score + 5;
else if ((sprites).equals(R.drawable.bad4))
score = score + 5;
else if ((sprites).equals(R.drawable.bad5))
score = score + 5;
else if ((sprites).equals(R.drawable.bad6))
score = score + 5;
else if ((sprites).equals(R.drawable.bad7))
score = score + 5;
else if ((sprites).equals(R.drawable.bad8))
score = score + 5;
else if ((sprites).equals(R.drawable.bad9))
score = score + 5;
else if ((sprites).equals(R.drawable.bad10))
score = score + 5;
else if ((sprites).equals(R.drawable.bad11))
score = score + 5;
else if ((sprites).equals(R.drawable.bad12))
score = score + 5;
else if ((sprites).equals(R.drawable.bad13))
score = score + 5;
else
score = score - 5;
}
d++;
if (d >= 10) {
d = 0;
createSprites();
}
break;
}
}
}
}
return true;
}
}
What I am trying to do is get,
if ((sprites).equals(R.drawable.bad1))
score = score + 5;
To check to see if somewhere in this code,
#Override
public boolean onTouchEvent(MotionEvent event) {
if (System.currentTimeMillis() - lastClick > 200) {
lastClick = System.currentTimeMillis();
synchronized (getHolder()) {
float x = event.getX();
float y = event.getY();
for (int i = sprites.size() - 1; i >= 0; i--) {
Sprite sprite = sprites.get(i);
Holds the value of one of the pics that are being deleted, but I am not sure how to code this properly. I am not sure if I need to place the pics into an array each time the randomizer runs or what but the code is taken from the "edu4java" tutorial from youtube.
I have the program on a loop as you can tell that I can delete the pics on touch and the score was right I just am not sure how to get the,
if ((sprites).equals(R.drawable.bad1))
score = score + 5;
To check to "see" the proper pic string. Do I need to check the array that the code "auto creates"? Is there a way to check and see what the value of a string is? Such as "seeing" what is actually being "held" by "sprite" or "sprites" ?

One problem is that you do not supply the source code for Sprite, but perhaps it looks like the code here? Given the code there, there is no neat solution to your problem with the class as it is.
So, how I would approach solving this problem is to add to each sprite a resource ID:
private Sprite createSprite(int resource) {
Bitmap bmp = BitmapFactory.decodeResource(getResources(), resource);
return new Sprite(this, bmp, resource);
}
Note that I add the extra resource parameter to the constructor. Furthermore, I would add to the Sprite class a method int Sprite.getResource(), so your collision detection code becomes:
if (sprite.isCollition(x, y))
{
if (sprite.getResource() == R.drawable.bad1)
score = score + 5;
else if (sprite.getResource() == R.drawable.bad2)
score = score + 5;
else ...
}
Note: this code is in no way optimal, but hopefully this will point you in the right direction to discover for yourself a better solution. Here in Stack Overflow we don't throw fishes, we teach people to fish.

You can't compare a bitmap with a resource Id, and actually trying to do it manually your self would end up in quiet exhaustive performance for a simple validation, what i would do and to keep it simple, i would create my own class that extends from Sprite, and in stead of passing the context and bitmap, i would pass the context and resource Id, then within this class i would decode the resource and keep a reference of the resource id, i would override the equal method from Sprites and use the reference used to create the object to do the comparison, this would be my class
public class MySprite extends Sprite{
private int bmpID;
public MySprite(Context context, int bmpID){
this.bmpID = bmpID;
Bitmap bmp = BitmapFactory.decodeResource(context.getResources(), bmpID);
super(context, bmp);
}
#Override
public boolean equals(Object o) {
if(!(o instanceof MySprite))return false;
return this.bmpID == MySprite.class.cast(o).getBmpId();
}
public int getBmpId(){
return bmpID;
}
}
This way i keep it as a simple int comparison, and most important you can use it to compare two objects of same bmpID, or something like what u wanted by doing this:
if ((sprites).getBmpId() == R.drawable.bad1))
score = score + 5;
Regards!

Related

Is it possible to get the new ImageDecoder class to return Bitmaps, one frame after another, manually?

Background
I'm trying to go over bitmaps of animated GIF&WEBP files manually (frame by frame), so that it would work not just for Views, but on other cases too (such as a live wallpaper).
The problem
Animated GIF/WEBP files are supported only from Android P, using ImageDecoder API (example here) .
For GIF, I wanted to try Glide for the task, but I've failed, so I've tried overcoming this, by using a library that allows to load them (here, solution here). I think it works fine.
For WebP, I thought I've found another library that could work on older Android versions (here, made fork here), but it seems that it can't handle WebP files well in some cases (reported here). I tried to figure out what's the issue and how to solve it, but I didn't succeed.
So, assuming that some day Google will support GIF&WEBP animation for older Android versions via the support library (they wrote it here), I've decided to try to use ImageDecoder for the task.
Thing is, looking in the entire API of ImageDecoder , it's quite restricted in how we should use it. I don't see how I can overcome its limitations.
What I've found
This is how ImageDecoder can be used to show an animated WebP on an ImageView (just a sample, of course, available here) :
class MainActivity : AppCompatActivity() {
#SuppressLint("StaticFieldLeak")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val source = ImageDecoder.createSource(resources, R.raw.test)
object : AsyncTask<Void, Void, Drawable?>() {
override fun doInBackground(vararg params: Void?): Drawable? {
return try {
ImageDecoder.decodeDrawable(source)
} catch (e: Exception) {
null
}
}
override fun onPostExecute(result: Drawable?) {
super.onPostExecute(result)
imageView.setImageDrawable(result)
if (result is AnimatedImageDrawable) {
result.start()
}
}
}.execute()
}
}
I've tried to read all of the documentations of ImageDecoder and AnimatedImageDrawable, and also look at its code, but I don't see how it's possible to manually go over each frame, and have the time that needs to be waited between them.
The questions
Is there a way to use ImageDecoder API to go over each frame manually, getting a Bitmap to draw and knowing how much time it's needed to wait between frames? Any workaround available? Maybe even using AnimatedImageDrawable ?
I'd like to do the same on older Android versions. Is it possible? If so how? Maybe on a different API/library? Google wrote it works on a way to use ImageDecoder on older Android versions, but I don't see it being mentioned anywhere (except for the link I've provided). Probably not ready yet... Android P didn't even reach 0.1% of users yet... Maybe Fresco can do it? I've tried to check it there too, but I don't see that it's capable of such a thing either, and it's a huge library to use just for this task, so I'd prefer to use a different library instead... I also know that libwebp is available, but it's in C/C++ and not sure if it's suited for Android, and whether there is a port for it on Java/Kotlin for Android.
EDIT:
Since I think I got what I wanted, for both a third party library and for ImageDecoder, to be able to get bitmaps out of animated WebP, I'd still want to know how to get the frame count and current frame using ImageDecoder, if that's possible. I tried using ImageDecoder.decodeDrawable(source, object : ImageDecoder.OnHeaderDecodedListener... , but it doesn't provide frame count information, and there is no way in the API that I can see that I can go to a specific frame index and start from there, or to know for a specific frame how long it needs to go to the next frame. So I made a reuqest about those here.
Sadly I also could not find that Google has ImageDecoder available for older Android versions, either.
It's also interesting if there is some kind of way to do the same as I did for the relatively new animation file of HEIC. Currently it's supported only on Android P.
OK, I got a possible solution, using Glide library, together with GlideWebpDecoder library .
I'm not sure if that's the best way to do it, but I think it should work fine. The next code shows how it's possible to make the drawable draw into the Bitmap instance that I create, for each frame that the animation needs to show. It's not exactly what I asked, but it might help others.
Here's the code (project available here) :
CallbackEx.kt
abstract class CallbackEx : Drawable.Callback {
override fun unscheduleDrawable(who: Drawable, what: Runnable) {}
override fun invalidateDrawable(who: Drawable) {}
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {}
}
MyAppGlideModule.kt
#GlideModule
class MyAppGlideModule : AppGlideModule()
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: WebpDrawable? = null
var gifDrawable: GifDrawable? = null
var callback: Drawable.Callback? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
useFrameByFrameDecoding()
// useNormalDecoding()
}
fun useNormalDecoding() {
//webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
Glide.with(this)
// .load(R.raw.test)
// .load(R.raw.fast)
.load(R.raw.example2)
// .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
imageView.setImageDrawable(drawable)
when (drawable) {
is GifDrawable -> {
drawable.start()
}
is WebpDrawable -> {
drawable.start()
}
}
}
})
}
fun useFrameByFrameDecoding() {
//webp url : https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp
Glide.with(this)
.load(R.raw.test)
// .load(R.raw.fast)
// .load(R.raw.example2)
// .load("https://res.cloudinary.com/demo/image/upload/fl_awebp/bored_animation.webp")
.into(object : SimpleTarget<Drawable>() {
override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
// val callback
when (drawable) {
is GifDrawable -> {
gifDrawable = drawable
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, bitmap.width, bitmap.height)
drawable.setLoopCount(GifDrawable.LOOP_FOREVER)
callback = object : CallbackEx() {
override fun invalidateDrawable(who: Drawable) {
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('#')} ${drawable.frameIndex}/${drawable.frameCount}")
}
}
drawable.callback = callback
drawable.start()
}
is WebpDrawable -> {
webpDrawable = drawable
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, bitmap.width, bitmap.height)
drawable.setLoopCount(WebpDrawable.LOOP_FOREVER)
callback = object : CallbackEx() {
override fun invalidateDrawable(who: Drawable) {
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable ${drawable.toString().substringAfter('#')} ${drawable.frameIndex}/${drawable.frameCount}")
}
}
drawable.callback = callback
drawable.start()
}
}
}
})
}
override fun onStart() {
super.onStart()
gifDrawable?.start()
gifDrawable?.start()
}
override fun onStop() {
super.onStop()
Log.d("AppLog", "onStop")
webpDrawable?.stop()
gifDrawable?.stop()
}
}
Not sure why SimpleTarget is marked as deprecated, and what I should use instead, though.
Using a similar technique, I've also found out how to do it using ImageDecoder, but not with the same functionality for some reason. A sample project available here.
Here's the code:
MainActivity.kt
class MainActivity : AppCompatActivity() {
var webpDrawable: AnimatedImageDrawable? = null
#SuppressLint("StaticFieldLeak")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val source = ImageDecoder.createSource(resources, R.raw.test)
object : AsyncTask<Void, Void, Drawable?>() {
override fun doInBackground(vararg params: Void?): Drawable? {
return try {
ImageDecoder.decodeDrawable(source)
} catch (e: Exception) {
null
}
}
override fun onPostExecute(drawable: Drawable?) {
super.onPostExecute(drawable)
// imageView.setImageDrawable(result)
if (drawable is AnimatedImageDrawable) {
webpDrawable = drawable
val bitmap =
Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, bitmap.width, bitmap.height)
drawable.repeatCount = AnimatedImageDrawable.REPEAT_INFINITE
drawable.callback = object : Drawable.Callback {
val handler = Handler()
override fun unscheduleDrawable(who: Drawable, what: Runnable) {
Log.d("AppLog", "unscheduleDrawable")
}
override fun invalidateDrawable(who: Drawable) {
who.draw(canvas)
imageView.setImageBitmap(bitmap)
Log.d("AppLog", "invalidateDrawable")
}
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
Log.d("AppLog", "scheduleDrawable next frame in ${`when` - SystemClock.uptimeMillis()} ms")
handler.postAtTime(what, `when`)
}
}
drawable.start()
}
}
}.execute()
}
override fun onStart() {
super.onStart()
webpDrawable?.start()
}
override fun onStop() {
super.onStop()
webpDrawable?.stop()
}
}
see ImageDecoder.Source ...
one needs to first create a source, with either:
// source from file
val source = ImageDecoder.createSource(file)
// source from byte buffer
val source = ImageDecoder.createSource(byteBuffer)
// source from resource
val source = ImageDecoder.createSource(resources, resId)
// source from URI
val source = ImageDecoder.createSource(contentResolver, uri)
// source from asset file
val source = ImageDecoder.createSource(assetManager, assetFileName)
and then decode, with either:
// create bitmap
val bitmap = ImageDecoder.decodeBitmap(source)
// create drawable
val drawable = ImageDecoder.decodeDrawable(source)
update: the problem is, that the resulting AnimatedImageDrawable doesn't have the two methods: getNumberOfFrames() and getFrame(int) as an AnimationDrawable has. as #androiddeveloper pointed out ...I've messed up two different classes. I've double-checked the documentation and there seems to be no way. with the GIFImageReader it still can be extracted (source):
ArrayList<BufferedImage> getFrames(File gif) throws IOException {
ArrayList<BufferedImage> frames = new ArrayList<BufferedImage>();
ImageReader ir = new GIFImageReader(new GIFImageReaderSpi());
ir.setInput(ImageIO.createImageInputStream(gif));
for(int i = 0; i < ir.getNumImages(true); i++) {
frames.add(ir.read(i));
}
return frames;
}
just tried to convert it to Kotlin, but javax.imageio.ImageIO is not available on Android.
I played with GIF images a few years ago. My idea is decode GIF images to frames, convert frames to bitmaps add create Animated Drawable from bitmaps and delay between frames. This is decode class:
public class GifDecoder extends Thread {
public static final int STATUS_PARSING = 0;
public static final int STATUS_FORMAT_ERROR = 1;
public static final int STATUS_OPEN_ERROR = 2;
public static final int STATUS_FINISH = -1;
private static final int MaxStackSize = 4096;
public int width; // full image width
public int height; // full image height
int[] lastPixels;
int[] dest;
private InputStream in;
private int status;
private boolean gctFlag; // global color table used
private int gctSize; // size of global color table
private int loopCount = 1; // iterations; 0 = repeat forever
private int[] gct; // global color table
private int[] lct; // local color table
private int[] act; // active color table
private int bgIndex; // background color index
private int bgColor; // background color
private int lastBgColor; // previous bg color
private int pixelAspect; // pixel aspect ratio
private boolean lctFlag; // local color table flag
private boolean interlace; // interlace flag
private int lctSize; // local color table size
private int ix, iy, iw, ih; // current image rectangle
private int lrx, lry, lrw, lrh;
private GifFrame currentFrame = null;
private boolean isShow = false;
private byte[] block = new byte[256]; // current data block
private int blockSize = 0; // block size
private int dispose = 0;
private int lastDispose = 0;
private boolean transparency = false; // use transparent color
// max decoder pixel stack size
private int delay = 0; // delay in milliseconds
private int transIndex; // transparent color index
// LZW decoder working arrays
private short[] prefix;
private byte[] suffix;
private byte[] pixelStack;
private byte[] pixels;
private GifFrame gifFrame; // frames read from current file
private int frameCount;
private GifAction action = null;
private byte[] gifData = null;
private int gifDataOffset;
private int gifDataLength;
private GifDecoder() {
}
public GifDecoder(byte[] data, GifAction act) {
this(data, 0, data.length, act);
}
public GifDecoder(byte[] data, int offset, int length, GifAction act) {
gifData = data;
action = act;
gifDataOffset = offset;
gifDataLength = length;
}
public GifDecoder(InputStream is, GifAction act) {
in = is;
action = act;
}
public void run() {
if (in != null) {
readStream();
} else if (gifData != null) {
readByte();
}
}
public void free() {
GifFrame fg = gifFrame;
while (fg != null) {
if (fg.image != null) {
fg.image.recycle();
}
fg.image = null;
fg = null;
gifFrame = gifFrame.nextFrame;
fg = gifFrame;
}
if (in != null) {
try {
in.close();
} catch (Exception ex) {
}
in = null;
}
gifData = null;
}
public int getStatus() {
return status;
}
public boolean parseOk() {
return status == STATUS_FINISH;
}
public int getDelay(int n) {
delay = -1;
if ((n >= 0) && (n < frameCount)) {
GifFrame f = getFrame(n);
if (f != null) delay = f.delay;
}
return delay;
}
public GifFrame getFrame(int n) {
GifFrame frame = gifFrame;
int i = 0;
while (frame != null) {
if (i == n) {
return frame;
} else {
frame = frame.nextFrame;
}
i++;
}
return null;
}
public int[] getDelays() {
GifFrame f = gifFrame;
int[] d = new int[frameCount];
int i = 0;
while (f != null && i < frameCount) {
d[i] = f.delay;
f = f.nextFrame;
i++;
}
return d;
}
public int getFrameCount() {
return frameCount;
}
public Bitmap getImage() {
return getFrameImage(0);
}
public Bitmap getFrameImage(int n) {
GifFrame frame = getFrame(n);
if (frame == null) {
return null;
} else {
return frame.image;
}
}
public int getLoopCount() {
return loopCount;
}
public GifFrame getCurrentFrame() {
return currentFrame;
}
public void reset() {
currentFrame = gifFrame;
}
public GifFrame next() {
if (isShow == false) {
isShow = true;
return gifFrame;
} else {
if (status == STATUS_PARSING) {
if (currentFrame.nextFrame != null) currentFrame = currentFrame.nextFrame;
//currentFrame = gifFrame;
} else {
currentFrame = currentFrame.nextFrame;
if (currentFrame == null) {
currentFrame = gifFrame;
}
}
return currentFrame;
}
}
private Bitmap setPixels() {
if (dest == null) dest = new int[width * height];
// fill in starting image contents based on last image's dispose code
if (lastDispose > 0) {
if (lastDispose == 3) {
// use image before last
int n = frameCount - 2;
if (n > 0) {
Bitmap lastImage = getFrameImage(n - 1);
if (lastPixels == null) lastPixels = new int[width * height];
lastImage.getPixels(lastPixels, 0, width, 0, 0, width, height);
} else {
lastPixels = null;
}
}
if (lastPixels != null) {
dest = Arrays.copyOf(lastPixels, lastPixels.length);
// copy pixels
if (lastDispose == 2) {
// fill last image rect area with background color
int c = 0;
if (!transparency) {
c = lastBgColor;
}
for (int i = 0; i < lrh; i++) {
int n1 = (lry + i) * width + lrx;
int n2 = n1 + lrw;
for (int k = n1; k < n2; k++) {
dest[k] = c;
}
}
}
}
}
// copy each source line to the appropriate place in the destination
int pass = 1;
int inc = 8;
int iline = 0;
for (int i = 0; i < ih; i++) {
int line = i;
if (interlace) {
if (iline >= ih) {
pass++;
switch (pass) {
case 2:
iline = 4;
break;
case 3:
iline = 2;
inc = 4;
break;
case 4:
iline = 1;
inc = 2;
}
}
line = iline;
iline += inc;
}
line += iy;
if (line < height) {
int k = line * width;
int dx = k + ix; // start of line in dest
int dlim = dx + iw; // end of dest line
if ((k + width) < dlim) {
dlim = k + width; // past dest edge
}
int sx = i * iw; // start of line in source
while (dx < dlim) {
// map color and insert in destination
int index = ((int) pixels[sx++]) & 0xff;
int c = act[index];
if (c != 0) {
dest[dx] = c;
}
dx++;
}
}
}
return Bitmap.createBitmap(dest, width, height, Config.ARGB_4444);
}
private int readByte() {
in = new ByteArrayInputStream(gifData, gifDataOffset, gifDataLength);
gifData = null;
return readStream();
}
private int readStream() {
init();
if (in != null) {
readHeader();
if (!err()) {
readContents();
if (frameCount < 0) {
status = STATUS_FORMAT_ERROR;
action.parseOk(false, -1);
} else {
status = STATUS_FINISH;
action.parseOk(true, -1);
}
}
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
} else {
status = STATUS_OPEN_ERROR;
action.parseOk(false, -1);
}
return status;
}
private void decodeImageData() {
int NullCode = -1;
int npix = iw * ih;
int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits,
code,
count, i, datum, data_size, first, top, bi, pi;
if ((pixels == null) || (pixels.length < npix)) {
pixels = new byte[npix]; // allocate new pixel array
}
if (prefix == null) {
prefix = new short[MaxStackSize];
}
if (suffix == null) {
suffix = new byte[MaxStackSize];
}
if (pixelStack == null) {
pixelStack = new byte[MaxStackSize + 1];
}
// Initialize GIF data stream decoder.
data_size = read();
clear = 1 << data_size;
end_of_information = clear + 1;
available = clear + 2;
old_code = NullCode;
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
for (code = 0; code < clear; code++) {
prefix[code] = 0;
suffix[code] = (byte) code;
}
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix; ) {
if (top == 0) {
if (bits < code_size) {
// Load bytes until there are enough bits for a code.
if (count == 0) {
// Read a new data block.
count = readBlock();
if (count <= 0) {
break;
}
bi = 0;
}
datum += (((int) block[bi]) & 0xff) << bits;
bits += 8;
bi++;
count--;
continue;
}
// Get the next code.
code = datum & code_mask;
datum >>= code_size;
bits -= code_size;
// Interpret the code
if ((code > available) || (code == end_of_information)) {
break;
}
if (code == clear) {
// Reset decoder.
code_size = data_size + 1;
code_mask = (1 << code_size) - 1;
available = clear + 2;
old_code = NullCode;
continue;
}
if (old_code == NullCode) {
pixelStack[top++] = suffix[code];
old_code = code;
first = code;
continue;
}
in_code = code;
if (code == available) {
pixelStack[top++] = (byte) first;
code = old_code;
}
while (code > clear) {
pixelStack[top++] = suffix[code];
code = prefix[code];
}
first = ((int) suffix[code]) & 0xff;
// Add a new string to the string table,
if (available >= MaxStackSize) {
break;
}
pixelStack[top++] = (byte) first;
prefix[available] = (short) old_code;
suffix[available] = (byte) first;
available++;
if (((available & code_mask) == 0) && (available < MaxStackSize)) {
code_size++;
code_mask += available;
}
old_code = in_code;
}
// Pop a pixel off the pixel stack.
top--;
pixels[pi++] = pixelStack[top];
i++;
}
for (i = pi; i < npix; i++) {
pixels[i] = 0; // clear missing pixels
}
}
private boolean err() {
return status != STATUS_PARSING;
}
private void init() {
status = STATUS_PARSING;
frameCount = 0;
gifFrame = null;
gct = null;
lct = null;
}
private int read() {
int curByte = 0;
try {
curByte = in.read();
} catch (Exception e) {
status = STATUS_FORMAT_ERROR;
}
return curByte;
}
private int readBlock() {
blockSize = read();
int n = 0;
if (blockSize > 0) {
try {
int count = 0;
while (n < blockSize) {
count = in.read(block, n, blockSize - n);
if (count == -1) {
break;
}
n += count;
}
} catch (Exception e) {
e.printStackTrace();
}
if (n < blockSize) {
status = STATUS_FORMAT_ERROR;
}
}
return n;
}
private int[] readColorTable(int ncolors) {
int nbytes = 3 * ncolors;
int[] tab = null;
byte[] c = new byte[nbytes];
int n = 0;
try {
n = in.read(c);
} catch (Exception e) {
e.printStackTrace();
}
if (n < nbytes) {
status = STATUS_FORMAT_ERROR;
} else {
tab = new int[256]; // max size to avoid bounds checks
int i = 0;
int j = 0;
while (i < ncolors) {
int r = ((int) c[j++]) & 0xff;
int g = ((int) c[j++]) & 0xff;
int b = ((int) c[j++]) & 0xff;
tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
}
}
return tab;
}
private void readContents() {
// read GIF file content blocks
boolean done = false;
while (!(done || err())) {
int code = read();
switch (code) {
case 0x2C: // image separator
readImage();
break;
case 0x21: // extension
code = read();
switch (code) {
case 0xf9: // graphics control extension
readGraphicControlExt();
break;
case 0xff: // application extension
readBlock();
String app = "";
for (int i = 0; i < 11; i++) {
app += (char) block[i];
}
if (app.equals("NETSCAPE2.0")) {
readNetscapeExt();
} else {
skip(); // don't care
}
break;
default: // uninteresting extension
skip();
}
break;
case 0x3b: // terminator
done = true;
break;
case 0x00: // bad byte, but keep going and see what happens
break;
default:
status = STATUS_FORMAT_ERROR;
}
}
}
private void readGraphicControlExt() {
read(); // block size
int packed = read(); // packed fields
dispose = (packed & 0x1c) >> 2; // disposal method
if (dispose == 0) {
dispose = 1; // elect to keep old image if discretionary
}
transparency = (packed & 1) != 0;
delay = readShort() * 10; // delay in milliseconds
transIndex = read(); // transparent color index
read(); // block terminator
}
private void readHeader() {
String id = "";
for (int i = 0; i < 6; i++) {
id += (char) read();
}
if (!id.startsWith("GIF")) {
status = STATUS_FORMAT_ERROR;
return;
}
readLSD();
if (gctFlag && !err()) {
gct = readColorTable(gctSize);
bgColor = gct[bgIndex];
}
}
private void readImage() {
ix = readShort(); // (sub)image position & size
iy = readShort();
iw = readShort();
ih = readShort();
int packed = read();
lctFlag = (packed & 0x80) != 0; // 1 - local color table flag
interlace = (packed & 0x40) != 0; // 2 - interlace flag
// 3 - sort flag
// 4-5 - reserved
lctSize = 2 << (packed & 7); // 6-8 - local color table size
if (lctFlag) {
lct = readColorTable(lctSize); // read table
act = lct; // make local table active
} else {
act = gct; // make global table active
if (bgIndex == transIndex) {
bgColor = 0;
}
}
int save = 0;
if (transparency) {
save = act[transIndex];
act[transIndex] = 0; // set transparent color if specified
}
if (act == null) {
status = STATUS_FORMAT_ERROR; // no color table defined
}
if (err()) {
return;
}
try {
decodeImageData(); // decode pixel data
skip();
if (err()) {
return;
}
frameCount++;
// create new image to receive frame data
// createImage(width, height);
Bitmap image = setPixels(); // transfer pixel data to image
if (gifFrame == null) {
gifFrame = new GifFrame(image, delay);
currentFrame = gifFrame;
} else {
GifFrame f = gifFrame;
while (f.nextFrame != null) {
f = f.nextFrame;
}
f.nextFrame = new GifFrame(image, delay);
}
// frames.addElement(new GifFrame(image, delay)); // add image to frame
// list
if (transparency) {
act[transIndex] = save;
}
resetFrame();
if (!action.parseOk(true, frameCount)) {
status = STATUS_FINISH;
return;
}
} catch (OutOfMemoryError e) {
Log.e("GifDecoder", ">>> log : " + e.toString());
e.printStackTrace();
}
}
private void readLSD() {
// logical screen size
width = readShort();
height = readShort();
// packed fields
int packed = read();
gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
// 2-4 : color resolution
// 5 : gct sort flag
gctSize = 2 << (packed & 7); // 6-8 : gct size
bgIndex = read(); // background color index
pixelAspect = read(); // pixel aspect ratio
}
private void readNetscapeExt() {
do {
readBlock();
if (block[0] == 1) {
// loop count sub-block
int b1 = ((int) block[1]) & 0xff;
int b2 = ((int) block[2]) & 0xff;
loopCount = (b2 << 8) | b1;
}
} while ((blockSize > 0) && !err());
}
private int readShort() {
// read 16-bit value, LSB first
return read() | (read() << 8);
}
private void resetFrame() {
lastDispose = dispose;
lrx = ix;
lry = iy;
lrw = iw;
lrh = ih;
lastPixels = dest;
lastBgColor = bgColor;
dispose = 0;
transparency = false;
delay = 0;
lct = null;
}
/**
* Skips variable length blocks up to and including next zero length block.
*/
private void skip() {
do {
readBlock();
} while ((blockSize > 0) && !err());
}
}
I upload the full demo source here. Hope it can help you.
EDIT: In actually implementing this, I encountered a couple unexpected problems, but nothing insurmountable:
AnimatedImageDrawable seems to ignore its configured bounds. I scaled the canvas instead.
For reasons I don't understand, AnimatedImageDrawable.draw() occasionally neglects to schedule the next frame. I decided to call the function twice. The second time, I translate the canvas so that all drawing is out of bounds, which should allow most of the work to be optimized away.
Here's the sample code.
import android.annotation.*;
import android.graphics.*;
import android.graphics.drawable.*;
import android.os.*;
import android.service.wallpaper.*;
import android.support.annotation.*;
import android.view.*;
#TargetApi(28)
public class TestWallpaper extends WallpaperService
{
#Override public Engine onCreateEngine()
{
return new Engine();
}
private class Engine extends WallpaperService.Engine implements Drawable.Callback
{
private final Drawable d;
private final Handler h = new Handler();
private float scaleX, scaleY;
private Engine()
{
this.setOffsetNotificationsEnabled(false);
Drawable d = null;
try
{
d = ImageDecoder
.decodeDrawable(ImageDecoder.createSource(getResources(), R.drawable.test));
d.setCallback(this);
// AnimatedImageDrawable seems to ignore its configured bounds and use its
// intrinsic bounds instead.
// In case they fix this bug, we'll go ahead and request the current
// behavior, and then before drawing we'll transform the canvas to compensate
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
if (d instanceof AnimatedImageDrawable)
{
final AnimatedImageDrawable anim = (AnimatedImageDrawable) d;
anim.setRepeatCount(AnimatedImageDrawable.REPEAT_INFINITE);
anim.start();
}
}
catch (Throwable t) // should never happen
{
t.printStackTrace();
}
this.d = d;
}
#Override public void invalidateDrawable(#NonNull Drawable _d)
{
if(isVisible())
draw(getSurfaceHolder().getSurface());
}
#Override public void scheduleDrawable(#NonNull Drawable _d, #NonNull Runnable _r, long _at)
{
if(isVisible())
h.postAtTime(_r, _d, _at);
}
#Override public void unscheduleDrawable(#NonNull Drawable _d, #NonNull Runnable _r)
{
h.removeCallbacks(_r, _d);
}
#Override public void onSurfaceChanged(SurfaceHolder _sh, int _format, int _w, int _h)
{
scaleX = (float) _w / d.getIntrinsicWidth();
scaleY = (float) _h / d.getIntrinsicHeight();
draw(_sh.getSurface());
}
#Override public void onSurfaceRedrawNeeded(SurfaceHolder _sh)
{
draw(_sh.getSurface());
}
private void draw(Surface _s)
{
try
{
final Canvas c = _s.lockCanvas(null);
c.scale(scaleX, scaleY);
d.draw(c);
// Sometimes AnimatedImageDrawable neglects to schedule the next frame
// after only one draw() of the current frame, so we'll draw() it again,
// but outside the canvas this time
c.translate(Float.MAX_VALUE, Float.MAX_VALUE);
d.draw(c);
//
_s.unlockCanvasAndPost(c);
}
catch (Throwable t)
{
t.printStackTrace();
// Most likely, the surface was destroyed while we were using it
// The new one will be delivered to onSurfaceChanged and we'll be fine
}
}
#Override public void onVisibilityChanged(boolean _visible)
{
super.onVisibilityChanged(_visible);
if(_visible)
draw(getSurfaceHolder().getSurface());
else
h.removeCallbacksAndMessages(null);
}
}
}

maze example not generating maze

I am following Maze game tutorial from youtube Android Programming - Maze Game Pt4
The tutor's code in the video generates random mazes. I have followed the code to the 'T' throughout but my emulator only gives the following output -
My emulator's output
Here is my code. Can anyone tell me what i missed from the tutorial I was following or if the tutorial itself has some coding errors? -
public class MazaGame extends View {
private Cell[][] cells;
private static final int COLS = 7, ROWS = 10;
private static final float WALL_THICKNESS = 4;
private float cellSize,hMargin,vMargin;
private Paint wallPaint;
private Random random;
public MazaGame(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
wallPaint = new Paint();
wallPaint.setColor(Color.BLACK);
wallPaint.setStrokeWidth(WALL_THICKNESS);
random = new Random();
createMaze();
}
private Cell getNeighbor(Cell cell){
ArrayList<Cell> neighbors = new ArrayList<>();
//left neighbor
if(cell.col >0)
if(!cells[cell.col-1][cell.row].visited)
neighbors.add(cells[cell.col-1][cell.row]);
//right neighbor
if(cell.col < COLS-1)
if(!cells[cell.col+1][cell.row].visited)
neighbors.add(cells[cell.col+1][cell.row]);
//top neighbor
if(cell.row >0)
if(!cells[cell.col][cell.row-1].visited)
neighbors.add(cells[cell.col][cell.row-1]);
//bottom neighbor
if(cell.row < ROWS-1)
if(!cells[cell.col][cell.row+1].visited)
neighbors.add(cells[cell.col][cell.row+1]);
if(neighbors.size()>0) {
int index = random.nextInt(neighbors.size());
return neighbors.get(index);
}
return null;
}
private void removeWall(Cell current, Cell next){
if(current.col == next.col && current.row == next.row+1){
current.topWall = false;
next.bottomWall = false;
}
if(current.col == next.col && current.row == next.row-1){
current.bottomWall = false;
next.topWall = false;
}
if(current.col == next.col+1 && current.row == next.row){
current.leftWall = false;
next.rightWall = false;
}
if(current.col == next.col-1 && current.row == next.row){
current.rightWall = false;
next.leftWall = false;
}
}
private void createMaze() {
Stack<Cell> stack = new Stack<>();
Cell current, next;
cells = new Cell[COLS][ROWS];
for (int x = 0; x < COLS; x++) {
for (int y = 0; y < ROWS; y++) {
cells[x][y] = new Cell(x, y);
}
}
do {
current = cells[0][0];
current.visited = true;
next = getNeighbor(current);
if (next != null) {
removeWall(current, next);
stack.push(current);
current = next;
current.visited = true;
} else
current = stack.pop();
}while (!stack.empty());
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.RED);
int width = getWidth();
int height = getHeight();
if(width/height < COLS/ROWS)
cellSize = width/(COLS+1);
else
cellSize = height/(ROWS+1);
hMargin = (width-COLS*cellSize)/2;
vMargin = (height-ROWS*cellSize)/2;
canvas.translate(hMargin,vMargin);
for(int x=0;x<COLS;x++){
for(int y=0;y<ROWS;y++){
if(cells[x][y].topWall)
canvas.drawLine(
x*cellSize,
y*cellSize,
(x+1)*cellSize,
y*cellSize,
wallPaint);
if(cells[x][y].leftWall)
canvas.drawLine(
x*cellSize,
y*cellSize,
x*cellSize,
(y+1)*cellSize,
wallPaint);
if(cells[x][y].bottomWall)
canvas.drawLine(
x*cellSize,
(y+1)*cellSize,
(x+1)*cellSize,
(y+1)*cellSize,
wallPaint);
if(cells[x][y].rightWall)
canvas.drawLine(
(x+1)*cellSize,
y*cellSize,
(x+1)*cellSize,
(y+1)*cellSize,
wallPaint);
}
}
}
private class Cell{
boolean
topWall = true,
rightWall = true,
bottomWall = true,
leftWall = true,
visited = false;
int col,row;
public Cell(int col, int row) {
this.col = col;
this.row = row;
}
}
}
I'm not going to take the time to fully internalize 100% of the code you posted, but this section looks suspicious:
do {
current = cells[0][0];
current.visited = true;
next = getNeighbor(current);
if (next != null) {
removeWall(current, next);
stack.push(current);
current = next;
current.visited = true;
} else
current = stack.pop();
}while (!stack.empty());
I suspect you want this to look like this instead:
current = cells[0][0];
current.visited = true;
do {
next = getNeighbor(current);
if (next != null) {
removeWall(current, next);
stack.push(current);
current = next;
current.visited = true;
} else
current = stack.pop();
}while (!stack.empty());
What you have right now will continuously reset current to be the cell at (0,0), because every time through the loop, you're executing
current = cells[0][0];
current.visited = true;
I suspect that this is just supposed to be the initial setup for the first cell, and you don't want this happening every time through the loop.

How can I make each individual frog stop using onTouch?

I am making a game where there there are frogs hopping around the screen. Once a frog is touched, I change the game image to what I have set as "deadFrog" and its movement stops. I have them all created under and array-list, and I am unsure as to how to only make changes to the individual frog. Right now, if one frog is tapped, all of the frogs stop moving and change to deadFrog. Hopefully you can help me fix the tactical nuke of a tap ;) *If you need any more information, just comment and I'll be sure to provide it!
Edit Is there not a way to access a single element in the blocks arraylist? I've tried doing blocks(1) for example but that's invalid.
Here is where the frogs are declared:
public void init() {
blocks = new ArrayList<Block>();
for (int i = 0; i < 5; i++) {
Block b = new Block(i * 200, MainActivity.GAME_HEIGHT - 95,
BLOCK_WIDTH, BLOCK_HEIGHT);
blocks.add(b);
tapped = false;
}
}
They are rendered with this:
private void renderFrogs(Painter g) {
if (!tapped) {
for (int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
if (b.isVisible()) {
Assets.runAnim.render(g, (int) b.getX(), (int) b.getY());
}
}
}
if (tapped) {
for (int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
if (b.isVisible()) {
g.drawImage(Assets.deadfrog, (int) b.getX(), (int) b.getY());
}
}
}
}
And this is the onTouchListener:
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
recentTouchY = scaledY;
} else if (e.getAction() == MotionEvent.ACTION_UP) {
for (int i = 0; i < blocks.size(); i++) {
Block b = blocks.get(i);
if ((scaledY >= b.getY() - BLOCK_HEIGHT || scaledY <= b.getY()) && (scaledX >= b.getX() || scaledX <= b.getX() + BLOCK_WIDTH)) {
tapped = true;
}
}
}
return true;
}
Of course all frogs turn dead, you are supposed to keep "tapped" variable for each frog, your tapped variable is for all frogs at once.
Declare a class
public class Frog extends View{
public Drawable liveFrog;
public Drawable deadFrog;
public boolean isDead;
public Point location;
public int width;
public int height;
public Frog(Context context, int x, int y,int width,int height){
super(context);
this.isDead = false;
this.location = new Point(x,y);
this.width = width;
this.height = height;
}
public void onDraw(Canvas c){
super.onDraw(c);
if(!isDead){
//draw live frog at x,y
}else {
//draw dead frog at x,y
}
}
}
then you array is supposed to contain frogs
public void init(Context context) {
blocks = new ArrayList<Frog>();
for (int i = 0; i < 5; i++) {
Frog b = new Frog(context,i * 200, MainActivity.GAME_HEIGHT - 95,
BLOCK_WIDTH, BLOCK_HEIGHT);
blocks.add(b);
}
}
private void renderFrogs() {
for(Frog f : blocks){
//cause redraw
f.invalidate();
}
}
here comes the fun part, when you tap the frog
public boolean onTouch(MotionEvent e, int scaledX, int scaledY) {
if (e.getAction() == MotionEvent.ACTION_DOWN) {
recentTouchY = scaledY;
} else if (e.getAction() == MotionEvent.ACTION_UP) {
for (int i = 0; i < blocks.size(); i++) {
Frog frog = blocks.get(i);
if ((scaledY >= frog.getY() - BLOCK_HEIGHT || scaledY <= frog.getY()) && (scaledX >= frog.getX() || scaledX <= frog.getX() + BLOCK_WIDTH)) {
frog.isDead = true;
//cause one frog redraw
frog.invalidate();
//if the event was handled, stop here (unless you can have multiple frogs one on top of the other ?
return true;
}
}
}
//if the event was not handled, let it bubble up
return false;
}
From what I can see, your "tapped" boolean is not a property of EACH frog. It is declared once, and when triggered, per your for loop, will make every frog dead (obviously, since that's what you're experiencing).
Once "tapped" is true, your for loop is going through every block and assigning a dead frog to it.
I think you need to create a Frog class, and store instances of those in your ArrayList. A variable of the new frog class will be "touched", and when that is triggered you will do something to that specific instance only.

Android game in surfaceview lagg spikes

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.

Collision Detection/Remove object from ArrayList

I am currently trying to test collision between a falling object and a box. I understand basic collision detection, but my problem here is that I have to test it for an indefinite number of falling objects. When these objects(blossoms) are created, they are stored in an ArrayList. The ArrayList handles the drawing of the object on the canvas (using a for each to update the position). My problem comes when a blossom is "caught" in the box. How do I make it disappear from the screen/removed from the array list without a null reference happening? I can show you the logic I have so far...please let me know what you think. I am REALLY stuck, but feel like I'm so close to figuring this out.
Blossom Class
public class Blossom{
private Bitmap blossom;
private int blossom_x = 0;
private int blossom_y = 0;
private int blossomWidth = 0;
private int blossomHeight = 0;
private boolean hit = false;
private Random generator = new Random();
RectF blossomRect;
private static final String TAG = "Debug";
public Blossom(Bitmap bitmap)
{
blossom = bitmap;
blossomWidth = blossom.getWidth();
blossomHeight = blossom.getHeight();
blossom_x = generator.nextInt(320-blossom.getWidth());
blossomRect = new RectF(blossom_x,blossom_y, blossom_x + blossomWidth, blossom_y + blossomHeight);
}
public Bitmap getBitmap()
{
return blossom;
}
public Boolean hit(int boxLeft, int boxTop, int boxRight,int boxBottom)
{
if(blossom_x > boxLeft & blossom_y > boxTop
& blossom_x + blossom.getWidth() < boxRight
& blossom_y + blossom.getHeight() < boxBottom)
{
Log.v(TAG, "Collision Detected");
return true;
}
else
{
return false;
}
}
public float getBlossomX()
{
return blossom_x;
}
public float getBlossomY()
{
return blossom_y;
}
public float getBlossomWidth()
{
return blossomWidth;
}
public float getBlossomHeight()
{
return blossomHeight;
}
public void Draw(Canvas canvas)
{
//draws the flower falling
if (hit == false)
{
canvas.drawBitmap(blossom, blossom_x,
blossom_y = blossom_y+5 , null);
}
}
public void UpdatePosition()
{
blossomRect.set(blossom_x, blossom_y, blossom_x + 25, blossom_y + 25);
}
}
BoardView
public class BoardView extends SurfaceView implements SurfaceHolder.Callback{
Context mContext;
Bitmap box =
(BitmapFactory.decodeResource
(getResources(), R.drawable.box));
private BoardThread thread;
private int box_x = 140;
private int box_y = 378;
private int boxWidth = box.getWidth();
private int boxHeight = box.getHeight();
private ArrayList<Blossom> blossomArrayList = new ArrayList<Blossom>();
private int blossomNum = 0;
private String score;
private int currentScore = 0;
Thread timer;
boolean mode = false;
boolean game = false;
private static final String TAG = "Debug";
final Paint scorePaint = new Paint();
public BoardView(Context context){
super(context);
scorePaint.setColor(Color.BLACK);
scorePaint.setTextSize(12);
scorePaint.setTypeface(Typeface.MONOSPACE);
//surfaceHolder provides canvas that we draw on
getHolder().addCallback(this);
// controls drawings
thread = new BoardThread(getHolder(),this, blossomArrayList, box_x, box_y,
boxWidth, boxHeight);
timer = new Thread(){
public void run(){
//makes sure the player still has 3 lives left
while(game == false){
uiCallback.sendEmptyMessage(0);
try {
Thread.sleep(2000); // wait two seconds before drawing the next flower
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //sleep for 2 seconds
}
}
};
timer.start();
//intercepts touch events
setFocusable(true);
}
#Override
public void onDraw(Canvas canvas){
canvas.drawColor(Color.WHITE);
score = "SCORE: " + currentScore;
//note: pay attention to order you draw things
//don't change order or else blossoms will fall
//on top of box, not "into" it.
//display the scoreboard
canvas.drawText(score,240,420,scorePaint);
//draw box and set start location
for(Blossom blossom: blossomArrayList) // getting errors here
{
blossom.Draw(canvas);
blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight);
}
canvas.drawBitmap(box, box_x, box_y, null);
}
#Override
public boolean onTouchEvent(MotionEvent event){
//handles movement of box
if(event.getAction() == MotionEvent.ACTION_DOWN){
if(event.getX() > box_x & event.getY() > box_y &
event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
{
mode = true;
}
}
if(event.getAction() == MotionEvent.ACTION_MOVE) {
if(event.getX() > box_x & event.getY() > box_y &
event.getX() < box_x + boxWidth & event.getY() < box_y + boxHeight)
{
mode = true;
}
if(mode == true){
box_x = (int)event.getX();
}
}
if(event.getAction() == MotionEvent.ACTION_UP){
mode = false;
}
invalidate();
return true;
}
#Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height ){
Log.v(TAG, "Surface Changed");
//somehow these don't seem to be working
}
#Override
public void surfaceCreated(SurfaceHolder holder){
thread.startRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder){
Log.v(TAG, "Surface Destroyed");
//somehow these don't seem to be working
thread.startRunning(false);
thread.stop();
timer.interrupt();
timer.stop();
}
private Handler uiCallback = new Handler(){
public void handleMessage(Message msg){
//add a new blossom to the blossom ArrayList!!
blossomArrayList.add(new Blossom(
(BitmapFactory.decodeResource
(getResources(), R.drawable.blossom))));
blossomNum++;
//remove neccesary blossoms from list
Log.v(TAG, "Number of Blossoms =" + blossomNum);
}
};
}
BoardThread
public class BoardThread extends Thread {
private SurfaceHolder surfaceHolder;
private BoardView boardView;
private ArrayList<Blossom> blossomArrayList;
private int boxX;
private int boxY;
private int boxWidth;
private int boxHeight;
private boolean mrun =false;
public BoardThread(SurfaceHolder holder, BoardView boardView2,
ArrayList<Blossom> blossomArrayList1,
int box_x, int box_y, int boxW, int boxH) {
surfaceHolder = holder;
boardView=boardView2;
blossomArrayList = blossomArrayList1;
boxX = box_x;
boxY = box_y;
boxW = boxWidth;
boxH = boxHeight;
}
public void startRunning(boolean run) {
mrun=run;
}
#Override
public void run() {
super.run();
Canvas canvas;
while (mrun) {
canvas=null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
//test for collision
Collision(blossomArrayList, boxX, boxY, boxWidth, boxHeight);
// draw flowers
boardView.onDraw(canvas); // and getting errors here - concurrent
}
} finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
public void Collision(ArrayList<Blossom> blossomArrayList, int box_x, int box_y,
int boxWidth, int boxHeight)
{
for(Blossom blossom: blossomArrayList)
{
blossom.UpdatePosition();
if(blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight) == true)
{
///if flower is caught, add to score
//currentScore += 100;
}
}
}
}
One way you could do this is to have a field on the Blossom that indicates whether it is active or not, then only draw it if it is active. If it is inactive, another Blossom could replace it in the list.
Setting a visibility flag is one way to go, however I'd recommend against it since you are adding an indeterminate amount of Bitmaps to an ArrayList...you'll find yourself running out of memory pretty quickly. Change your collision detection iterator from a foreach to a handwritten loop, this will avoid concurrency issues you may run to in the code you have listed above.
for (int i = 0; i < blossomArrayList.size(); i++)
{
if(blossom.hit(box_x,box_y, box_x + boxWidth, box_y + boxHeight)) {
blossomArrayList.remove(i);
}
}
Additionally, I'd recommend changing all your ArrayList foreach iterators to hand written for loops, as iterating ArrayLists (but not any other object) is relatively slow on Android and can lead to unexpected concurrency issues.
Thirdly, it seems as though you only need to run your Collision() method once after your UpdatePositions loop has completed since you're already checking every Blossom in your Collision() method.

Categories

Resources