i'm working on a graphic interface for drawing subway maps. A line is represented with station as circles and a polyline to link them.You can move the stations with a mouseDrag and of course it updates the displaying map in real time. My problem is when stations comes to a certain angle, there is a polyline distortion and the corner created by the 2 lines is out of the station circle display, i'd like to know if there is a way to avoid this.
screenshots of the app with the polyline issue
here's my code for the polyline's draw
//x and y point array creation
xPoints = new int[this.stationViews.size()];
yPoints = new int[this.stationViews.size()];
for (int i=0;i<this.stationViews.size();i++) {
//fill arrays with the center point of circles representing stations
xPoints[i] = this.stationViews.get(i).getStation().getPosX()-this.stationViews.size()/2;
yPoints[i] = this.stationViews.get(i).getStation().getPosY()-this.stationViews.size();
}
//setting color
g2D.setColor(this.line.getColor());
//set stroke width relative to the zoom level
int strokeWidth=5;
if(!this.stationViews.isEmpty()) {
if (this.stationViews.get(0).getStationSize()>14) {
strokeWidth = this.stationViews.get(0).getStationSize()-13;
}else {
strokeWidth = 3;
}
}
g2D.setStroke(new BasicStroke(strokeWidth));
//draw the polyline
if (this.stationViews.size() >1) {
g2D.drawPolyline(xPoints, yPoints, this.stationViews.size());
}
//draw the station (g2D.drawCircle)
for (StationView stationView : stationViews) {
stationView.affiche(g2D,this.line.getColor());
}
thank you for your help
That is called the miter. You seem to be per default using JOIN_MITER, sharp joining of extended lines at the end, which can point far out of the join for small angles.
g2d.setStroke(new BasicStroke(strokeWidth,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 5));
miter a surface forming the beveled end or edge of a piece where a joint is made by cutting two pieces at an angle and fitting them together.
It is also a bishop's cap with a pointy top, hence the name.
Related
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.
I'm trying to detect a collision between a small rectangle around the cursor and a "Connector", which is basically just a line between two points.
Now, I've decided to use the Intersector.intersectLinePolygon(p1, p2, polygon) method to do so, but when I run the code. It detects a collision everytime any of the rectangle X or Y points are in the same range as the line's bounding box and I can't really get my head around it. The desired result is the collision reporting only when the rectangle is actually touching the line.
Vector3 worldPos = cam.unproject(new Vector3(mouseX, mouseY, 0));
Rectangle rect = new Rectangle(worldPos.x-4, worldPos.y-4, 8, 8);
Boolean connectorIntersected = false;
for (int i = 0; i < nodeConnectorHandler.getAllConnectors().size(); i++) {
//Getting two points that make the connector line
Node n1 = nodeConnectorHandler.getAllConnectors().get(i).getFrom();
Node n2 = nodeConnectorHandler.getAllConnectors().get(i).getTo();
float x1 = n1.getCX();
float y1 = n1.getCY();
float x2 = n2.getCX();
float y2 = n2.getCY();
//Making a polygon out of rect
Polygon p = new Polygon(new float[] {
rect.getX(),
rect.getY(),
(rect.getX()+8f),
rect.getY(),
(rect.getX()+8f),
(rect.getY()+8f),
rect.getX(),
(rect.getY()+8f)
});
//Checking if the line intersects the polygon (representing the rectangle around the cursor)
if (Intersector.intersectLinePolygon(new Vector2(x1,y1), new Vector2(x2,y2), p))
{
selectedIndex = nodeConnectorHandler.getAllConnectors().get(i).getID();
System.out.println("ConnectorIntersected!");
connectorIntersected = true;
}
break
}
The code reports a collision everytime the rectangle is in these areas (shown in yellow, aprox):
photoshopped image link
The red line inbetween those 2 dots is the "connector"
Cursor is right below the line. It reports a collision in those yellow areas spanning across the whole game world.
I suppose I'm either not using the function properly or that I've made some obvious mistake. Or is this how the function should react? I really don't know. Thanks for any help :)
Ok, apparently I used the wrong method. intersectSegmentPolygon works as expected. My bad ¯_(ツ)_/¯.
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.
More adventures with Graphics2D!
This time I'm investigating the various paint / colour modes and ways to paint complex borders. The typical method is to fill the Shape or Polygon, and then draw the same Shape over the top (in a different colour). I'm familiar with setting Strokes and so forth to vary border thickness and other bits.
My latest attempt was to use RadialGradientPaint as a way of defining multiple bands of colour, but this doesn't appear to apply to the draw / drawPolygon call (or the extra colours aren't showing up.
The draw call is drawing a thickness 3 empty Polygon, but as a uniformly-blue strip. Below is the gradient / paintComponent code:
g2d.setStroke(new BasicStroke(h.getHexBorderWidth())); // this is set to 3
Point2D center = new Point2D.Float(50, 50);
float radius = 1;
float[] dist = {0.0f, 0.5f, 1.0f};
Color[] colors = {Color.RED, Color.WHITE, Color.BLUE};
// the float value is defined as 0.0 in the constructor
RadialGradientPaint p =
new RadialGradientPaint(center, radius, new Point2D.Float(), dist, colors,
CycleMethod.NO_CYCLE);
g2d.setPaint(p);
if(((BasicHexagon) h).isAnimating()
&& ((BasicHexagon) h).getCurrentTransform() != null) { // just general null checks
g2d.draw(((BasicHexagon) h).getCurrentTransform()); // this is a transformed Shape
} else {
g2d.drawPolygon(h.getBase()); // this is a Polygon
}
Any better ways I'm missing out on, or any way that this isn't being called correctly?
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.