I have created Chain shapes (static bodies) all around the perimeter of the game screen whose dimensions are:
(2f*camera.viewportWidth,2f*camera.viewportHeight).
Each chain shape has a density of 5f.
I've also created a Circle Shape (dynamic body) whose
density = 0.5f
friction = 0.25f
restitution =0.2f.
In addition, I've created a polygon shape set as a box shape (dynamic body) which has the same density, restitution and friction as the circle shape.
The world's gravity is (0,-5.8f).
All shapes render appropriately. However, the box shape just keeps falling right through the bottom chain shape which is located at the bottom of the screen. The circle shape doesn't go through, but the box shape does go through. I don't want this to happen. The size of the box shape is
(1.96f*camera.viewportWidth, 1.96f*camera.viewportHeight).
The position of this body(box shape) is set to
(0.02f*camera.viewportWidth, 0.02f*camera.viewportHeight).
I don't know why the box shape just keeps falling through and is not stopped by the bottom chain shape, just like the circle shape is stopped. Can anyone provide any insight?
Also, the reason I am trying to set up my box2d world like this is to eliminate some camera lagging movement when I use camera.translate to move around the world. My idea is to move the box shape by applying linear velocities to its body. Please any thoughts would be appreciated.
You probably did not set filtering on those fixtures. Take a look at the following example
FixtureDef f = new FixtureDef();
f.density = density;
f.friction = friction;
f.restitution = restitution;
now if you want Box2D to handle collisions for you, you should tell it which fiture will collide with which and you are doing it by assigning categoryBits and maskBits like this:
f.filter.categoryBits = categoryBits; //who am I?
f.filter.maskBits = maskBits; //with who I will collide?
Bits should be short type and power of two - you cannot have two bits defined as same value. You can set many maskBits by using logical sum ('|' operator).
public static final short BIT_F1 = 2;
public static final short BIT_F2 = 8;
public static final short BIT_F3 = 32;
FixtureDef f1, f2, f3;
//creating f1, f2, f3...
f1.filter.categoryBits = BIT_F1;
f2.filter.categoryBits = BIT_F2;
f3.filter.categoryBits = BIT_F3;
f1.filter.maskBits = BIT_F1; //now every body having f1 fixture will collide with another f1
f2.filter.maskBits = BIT_F1; //f2 will collide with f1
f3.filter.maskBits = (short)(BIT_F1 | BIT_F2 | BIT_F3); //f3 collides with everything
Please notice that if you want fixture to avoid any collision you can it to be a sensor like:
f.isSensor = sensor;
Here is link to official documentation although it is not very helpful to be honest.
Related
I am working on a project in LibGDX, and I am using Scene2D actors for some of my sprites. In this regard, I have a sprite, which is spawning somewhere on the screen and needs to move to another position on the screen. To do this I am using the moveTo(xPos, yPos, duration, interpolation) method in the Actions, to make the move animation.
However, when I use this approach, the actor moves like I told it to, but it only moves in a straight line, from point A to B. I have tried several Interpolation options, like Circle interpolation and such, but it seems only to impact the speed of the animation line.
So now my question: How do I make my animation make a smooth curved line (See picture), from A to B?
I am currently using this code to make the Actions animation:
adultCustomerPointActor.addAction(Actions.sequence(
Actions.moveTo(300, 200, 2f, Interpolation.circle)
));
Thanks in advance for your help :)
It's a geometry problem. Using vectors, find the point halfway between the two points:
vec1.set(bx, by).sub(ax, ay).scl(0.5f).add(ax, ay);
Get another vector that is 90 or 270 to from the vector between the points:
vec2.set(bx, by).sub(ax, ay).rotate90().add(vec1);
This vec2 can be scaled to adjust how extreme curvature of the arc is. If you leave it alone, you'll have a quarter circle. You can also scale it negative to reverse the curvature.
Then add the second vector to the first to find the center point of your arc, which we can call point C.
vec1.set(bx, by).sub(vec2); // CB
vec3.set(ax, ay).sub(vec2); // CA
float angle = vec1.angle(vec3);
Now you need a vector that points from point C to point A. You will rotate this vector until it reaches point B. So you need the angle between CA and CB.
So here's a very simplistic class that implements this. It doesn't account yet for deciding if you want the arc to go up or down and if you want to scale how extreme it looks. You could add those as additional parameters with getters/setters. I haven't tested it, so it may need some debugging.
public class ArcToAction extends MoveToAction {
private float angle;
private final Vector2 vec1 = new Vector2(), vec2 = new Vector2(), vec3 = new Vector2();
#Override
protected void begin () {
super.begin();
float ax = target.getX(getAlignment()); // have to recalculate these because private in parent
float ay = target.getY(getAlignment());
vec1.set(getX(), getY()).sub(ax, ay);
vec2.set(vec1).rotate90();
vec1.scl(0.5f).add(ax, ay);
vec2.add(vec1);
vec1.set(bx, by).sub(vec2); // CB
vec3.set(ax, ay).sub(vec2); // CA
angle = vec1.angle(vec3);
}
protected void update (float percent) {
if (percent >= 1){
target.setPosition(getX(), getY(), getAlignment());
return;
}
vec1.set(vec3).rotate(percent * angle);
target.setPosition(vec1.x, vec1.y, getAlignment());
}
}
If you want to support automatic pooling, you can add a method like this:
static public ArcToAction arcTo (float x, float y, float duration, Interpolation interpolation) {
ArcToAction action = Actions.action(ArcToAction .class);
action.setPosition(x, y);
action.setDuration(duration);
action.setInterpolation(interpolation);
return action;
}
Earthquake threat circle on the map
I am using UnfoldingMaps to display earthquake information on the map.
I plan to show the threat circle on the map.
A circle is drawn given its radius and center position in pixels. How to get the radius is the problem I met.
Suppose I have the threat circle radius R in kilometers and the center marker A.
I want to create a marker B on the circle so that I can use the screen distance as the screen radius.
I decided to create B with the same longitude but a different latitude from A. I change R to delta latitude.
But after drawing the circle I found it is not the right one since the red triangular should be in the circle according to their distance.
The main difficulty is exactly how to calculate screen radius according to kilometers.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
float deltaLat=(float) (threatCircle()/6371/2/3.1415927*360);
Location centerLocation = this.getLocation();
Location upperLocation = new Location(centerLocation);
upperLocation.setLat(centerLocation.getLat() + deltaLat);
SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = this.getScreenPosition(map);
ScreenPosition upper = upperMarker.getScreenPosition(map);
radius = Math.abs(upper.y - center.y);
setThreatCircleOnScreen(radius);
}
This is going to depend on two things: the zoom level of the map, and the projection you're using.
You need to unproject kilometers to pixels, and you can probably figure out how to do that using google and the Unfolding API.
For example, I found a MercatorProjection class that contains a constructor that takes a zoom level, and methods for projecting and unprojecting points between world coordinates and pixel coordinates.
That's just a starting point, since I'm not sure what units those methods are taking, but hopefully this is a direction for you to take your googling and experimenting.
I'd recommend trying to get something working and posting an MCVE if you get stuck. Good luck.
Now I have the answer for this question. Hope it will be helpful for others.
Earthquake threat circle on the map
My early solution to calculate radius in pixels from km is correct. I think it a simple and powerful idea (independent of projecting API)
The only problem is I should use diameter rather than radius in drawing the circle. I should draw with d=2r like this
float d = 2 * threatCircleRadius();
pg.noFill();
pg.ellipse(x,y,d,d);
I found another cleaner solution like below by consulting the author of UnfoldingMaps. (https://github.com/tillnagel/unfolding/issues/124)
My early solution first changes distance to delta latitude, then create new location by changing latitude.
The new solution use the API GeoUtils.getDestinationLocation(sourceLocation, compassBearingDegree, distanceKm) to directly get the new location!
In addition, I needn't create a new marker to find its screen position.
public void calcThreatCircleOnScreen(UnfoldingMap map) {
float radius = 0;
Location centerLocation = this.getLocation();
Location upperLocation = GeoUtils.getDestinationLocation(centerLocation, 0, threatCircle());
//SimplePointMarker upperMarker = new SimplePointMarker(upperLocation);
ScreenPosition center = map.getScreenPosition(centerLocation);
ScreenPosition upper = map.getScreenPosition(upperLocation);
radius = PApplet.dist(center.x, center.y, upper.x, upper.y);
setThreatCircleOnScreen(radius);
}
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
I'm trying to make a game where the player ( the circle ) has to collect some stars. The stars will be at different positions and in order to get the stars the player must draw ramps in order to reach the stars. Picture below.
http://3w-bg.org/game/pic.PNG
The red line is where the user has drawn on the screen.
Ok so i capture the coordinates when the user touches and drags on the screen and then i use this coordinates to create a ChainShape for the line. The problem is that the line is drawn nowhere near the touched area. Picture below.
http://3w-bg.org/game/pic2.PNG
The world and the screen positions are not the same to my understanding. So how can i draw the chainshape line exactly where the user has touched. Tried camera.project/unproject but that didn't help.
Usually when using Box2D you should have some kind of pixel-to-meter ratio defined. This is done in order to keep the coordinates in your physics world smaller to keep numeric stability.
When using a Camera and a constant PIXEL_TO_METER to convert the values, you can convert your coordinates like this:
public static Vector2 screenToPhysics(Camera camera, Vector2 screenPos) {
Vector3 worldPos = camera.unproject(new Vector3(screenPos.x, screenPos.y, 0));
return new Vector2(worldPos.x, worldPos.y).scl(1f / PIXEL_TO_METER);
}
public static Vector2 physicsToScreen(Camera camera, Vector2 physicsPos) {
Vector3 worldPos = new Vector3(physicsPos.x, physicsPos.y, 0).scl(PIXEL_TO_METER);
Vector3 screenPos = camera.project(worldPos);
return new Vector2(screenPos.x, screenPos.y);
}
I am trying to re-create Pong using LibGDX and Box2D. I have 2 problems, if I fix one it creates another.
My paddles are currently set to Kinematic and are controlled using the up/down keys via a controller class. This works just fine and I can happily play back and forth.
Problem being, my walls are static bodies and my paddles just travel right through them.
Now I can fix this by simple changing the paddle body to a dynamic one, this stops the paddles from going through the walls but then when my ball strikes off a paddle, it goes flying off the X axis and off the screen.
I have tried adding an update method in my controller class as follows:
public void update(float delta){
paddleBodyPosY = paddleBody.getPosition().x;
paddleBodyPosY = paddleBody.getPosition().y;
System.out.println(paddleBodyPosY);
}
The console reports the paddle position being updated every frame, from top to bottom of screen.
So I went back to my GameScreen class and tried all sorts of code in the Render() method like so:
if(playerOnePaddle.paddleBodyPosY < 0){
playerOnePaddle.paddleBody.getPosition().y = 0;
System.out.println("resetting paddle");
}
I have tried LOADS of variations, I can easily break movement by calling paddleBody.setLinearVelocity(0,0) but then it gets stuck like this and it's not movable anymore. Obviously the problem must lie with the fact that I can't set a position using a getter lol.
Any ideas? If you need more snippets ask, I didn't want to overload the question with 100 lines of code you don't need to see :S.
The paddle creation method:
public void createPaddle(World world, float x, float y){
//Define a body for the paddle
BodyDef paddleBodyDef = new BodyDef();
paddleBodyDef.type = BodyType.KinematicBody;
paddleBodyDef.position.set(x, y);
//Define a shape for the paddle
PolygonShape paddleShape = new PolygonShape();
paddleShape.setAsBox(paddleWidth, paddleHeight);
//Define a fixture for the paddle
FixtureDef paddleFixtureDef = new FixtureDef();
paddleFixtureDef.shape = paddleShape;
paddleFixtureDef.density = 0;
//Ensures ball bounces off paddle
//Consistently without losing velocity
paddleFixtureDef.restitution = 1.005f;
//Create the paddles
paddleBody = world.createBody(paddleBodyDef);
paddleFixture = paddleBody.createFixture(paddleFixtureDef);
paddleShape.dispose();
}
Heh, so what I did was.
Set my paddles to dynamic, then set there mass to a stupidly high number so that the a ball would not move them on the x axis, well not enough for the human eye to see at least.
Seems like a cheap fix, if anyone has anything better...that would be great lol