Java-Gaming.org    
Featured games (81)
games approved by the League of Dukes
Games in Showcase (497)
Games in Android Showcase (114)
games submitted by our members
Games in WIP (563)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  [LibGDX] Box2D and LibGDX coordinate system issues  (Read 888 times)
0 Members and 1 Guest are viewing this topic.
Offline kutucuk

Senior Member


Medals: 5
Exp: 3 years



« Posted 2014-02-22 19:54:42 »

It seems that Box2D coordinate origin is the center of the screen, and LibGDX coordinate origin is the top left side of the screen. This causes problems when I try to draw a sprite on a Body.

This is how I create a body and draw a sprite:
EDIT: I POSTED THE WHOLE CLASS BELOW, ORIGINALLY I JUST PASTED THE CONSTRUCTOR.
width is an array which has 3 elements, these are x1, y and x2 for creating the shape. I'm using y as y1 and y2.
Drawing is simple enough, just sprite.draw(spriteBatch);

As you can see, I'm keeping a log of the body position and sprite position. Both the bodies and sprites are rendered using the same camera. My scaling is correct, my bodies and sprites are in the same size.

The weird thing is that, body position and sprite position are same, but they are not the same in the screen. There is a GROWING gap between sprite and body.
For example, if I render a body and a sprite on width[]{1, 5, 7}, it is fine. The body and the sprite are in the same position. But if I render on width[]{1,100,7}, the gap is about 10 cm.

What do I do to fix this?

EDIT: I POSTED THIS ON BAD LOGIC GAMES FORUM AS WELL. THE SUGGESTION WAS THAT I PASTE MY WHOLE CODE HERE. SO THAT'S WHAT I'M GOING TO DO:
This is my GameScreen class, which can be considered as a world that renders and updates everything:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
public class GameScreen implements Screen {

   // 2x32 image is 1x1 meter
  public final float WTB = 1 / 32f;

   private OrthographicCamera camera;
   private Box2DDebugRenderer renderer;
   private World world;

   // Bottom. I'm putting a static body at the bottom of the screen so the player doesn't fall
  private ChainShape borderShape;
   private FixtureDef borderFixture;
   private BodyDef borderBody;
   private Body ground;
   //These two will be used for my randomization process, which I haven't done yet.
  private Vector2 bottomLeft, bottomRight;

   
   private SpriteBatch spriteBatch;
   Player player; //This is just a dynamic body with a sprite attached to it.
  //After I realized the problem, I needed more than one platforms, so I made an array for them.
  private Array<Platform> testPlatforms = new Array<Platform>();


   @Override
   public void render(float delta) {
      Gdx.gl.glClearColor(0, 0, 0, 1);
      Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
     
      renderer.render(world, camera.combined);

      spriteBatch.begin();
      player.render(); //Player works perfectly.
     // Whether I iterate or render them seperately, the result is the same
     Iterator<Platform> iterate = testPlatforms.iterator();
      while (iterate.hasNext()) {
         Platform p = iterate.next();
         p.render();
      }
      spriteBatch.end();

      //Normally the rest is in another method called update(), but I just combined them here.
     world.step(1 / 60f, 1,1);
      // Follow the player - I removed this, it didn't change anything.
     camera.unproject(new Vector3(player.body.getPosition().x, player.body.getPosition().y, 0));
      camera.position.y = player.body.getPosition().y;
      // Limit the movement
     if (player.body.getPosition().x > bottomRight.x) {
         player.body.setLinearVelocity(player.body.getLinearVelocity().x* (-0.75f), player.body.getLinearVelocity().y);
         player.body.setTransform(player.body.getPosition().sub(2 * WTB, 0),player.body.getAngle());      
      } else if (player.body.getPosition().x < bottomLeft.x) {
         player.body.setLinearVelocity(player.body.getLinearVelocity().x * (-0.75f), player.body.getLinearVelocity().y);
         player.body.setTransform(player.body.getPosition().add(2 * WTB, 0),player.body.getAngle());
      }
      camera.update(true);
      player.update();
      spriteBatch.setProjectionMatrix(camera.combined);
   }

   @Override
   public void resize(int width, int height) {
      //It still shows in meters.
     camera.viewportWidth = width * WTB;
      camera.viewportHeight = height * WTB;
      camera.update(true);
   }

   @Override
   public void show() {
      // Camera shows in meters now, not in pixels.
     camera = new OrthographicCamera(Gdx.graphics.getWidth() * WTB,
            Gdx.graphics.getHeight() * WTB);

      renderer = new Box2DDebugRenderer();
      world = new World(new Vector2(0, 0), true);

      spriteBatch = new SpriteBatch();
      spriteBatch.setProjectionMatrix(camera.combined);

      player = new Player(spriteBatch, world, WTB);

      bottomLeft = new Vector2(-Gdx.graphics.getWidth() / 2 * WTB, -Gdx.graphics.getHeight() / 2 * WTB + player.body.getPosition().y);
      bottomRight = new Vector2(Gdx.graphics.getWidth() / 2 * WTB, -Gdx.graphics.getHeight() / 2 * WTB + player.body.getPosition().y);
      borderShape = new ChainShape();
      borderShape.createChain(new Vector2[] { bottomLeft, bottomRight });
      borderFixture = new FixtureDef();
      borderFixture.density = 5;
      borderFixture.friction = .1f;
      borderFixture.restitution = .9f;
      borderFixture.shape = borderShape;
      borderBody = new BodyDef();
      borderBody.type = BodyDef.BodyType.StaticBody;

      ground = world.createBody(borderBody);
      ground.createFixture(borderFixture);
      borderShape.dispose();

      //To keep here as clean as possible, I made another method for controls.
     controls();

      //TESTING
     platforms = new Array<Platform>();
      testPlatforms.add(new Platform(spriteBatch, world, WTB, -2, 9, 19));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, 0, 4, 6));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, 0, 4, 12));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, 0, 3, 32));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, 2, 7, 29));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, 4, 10, 50));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, -10, 3, 80));
      testPlatforms.add(new Platform(spriteBatch, world, WTB, -1, 3, 110));
   }

   public void controls() {
      Gdx.input.setInputProcessor(new InputProcessor() {
//touchUp, touchDragged, touchDown, scrolled, mouseMoved, keyUp, keyTyped are empty.

         @Override
         public boolean keyDown(int keycode) {
            switch (keycode) {
            case Keys.W:
               player.body.applyForceToCenter(0, 1000, true);
               break;
            case Keys.A:
               player.body.applyForceToCenter(-500, 0, true);
               break;
            case Keys.S:
               player.body.applyForceToCenter(0, -500, true);
               break;
            case Keys.D:
               player.body.applyForceToCenter(500, 0, true);
               break;
            default:
               break;
            }
            return true;
         }
      });
   }
//Hide, pause, resume and dispose are empty methods for now.
}


And this is my Platform class, which I use to create platforms which the player should avoid (Originally the player would jump on that, but since this is a test project now, I am not sure what to do with these.
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
public class Platform {

   public final float WTB;

   // Drawing
  public Sprite sprite;
   public Texture texture;
   public TextureRegion region;
   public SpriteBatch spriteBatch;

   // Box2D
  public Body body;
   public FixtureDef fixtureDef;
   public BodyDef bodyDef;

   public World world;
   public float x1,x2,y;

   public Platform(SpriteBatch spriteBatch, World world, float WTB,
         float x1, float x2, float y) {
      this.WTB = WTB;
      this.spriteBatch = spriteBatch;
      this.world = world;
      this.x1 = x1;
      this.x2 = x2;
      this.y = y;
     
      texture = new Texture("data/bridgeLogs.png");
      texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
      texture.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
      region = new TextureRegion(texture,0f,0f,(x2 - x1)/texture.getWidth()/WTB,1f);
      sprite = new Sprite(region);
      sprite.setSize(region.getRegionWidth(), region.getRegionHeight());
      sprite.setScale(WTB);
     
      PolygonShape shape = new PolygonShape();
      shape.setAsBox((x2 - x1)/2, region.getRegionHeight()*WTB/2, new Vector2((x1+x2)/2,y), 0);
     
      fixtureDef = new FixtureDef();
      fixtureDef.density = 1;
      fixtureDef.friction = .2f;
      fixtureDef.restitution = .4f;
      fixtureDef.shape = shape;
      bodyDef = new BodyDef();
      bodyDef.type = BodyDef.BodyType.StaticBody;
      bodyDef.position.set(x1, y);
      body = world.createBody(bodyDef);
      body.createFixture(fixtureDef);
      body.setFixedRotation(true);

      }
   }

   public void render() {
// TRIED this in many different ways, like subtracting the half of the sprite height and width, still got no luck
     sprite.setOrigin(body.getPosition().x, body.getPosition().y);
      sprite.setPosition(body.getPosition().x, body.getPosition().y);
      sprite.setRotation(body.getAngle() * MathUtils.radDeg);
      sprite.draw(spriteBatch);
   }



If you are willing to run this, you don't need my player class, you can just make the camera move in y axis.
Offline Gibbo3771
« Reply #1 - Posted 2014-02-22 20:27:46 »

1  
2  
3  
sprite.setOrigin(body.getPosition().x, body.getPosition().y);
sprite.setPosition(body.getPosition().x, body.getPosition().y);
sprite.setRotation(body.getAngle() * MathUtils.radDeg);


This code is useless in the construction of your object, why? The body position will most likely change overtime.

Quote
There is a GROWING gap between sprite and body.

Read above, if the body is moving ever so slightly, this is why. You are not updating the position of the sprite each frame, at least I presume you are not.

Secondly, LibGDX sprite origin is bottom left and Box2D body origin is normally in the centre of the body. So therefore to position the sprite you should be doing something like this pseudo code:

1  
sprite.x = body.x - sprite.getWidth / 2


If you want to actually draw the sprite at the correct place and keep it there, you need a loop in the render() thread like so

1  
2  
3  
4  
5  
6  
7  
8  
   Array<Body> tmpBodies = new Array<Body>;
   
        world.getBodies(tmpBodes);

   for(Body body : tmpBodies){
      Sprite sprite = (Sprite) body.getUserData();
      // Position it, scale it, draw it
  }


However this comes with a big downfall, you need to set the bodies user data to the sprite when it has has better uses (contact filtering).

So instead, what I do is:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
Array<SomeEntityClass> entities = new Array<SomeEntityClass>;
   
   for(Entity entity : SomeEntityClass){
      if(entity == null) continue;
      if(entity.getBody() == null) continue;
      if(entity.getSprite() == null) continue;
      Body body = entity.getBody();
      Sprite sprite = entity.getSprite();
      // Position it, scale it, draw it
  }


Null checks look horrible but incase you happen to be multi-threading and delete said entity mid loop and suddenly the sprite or body gets nulled, prevents program crashing.

Quote
width is an array which has 3 elements, these are x1, y and x2 for creating the shape. I'm using y as y1 and y2.

Can't say i've ever done that, I just use some fields for width and height and such, seems simpler and code looks a bit better imo.

"This code works flawlessly first time and exactly how I wanted it"
Said no programmer ever
Offline kutucuk

Senior Member


Medals: 5
Exp: 3 years



« Reply #2 - Posted 2014-02-22 21:01:10 »

Body is a static one, so it will stay wherever it is created. That's why I typed them in the constructor.

Also, what you suggest has the same problem, the gap gets bigger as the coordinates get bigger. I know that, because I tried that. I tried again, with Here is a screenshot with this code (Coordinates are: x:0, y:200, x2:300, using this constructor: shape.setAsBox(hx, hy, center, angle);:
1  
2  
3  
sprite.setOrigin(body.getPosition().x, body.getPosition().y);
sprite.setPosition(body.getPosition().x - sprite.getWidth()*WTB/2, body.getPosition().y - sprite.getHeight()*WTB/2);
sprite.setRotation(body.getAngle() * MathUtils.radDeg);



Image doesn't show up on my computer so: https://www.dropbox.com/s/vxxbw2tacmby3do/screen.png

But the main problem is not their positions, my first image is in the bottom of the screen and body and the sprite fit perfect. But another body on the top of the screen which is created using the exact same class and method, has its body and sprite in completely different places.
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline kutucuk

Senior Member


Medals: 5
Exp: 3 years



« Reply #3 - Posted 2014-02-22 21:18:15 »

Check the link for my problem, I think it tells more than words: https://www.dropbox.com/s/v52aad27hbw0j4h/Screenshot%202014-02-22%2023.22.57.png
Offline kutucuk

Senior Member


Medals: 5
Exp: 3 years



« Reply #4 - Posted 2014-02-24 19:40:41 »

I posted this on badlogicgames forum as well. I thought it's only fair that you should know about this. I'll keep you updated if there is a new answer there which solves my problem.
Here is the link: http://badlogicgames.com/forum/viewtopic.php?f=11&t=12961

People over there gave me some suggestions, which I am already using. And BurningHand gave me idea that I should post my whole post to help you help me better. So, I modified the first post in this topic.

I also have a video to explain what my problem is: http://videobam.com/SFfHI
English is not my native language, so maybe I couldn't explain it. The video probably will tell more than words here.

Also, I'm still in need of help with this Smiley
Offline pitbuller
« Reply #5 - Posted 2014-02-28 22:30:55 »

Box2d coordinate system origo is in the origo of that coordinate system. It's that simple really. Box2d does not know or care how its rendered. To render box2d world you just set camera which consist of view matrix and projection that simply map points from one space to other.
1  
Box2d world space -> view space -> screen space.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

BurntPizza (22 views)
2014-09-19 03:14:18

Dwinin (35 views)
2014-09-12 09:08:26

Norakomi (62 views)
2014-09-10 13:57:51

TehJavaDev (89 views)
2014-09-10 06:39:09

Tekkerue (44 views)
2014-09-09 02:24:56

mitcheeb (65 views)
2014-09-08 06:06:29

BurntPizza (47 views)
2014-09-07 01:13:42

Longarmx (35 views)
2014-09-07 01:12:14

Longarmx (40 views)
2014-09-07 01:11:22

Longarmx (36 views)
2014-09-07 01:10:19
List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06

List of Learning Resources
by SilverTiger
2014-07-31 11:54:12

HotSpot Options
by dleskov
2014-07-08 01:59:08
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!