There was a problem with box2d. I wanted to add to the already existing game physics , but ran into problems. First, the game world I draw with calculation
public Hero(float x, float y, int width, int height) {
this.width = width;
this.height = height;
position = new Vector2(x, y);
velocity = new Vector2(0, 0);
acceleration = new Vector2(0, -420);
}
public void update(float delta){
velocity.mulAdd(acceleration, delta);
if(velocity.y < -200){
velocity.y = -200;
}
position.mulAdd(velocity,delta);
}
public void onTap(){
velocity.y = 140;
}
the body of the hero falls from the standard setting, but the body box that I have added to the test behaves very strangely.
Question number 1. Why box2d box more than a hero with this setting, but when I divide by two, it becomes similar to the size of the texture of the hero? May be linked such effekts so that the body is drawn based on the center in all directions 2. Why is the body in the world with gravity -420 falls continuously at the same speed, but not as much as my hero. How to achieve a similar effect it?
hero = new Hero(30, midPointY, 18, 21);
hero1 = new Box2Dhero(world, 90, midPointY, 18, 21);
Its box2d hero constructor
public Box2Dhero(World world, float x, float y, int width, int height ) {
bodyDef = new BodyDef();
bodyDef.position.set(x,y);
bodyDef.type = BodyDef.BodyType.DynamicBody;
body = world.createBody(bodyDef);
box = new PolygonShape();
box.setAsBox(width,height);
fixtureDef = new FixtureDef();
fixtureDef.shape = box;
body.createFixture(fixtureDef);
}
My GameWorld size
float screenWidth = Gdx.graphics.getWidth();
float screenHeight = Gdx.graphics.getHeight();
float gameHeight = 385;
float gameWidth = screenWidth / (screenHeight / gameHeight);
Your observation about box being based on center with unit length in all directions is correct.
It should affect your comparison in the sense that origins of box2d's box and your box do not match. This effect should be relatively small.
You are clamping your velocity in y direction to -200. Making such an assumption may not be a good idea for comparison. Finding a resource regarding what does box2d do would be a good idea (source perhaps).
Most physics engines perform uniform time stepping to be deterministic. Box2D is one of them. You can read about uniform time stepping here.
There might be many more differences/optimizations, looking in source and comparing would be most efficient solution for you in my opinion.
Good luck.
Related
I am creating a top-down shooter game, and whenever I move the camera, or zoom, black likes appear like a grid
I am using Tiled to create the map, and I have the camera following my centered box2d body. I have found that making the camera position equal the position of the box2d body with an int cast results in the black lines disappearing like this:
The problem though, is that because I have the game scaled down, the player will move for a second or two and then when the player reaches the next whole number on either axis, the camera snaps to the player, which is not what I want for the game as it's jarring. The player's movement is granular, but, while rounded, the camera's is not. I do not know if this is a problem with my tile sheet or if it's something I can fix by altering some code. I have tried all different kinds of combinations of padding, and values of spacing and margins. So ultimately, how can I have the camera match the player's position smoothly and not cause the black lines? I'd greatly appreciate any help or recommendations. Thank you in advance!
Where I am type casting the player's float position to an int in game class:
public void cameraUpdate(float delta) {
//timeStep = 60 times a second, velocity iterations = 6, position iterations = 2
world.step(1/60f, 6, 2); //tells game how many times per second for Box2d to make its calculations
cam.position.x = (int)playerOne.b2body.getPosition().x;
cam.position.y = (int)playerOne.b2body.getPosition().y;
cam.update();
}
Majority of player class:
public class PlayerOne extends Sprite implements Disposable{
public World world; // world player will live in
public Body b2body; //creates body for player
private BodyDef bdef = new BodyDef();
private float speed = 1f;
private boolean running;
TextureAtlas textureAtlas;
Sprite sprite;
TextureRegion textureRegion;
private Sound runningSound;
public PlayerOne(World world) {
this.world = world;
definePlayer();
textureAtlas = new TextureAtlas(Gdx.files.internal("sprites/TDPlayer.atlas"));
textureRegion = textureAtlas.findRegion("TDPlayer");
sprite =new Sprite(new Texture("sprites/TDPlayer.png"));
sprite.setOrigin((sprite.getWidth() / 2) / DunGun.PPM, (float) ((sprite.getHeight() / 2) / DunGun.PPM - .08));
runningSound = Gdx.audio.newSound(Gdx.files.internal("sound effects/running.mp3"));
}
public void definePlayer() {
//define player body
bdef.position.set(750 / DunGun.PPM, 400 / DunGun.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
//create body in the world
b2body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(12 / DunGun.PPM);
fdef.shape = shape;
b2body.createFixture(fdef);
}
public void renderSprite(SpriteBatch batch) {
float posX = b2body.getPosition().x;
float posY = b2body.getPosition().y;
float posX2 = (float) (posX - .14);
float posY2 = (float) (posY - .1);
sprite.setSize(32 / DunGun.PPM, 32 / DunGun.PPM);
sprite.setPosition(posX2, posY2);
float mouseX = Level1.mouse_position.x; //grabs cam.unproject x vector value
float mouseY = Level1.mouse_position.y; //grabs cam.unproject y vector value
float angle = MathUtils.atan2(mouseY - getY(), mouseX - getX()) * MathUtils.radDeg; //find the distance between mouse and player
angle = angle - 90; //makes it a full 360 degrees
if (angle < 0) {
angle += 360 ;
}
float angle2 = MathUtils.atan2(mouseY - getY(), mouseX - getX()); //get distance between mouse and player in radians
b2body.setTransform(b2body.getPosition().x, b2body.getPosition().y, angle2); //sets the position of the body to the position of the body and implements rotation
sprite.setRotation(angle); //rotates sprite
sprite.draw(batch); //draws sprite
}
public void handleInput(float delta) {
setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2 + (5 / DunGun.PPM));
this.b2body.setLinearVelocity(0, 0);
if(Gdx.input.isKeyPressed(Input.Keys.W)){
this.b2body.setLinearVelocity(0f, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.S)){
this.b2body.setLinearVelocity(0f, -speed);
}if(Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, speed);
}
if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, -speed );
}if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, -speed);
}
Where I declare the pixels per meter scale:
public class DunGun extends Game{
public SpriteBatch batch;
//Virtual Screen size and Box2D Scale(Pixels Per Meter)
public static final int V_WIDTH = 1500;
public static final int V_HEIGHT = 800;
public static final float PPM = 100; //Pixels Per Meter
Game render and resize methods:
#Override
public void render(float delta) {
cameraUpdate(delta);
playerOne.handleInput(delta);
//clears screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
cam.zoom -= .01;
}
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
cam.zoom += .01;
}
mapRenderer.render();
b2dr.render(world, cam.combined); //renders the Box2d world
mapRenderer.setView(cam);
//render our game map
//mapRenderer.render(); // renders map
//mapRenderer.render(layerBackround); //renders layer in Tiled that p1 covers
game.batch.setProjectionMatrix(cam.combined); //keeps player sprite from doing weird out of sync movement
mouse_position.set(Gdx.input.getX(), Gdx.input.getY(), 0);
cam.unproject(mouse_position); //gets mouse coordinates within viewport
game.batch.begin(); //starts sprite spriteBatch
playerOne.renderSprite(game.batch);
game.batch.end(); //starts sprite spriteBatch
//mapRenderer.render(layerAfterBackground); //renders layer of Tiled that hides p1
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true); //updates the viewport camera
}
I solved it by fiddling around with the padding of the tilesets in GDX Texture Packer. I added 5 pixels of padding around the 32x32 tiles. I set the margins to 2, and spacing to 4 in Tiled. I had tried a lot of different combinations of padding/spacing/margins that didn't work which made me think it was a coding problem, but those settings worked, and I didn't have to round the floats.
I have a box2d body which is simply a rectangle (the spaceship).
This ship flies around, affected by the gravity of planets (static circle bodies) just fine.
I have tried changing the ship fixture shape to both (1) a triangle fixture and also (2) two rectangular fixtures put together. Both of these body configurations are causing problems.
Given the change in volume, to make up for the difference in mass I calculated the necessary new densities so that these bodies have the same mass as the original rectangle body. However when launching the ship with the same linear impulse as before, the triangular body and multi-rectangular body both act differently than the original body and shoot forward much faster.
I have tried further tweaking the ship density as well as the linear impulse and also the planet gravity to return the ship to a normal pace of movement,
however when doing that the ship starts to act weird and will do things like suddenly switch direction in mid-air even though there's no collision. This collision-looking event does not happen when the density/gravity/linear impulse are all the same as before, however in that situation the ship is moving too fast for my needs.
Basically I don't know why these bodies are acting differently when there is no change in mass or anything besides shape and size. There's no linear damping or collisions happening. I don't know what else could possibly be affecting the ship.
Any help or ideas is greatly appreciated.
Here is the code that I believe is relevant:
public void createShipBody() {
density = 0.05f; //normal rectangle
//density = 0.06666667f; //density for the two rectangles together
restitution = 1.0f;
bodyDef.type = BodyDef.BodyType.DynamicBody;
setBodyDefPosition(bodyDef);
body = GameCore.world.createBody(bodyDef);
setShipShapeAndFixture();
}
// For with the original rectangle
private void setShipShapeAndFixture() {
PolygonShape shape = new PolygonShape();
shape.setAsBox(Utility.pixelsToMeters(bodyWidth / 2), Utility.pixelsToMeters(bodyHeight / 2));
createFixtureDef(shape);
shape.dispose();
}
// For with triangle fixture
private void setShipShapeAndFixture() {
Vector2[] vertices = new Vector2[3];
vertices[0] = new Vector2(0, 0);
vertices[1] = new Vector2(Utility.pixelsToMeters(bodyWidth), 0);
vertices[2] = new Vector2(Utility.pixelsToMeters(bodyWidth/2), Utility.pixelsToMeters(bodyHeight));
shape.set(vertices);
createFixtureDef(shape);
shape.dispose();
}
// For multi-rectangle body
private void setShipShapeAndFixture() {
PolygonShape shape = new PolygonShape();
shape.setAsBox(Utility.pixelsToMeters(bodyWidth / 2), Utility.pixelsToMeters(bodyHeight / 4));
PolygonShape shape2 = new PolygonShape();
shape2.setAsBox(Utility.pixelsToMeters(bodyWidth / 4), Utility.pixelsToMeters(bodyHeight / 4), new Vector2(0, Utility.pixelsToMeters(bodyHeight/2)), 0);
createShipMultiFixtureDef(shape, shape2);
shape.dispose();
shape2.dispose();
}
private void createFixtureDef(PolygonShape shape) {
FixtureDef polygonFixtureDef = new FixtureDef();
polygonFixtureDef.shape = shape;
polygonFixtureDef.density = density;
polygonFixtureDef.restitution = restitution;
body.createFixture(polygonFixtureDef);
}
private void createShipMultiFixtureDef(PolygonShape shape, PolygonShape shape2) {
FixtureDef polygonFixtureDef = new FixtureDef();
polygonFixtureDef.shape = shape;
polygonFixtureDef.density = density;
polygonFixtureDef.restitution = restitution;
FixtureDef polygonFixtureDef2 = new FixtureDef();
polygonFixtureDef2.shape = shape2;
polygonFixtureDef2.density = density;
polygonFixtureDef2.restitution = restitution;
body.createFixture(polygonFixtureDef);
body.createFixture(polygonFixtureDef2);
}
/**
* This is inside the Ship class
* #param x From world center of release
* #param y From world center of release
* #param distance The capped distance of release from shipCenter, to determine force of impulse
*/
protected void applyLinearImpulse(float x, float y, float distance, float maxDistance) {
float deltaX = x - getXinPixels();
float deltaY = y - getYinPixels();
float scale = 0.8f; // Have tried playing around with this with different bodies
float force = scale * (distance/maxDistance);
float angle = (float) Math.atan2(deltaY, deltaX);
bodyWrapper.body.applyLinearImpulse(new Vector2((float) Math.cos(angle) * force,
(float) Math.sin(angle) * force), getWorldCenter(), true);
}
// This is called on each planet every game update
protected void applyGravity() {
for (SpaceObject spaceObject:spaceObjects) {
float scalar = 2.334f;
if(spaceObject instanceof Ship) {
if (gameCore.shipInMotion) {
scalar = 2f; //Have tried playng with this for different bodies
}
else {
continue; //No gravity to ship until it's launched
}
}
Vector2 objectWorldCenter = spaceObject.getWorldCenter();
Vector2 planetWorldCenter = getWorldCenter();
float planetDiameter = Utility.pixelsToMeters(getWidth());
float distance = Utility.distance(planetWorldCenter.x, planetWorldCenter.y, objectWorldCenter.x, objectWorldCenter.y);
float xDistance = planetWorldCenter.x - objectWorldCenter.x;
float yDistance = planetWorldCenter.y - objectWorldCenter.y;
float x = (float) ((xDistance * planetDiameter * scalar) / (distance*distance));
float y = (float) ((yDistance * planetDiameter * scalar) / (distance*distance));
Vector2 gravity = new Vector2(x, y);
spaceObject.bodyWrapper.body.applyForceToCenter(gravity, true);
}
}
I am making a game in Java (No Libraries).
It's a 2D top-down game where the player can walk and is faced towards the mouse cursor.
public Player(int x, int y, int health, int tileId) {
super(x, y, health);
tile = new Tile(tileId, false);
mouseInput = new MouseHandler(screen);
}
public void tick() { // Executed by game tick.
// x = playerX and y = playerY
int cursorX = mouseInput.getMousePos()[0];
int cursorY = mouseInput.getMousePos()[1];
float X = cursorX - x;
float Y = cursorY - y;
rotation = Math.atan2(Y, X);
}
It looks good as long the player is at (0,0)
If the player moves and the mouse coordinates become negative it begins to show strange behaviour (Look at video below)
Youtube: https://www.youtube.com/watch?v=M6ZHCrWvt3Y
The rotation of the sprite is done in another class 'Screen.java'
By using:
if (rotation < 360)
rotation++
else
rotation = 0
I verified that the rotation is working correctly.
EDIT:
public BufferedImage rotate(BufferedImage img, double degree) {
AffineTransform tx = new AffineTransform();
tx.rotate(degree, 4, 4);
AffineTransformOp op = new AffineTransformOp(tx,AffineTransformOp.TYPE_BILINEAR);
BufferedImage image = op.filter(img,null);
return image;
}
Okay i fixed it.
The problem was the game scale i am making an 2d game and set the width, height and the scale.
But i didn't divide the mouseX and mouseY by the scale.
public void mouseMoved(MouseEvent e) {
mouseX = e.getX() / game.getScale();
mouseY = e.getY() / game.getScale();
}
I found the problem by accident when messing with the gamescale.
So I am making a game in box 2d. In one part I need a ball which has a constant bounce height. I set the ball's restitution to 1 and am applying no force on it whatsoever (except gravity of course). Now this ball, on every bounce, bounces a bit higher everytime till it goes out of the top edge of the screen. What is wrong with this code?
public class Ball implements ApplicationListener {
World world ;
Box2DDebugRenderer debugRenderer;
OrthographicCamera camera;
static final float BOX_STEP=1/60f;
static final int BOX_VELOCITY_ITERATIONS=8;
static final int BOX_POSITION_ITERATIONS=3;
static final float WORLD_TO_BOX=0.01f;
static final float BOX_WORLD_TO=100f;
Rectangle ball;
Body body;
/* (non-Javadoc)
* #see com.badlogic.gdx.ApplicationListener#create()
*/
#Override
public void create() {
world = new World(new Vector2(0, -10), true);
camera = new OrthographicCamera();
camera.viewportHeight = 480;
camera.viewportWidth = 800;
camera.position.set(camera.viewportWidth * .5f, camera.viewportHeight * .5f, 0f);
camera.update();
//Ground body
BodyDef groundBodyDef =new BodyDef();
groundBodyDef.position.set(new Vector2(0, 10 * WORLD_TO_BOX));
Body groundBody = world.createBody(groundBodyDef);
PolygonShape groundBox = new PolygonShape();
float w = (camera.viewportWidth * 2) * WORLD_TO_BOX;
float h = 10.0f * WORLD_TO_BOX;
groundBox.setAsBox(w,h);
groundBody.createFixture(groundBox, 0.0f);
String a="gb";
groundBody.setUserData(a);
//Dynamic Body
BodyDef bodyDef = new BodyDef();
bodyDef.type = BodyType.DynamicBody;
float posX = (camera.viewportWidth / 8) * WORLD_TO_BOX;
float posY = (camera.viewportHeight / 2) * WORLD_TO_BOX;
bodyDef.position.set(posX, posY);
body = world.createBody(bodyDef);
// create a Rectangle to logically represent the ball
CircleShape dynamicCircle = new CircleShape();
dynamicCircle.setRadius(1f);
FixtureDef fixtureDef = new FixtureDef();
fixtureDef.shape = dynamicCircle;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.0f;
fixtureDef.restitution =1f;
body.createFixture(fixtureDef);
debugRenderer = new Box2DDebugRenderer();
}
#Override
public void dispose() {
}
#Override
public void render() {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
debugProjection.set(camera.combined);
debugProjection.scl(BOX_WORLD_TO);
debugRenderer.render(world, debugProjection);
world.step(BOX_STEP, BOX_VELOCITY_ITERATIONS, BOX_POSITION_ITERATIONS);
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
This may be caused by numerical instability. Repeatedly calculating positions of your ball inevitably introduces tiny numerical errors (because a float can only hold a limited number of digits). These may grow and add up from iteration to iteration, bounce to bounce.
You can try using restitution 0.9999999 or something. But chances are that there is no restitution value which gives desired results.
Try to actively compensate for the numerical instability e.g. by controlling the ball's position and/or velocity in situations in which you can calculate values non-iteratively. (I do not know Box2D. Maybe when the ball hits the ground you can reset its speed or something.)
Ok I solved it by setting the restitution to 0 and applying an upward force of 5N on each contact. As halfbit said, the problem is that because box2d uses float, the position calculations are not precise which causes minor changes in height on every bounce.
If I have done the math right for the calculations, your sphere has a radius of 1m, and your box as dimensions of 16m x 0.1m.
In Box2d, everything needs to be on the scale of 0.1m to 10m. The reason your bodies may be numerically unstable is because the ratio of the width to height is greater than the normal sizes of objects. While you may get some stability by changing the restitution, I think you will always be on the "hairy edge" with these dimensions.
Try changing the dimensions of your objects so the width is 8 and the height is 0.1.
What is a method in Java that draws a rectangle given the following:
The coordinates of the center of the square
The angle of the rectangle from vertical, in degrees
To draw a rectangle in the way you suggest you need to use the class AffineTransform. The class can be used to transform a shape in all manner of ways. To perform a rotation use:
int x = 200;
int y = 100;
int width = 50;
int height = 30;
double theta = Math.toRadians(45);
// create rect centred on the point we want to rotate it about
Rectangle2D rect = new Rectangle2D.Double(-width/2., -height/2., width, height);
AffineTransform transform = new AffineTransform();
transform.rotate(theta);
transform.translate(x, y);
// it's been while, you might have to perform the rotation and translate in the
// opposite order
Shape rotatedRect = transform.createTransformedShape(rect);
Graphics2D graphics = ...; // get it from whatever you're drawing to
graphics.draw(rotatedRect);
For the first point, you can just figure out the coordinates of the center of the square by using a distance formula, (int)Math.sqrt((x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2)); them divide by 2. you can do this for the width and height. I don't know enough about Java draw to give you better answers based on what was in your question but I hope that helps.
For the second, you would need to just create a polygon right?