In a Java 2D game, I have a rectangular sprite of a tank. The sprite can rotate in any angle, and travel in the direction of that angle.
This sprite needs to have a bounding box, so I can detect collision to it.
This bounding box needs to:
Follow the sprite around the screen.
Rotate when the sprite rotates.
Obviously it should be invisible, but right now I'm drawing the box on the screen to see if it works. It doesn't.
My problem is this:
When the sprite travels parallel to the x axis or y axis, the box follows correctly and keeps 'wrapping' the sprite precisely.
But when the sprites travles diagonaly, the box doesn't follow the sprite correctly.
Sometimes it moves too much along the x axis and too little along the y axis. Sometimes the opposite. And maybe sometimes too much both or too little on both. Not sure.
Could you look at my code and tell me if you see anything wrong?
(Please note: The bounding box most of the time is actually just two arrays of coordinates, each one containing 4 values. The coordinates are used to form a Polygon when collision is checked, or when the box is drawn to the screen).
Relevant code from the Entity class, the superclass of Tank:
int[] xcoo = new int[4]; // coordinates of 4 vertices of the bounding box.
int[] ycoo = new int[4];
double x,y; // current position of the sprite.
double dx,dy; // how much to move the sprite, and the vertices of the bounding box.
double angle; // current angle of movement and rotation of sprite and bounding-box.
// Returns a Polygon object, that's the bounding box.
public Polygon getPolyBounds(){ return new Polygon(xcoo,ycoo,xcoo.length) ; }
public void move(){
// Move sprite
x += dx;
y += dy;
// Move vertices of bounding box.
for(int i=0;i<4;i++){
xcoo[i] += dx;
ycoo[i] += dy;
}
// Code to rotate the bounding box according to the angle, will be added later.
// ....
}
Relevant code from the Board class, the class that runs most of the game.
This is from the game-loop.
// keysPressed1 is an array of flags to tell which key is currently pressed.
// if left arrow is pressed
if(keysPressed1[0]==true)
tank1.setAngle(tank1.getAngle()-3);
// if right arrow is pressed
if(keysPressed1[1]==true)
tank1.setAngle(tank1.getAngle()+3);
// if up arrow is pressed (sets the direction to move, based on angle).
if(keysPressed1[2]==true){
tank1.setDX(2 * Math.cos(Math.toRadians(tank1.getAngle())));
tank1.setDY(2 * Math.sin(Math.toRadians(tank1.getAngle())));
tank1.move(); // should move both the sprite, and it's bounding box.
}
Thanks a lot for your help. If you need me to explain something about the code so you can help me, please say so.
Your sprite is using doubles and your bounding box is using ints, see these declarations:
int[] xcoo = new int[4];
double x, y
And the following updates:
(double dx, dy, showing it is a double)
x += dx
xcoo[i] += dx
In the latter (the bounding box) you are adding an int to a double which causes it to drop it's decimal places as it is being cast to an integer.
Hence why they do not follow the sprite exactly, as an int can never follow a double.
To solve this you need xcoo, ycoo and corresponding methods to work with double instead of int.
Update: So Polygon only takes Integers appereantly, to solve that take a look at the following question: Polygons with Double Coordinates
You should be using Path2D.Double
Related
I started learning LibGdx and Java recently, and it has been going well so far.
I'm facing an issue with collision detection.
I have two sprites which can be represented as two shapes, a polygon and a circle, which will collide/intersect at any given moment. Once these two shapes collide, something will get triggered.
So far, this is what I have done. It kinda works but it is not accurate. This is called inside the Render() function:
public boolean CollectPowerUp(PowerUps powerUp) {
if (powerUp.position.dst(position) < Constants.PLAYER_HEIGHT -3) {
Gdx.app.log("Collected PowerUp", "TRUE");
EnablePowerUp(powerUp);
return true;
}
return false;
I have searched many websites, and most of the solutions include other softwares like 2DCube or PhysicsEditor. Is it possible to perform this intersection solely by using LibGdx and Java? If so, what should I look into?
Thanks
Intersector class having many static method that can be used for collision detection.
If your polygon is rectangle you can use :
Intersector.overlaps(Circle c, Rectangle r)
else
Polygon polygon=new Polygon();
polygon.setVertices(new float[]{0,0,.......});
Circle circle=new Circle(x, y, radius);
float points[]=polygon.getTransformedVertices();
for (int i=0;i<points.length;i+=2){
if(circle.contains(points[i],points[i+1])){
System.out.println("Collide");
}
}
EDIT
Above code only detect collision if polygon vertices are inside circle, what if
circle is completely inside polygon
some part of circle is inside polygon but vertices are outside the circle
Create a polygon for circle that act as circle in view and polygon in model
float radius=100;
FloatArray floatArray=new FloatArray();
int accuracy=24; // can be use 1 for complete circle
for (int angle=0;angle<360;angle += accuracy){
floatArray.add(radius * MathUtils.cosDeg(angle));
floatArray.add(radius * MathUtils.sinDeg(angle));
}
Polygon circle=new Polygon(floatArray.toArray()); // This is polygon whose vertices are on circumference of circle
float[] circularPoint=circle.getTransformedVertices();
for (int i=0;i<circularPoint.length;i+=2){
if(polygon.contains(circularPoint[i],circularPoint[i+1])){
System.out.println("Collide With circumference");
break;
}
}
There's a nice article on collision detection on www.gamedevelopment.blog which shows how to detect collisions with most shapes. This is the Libgdx circle, polygon collision detection method shown in the article.
public boolean contains (Polygon poly, Circle circ) {
final float[] vertices = poly.getTransformedVertices(); // get all points for this polygon (x and y)
final int numFloats = vertices.length; // get the amount of points(x and y)
// loop through each point's x and y values
for (int i = 0; i < numFloats; i += 2) {
// get the first and second point(x and y of first vertice)
Vector2 start = new Vector2(vertices[i],vertices[i + 1]);
// get 3rd and 4th point (x and y of second vertice) (uses modulo so last point can use first point as end)
Vector2 end = new Vector2(vertices[(i + 2) % numFloats], vertices[(i + 3) % numFloats]);
// get the center of the circle
Vector2 center = new Vector2(circ.x, circ.y);
// get the square radius
float squareRadius = circ.radius * circ.radius;
// use square radius to check if the given line segment intersects the given circle.
return Intersector.intersectSegmentCircle (start, end, center, squareRadius);
}
}
There are many useful methods in the Intersector class which can be used for collision detection.
Essentially, what is happening is there is some strange warping of the 3D cube being rendered by my raytracer, which continues to worsen as the camera moves up, even if the cube is in the same location on the screen.
The code is at http://pastebin.com/HucgjRtx
Here is a picture of the output:
http://postimg.org/image/5rnfrlkej/
EDIT: Problem resolved as being that I was just calculating the angles for vectors wrong. The best method I have found is creating a vector based on your FOV (Z) current pixel X, and current pixel Y, then normalizing that vector.
It looks like you're calculating rays to cast based on Euler angles instead of the usual projection.
Typically a "3D" camera is modeled such that the camera is at a point with rays projecting through a grid spaced some distance from it... which is, incidentally, exactly like looking at a monitor placed some distance from your face and projecting a ray through each pixel of the monitor.
The calculations are conceptually simple in fixed cases.. e.g.
double pixelSpacing = 0.005;
double screenDistance = 0.7;
for (int yIndex= -100; yIndex<= 100; yIndex++)
for (int xIndex= -100; xIndex<= 100; xIndex++) {
Vector3 ray = new Vector3(
xIndex * pixelSpacing,
yIndex * pixelSpacing,
screenDistance
);
ray = vec.normalize();
// And 'ray' is now a vector with our ray direction
}
You can use one of the usual techniques (e.g. 4x4 matrix multiplication) if you want to rotate this field of view.
What I'm trying to do is basically the thing you can do in the desktop when you click and drag te mouse making a square. The problem is I don't know how to make it draw "backwards" or how to clean the previous parameters when you start a new square. here is the entire code:
public void paint (Graphics j){
super.paint(j);
j.drawRect(x,y,z,w);
}
private void formMousePressed(java.awt.event.MouseEvent evt) {
x=evt.getX();
y=evt.getY();
repaint();
}
private void formMouseDragged(java.awt.event.MouseEvent evt) {
z=evt.getX();
w=evt.getY();
repaint();
}
The signature for drawRect is: drawRect(int x, int y, int width, int height). You need to calculate the top left corner of the square, and the width and height.
The top-left corner is (min(x, z), min(y, w)).
The width is abs(x-z) and the height is abs(y-w)
Putting this together we get
Try
j.drawRect(Math.min(x, z), Math.min(y, w), Math.abs(x - z), Math.abs(y - w));
Why does this work? Well you're given 2 points. It's a well known fact that 2 points can determine a square(opposite corners). The first problem is that you have to translate the points you're given, into an input that java likes. In this case, you first need the upper left hand corner. You don't know which point you have is that corner, or actually it could be that neither of them are.
So what do we know about the upper left corner? We know that it's x value is the smallest x value that exists in the square. We also know that at least one of the 2 points given rest on that same edge. Using this information we can determine that the x coordinate of the top left corner is the smallest x value of our 2 points. Or min(x, z). We use the same procedure to find the y coordinate.
Now width and height are easy. The width is the right edge - the left edge. We don't know which point is the right side, and which is the left side, but it doesn't matter. If we take the absolute value of the difference will always give you the positive difference between the points. In this case abs(x-z). The process is the same for the height.
As for resetting the square try adding a formMouseReleased method and setting x, y, z, w to 0.
I think you might create a method that resets the parameters
something like: void modifyMouse() in your Mouse class
//your parameters=0
I might try to give you a better help if you clarify your question, for now try that.
I'm making pong in Java and wanted to make the game more fun by assigning different reflection logic to each part of the paddle, like so:
(ball hittins outter edges of paddle will have a different effect than it hitting the middle of the paddle)
The paddle extends Rectangle2D so I could use Rectangle2D's intersects() method to determine if the ball has touched any part of it...
Is it possible to determine where exactly the ball has hit on the paddle?
What I'm planning to do is,
calculate angle of incidence and reflective angle based on that...
If the ball hits at a point x on the paddle... I will change the reflection angle accordingly
Thanks
Is it possible to determine where exactly the ball has hit on the paddle?
If it were me, I would grab the current co-ordinates of both the ball and the paddle. For the paddle, you can get two sets of y co-ordinates, to describe the line facing the ball. Ie:
int paddleY1 = paddle.y;
int paddleY2 = paddle.y + paddle.width;
// assuming the paddle can only go up and down, y is the only co-ordinate that matters.
Then, you can compute the mid point of the paddle as:
int paddleYMid = (paddleY1 + paddleY2) / 2;
You can find out if the ball hit the left or right side of the paddle by comparing the y co-ordinates. Ie:
if(ball.y > paddleYMid)
{
// Right side of the paddle.
}
else
{
// Left side of the paddle.
}
Then it's up to you to develop further refinement.
Since the paddle is always vertical (parallel to Y axis), the point of collision of the ball and the paddle will happen at the same y-coordinate as the center of the ball. That is:
if (ball.centerX - ball.radius <= paddle.rightX && ball.velocityX < 0)
{
// collision point, if any, is at (ball.centerX - ball.radius, ball.centerY)
if (ball.centerY >= paddle.bottomY && ball.centerY <= paddle.topY)
{
// handle collision
}
}
As for the handling of the collision itself, you may not have to deal with angle of reflection, etc, but work solely with the raw values of x and y velocity. For example, to simulate a perfectly elastic collision, simply do:
ball.velocityX = -ball.velocityX;
And to account for ball reflecting at a higher or lower angle, you can scale the y velocity based on the position of the collision from the center of the paddle, eg.
ball.velocityY += SCALE_CONSTANT * (ball.centerY - ((paddle.topY + paddle.bottomY) / 2));
To find the exact spot where the ball hits the paddle, you can formulate the problem as a line intersection problem.
The paddle can be represented as a vertical line at the x-coordinate (+thickness if needed, and corrected for the balls diameter) of the paddle. The ball can then be represented as a line along its movement vector (this line could be simply defined by its current position and its next position if it were to move unimpended).
The problem can now be solved using a line intersection algorythm.
Since the paddle is a vertical line, the equations can be simplified to just answer the question at which Y will the ball pass the paddle's X. Thats also a common problem encountered and solved by line clipping: http://en.wikipedia.org/wiki/Line_clipping (the intersection points can also be computed directly, but I can't find the formula atm).
How could I draw a quadratic curve or a trigonometric curve (such as sin(x)) on a Canvas?
Like you, I needed to draw a curved line from point(x1, y1) to point (x2, y2). I did some searching around which lead me to the Path class (android.graphics.Path). Path has numerous methods for drawing lines. Once you have created a path you use a draw method to make the actual line. The paths can be rotated, transformed, saved, and added to. There are arcs, circles, and rectangles that be drawn with this class too.
http://developer.android.com/reference/android/graphics/Path.html
Set start point of path → mPath.moveTo(x1, y1);
Set constant and end points → mPath.quadTo(cx, cy, x2, y2);
Convert path to line → canvas.drawPath(mPath, mPaint);
Here is a drawEquation() method I wrote for a Graph class - I think it may help. The basic idea to create a method that accepts an equation (which is basically just a function) like
function(x) = Math.sin(x);
and then loop through the bounds of the graph and draws small segments connecting each point. The transformContext() just inverts the canvas context so that increasing values of y go upwards and not downwards:
Graph.prototype.transformContext = function(){
var canvas = this.canvas;
var context = this.context;
// move context to center of canvas
this.context.translate(this.centerX, this.centerY);
// stretch grid to fit the canvas window, and
// invert the y scale so that that increments
// as you move upwards
context.scale(this.scaleX, -this.scaleY);
};
Graph.prototype.drawEquation = function(equation, color, thickness){
var canvas = this.canvas;
var context = this.context;
context.save();
this.transformContext();
context.beginPath();
context.moveTo(this.minX, equation(this.minX));
for (var x = this.minX + this.iteration; x <= this.maxX; x += this.iteration) {
context.lineTo(x, equation(x));
}
context.restore();
context.lineJoin = "round";
context.lineWidth = thickness;
context.strokeStyle = color;
context.stroke();
};
Most drawing APIs dont provide such functions, you will have to calculate the pixels of your desired curve in pixels and draw piece by piece on the canvas using one or more calls to the canvas API.
Use Canvas.drawPath and Path.quadTo.
I'm going to assume that you are familiar with drawing basic lines on a canvas, if not then respond back and we can delve further back. However, as far as just drawing a sine function there is a function within the Math class that has just what you need.
http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Math.html#sin%28double%29
From there you just need to pass your x variable(in radians) into the function and save it's output as a y variable. This represent a point on your graph. Now increment the x1 variable by a small amount (perhaps 1/100 of your graph, though you will need to adjust this to taste), run it through the function again and save those variables(x2 and y2) as your second point. Draw a line between these two points. Save your x2,y2 variables as x1, y1 and increment your x value again to find the third point, so on and so forth. This is not a "true" curve as it is really just a series of lines which approximate the function, a calculus approach if you will.
So:
x1 = x; // where x is some point on the x axis which you would like to start graphing at.
y1 = sin(x);
x2 = x1 + increment;
y2 = sin(x2);
//Draw a line here
x1 = x2;
y1 = y2;
//return to top, this code would obviously be in a loop in which uses increment as it's own increment with the initial value being equal to the amount you want to increment each time(let's say....5) and the "next" statement being increment = increment + 5.
There is also a GraphCanvas class which I am unfamiliar with which appears to take those same points and draw the curve between them, though I am unsure what sort of transform is being used to draw the curve and how accurate that is. Here is the Class:
http://www.java2s.com/Code/Java/Swing-Components/GraphCanvas.htm