Java Arc2D Collision detection (With Rotation) - java

I have tried to create NPC character that can "see" the player by using cones of vision.
The NPC will rotate back and forth at all times.
My problem is that the arc has a generic and unchanging position, but when its drawn to the screen it looks correct.
[Screenshots of the collisions in action][1]
[GitHub link for java files][2]
I'm using Arc2D to draw the shape like this in my NPC class
// Update the shapes used in the npc
rect.setRect(x, y, w, h);
ellipse.setFrame(rect);
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
/ CenterX, CenterY (of the npc),
/ the distance from the arc to the npc
/ a constant value around 45 degrees and a constant value around 90 degress (to make a pie shape)
I've tried multiplying the position and the angles by the sin and cosine of the NPC's current angle
something like these
visionArc.setArcByCenter(cx * (Math.cos(Math.toRadians(angle))), cy (Math.sin(Math.toRadians(angle)), visionDistance, visionAngle, visionAngle * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle - angle, (visionAngle + angle) * 2, Arc2D.PIE);
or
visionArc.setArcByCenter(cx, cy, visionDistance, visionAngle * (Math.cos(Math.toRadians(angle))), visionAngle * 2, Arc2D.PIE);
I've tried a lot but can't seem to find what works. Making the vision angles not constant makes an arc that expands and contracts, and multiplying the position by the sin or cosine of the angle will make the arc fly around the screen, which doesn't really work either.
This is the function that draws the given NPC
public void drawNPC(NPC npc, Graphics2D g2, AffineTransform old) {
// translate to the position of the npc and rotate
AffineTransform npcTransform = AffineTransform.getRotateInstance(Math.toRadians(npc.angle), npc.x, npc.y);
// Translate back a few units to keep the npc rotating about its own center
// point
npcTransform.translate(-npc.halfWidth, -npc.halfHeight);
g2.setTransform(npcTransform);
// g2.draw(npc.rect); //<-- show bounding box if you want
g2.setColor(npc.outlineColor);
g2.draw(npc.visionArc);
g2.setColor(Color.BLACK);
g2.draw(npc.ellipse);
g2.setTransform(old);
}
This is my collision detection algorithim - NPC is a superclass to ninja (Shorter range, higher peripheral)
public void checkNinjas(Level level) {
for (int i = 0; i < level.ninjas.size(); i++) {
Ninja ninja = level.ninjas.get(i);
playerRect = level.player.rect;
// Check collision
if (playerRect.getBounds2D().intersects(ninja.visionArc.getBounds2D())) {
// Create an area of the object for greater precision
Area area = new Area(playerRect);
area.intersect(new Area(ninja.visionArc));
// After checking if the area intersects a second time make the NPC "See" the player
if (!area.isEmpty()) {
ninja.seesPlayer = true;
}
else {
ninja.seesPlayer = false;
}
}
}
}
Can you help me correct the actual positions of the arcs for my collision detection? I have tried creating new shapes so I can have one to do math on and one to draw to the screen but I scrapped that and am starting again from here.
[1]: https://i.stack.imgur.com/rUvTM.png
[2]: https://github.com/ShadowDraco/ArcCollisionDetection

After a few days of coding and learning and testing new ideas I came back to this program and implemented the collision detection using my original idea (ray casting) and have created the equivalent with rays!
Screenshot of the new product
Github link to the project that taught me the solution
Here's the new math
public void setRays() {
for (int i = 0; i < rays.length; i++) {
double rayStartAngleX = Math.sin(Math.toRadians((startAngle - angle) + i));
double rayStartAngleY = Math.cos(Math.toRadians((startAngle - angle) + i));
rays[i].setLine(cx, cy, cx + visionDistance * rayStartAngleX, cy + visionDistance * rayStartAngleY);
}
}
Here is a link the the program I started after I asked this question and moved on to learn more, and an image to what the new product looks like
(The original github page has been updated with a new branch :) I'm learning git hub right now too
I do not believe that using Arc2D in the way I intended is possible, however there is .setArcByTangent method, it may be possible to use that but I wasn't going to get into that. Rays are cooler.

Related

libGdx collision detection Polygons

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.

Bullets not getting shot out of the gun

I'm making a little game just for fun and I got stuck making the bullets come out of the gun. In the code below, the direction of the player is a degree angle called rot.
float gunOffsetX = 106, gunOffsetY = 96;
double angle = Math.toRadians(rot); // convert direction of player from degrees to radians for sin and cos
x = getX(); // player X
y = getY(); // player Y
float bulletX = (float) (x + (gunOffsetX * Math.cos(angle) - gunOffsetY * Math.sin(angle)));
float bulletY = (float) (y + (gunOffsetX * Math.sin(angle) + gunOffsetY * Math.cos(angle)));
Instances.fire.add(new Fire(bulletX, bulletY, rot, weapon));
Also tried:
bulletX = (float) (x + Math.cos(angle + Math.atan2(gunOffsetX, gunOffsetY)) * Point2D.distance(0, 0, gunOffsetX, gunOffsetY));
But same results
Supposedly the bullets should spawn at the end of the gun but this isn't the case as you can see in the following gif..
Any help appreciated
One big issue (at least in my opinion) is how this game handles anchor points of shapes, like the player.
We can highlight the anchor point by drawing a little red rectangle on its place:
g.setColor(Color.RED);
g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);
This comes into the Draw#renderGame(Graphics2D) method, so it looks like:
private void renderGame(Graphics2D g) {
g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
g.drawImage(player.getCurrentFrame(), (int)player.getX(), (int)player.getY(), player.getWidth(), player.getHeight(), null);
g.setColor(Color.RED);
g.drawRect((int)player.getX() -5, (int)player.getY() -5, 10, 10);
g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
//...
then we'll see that the anchor point is not in the center of the image:
As you can see, the anchor point (the original (0,0) point before the rotation) isn't in the center of the image and the crosshair is related to it, instead of the view of the player.
This happens due to the shifting operation while the player rotation:
g.rotate(Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
//...
g.rotate(-Math.toRadians(player.rot), player.getX()+64, player.getY()+64);
You're shifting the postion with +64. I suggest to remove that and add the shifting to the g.drawImage call instead, so the anchor point is correctly in the center (mind that I avoided the fixed value 64):
g.rotate(Math.toRadians(player.rot), player.getX(), player.getY());
g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2), player.getWidth(), player.getHeight(), null);
g.rotate(-Math.toRadians(player.rot), player.getX(), player.getY());
Then you now fire the gun you'll see that the bullet always "starts" from a certain position from the player. The problem here is the incorrect offset you used. The proper values are:
float gunOffsetX = 35, gunOffsetY = 29;
(I got them by trial and error, so you may adjust them a bit more, if you like)
Now it looks like this:
As you can see, the shot is still a bit misplaced, but this happens due to the incorrect rotation of the bullet (like you did it for the player shape):
g.rotate(Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX()+f.getWidth()/2, f.getY()+f.getHeight()/2);
It should look like this (without any X or Y adjustments):
g.rotate(Math.toRadians(f.rot), f.getX(), f.getY());
g.drawImage(f.img, (int)f.getX(), (int)f.getY(), f.getWidth(), f.getHeight(), null);
g.rotate(-Math.toRadians(f.rot), f.getX(), f.getY());
The end result is:
The player now correctly looks at the crosshair and the shots are placed in front of the gun.
If you like to fire directly through the center of the crosshair, you'll only need to adjust the player position and the bullet offset a bit.
Player (in Draw#renderGame(Graphics2D)):
g.drawImage(player.getCurrentFrame(), (int)player.getX() - (player.getWidth() / 2), (int)player.getY() - (player.getHeight() / 2) - 30, player.getWidth(), player.getHeight(), null);
(mind the -30 in (int)player.getY() - (player.getHeight() / 2) - 30)
Bullet:
float gunOffsetX = 35, gunOffsetY = 0;
Now the bullet travels right through the crosshair (mind that the red rectangle is right on the weapon):
(I'm a bit too stupid to create proper GIF files, so I can only provide pictures)
Now you have the necessary offset values to get the result you want, but you should definitely try to understand why the values are like they are right now. You need to replace them later with dynamic values, since different weapons need different offsets for the bullet, because the player image differs. It should be helpful to have some kind of class with instances for each weapon type, which contains the images and the coordinates where the weapon barrel is located in the image. Then you can use these coordinates to correctly set the offsets for the bullet image.

Firing projectiles in a circle

So I can't seem to find an answer to this, but I am trying to fire bullets into a circle. I have a simple class for a circular path that I attach to a bullet and it reads from that class a position when given a time value. The bullet simply increments this time value, constantly updating its position to the next. This can be improved but until I get the logic down this is what I have. I know this method works because I tried it with a linear path. The problem is applying it to a circular path.
I want the bullet to circle around a point (say Point 'Center') with a given radius and speed. I want all bullets to travel at the same speed no matter the radius of the circle so a larger circle will take longer to complete than a shorter one. Currently what is happening is I have the CircularPath object giving saying x = r * cos(t) and y = r * sin (t) where t is in radians, but this is making a circle that increases in speed as the radius increases and the radius and center of this circle is completely off. The bullets are starting in the correct position, except the radius and speeds are off. I hope I am describing this adequately. I will post the code for anyone to inspect.
package io.shparki.tetris.go;
import io.shparki.tetris.util.Point2D;
import java.awt.Color;
import java.awt.Graphics2D;
public class CircularPath extends Path{
private double radius;
// Where Start is the center and end is the location of mouse
// Radius will be distance between the two
public CircularPath(Point2D start, Point2D end) {
super(start, end);
radius = normalToEnd.getLength();
color = Color.YELLOW;
}
public Point2D getPointAtTime(double time){
double px = start.getX() + radius * Math.cos(Math.toRadians(time));
double py = start.getY() - radius * Math.sin(Math.toRadians(time));
return new Point2D(px, py);
}
public double getFinalTime() { return 0; }
public CircularPath getClone() { return new CircularPath(start.getClone(), end.getClone()); }
public void update(){
super.update();
radius = normalToEnd.getLength();
}
public void render(Graphics2D g2d){
super.render(g2d);
g2d.drawLine((int)start.getX(), (int)start.getY(), (int)end.getX(), (int)end.getY());
//g2d.drawOval((int)(start.getX() - radius), (int)(start.getY() - radius), (int)radius * 2, (int)radius * 2);
}
}
x = r * cos(t/r)
y = r * sin(t/r)
The other solution is to model 2d momentum and impose a "gravitational force" toward the center point (or ellipsoidal focus, more generally) that you want the moving object to orbit around.
(The classic Space Wars game was implemented on a machine too slow to handle the trig computations in realtime, so they precomputed a 2d array each for the x and y components of the gravity field; they could then just do a table lookup based on the ship's last position and use that to update its momentum, which was then used to update its position. Slower machines forced more clever solutions.)

Collision detection in libgdx for triangles

I am making a simple game with libgdx and wanted to add some simple collision detection. I already managed to express my player by using a simple rectangle:
boundingBox = new Rectangle(x + 10, y + 10, 13, 21);
but my obstacles seem to be much more complicated.
They are supposed to be spikes over which the player can jump and have a triangle shape. They pretty much look like this:
http://kayin.pyoko.org/iwbtg/forums/Smileys/iwbtg/spikes.gif
As far as I noticed there is no triangle shape in libgdx. I already tried using polygons but they seem far too complicated for my purposes.
Is there an easy way to implement an accurate hitbox for them?
Thanks in advance for reading my post : )
EDIT:
Thanks everyone for your responses, everything works fine now, besides drawing my polygons for testing purposes. When I call
shapeRenderer.polygon(kid.getVertices());
it only draws my polygon in the top left corner, since it's defined as
boundingBox2.setVertices(new float[] { 10, 10, 10, 31, 23, 31, 23, 10 });
But I move it around in the update method of my kid class by using
boundingBox2.setPosition(position.x, position.y);
Is there a way to use that position change inside
shapeRenderer.polygon(kid.getVertices()); ?
Anyways I really appreciate your help and after sorting out this problem I will close this thread : )
Create a Polygon of your rectangle and your triangle.
You can even create a custom polygon if you want to add more advanced shapes.
To convert from rectangle to polygon is very easy, i made a method some months ago
public static float[] rectangleToVertices(float x, float y, float width,
float height) {
float[] result = new float[8];
result[0] = x;
result[1] = y;
result[2] = x + width;
result[3] = y;
result[4] = x + width;
result[5] = y + height;
result[6] = x;
result[7] = y + height;
return result;
}
The good thing about libGDX polygon class is that you can move your polygon or even rotate it, and get the transformed vertices!
Now you can use the Intersector class
public static boolean overlapConvexPolygons(Polygon p1,
Polygon p2)
Check whether specified convex polygons overlap.
Parameters:
p1 - The first polygon.
p2 - The second polygon.
Returns:
Whether polygons overlap.
For testing purposes, after you end your sprite batch do like this
batch.end(); // you end your spritebatch
renderer.setProjectionMatrix(camera.combined);
renderer.begin(ShapeType.Line)
renderer.polygon(polygonname.getVertices());
renderer.end();
Now you will be able to see your polygon.

How to position a Node along a circular orbit around a fixed center based on mouse coordinates (JavaFX)?

Im trying to get into some basic JavaFX game development and I'm getting confused with some circle maths.
I have a circle at (x:250, y:250) with a radius of 50.
My objective is to make a smaller circle to be placed on the circumference of the above circle based on the position of the mouse.
Where Im getting confused is with the coordinate space and the Trig behind it all.
My issues come from the fact that the X/Y space on the screen is not centered at 0,0. But the top left of the screen is 0,0 and the bottom right is 500,500.
My calculations are:
var xpos:Number = mouseEvent.getX();
var ypos:Number = mouseEvent.getY();
var center_pos_x:Number = 250;
var center_pos_y:Number = 250;
var length = ypos - center_pos_y;
var height = xpos - center_pos_x;
var angle_deg = Math.toDegrees(Math.atan(height / length));
var angle_rad = Math.toRadians(angle_deg);
var radius = 50;
moving_circ_xpos = (radius * Math.cos(angle_rad)) + center_pos_x;
moving_circ_ypos = (radius * Math.sin(angle_rad)) + center_pos_y;
I made the app print out the angle (angle_deg) that I have calculated when I move the mouse and my output is below:
When the mouse is (in degrees moving anti-clockwise):
directly above the circle and horizontally inline with the center, the angle is -0
to the left and vertically centered, the angle is -90
directly below the circle and horizontally inline with the center, the angle is 0
to the right and vertically centered, the angle is 90
So, what can I do to make it 0, 90, 180, 270??
I know it must be something small, but I just cant think of what it is...
Thanks for any help
(and no, this is not an assignment)
atan(height/length) is not enough to get the angle. You need to compensate for each quadrant, as well as the possibility of "division-by-zero". Most programming language libraries supply a method called atan2 which take two arguments; y and x. This method does this calculation for you.
More information on Wikipedia: atan2
You can get away without calculating the angle. Instead, use the center of your circle (250,250) and the position of the mouse (xpos,ypos) to define a line. The line intersects your circle when its length is equal to the radius of your circle:
// Calculate distance from center to mouse.
xlen = xpos - x_center_pos;
ylen = ypos - y_center_pos;
line_len = sqrt(xlen*xlen + ylen*ylen); // Pythagoras: x^2 + y^2 = distance^2
// Find the intersection with the circle.
moving_circ_xpos = x_center_pos + (xlen * radius / line_len);
moving_circ_ypos = y_center_pos + (ylen * radius / line_len);
Just verify that the mouse isn't at the center of your circle, or the line_len will be zero and the mouse will be sucked into a black hole.
There's a great book called "Graphics Gems" that can help with this kind of problem. It is a cookbook of algorithms and source code (in C I think), and allows you to quickly solve a problem using tested functionality. I would totally recommend getting your hands on it - it saved me big time when I quickly needed to add code to do fairly complex operations with normals to surfaces, and collision detections.

Categories