positioning a sprite in libgdx - java

I've got a problem to draw a sprite to my project.
I have a map (960x900) divided into tiles (64x64).
As you can see in the picture, when i click on the bottom left corner of the purple square, the position is (0;0), and when I click on the top right corner of purple square, the position is (36;47)
The problem is that the picture of the purple square has a size of 32x32, and when I draw this picture with libgdx on the screen, the size doesn't match.
Another example: the square with black border has a size of 64x64. So if I draw the purple square in front of the black, the purple should be the half (in height and in width) of the black, no?
Does anyone know why libgdx resizes the purple square?
Sprite sprite = new Sprite(new Texture("assets/purpleSquare.png"));
i draw it in a method
public void render(SpriteBatch batch) {
batch.draw(sprite, 0, 0);
}
I don't know why the picture is resized by libgdx.. I have also tried to do
batch.draw(sprite, 0, 0, width, heigth);
To precise the sprite's size but it doesn't work too..

The size on screen bears no direct relation to the size of the original image. When you draw a sprite you provide the SpriteBatch with a position, width, and height in world coordinates. The sprite will be stretched to fit these world coordinates, regardless of the original image size.
When you click the screen, you are clicking in screen coordinates. The relation between screen and world coordinates is determined by the projection matrix that you use with the SpriteBatch. The projection matrix is typically controlled with a Camera or Viewport object, which you can use to convert between the two coordinate systems using the project and unproject methods.

I'm happy to see that after many hours i found a solution, even if i know which is not correct.
I would like some help to understand the problem's origin.
With this parts of code :
public void update(float delta) {
batch.begin();
drawBackground(); // Draw the background
drawButton(); // Draw the play/pause button
batch.end();
drawMap(); // draw a tmx map made with tiled
batch.begin();
if(!world.isInitial()) {
renderMonster(); // method which draw the monster
}
renderTower(); // method which draw the tower's level
batch.end();
}
I don't understand why i have to do "batch.begin()" and "batch.end()" twice.
I dont' understant why with this code, the purple square is resized.
With this code :
public void update(float delta) {
batch.begin();
drawBackground();
drawButton();
batch.end();
drawMap();
------->sb = new SpriteBatch();
sb.begin();
if(!world.isInitial()) {
renderMonster();
}
renderTower();
sb.end();
}
this line that i add fix the bug with the purple square. If i work with two SpriteBatch (because with one, if i reinitialize SpriteBatch in update method, my pause/play button diseapper) and i initialise the second (SpriteBatch sb) in the update method.
It is correct to initialise a SpriteBatch every time i'm passing on the update method ? There's no method with a SpriteBatch to avoid this problem ?

Related

Drawing rectangle and shape-rendered rectangle at mouse position

I'm trying to draw a bounding box that follows the mouse and is positioned beneath it but the rectangle doesn't move with it, it moves too fast or too slow and not at the right place.
I've tried getting the mouse coordinates and passing them in to a rectangle, then I draw a rectangle at its position so I can see where it is.
mouseRect is a rectangle drawn at the mouse's position:
mouseRect.set((float)Gdx.input.getX(), (float)Gdx.input.getY(), 32, 32);
This is my render method that is meant to draw a rectangle at mouseRect's position:
shapeRenderer.setProjectionMatrix(cam.combined);
shapeRenderer.begin(ShapeType.Filled);
shapeRenderer.setColor(Color.BLUE);
shapeRenderer.rect(mouseRect.x, mouseRect.y, mouseRect.width, mouseRect.height);
shapeRenderer.end();
The box does move when the mouse moves but it's far too fast and it's not positioned under the mouse as should be expected (there's a giant offset and seems to be inverted but when I make it negative it still doesn't work).
I think you should read about coordinate systems. The mouse system is y-down and screen dependent, while (if I remember correctly), the camera is y-up, centered, and uses the viewport size.
I would suggest using a Stage with a listener for mouseMoved or touchDragged events, which have stage coordinates. Add an actor drawing a rectangle to the stage. You could then move the actor accordingly. You wouldn't have to handle coordinate system changes.
It could look like this:
Actor rectangleActor = new RectangleActor();
stage.addActor(rectangleActor);
stage.addListener(new InputListener() {
public boolean mouseMoved(InputEvent event, float x, float y) {
rectangleActor.setPosition(event.getStageX(), event.getStageY());
return false;
}
})
See this answer for how to draw a rectangle in an actor.

How Camera works in Libgdx and together with Viewport

If you work with LibGdx it goes not long until you come to Camera and viewport. If you work the first time with camera and Viewport you get some questions about how it works and how to use it. So:
How can I use a Camera in LibGdx? What's viewport width and height?
What is a Viewport, how can I use it and how it works together with the Camera?
How can I use a Camera in LibGdx? What's viewport width and height?
Firstly it's important that you know the Camera works with World units not with Pixels. World units are not a regular Unit. You self can define how much one World Unit is. Later more.
First, we create an OrthographicCamera a SpriteBatch and a Texture:
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture img;
#Override
public void create () {
//We create a OrthographicCamera through which we see 50x50 World Units
camera = new OrthographicCamera(50,50);
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
We create a OrthographicCamera and in the Constructor we define how many World Units we see if we look through this camera into our world. In our example 50 x 50 World Units these are the viewport width and height.
So we have created a Camera with a viewport width and height of 50.
In the render() method we render our image:
#Override
public void render () {
//Clear the screen (1)
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Set ProjectionMatrix of SpriteBatch (2)
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw image on position 0, 0 with width 25 and height 25 (3)
batch.draw(img, 0, 0, 25, 25);
batch.end();
}
(1) Clear the Screen, if we don't do that every Texture will draw over the other and if we draw a Animation we will see the old Frames.
(2) The batch is our Drawer he draws our Images, Animations etc. Default he draws a World which has so many World Units like the Screen has Pixels so in this case 1 World Unit = 1 Pixel. But now we will see 50 x 50 World Units doesn't matter how big the screen is. To say the Batch that he should draw what we see through our camera we must call: batch.setProjectionMatrix(camera.combined);
(3) Now we draw our img on Position 0,0 But 0, 0 doesn't mean on Pixel position 0,0 it means the Image will be drawn on World position 0,0 also width and height are not in Pixels they are in World units so the img will be drawn on Position 0,0 25x25 World Units big. So on a 50x50 viewport, the image fills one-quarter of the whole screen.
The Image fill one-quarter of the whole screen exactly as expected. But why it is on the right top and not on the bottom left?
The Problem is that the center of the Camera point on the position 0,0
So our Image is drawn on position 0,0 he fills the top right corner.
We must set the position of the camera so 0,0 is in the bottom left corner:
camera = new OrthographicCamera(50,50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
In the render() method we must add camera.update() because every time we change the position or the scale or what else of the camera we must update the camera.
Now the Image is in the bottom left.
Where are the Pixels?
We always speak about World units but where are the Pixels? The pixels are still there. If we have a Screen size of 200 x 200 pixels the batch will always draw 200 x 200 pixels. With the method batch.setProjectionMatrix(camera.combined); we only say the batch how much World Units are one Pixel.
If we have a Screen with 200 x 200 pixels and we create a Camera with a viewport of 50 x 50 world units the SpriteBatch know 1 WorldUnit = 4 Pixels.
Now we draw a Image which is 25 x 25 World Units big the SpriteBatch knows he must draw the image 25 * 4 = 100 pixel big.
So the pixels still there but it's easier to think in World Units.
If it's not clear enough here is a little bit more detailed description: Libgdx's World Units
Box2d
It's also very important to think in World Units if you use Box2d because Box2d works with Meters. So if you create a Body with a Force off 5 on the x axis, the Body is 5 m/s fast.
And now it's very cool to work with World Units because you can say 1 World Unit = 1 Meter so you can create a object with a width of 10 and you know after one second the Body will be in the Center of the Object. If you work with Pixels you will have a Problem if you have a different Screensize.
What is a Viewport, how can I use it and how it works together with the Camera?
Now we have the big Problem about different Screen sizes.
Suddenly we have a Screen size of 350 x 200 pixels, now the Image will be stretched and don't look so nice as before.
For this Problem we use Viewports a few Viewports are StretchViewport, FitViewport and ExtendViewport. All viewports you can find here: https://github.com/libgdx/libgdx/wiki/Viewports.
Firstly what is a Viewport.
Imagine the camera is a speaker who speaks English. Different Screen Sizes are other People who speak German, French, Chinese etc. and the Viewport is the translator. The Translator doesn't change the sense of that what the English Speaker says but he adapts it so the others can understand it. Same are camera and Viewport. Viewport doesn't say or change what you can see on your screen if you run the program. He only handles that you always see the same on different Screen sizes. A Camera can life without Viewport. A Viewport not without Camera.
Add a viewport Object:
private Viewport viewport;
and the resize() method:
#Override
public void resize (int width, int height) {
viewport.update(width, height);
}
StretchViewport
Create a StretchViewport:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new StretchViewport(camera.viewportWidth, camera.viewportHeight, camera);
In the StretchViewport Constructor, we define the viewport width and height and the Camera.
Now we get the same result as before if we have different Screensizes the Images will be stretched.
FitViewport
Maybe we won't stretch our Images we will matter about the ratio of x and y.
The ratio of x and y means: is an Object 2 width and 1 height he will always twice as wide as high for example 200x100, 30x15 but not 20x15.
Create a FitViewport:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new FitViewport(camera.viewportWidth, camera.viewportHeight, camera);
Now the Image will always be a square. To see the Bars on the Side lets draw the Image as big as our viewport:
batch.draw(img, 0, 0, 50, 50);
The Image has a Ratio of 1 because of 50(width)/50(height) = 1 so the Image will always have the same width and height. The Bars on the side are outside of our Viewport and will be drawn in the color you define here: Gdx.gl.glClearColor(1, 1, 1, 1);
ExtendViewport
Maybe we won't Bars on the Side then we can take a ExtendViewport. The ExtendViewport keeps the world aspect ratio without bars by extending the world in one direction. Means on a screen where the aspect ratio between width and height are bigger you will see more of the world.
On a screen 400x200 aspect ration = (400/200 = 2) you will see more than on a screen of 300x200 (300/200 = 1.5);
To show this create a ExtendViewport and draw the Image bigger than the viewport and a second small Image:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new ExtendViewport(camera.viewportWidth, camera.viewportHeight, camera);
// in render() method
batch.begin();
batch.draw(img, 0, 0, 100, 50);
batch.draw(img, -20, 0, 20, 20);
batch.end();
If we now start our Program with a Screen size of 200x200 we see:
And if we resize the Screen on x axis To make the screen wider:
Now we can see more off the first Image and additinal the Second image but the ratio will always be the same. The Image is only stretched because we draw it 100x50 not because of resizing.
I hope this will clear some Questions about Camera and Viewport if you will learn more, read and look some tutorials and read the LibGdx wiki: https://github.com/libgdx/libgdx/wiki

Sprites not fitting according to different screen resolutions on Libdx

I'm testing my sprite that has the game title, and on my Motorola Moto G 2nd generation the dimensions of the sprite looks good but I'm testing also on my mothers phone, a Samsung GT-S5830i, and the height of the sprite looks stretched out.
I'm also trying to understand the concept of Viewport (I'm using the StretchViewport), but I don't know if I'm doing right. My game are designed for mobile, not desktop.
I did that to my SplashScreen:
this.gameTitle = new Sprite(new Texture(Gdx.files.internal("images/GameTitle.png")));
this.gameTitle.setSize(Configuration.DEVICE_WIDTH - 50, this.gameTitle.getHeight() * Configuration.DEVICE_HEIGHT / Configuration.DEVICE_WIDTH);
The DEVICE_HEIGTH and DEVICE_WIDTH are constants about the dimension of the screen. And the "-50" is a margin to the sprite
In my Viewport I used the real size of the screen for the dimensions, or should I use a virtual dimension? But how it works?
This is a part of my main class, what can I change?
// Create the Orthografic camera
this.orthoCamera = new OrthographicCamera(Configuration.DEVICE_WIDTH, Configuration.DEVICE_HEIGHT);
this.orthoCamera.setToOrtho(false, Configuration.VIRTUAL_GAME_WIDTH, Configuration.VIRTUAL_GAME_HEIGHT);
this.orthoCamera.position.set(this.orthoCamera.viewportWidth / 2f, this.orthoCamera.viewportHeight / 2f, 0);
this.orthoCamera.update();
// Combine SpriteBatch with the camera
this.spriteBatch.setTransformMatrix(this.orthoCamera.combined);
// Create the ViewPort
this.viewPort = new ExtendViewport(Configuration.DEVICE_WIDTH, Configuration.DEVICE_HEIGHT);
I updated my viewport to the ExtendViewport as you said.
Main class render method:
public void render() {
super.render();
// Update Orthographic camera
this.orthoCamera.update();
// Combine SpriteBatch with the camera
this.spriteBatch.setTransformMatrix(this.orthoCamera.combined);
}
Screen class render method:
#Override
public void render(float delta) {
// OpenGL clear screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(Gdx.gl.GL_COLOR_BUFFER_BIT | Gdx.gl.GL_DEPTH_BUFFER_BIT);
// SpriteBatch begins
this.game.spriteBatch.begin();
// Display the ClimbUp logo
this.gameTitle.draw(this.game.spriteBatch);
this.character.draw(this.game.spriteBatch);
// SpriteBatch ends
this.game.spriteBatch.end();
}
If you don't want stuff to look distorted on some devices and you don't want black bars (which none of your customers will like), you need to use an ExtendViewport instead of StretchViewport. And the dimensions you give it should be virtual dimensions based on whatever units you would like to work with.
For example, assuming a landscape orientation game, you could use 800 x 480 as virtual dimensions, and then you know that anything within that area (in world units) will be shown on the screen and you can design your game for that. On narrower devices (4:3 ratio) there will be more than 480 vertical units shown, and on wider devices (16:9 ratio) there will be more than 800 horizontal units shown.
There's one other option that avoids black bars and distortion, and that's FillViewport. But I think in general that's not a good option because you have no easy way to predict how much of your virtual dimensions are going to get cropped off.
Based on your edited question, here's what I would change in your code:
//No need to create your own camera. ExtendViewport creates its own.
// Pointless to call this now before resize method is called. Call this in render
//XXX this.spriteBatch.setTransformMatrix(this.orthoCamera.combined);
//This is where you give the viewport its minimum virtual dimensions
this.viewPort = new ExtendViewport(Configuration.VIRTUAL_GAME_WIDTH, Configuration.VIRTUAL_GAME_HEIGHT);
//Get reference to the viewport's camera for use with your sprite batch:
this.orthoCamera = (OrthographicCamera) this.viewport.getCamera();
Then in the resize method:
orthoCamera.setPosition(/*wherever you want it*/);
viewport.update(width, height, false); //these are actual device width and height that are provided by the resize method parameters.
You might want to position your camera in relation to the size calculated by the viewport. Then you should omit the setPosition line above, and instead calculate it after calling viewport.update. For example if you want 0,0 in the bottom left of the screen:
viewport.update(width, height, false);
orthoCamera.setPosition(orthoCamera.viewportWidth/2f, orthoCamera.viewportHeight/2f);
In your render method you can put this before spriteBatch.begin():
orthoCamera.update(); //good idea to call this right before applying to SpriteBatch, in case you've moved it.
spriteBatch.setProjectionMatrix(orthoCamera.combined);

Android LibGDX setting projection matrix combined causes sprites to vertically flip over

public void render() {
this.spriteRenderer.setProjectionMatrix(this.instance.camera.combined);
this.spriteRenderer.begin();
if (!this.ballon.isPoping()) {
Sprite s = this.ballon.getSprite();
spriteRenderer.draw(s, 50, 50, s.getWidth(), s.getHeight());
}
// else {
// if (!this.ballon.isPoped()) {
// Sprite s = this.ballon.getCurrentAnimation();
// this.spriteRenderer.draw(s, this.ballon.getX(),
// this.ballon.getY(),
// this.ballon.getSprite().getWidth() * SpriteConfiguration.BALLON_SCALE,
// this.ballon.getSprite().getHeight() * SpriteConfiguration.BALLON_SCALE);
// this.ballon.processAnimation();
// }
// }
this.spriteRenderer.end();
}
I have set camera.setToOrtho to true with my width, and height. whenever I run that render method, my ballon sprite is basically vertically turned, looks like it flipped by 180 degrees over, like the image is saved as a standing ballon, and upon draw it looks like its turned, so I have to rotate the screen to see it.
The reason I used setProjectionMatrix is because the default location of the screen (0, 0) on android portrait mode I assume, is top-right corner, which on libGDX it is automtaically at bottom left corner, so basically setToOrtho fixes it for libGDX, but the phone's default location doesn't change, and I want it to change & be the same as libGDX's one because I need to handle screen touching, and get the correct position.
So basically setProjectionMatrix & setToOrtho(true...) does it's job, but the only problem that it just rotates the image by 180 degrees.
Does anyone know why it does that?

LibGDX - Set a background image ( to be static)

I set the background image like this -
batch.draw(Assets.back_sprite, 0, 0, ResX, ResY);
The problem is when i move the camera -
camera.translate(2,0);
The image starting to move backwards and eventually disappears, because I drew it at (0,0)
and the camera is moving in a speed of (2,0), that's why the the image goes away.
The camera is ortho not perspective.
How can i make the image to be static and always stay there?
Any ideas?
Thanks in advance :P
Render it with another camera
OrthographicCamera mStageCamera;
OrthographicCamera mFixedCamera;
SpriteBatch mBatch;
#Override
public void render() {
mBatch.setProjectionMatrix(mFixedCamera.combined);
mBatch.begin();
//render "static" elements
mBatch.end();
mBatch.setProjectionMatrix(mStageCamera.combined);
mBatch.begin();
//render "movable" elements
mBatch.end();
}

Categories