Rotational Pixel-Perfect Collision Detection - java

So I am working on an Android game and I have two images on the screen. I currently have the pixel-perfect collision detection working great. My problem is when I rotate one of the images and I check the pixels for a collision. The pixels are still oriented in the original way the image was loaded, so it is not as perfect as it was... I can get all of the pixels in an array, or a 2d array. But I am currently just accessing them by the getPixel(x, y) method in the Bitmap class.
Does anyone know of an algorithm to rotate the values in an array based on an arbitrary number of degrees? Or any other way of solving this problem?

Have you looked at AffineTransform?
It's what I have used to rotate sprites and images in the past.

I used this code to rotate and get a pixel value from an image:
Image rotatedImage = new BufferedImage(imageToRotate.getHeight(null), imageToRotate.getWidth(null), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D) rotatedImage.getGraphics();
// Set rotation here
g2d.rotate(Math.toRadians(90.0));
g2d.drawImage(imageToRotate, 0, -rotatedImage.getWidth(null), null);
g2d.dispose();
int pixelColor = ((BufferedImage)rotatedImage).getRGB(x, y);

Related

Java rotated buffered image gets cut off

Firstly, I am trying to make a simple game in Java. I have a viewport that shows a tile map and I have a tank in the middle that moves by controlling the JScrollBars of the scrollpane in which the viewport resides in. So far everything has been going well, until I needed to rotate an image. Here is a picture of the game: Note: the tank body and tilemap are on seperate panels and do not share the same graphics.
Picture of non rotated tank body:
Essentially, I want to rotate a buffered image around its center (rotating in place) using arrow keys. I already have the code for the keys, and I also have a method to try and rotate the buffered image given a buffered image and angle in degrees (the angle is changed to radians in the method). This method will return a buffered image that is rotated correctly. Here is the code:
public static BufferedImage rotateImage(BufferedImage image, double angle) {
if(angle == 0)
return image;
else {
angle = Math.toRadians(angle);
double x = Math.abs(Math.cos(angle));
double y = Math.abs(Math.sin(angle));
int newWidth = (int) Math.floor(image.getWidth()*x + image.getHeight()*y);
int newHeight = (int) Math.floor(image.getHeight()*x + image.getWidth()*y);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, image.getType());
Graphics2D tool = rotated.createGraphics();
AffineTransform transformer = new AffineTransform();
transformer.rotate(angle, image.getWidth()/2, image.getHeight()/2);
tool.drawImage(image, transformer, null);
tool.dispose();
return rotated;
}
}
However, as the title suggests, the image gets cut off at the top and left sides of the image when rotated as shown:
Picture of rotated tank body:
So I have looked at many different forums but I could not solve my problem. I could add whitespace around the image, but that interferes a lot with collision detection which I plan to do later on. I know that it has to do something with the original display being smaller than the display of the rotated image, and I have tried to translate accordingly in many ways. If I translate with this line of code specifically,
transformer.translate((newWidth - image.getWidth())/2, (newHeight - image.getHeight())/2);
Then the image (tank body) rotates without cutting, but bounces out of place as shown (I drew a rectangle to show where it was):
Picture of rotated tank with translation:
I also have tried negating the translations too but it only avails to funky movements.
So, I really have no clue how to solve this, and I have been spending too much time on this problem. I would really appreciate a helpful answer that directly edits my method if possible.
Answer
So here is the opening idea that I needed to realize to answer this problem.
The method to translate and rotate is meant so that the image is not cut off. However, it won't be around the center as intended as seen in the 3rd picture. But again, the method is not intended to recenter it. The painting code itself needs to account for this shift. I simply added variables to account for this:
xOffset = (newWidth - image.getWidth())/2;
yOffset = (newHeight - image.getHeight())/2
And simply subtracted these from where I was painting the tank's body.
Thanks to #camickr for the solution
When rotating a square sprite around the center point, the target image should be larger than the original image by a factor of the square root of 2 (approx. 1.41). For example, a sprite will not be clipped at a rotation angle of 45 °.
I hope this information helps you to solve your problem.

Alternative to using Graphics2D Area?

I'm making a game where you start by drawing your map. At the moment it works by having an Area instance variable and then when the player clicks/drags the mouse it adds Ellipse2Ds to it. Here's what I mean:
Area land = new Area();
And then in the MouseDragged method
Point2D mouse = e.getPoint();
Ellipse2D ter = new Ellipse2D.Double(mouse.getX() - drawRad, mouse.getY() - drawRad, drawRad*2, drawRad*2);
land.add(new Area(ter));
And then basically the same except land.subtract(new Area(ter)) for erasing.
My problem with this is that after doing a lot of drawing it becomes very slow to draw the Area. the other problem is that I would like to be able to easily get the outline of the drawing, and I haven't found a good way of doing that using Areas. Using area.getPathIterator is not nearly accurate enough.
So I'm wondering what other ways of saving drawings are? I can't just have an array of Ellipses because then erasing wouldn't work.
Thanks!
If you are drawing the same thing over and over it may be worth while to draw it to an image once or load it from a file at start up and then just paint the image rather than painting all the individual shapes.
To load from a file, put your picture file in the same directory as your .java file and load:
BufferedImage img = ImageIO.read(this.getClass().getResource("picture.png"));
To draw on the image once:
BufferedImage img = new BufferedImage(width,height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g = img.createGraphics();
In your paintComponent method you draw the image somewhere.
g.drawImage(img, 0, 0, this);
If you have something moving over a background, instead of adding and subtracting the moving item. Just draw the background and then draw the thing that moves, drawing the background will effectively erase the old position with less computation.

Bitmap's bounds in Android Studio

My question is: a bitmap had to be square or is possible delete the invisible parts around the colored image? I have a bitmap in a SurfaceView with an hand in the center and i want calculate the bounds of this hand deleting the invisible around it, cause i have problems with the onClick Method. Without calculate every X and Y, is possible know the bounds of the hand with a Method or other things? Thanks in advance.
The best thing you could do would be to take the picture of the hand and crop it down to the size of the hand.
This way, you'll have a smaller file and won't have to implement some kind of code work around.
About your first question: any bitmap that retains some level of transparency has to have an alpha channel so in your case a hand has an alpha channel thus you cannot just delete those alpha pixels, because if you do the transparent part will remain black. You will have to use either ARGB_4444 format or ARGB_8888 format to retain this alpha channel. As far as getting the bitmaps bounds, use a Rect or some bounding shape maybe an oval, to accurately know if your finger is touching it, you can't just know exactly if your touch is within the bounds of this hand because this hand image, at certain portions contain different widths, and heights, however you can test if your touch is touching your hand exactly by using pixel perfect collision. Here is how it works:
class drawingView extends View {
Rect rect = new Rect();
Bitmap bitmap = yourHand;
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, x, y, null);
rect.set(x, y, bitmap.getWidth(), bitmap.getHeight());
invalidate();
}
public void onClick() {
if(rect.contains(event.getX(), event.getY(), && bitmap.getPixel(event.getX() - rect.left, event.getY() - rect.top) != Color.TRANSPARENT) {
// you know you exactly touched the hand even out of the transparent region
}
}
I started you off I'm sure you will understand what's going on here.
Hope this helps :)
The idea is to use a png file with the right transparency in place, which means the part around the hand should have the transparency .
Now for retaining the transparency of the image when using the BitmapFactory,make sure that your image is pulled in as ARGB_8888

Scaling an image and place it in the specific location (coordinates) in Android's LinearLayout

I'm not very skilled in android application developing, and I'm working on a test app. I detected the face and the eyes, now I'm going to draw something like acne or scar on the face (e.g below the eyes on the cheek) based on the coordinates of the eyes. Later, I'm going to put eye-glass or hat on the appropriate locations.
I know the coordinates of the left and the right eye (leftEyePosx... [and so on] in the code). For example 136x168 [left] and 216x168 [right] (mirrored in picture). Now, I'm calculating the scale: glass bitmap should be scaled around 80 pixels (216-136) or bigger for the width, and 80 pixels multiplied with the original image's aspect ratio for the height (e.g. 80 * 0.7). I have the bitmap with the code:
glassBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.glass_01);
Now, how can I re-scale the eye-glass and use the method canvas.drawBitmap() and use eye coordinates and draw the glass on the face? Or should I use another way?
Thanks in advance, and sorry for my bad English :)
First you scale the bitmaps:
Bitmap scaledBitmap = Bitmap.createScaledBitmap(originalBitmap, targetWidth, targetHeight, false);
Then you draw one bitmap on top of the other, at xPos and yPos:
Canvas canvas = new Canvas(baseBitmap);
Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
canvas.drawBitmap(overlayBitmap, xPos, yPos, paint);
xPos and yPos are the upper left corner of the overlay bitmap. For example, if the base image eye is supposed to show in the middle of the overlay bitmap, you have to adjust for this by deducting half the width and height to find the desired xPos and yPos.

How to make a BufferedImage work with this image? (java)

I've got this picture:
Imagine this as a 4x4 tile world of which the tiles are 32x32 pixels each.
then take a look at picture:
Look at the stone tile, it has edges which are outside the grid. Can I use a bufferedImage for this or do I need to do something else to make this work?
If so, could you help me with it by explain it because I've got no clue on how to achieve this is my game?
another example picture:
Let me explain it More Clearly... 1st image = Grid, 2nd Image = Tile Overlapping the grid... (thats what i want to have because then it's a new tile which I can use to make my game look better!), 3d Image = An example of how it would tile!
To completely eliminate boundary artifact, you can use Penrose tiles.
You can mitigate the edge artifact using anti-aliasing. This example uses TexturePaint with three variant shades of each color: original, darker and lighter. You can experiment with a larger number of shades for better results.
Simply use PhotoShop to edit the image to 32x32 pixel size....then use it in your game....
/////////////EDITED//////////////////
As shown here, AffineTransformOp offers the additional flexibility of choosing the interpolation type.
BufferedImage before = getBufferedImage(encoded);
int w = before.getWidth();
int h = before.getHeight();
BufferedImage after = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
AffineTransform at = new AffineTransform();
at.scale(2.0, 2.0);
AffineTransformOp scaleOp =
new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
after = scaleOp.filter(before, after);

Categories

Resources