In Android I use a SurfaceView to display a simple 2D game. The bitmaps (.png) with alpha (representing the game objects) are drawn on the canvas.
Now I would like to do a simple but accurate collision detection. Checking whether these bitmaps are overlapping is quite easy.
But how do I check for collisions when these bitmaps have transparent areas? My challenge is detecting whether two balls collide or not. They fill up the whole bitmap in width and height both but in all four edges, there are transparent areas of course as it's a circle in a square.
What is the easiest way to detect collisions there only if the balls really collide, not their surrounding bitmap box?
Do I have to store coordinates of as many points on the ball's outline as possible? Or can Android "ignore" the alpha-channel when checking for collisions?
Another method I can think of will work with simple objects that can be constructed using Paths.
Once you have two objects whose boundaries are represented by paths, you may try this:
Path path1 = new Path();
path1.addCircle(10, 10, 4, Path.Direction.CW);
Path path2 = new Path();
path2.addCircle(15, 15, 8, Path.Direction.CW);
Region region1 = new Region();
region1.setPath(path1, clip);
Region region2 = new Region();
region2.setPath(path2, clip);
if (!region1.quickReject(region2) && region1.op(region2, Region.Op.INTERSECT)) {
// Collision!
}
Once you have your objects as Paths, you can draw them directly using drawPath(). You can also perform movement by transform()ing the path.
If it is ball collision you can perform analitical collision detection - it will be much faster then per-pixel detection. You only need to have two centers of balls (x1,y1) and (x2,y2) and radius r1 for the first ball and r2 for second one. Now if distance between centers of ball is less or equal of sum of radius then the balls are colliding:
colide = sqrt((x1-x2)^2+(y1-y2)^2)<=r1+r2
but a little faster way is to compare square of this value:
colide = (x1-x2)^2+(y1-y2)^2<=(r1+r2)^2
It's much easier to use an existing library like AndEngine instead of reinventing the wheel. I'm not sure if it can be used with a SurfaceView though. Check this article: Pixel Perfect Collision Detection for AndEngine.
Related
im working on an app, that displays large(around 2000x2000px) bitmap in imageview. This image has to be that large since user can pinch to zoom it in order to see some details. App has to be able to draw circles on that image, and also to display image alone, without circles on it. I was using 2 layers but the problem is memory since 2k x 2k px is around 16mb of memory, and creating another bitmap(another 16mb), just to draw a few circles, is pointless in my opinion. Is there any way, that you can draw simple primitives on image, and also be able to display it without primitives(circles in my case)?
Maybe somehow to store only modified pixels or sth?
Thanks!
You don't need to make another 2000x2000 Bitmap to draw those circles on. Just 'prerender' a circle, and then choose where you draw it.
I'm working under the assumption that you're drawing your 'big' image on a Canvas, since you have zooming features etc.
If you're not, you'll need to override your SurfaceView's onDraw(Canvas canvas) method so that you can access the SurfaceView Canvas. I won't go into depth about that part since again I'm assuming you have it, but if not the implementation of that function would look like this:
//Overriding SurfaceView onDraw(Canvas canvas)
#Override
protected void onDraw(Canvas surfaceCanvas) {
if(canvas == null) return; //No Canvas? No point in drawing then.
surfaceCanvas.drawColor(Color.BLACK);
//Draw your 'big' image on the SurfaceView Canvas
insertYourBigImageDrawingFunctionHere(surfaceCanvas);
//Now draw your circles at their correct positions...
insertCircleDrawingFunctionHere(surfaceCanvas);
}
Now that you have access to the SurfaceView Canvas, you can choose precisely how things are drawn on it. Like circles for example...
I want to draw your attention to the multiple Canvas' being used below (surfaceCanvas vs. circleCanvas). I once thought that Canvas was a kind-of 'one Canvas for the whole app/activity' implementation, but it isn't. You are free to create Canvas' as you please. It is merely an instance of a tool to draw onto Bitmaps. This was a HUGE revelation for me, and gave me much more robust control over how Bitmaps are composed.
public void myCircleDrawingFunction(Canvas surfaceCanvas){
//Make a new Bitmap for your circle
Bitmap.Config conf = Bitmap.Config.ARGB_4444;
tinyCircleBMP = Bitmap.createBitmap(10,10, conf);
//Make a new canvas using that Bitmap as the source...
Canvas circleCanvas = new Canvas(cacheBmp);
//Now, perform your drawing on the `Canvas`...
Paint p = new Paint();
circleCanvas.drawCircle(5, 5, 5, p);
//Now the `Bitmap` has a circle on it, draw the `Bitmap` on the `SufaceView Canvas`
surfaceCanvas.drawBitmap(tinyCircleBMP, 10, 10, p);
//Replace the '10's in the above function with relevant coordinates.
}
Now obviously, your circles will zoom/pan differently to your 'big' image, since they are no longer being drawn at the same size/position of the 'big' image. You will need to consider how to translate the positions of each circle taking into account the current scale and position of the 'big' image.
For example, if your image is zoomed in to 200%, and a circle is supposed to appear 100px from the left of the big image, then you should multiply the pixel values to take into account the zoom, like this
(PsuedoCode):
drawCircleAtX = Bitmap.left * BitmapZoomFactor
If you are using the canvas API (if not I would suggest to)? if so you are just draw your image on the canvas and then the primitive shapes on top of the same canvas before display. This way you just keep a reference of the circles position in some basic data types and scale them as the user moves around and zooms, so you know where to draw them each frame.
Does Android Region (android.graphics.Region) always have a rectangular area or can it be polygonal or rounded (curvy)?
Actually I have to do some Region.Op.UNION and Region.Op.INTERSECTION operation with multiple regions.
I want to know the shape of Ultimate Output Region, does it still have a rectangular area or not?
It can be complex (isComplex()), i.e. it consists of more than one rectangle. Not sure what do you mean by "curvy", but it can be polygonal. If I understand it correctly, you can use getBoundaryPath() to get the Path describing resulting shape.
Nothing in the documentation would lead one to conclude that a Region can be anything but a rectangle, it being constructed from either a rectangle, an x,y coordinate plus width and height, or by another region.
One can describe a rectangle from a path, so getBoundaryPath() does not necessarily conclude that a non-rectangle is possible. An encompassing rectangular boundary may instead be implied.
The isComplex() property only says that it consists of multiple rectangles. Are they all bound by a single exterior, defining rectangle? If so, how do we separate them? In the absence of sufficient documentation, one cannot tell without experimentation:
The following code describes a path and creates a polygonal region. We start with an array of any number of coordinate pairs. Then:
//describe a path corresponding to the transformed polygon
Path transformPath;
transformPath = new Path();
//starting point
transformPath.moveTo(getTransformedPolygon()[0], getTransformedPolygon()[1]);
//draw a line from one point to the next
for(int i = 2; i < arrayCoordinates.length; i = i + 2)
{
transformPath.lineTo(arrayCoordinates[i], arrayCoordinates[i + 1]);
}
//then end at the starting point to close the polygon
transformPath.lineTo(arrayCoordinates[0], arrayCoordinates[1]);
//describe a region (clip area) corresponding to the game area (my example is a game app)
Region clip = new Region(0, 0, gameSurfaceWidth, gameSurfaceHeight);
//describe a region corresponding to the transformed polygon path
transformRegion = new Region();
transformRegion.setPath(transformPath, clip);
If you display the region as a string, you will see the several pair of coordinates that make up the polygonal shape.
I am trying to animate several shapes(paths) by drawing them on the surface holders canvas.
At first I was drawing them as paths and everything was fine, the movement was smooth.
As I increased the number of objects(shapes) the performance decreased and I made some
tests to see if instead of drawing shapes drawing bitmaps is faster. And.. drawing
bitmaps seems to be considerable faster (less computation maybe) BUT the movement is
not smooth. It looks like the bitmaps always move from pixel to pixel instead of using anti alias to, I dont know, draw states as half pixel.
The signature of the method looks like :
canvas.drawBitmap(cloudBitmap, float left, float top, Paint p);
which suggests that I should be able to draw a bitmap at 0.5f pixels.
Any idea why ?
I think it might be due to the bitmap being drawn without filtering it for smoothness. Have you set the paint to smooth the bitmap? If not, that might be your solution.
Paint paint = new Paint();
paint.setFilterBitmap(true);
I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.
I'm currently writing an android side-scrolling game and am having trouble filling a path with a repeating bitmap image. I'm creating a path from a number of coordinates to make up the "ground" area. I have a character who is fixed in the middle of the canvas and screen and am moving the path to represent the movement of the character. I've been able to fill the path with a repeating image using a BitmapShader. Also I can move the path shape from side to side on the screen. However, the Bitmapshader seems to be using a default origin of 0,0 which means the shader is always drawing the ground repeating image in the same place. This means that even though the path is moving the ground repeated image never appears to move. Does anyone have any idea how to change the origin of the shader or know of a better way to fill the path with a repeating image?
Alternatively, can anyone suggest a better solution for filling a drawable shape with an image?
Thanks
Andy
Thanks, had a look at those...Replica Island seems to use OpenGL quite a lot which is a bit beyond me at present and Snake didn't quite do what I was looking for...eventually got there..
//Shape pathShape = this.getPathShape();
Bitmap groundImage = ImageHandler.getmGroundImage();
int offset = groundImage.getWidth()-(xPosition%groundImage.getWidth());
Path path = new Path();
path.moveTo(coordinates.get(0).getmX(), coordinates.get(0).getmY());
for ( ShapeCoordinate coordinate : coordinates ) {
path.lineTo(coordinate.getmX(), coordinate.getmY());
}
path.lineTo(coordinates.get(coordinates.size()-1).getmX(), mYBase);
path.lineTo(coordinates.get(0).getmX(), mYBase);
path.lineTo(coordinates.get(0).getmX(), coordinates.get(0).getmY());
path.close();
PathShape shape = new PathShape(path,canvas.getWidth(),canvas.getHeight());
BitmapShader bs = new BitmapShader(groundImage, Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);
Matrix matrix = new Matrix();
matrix.reset();
matrix.setScale(1,1);
matrix.preTranslate(offset, 0);
bs.setLocalMatrix(matrix);
ShapeDrawable sd = new ShapeDrawable(shape);
sd.setColorFilter(Color.argb(255, 50*(mLevel+1), 50*(mLevel+1), 50*(mLevel+1)), Mode.LIGHTEN);
sd.getPaint().setShader(bs);
sd.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
sd.draw(canvas);
Have you looked at the Snake example in the android sdk? Also Replica Island is another example of how to do a tile engine in android.