Java detect collision for fast moving objects - java

I have problem, that balls that move too fast can fly through wall (walls are 4 pixels wide, and speed of ball occasionally is more than 400 pixels per second (which is more than 4 pixels per update assuming fps is 60)). I researched it on StackOverflow, but the solution for others is not suitable for me, as they are using rectangles, and i am using pixel collision. Here is method which returns if ball intersects with wall (method is in Ball class):
public boolean intersects(Wall w) {
BufferedImage im1 = new BufferedImage (size, size, BufferedImage.TYPE_INT_ARGB); // size is diameter of the ball
BufferedImage im2 = new BufferedImage (size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g1 = im1.createGraphics();
Graphics2D g2 = im2.createGraphics();
g1.translate(-x + size/2, -y + size/2);
g2.translate(-x + size/2, -y + size/2);
render(g1);
w.render(g2);
g1.dispose();
g2.dispose();
for (int x = 0; x < im1.getWidth(); x++){
for (int y = 0; y < im1.getHeight(); y++){
Color c1 = new Color(im1.getRGB(x, y), true);
Color c2 = new Color(im2.getRGB(x, y), true);
if (c1.getAlpha() != 0 && c2.getAlpha() != 0){
return true;
}
}
}
return false;
}
Here is how ball is drawn:
public void render(Graphics2D g) {
color = new Color (Color.HSBtoRGB(hue, 0.5f, 0.5f));
g.setColor (color);
g.fillOval((int)(x-size/2), (int) (y-size/2), size, size);
}
The wall is simply defined as 2 points, and here is how wall is drawn:
public void render(Graphics2D g2) {
g2.setColor(new Color(r, g, b));
g2.setStroke(new BasicStroke(width)); //width = 4
g2.draw(new Line2D.Float(p1.x, p1.y, p2.x, p2.y));
}

I have a couple of quick ideas that you might want to attempt.
Why not check the endpoints against the dimensions of the wall (some simple algebra and I think what you might already be trying to do). If you need help with the algebra involved I'll include a link below. Basically just record the starting point of the ball (prior to moving), and on update check the old location versus the estimated location, and run a function to see if a collision occurs. This will become tricky however if you want realistic physics.
https://gamedev.stackexchange.com/questions/26004/how-to-detect-2d-line-on-line-collision
In the above link, just assume your walls are one line, and the previous coordinates and the expected next coordinates of the ball form the other line. This method works very well if you only have the ball moving in straight lines.
Your only alternative might be to have two simultaneous models running (basically a visual space which you have in showing the ball and walls, and a virtual setup checking the physics behind what you are showing.

Related

Blending motion for circles

How can I blend a moving circle's motion in an CPU efficient way which still allows for fairly accurate collision detection? I've tried stretching and rotating and ellipse, but it looks unnatural. I've tried drawing in between objects my sketch below. But the motion is still jagged between the frames, and it's expensive to draw a lot of in between circles with transparency. Is there a better way to do this? For example, bridging the shapes or smoothing motion with a bezier transformation? Maybe holding the last two locations in an array and using the current and last two to draw a bezier curve and drawing a curved shape between the last location and current location with the data from all three points? How do you deal with fast motion where you need to avoid the "..." effect, but also need to use the current location for collision detection and so on?
int xprev;
int yprev;
int xcur;
int ycur;
float size = 10;
float scale;
int rotation = 0;
void setup() {
size(600, 600);
}
void draw () {
background(0);
xcur= int(cos(radians(rotation))*200) + width/2;
ycur = int(sin(radians(rotation))*200) + height/2;
rotation = rotation + 5;
//scale = dist(xcur, ycur, xprev, yprev)/2;
//println(scale);
drawEllipse();
xprev = xcur;
yprev = ycur;
}
void drawEllipse() {
for (int i = 1; i < 10; i++) {
noStroke();
fill(255, i*20);
ellipse(xprev+ (xcur - xprev) * i/10, yprev + (ycur - yprev) * i/10, size, size);
}
fill(255);
ellipse(xcur, ycur, size, size);
}

How can I draw images into JavaFX Canvas without blur/aliasing in a given area?

I am trying to draw an image in a JavaFX Canvas. However, the conventional drawImage() method of GraphicsContext seems to produce some sort of blurred or aliased results. Probably because I am using a Retina MacBook Pro.
I found a solution here: https://stackoverflow.com/a/26706028/555690
public class ImageRenderer {
public void render(GraphicsContext context, Image image, int sx, int sy, int sw, int sh, int tx, int ty) {
PixelReader reader = image.getPixelReader();
PixelWriter writer = context.getPixelWriter();
for (int x = 0; x < sw; x++) {
for (int y = 0; y < sh; y++) {
Color color = reader.getColor(sx + x, sy + y);
if (color.isOpaque()) {
writer.setColor(tx + x, ty + y, color);
}
}
}
}
}
It works, but sometimes I need to draw the target rect with a particular size (so I need to add parameters for int tw and int th), but I don't know how to adjust the logic of the above method to make use of them.
How can I draw images into JavaFX Canvas without blur/aliasing in a given area?
I also have a MacBook Pro Retina and I had the same problem but if you do it right, the images that you draw on a canvas are not blurred.
What you have to keep in mind is that the Retina Mac has a pixel scale factor of 2. So if you ask for a canvas of say 1000x1000 you will get a canvas which has a size of 1000x1000 raster units or virtual pixels but internally it is backed by an image of 2000x2000 pixels. Now assume you have an image with a size of 400x400 pixels and you want to display that in the upper left corner of your canvas without blurring. You then have to use the drawImage function in the following way:
gc.drawImage(img, 0, 0, 400, 400, 0, 0, 200, 200);
Another scenario might be that you want to fill the upper quarter of the canvas with an image which is an area of 500x500 raster units. That means you will have to provide an image with 1000x1000 pixels and place it at
gc.drawImage(img, 0, 0, 1000, 1000, 0, 0, 500, 500);
I hope this helps to understand the issue.

flipping sprite image in Android game

I am making a game in Android that requires a list of sprites to running from right to left so I try to flip the image using the code below.
It slows the game speed down so much, moves fast running to the right but slows down so much running to the left.
public void Draw(Canvas spriteBatch, int X, int Y, int imageIndex,int flip)
{
int drawY = (imageIndex / columns);
int drawX = imageIndex - drawY * columns;
int spriteX = drawX * spriteWidth;
int spriteY = drawY * spriteHeight;
Rect src = new Rect( spriteX, spriteY,spriteX + spriteWidth, spriteY + spriteHeight);
Rect dst = new Rect(X, Y, X + spriteWidth,Y + spriteHeight);
location.X = X;
location.Y = Y;
if(flip == 1)
{
//here image is flipped
spriteBatch.save();
spriteBatch.scale(-scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
//Use simple use this to flip image canvas.scale(-1, 1)
}
else if(flip == 0)
{
//draws sprite without flipping
spriteBatch.save();
spriteBatch.scale(scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
}
this.SetFrame(imageIndex);
}
I can flip using matrix but I can't draw a sprite using matrix.
Is there a way to draw sprite using matrix and would it make it faster?
matrix.reset();
matrix.setTranslate(location.X, location.Y);
matrix.setScale(-scaleX,scaleX);
matrix.postTranslate(location.X + texture2D.getWidth(), location.Y);
Or is there another way that is faster?
So I think that the matrix solution should will work perfectly the canvas object includes it's own built in matrix which if you manipulate will affect the output of all graphics after that point. So you just set the matrxi and then do the draw.
You're solution is okay but you're doing it wrong - rather do this part ONCE:
Create the left-ward facing sprite as you do at the beginning and then store it. Then EACH FRAME: Use this 'cached' copy of the inverted bitmap that you created.
A third solution (which I haven't tried but might work) is to manipulate the destination rectangle so that the left and right edges are swapped - the canvas docs say, "Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle." - I suspect that this might include negative scaling to fit the space where the right edge is smaller than the left although the docs do not explicitly say so.

How to draw a circle within a circle?

I'm trying to get a bunch of small circles that have varying shades of green to be drawn within a big circle to get a "bush" look, but I can't figure out how to get all the small circles within the shape of a big circle. I can only figure out how to get it within a rectangle.
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i = 0; i < 1000; i++){
int redV = (int) ((Math.random() * 100) + 27);
g.setColor(new Color(red, red + 31, red - 15));
int x = (int) ((Math.random() * 400) + 150);
int y = (int) ((Math.random() * 500) + 200);
g.fillOval(x, y, 50, 50);
}
}
I guess you have to do some geometry here, and verify whether the x and y coordinates generated randomly are within your circle. As you said, within a rectangle is easy (because you just check that x > left, x+50 < right, y > top, y+50 < bottom), however for a circle you have to use the equation of a circle and check that (x,y) and (x+50,y+50) are within it before actually doing the fillOval().
I think you have a simple way out by using the Java 2D Shape.contains(), which is implemented by Ellipse2D. So essentially you create an instance of Ellipse2D.Double or Ellipse2D.Float for the greater circle, and then just call contains() each time you generate the coordinates to check they are within it before drawing them.
I think you can just change the Color slightly, and increment/decrement x, y, width, and height slightly to get them to be within the older circle. The new oval should be painted over the old one.
Choose the point that should be the center of the big circle, and draw the big circle relative to that (e.g. using java.awt.geom.Ellipse2D).
You can then use the center of the big circle and its radius to position the other smaller circles relative to that also, inside the circumference.

How to draw a decent looking Circle in Java

I have tried using the method drawOval with equal height and width but as the diameter increases the circle becomes worse looking. What can I do to have a decent looking circle no matter the size. How would I implement anti-aliasing in java or some other method.
As it turns out, Java2D (which I'm assuming is what you're using) is already pretty good at this! There's a decent tutorial here: http://www.javaworld.com/javaworld/jw-08-1998/jw-08-media.html
The important line is:
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
you can set rendering hints:
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
Two things that may help:
Use Graphics2D.draw(Shape) with an instance of java.awt.geom.Ellipse2D instead of Graphics.drawOval
If the result is still not satisfactory, try using Graphics2D.setRenderingHint to enable antialiasing
Example
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Shape theCircle = new Ellipse2D.Double(centerX - radius, centerY - radius, 2.0 * radius, 2.0 * radius);
g2d.draw(theCircle);
}
See Josef's answer for an example of setRenderingHint
Of course you set your radius to what ever you need:
#Override
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
Ellipse2D.Double hole = new Ellipse2D.Double();
hole.width = 28;
hole.height = 28;
hole.x = 14;
hole.y = 14;
g2d.draw(hole);
}
Thanks to Oleg Estekhin for pointing out the bug report, because it explains how to do it.
Here are some small circles before and after. Magnified a few times to see the pixel grid.
Going down a row, they're moving slightly by subpixel amounts.
The first column is without rendering hints. The second is with antialias only. The third is with antialias and pure mode.
Note how with antialias hints only, the first three circles are the same, and the last two are also the same. There seems to be some discrete transition happening. Probably rounding at some point.
Here's the code. It's in Jython for readability, but it drives the Java runtime library underneath and can be losslessly ported to equivalent Java source, with exactly the same effect.
from java.lang import *
from java.io import *
from java.awt import *
from java.awt.geom import *
from java.awt.image import *
from javax.imageio import *
bim = BufferedImage(30, 42, BufferedImage.TYPE_INT_ARGB)
g = bim.createGraphics()
g.fillRect(0, 0, 100, 100)
g.setColor(Color.BLACK)
for i in range(5):
g.draw(Ellipse2D.Double(2+0.2*i, 2+8.2*i, 5, 5))
g.setRenderingHint( RenderingHints. KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON)
for i in range(5):
g.draw(Ellipse2D.Double(12+0.2*i, 2+8.2*i, 5, 5))
g.setRenderingHint( RenderingHints. KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE)
for i in range(5):
g.draw(Ellipse2D.Double(22+0.2*i, 2+8.2*i, 5, 5))
#You'll probably want this too later on:
#g.setRenderingHint( RenderingHints. KEY_INTERPOLATION,
# RenderingHints.VALUE_INTERPOLATION_BICUBIC)
#g.setRenderingHint( RenderingHints. KEY_RENDERING,
# RenderingHints.VALUE_RENDER_QUALITY)
ImageIO.write(bim, "PNG", File("test.png"))
Summary: you need both VALUE_ANTIALIAS_ON and VALUE_STROKE_PURE to get proper looking circles drawn with subpixel accuracy.
Inability to draw a "decent looking circle" is related to the very old bug 6431487.
Turning antialiasing on does not help a lot - just check the kind of "circle" produced by the drawOval() or drawShape(Eclipse) when the required circle size is 16 pixels (still pretty common for icon size) and antialiasing is on. Bigger antialiased circles will look better but they are still asymmetric, if somebody will care to look at them closely.
It seems that to draw a "decent looking circle" one has to manually draw one. Without antialiasing it will be midpoint circle algorithm (this question has an answer with a pretty java code for it).
EDITED: 06 September 2017
That's an algorithm invented by me to draw a circle over a integer matrix. The same idea could be used to write a circle inside a BufferedImage.
If you are trying to draw that circle using the class Graphics this is not the answare you are looking for (unless you wish to modify each color-assignement with g.drawLine(x, y, x+1, y), but it could be very slow).
protected boolean runOnCircumference(int[][] matrix, int x, int y, int ray, int color) {
boolean ret;
int[] rowUpper = null, rowInferior = null, rowCenterUpper = null, rowCenterInferior = null;
if (ret = ray > 0) {
if (ray == 1) {
matrix[y][x + 1] = color;
rowUpper = matrix[++y];
rowUpper[x] = color;
rowUpper[x + 2] = color;
matrix[y][x] = color;
} else {
double rRay = ray + 0.5;
int r = 0, c = 0, ray2 = ray << 1, ray_1 = ray - 1, halfRay = (ray >> 1) + ray % 2, rInf,
ray1 = ray + 1, horizontalSymmetricOldC;
// draw cardinal points
rowUpper = matrix[ray + y];
rowUpper[x] = color;
rowUpper[x + ray2] = color;
matrix[y][x + ray] = color;
matrix[ray2 + y][x + ray] = color;
horizontalSymmetricOldC = ray1;
rInf = ray2;
c = ray_1;
for (r = 0; r < halfRay; r++, rInf--) {
rowUpper = matrix[r + y];
rowInferior = matrix[rInf + y];
while (c > 0 && (Math.hypot(ray - c, (ray - r)) < rRay)) {
rowUpper[x + c] = color;
rowUpper[x + horizontalSymmetricOldC] = color;
rowInferior[x + c] = color;
rowInferior[x + horizontalSymmetricOldC] = color;
// get the row pointer to optimize
rowCenterUpper = matrix[c + y];
rowCenterInferior = matrix[horizontalSymmetricOldC + y];
// draw
rowCenterUpper[x + r] = color;
rowCenterUpper[x + rInf] = color;
rowCenterInferior[x + r] = color;
rowCenterInferior[x + rInf] = color;
horizontalSymmetricOldC++;
c--;
}
} // end r circle
}
}
return ret;
}
I tried it so many times, verifying manually it correctness, so I think it will work. I haven't made any range-check just to simplify the code.
I hope it will help you and everyone wish to draw a circle over a matrix (for example, those programmer who tries to create their own videogames on pure code and need to manage a matrix-oriented game-map to store the objects lying on the game-map [if you need help on this, email me]).

Categories

Resources