Draw sprite on Body - camera View Port and resize - libgdx

I'm drawing sprites uppon bodies, with this method (inside my body wrapper):
private void drawBaseSprite(Batch batch){
Sprite baseSprite = this.getBaseSprite();
Vector3 bodyPixelPos = camera.project(new Vector3(body.getPosition().x, body.getPosition().y, 0));
// TODO: 17/11/16 Review this
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom ;
baseSprite.setSize(w,h);
baseSprite.setOrigin(w/2, h/2);
baseSprite.setPosition(bodyPixelPos.x -w/2, bodyPixelPos.y - h/2);
baseSprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
baseSprite.draw(batch);
}
Everything is good, until I try resizing the windows. Well, I'm following this resize logic (implements Screen) :
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
stage.camera.setToOrtho(false, Constants.VIEWPORT_HEIGHT *width/(float)height, Constants.VIEWPORT_HEIGHT);
}
Before resize:
After resize (larger width):
I find this absurd because this:
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom ;
doesn't change, while the images are x-scaled.

Am I correct in understanding that your problem is the image not staying on top of the body?
If so, the reason why it doesn't is that you are setting the size of the image relative to the camera, not to the body it is supposed to cover. When your camera's zoom is changed, you adjust the size of the image, but not the body, and they get out of sync.
I would recommend changing your logic such that the image height/width are determined purely by the body, and your body is scaled based on the camera zoom.

Well, this solved my problem, but I still don't deeply-know why.
private void drawBaseSprite(Batch batch){
Sprite baseSprite = this.getBaseSprite();
batch.setProjectionMatrix(camera.combined);
float someScale = 0.1f;
float w = scale * (baseSprite.getTexture().getWidth())/camera.zoom *someScale;
float h = scale * (baseSprite.getTexture().getHeight())/camera.zoom *someScale;
Vector3 bodyPixelPos = camera.project(new Vector3(body.getPosition().x, body.getPosition().y, 0))
.scl(someScale*camera.viewportHeight/(Gdx.graphics.getHeight()/20f)).sub(w/2, h/2, 0);
baseSprite.setSize(w,h);
baseSprite.setOrigin(w/2, h/2);
baseSprite.setPosition(bodyPixelPos.x, bodyPixelPos.y);
baseSprite.setRotation(body.getAngle() * MathUtils.radiansToDegrees);
baseSprite.draw(batch);
}

Related

Libgdx Rendering Bitmap font to pixmap texture causes extremely slow rendering

I'm using libgdx scene2d to render 2d actors. Some of these actors originally included scene2d Label actors for rendering static text. The Labels work fine but drawing ~20 of them on the screen at once drops the frame rate by 10-15 frames, resulting in noticeably poor rendering while dragging.
I'm attempting to avoid the Labels by pre-drawing the text to textures, and rendering the textures as scene2d Image actors. I'm creating the texture using the code below:
BitmapFont font = manager.get(baseNameFont,BitmapFont.class);
GlyphLayout gl = new GlyphLayout(font,"Test Text");
int textWidth = (int)gl.width;
int textHeight = (int)gl.height;
LOGGER.info("textHeight: {}",textHeight);
//int width = Gdx.graphics.getWidth();
int width = textWidth;
//int height = 500;
int height = textHeight;
SpriteBatch spriteBatch = new SpriteBatch();
FrameBuffer m_fbo = new FrameBuffer(Pixmap.Format.RGB565, width,height, false);
m_fbo.begin();
Gdx.gl.glClearColor(1f,1f,1f,0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
Matrix4 normalProjection = new Matrix4()
.setToOrtho2D(0, 0, width, height);
spriteBatch.setProjectionMatrix(normalProjection);
spriteBatch.begin();
font.draw(spriteBatch,gl,0,height);
spriteBatch.end();//finish write to buffer
Pixmap pm = ScreenUtils.getFrameBufferPixmap(0, 0, (int) width, (int) height);//write frame buffer to Pixmap
m_fbo.end();
m_fbo.dispose();
m_fbo = null;
spriteBatch.dispose();
Texture texture = new Texture(pm);
textTexture = new TextureRegion(texture);
textTexture.flip(false,true);
manager.add(texture);
I assumed, and have read, that textures are often faster. However when I replaced the Labels with the texture, it had the same, if not worse, affect on the frame rate. Oddly, I'm not experiencing this when adding textures from a file, which makes me think I'm doing something wrong in my code. Is there a different way I should be pre-rendering these pieces of text?
I have not tested this, but I think you can enable culling for the whole Stage by setting its root view to use a cullingArea matching the world width and height of the viewport. I would do this in resize after updating the Stage Viewport just in case the update affects the world width and height of the viewport.
#Override
public void resize(int width, int height) {
//...
stage.getViewport().update(width, height, true);
stage.getRoot().setCullingArea(
new Rectangle(0f, 0f, stage.getViewport().getWorldWidth(), stage.getViewport().getWorldHeight())
);
}
It will only be able to cull Actors that have their x, y, width, and height set properly. This is true I think of anything in the scene2d UI package, but for your own custom Actors you will need to do it yourself.
If a child of your root view is a Group that encompasses more than the screen and many actors, you might want to cull its children, too, such that even if the group as a whole is not culled by the root view, the group can still cull a portion of its own children. To figure out the culling rectangle for this, I think you would intersect a rectangle of the group's size with the viewport's world size rectangle offset by -x and -y of the group's position (since the culling rectangle is relative to the position of the group it's set on).

Scale and transform in LibGDX

I have to do something like this in my Android App with LibGDX:
The ball is a Image that has a drag listener and added to a Stage:
ballImage.addListener(new DragListener() {
public void drag(InputEvent event, float x, float y, int pointer) {
ballImage.moveBy(x - ballImage.getWidth() / 2, y - ballImage.getHeight() / 2);
}
});
which works great. But I have a problem with scaling the ball. I can't scale the ball and maintain it's position and drag stop working normally (ball jump outside screen just on touch). I tried something like this:
massSlider.addListener(new ChangeListener() {
public void changed (ChangeEvent event, Actor actor) {
float x = ballImage.getX() - ballImage.getWidth() / 2;
float y = ballImage.getY() - ballImage.getHeight() / 2;
ballImage.moveBy(x, y);
ballImage.setScale(massSlider.getValue());
ballImage.moveBy(-x, -y);
}
});
Also, I was trying to use scaleBy()/setScale() and moveBy()/setPosition(), or to not move the ball at all before and after scaling, but nothing seems to work. What I do wrong?
Scaling is usually something that you want to (and easily can) avoid. It is nice to use scaling e.g. for actions to create a short pop out or pop in effect. But apart from that it's usually much better to adjust the size instead. So instead of using setScale(a) you do: setSize(a * unscaledWidth, a * unscaledHeight). Note that you will have to keep track of the unscaled width and height for this.

Game takes up only the top right corner of the screen in LIBGDX

I am working on LIBGDX . My game takes up only the top right corner of the screen rather taking up the entire width and height.
In my update method of the screen, I have this code,
public void update(float dt) {
......
gamecam.position.x = this.gamehero.heroBody.getPosition().x; -- 1
gamecam.position.y = this.gamehero.heroBody.getPosition().y; -- 2
......
}
But, when I remove the second line of the code the game takes up the entire screen but the camera doesn't follow the player on y axis . I have attached the screen shot of the game when I include the second line of the code,
And, when I remove the second line of code, It takes up the entire screen
I don't know how to resolve it. Any suggestions would be better. Thanks
Create your camera with this:
gamecam = new OrthographicCamera(SCREEN_WIDTH, SCREEN_HEIGHT);
gamecam.position.set(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0);
Update camera gamecam.update() after each position editing.
public void update(float dt) {
gamecam.position.x = this.gamehero.heroBody.getPosition().x; // -- 1
gamecam.position.y = this.gamehero.heroBody.getPosition().y; // -- 2
gamecam.update(); // -- 3
}
Kolesnikovich is on the right track with the top of his code. But unfortunately positions the camera exactly like you in the end.
What you are actually doing is setting the centered position of the cam. So all you need to do is increment the camera's position coordinates by half the viewport width and height.
public void update(float dt) {
gamecam.position.x = this.gamehero.heroBody.getPosition().x +
gamecam.getViewportWidth / 2;
gamecam.position.y = this.gamehero.heroBody.getPosition().y +
gamecam.getViewportHeight / 2;
gamecam.update(); // -- 3
}

How to resize a texture maintaining aspect ratio in libgdx

I have a .png image "circle.png".
The problem is when I try to resize this in sb.draw() method it disorientates (stretches). It works fine when I draw it with it's default size;
// inside constructor
{
Texture texture = new Texture("circle.png");
...
.
}
// render method
public void render (SpriteBatch sb){
sb.begin();
sb.setProjectionMatrix(camera.combined);
sb.draw(texture,x,y,100,100);// 100 for both width and height
sb.end();
}
//camera setup
camera = new OrthographicCamera();
camera.setToOrtho(false, width / 2, height / 2);
// width n height is 480x640
First image is one without resizing
Second image is while resizing
Your image is probably not a square. To maintain the aspect ratio, use math:
100 = texture.getWidth()/a
a = texture.getWidth()/100
So use:
sb.draw(texture,x,y,100,texture.getHeight()/(texture.getWidth()/100);
I hope it helps!
staticcasty

Scaling up viewport

My game is 200 x 320 pixels and I want to scale this up to fit any screen. The problem is I need to scale this up in integer multiples of these dimensions so the upscaled pixels don't look uneven.
What should I put in my constructor, render() and resize() methods to achieve this?
In the constructor I now have:
camera = new OrthographicCamera();
camera.setToOrtho(false,200,320);
stage = new Stage(new ScalingViewport(Scaling.fill,200,320, camera));
In render():
camera.update();
game.batch.setProjectionMatrix(camera.combined);
And in resize():
int widthScale = width/200;
int heightScale = height/320;
int newScale = Math.max(widthScale, heightScale);
int multiplesWidth = newScale*200;
int multiplesHeight = newScale*320;
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
camera.setToOrtho(false, 200, 320);
My problem is that the content isn't centered. How and where can I center whatever is on the screen? I tried with:
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
but the screen is blank!
I think the problem is that you are trying to center the screen using the screen dimensions and not the world dimensions.
Try replacing
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
with
camera.position.set(stage.getViewport().getWorldWidth()/2f, stage.getViewport().getWorldHeight()/2f, 0);
EDIT:
Okay, so here is an explanation why I proposed you set the camera to (width/2, height/2). (I will leave out the third z-Coordinate from now on as it will always be 0.)
camera.position.set() sets the center point of the camera. At (0,0) the camera is positioned right at the center of the screen, depicted by the red rectangle below. What libgdx does by default, and what I proposed offsets the camera by width/2(the blue line) and height/2(the green line) which translates to the magenta rectangle.
Side note: the reason camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0); didn't work is because Gdx.graphics.getX() returns the width and height of the window in pixels, not in the measurement system the Viewport uses. This offsets the camera about a meter up and to the right, where you obviously didn't draw anything.
If you want the red rectangle as camera, you can change the last argument of
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
to false. Or use camera.position.set(0,0,0);