libgdx - ui SpriteBatch needs to scale based on window size? - java

I am currently using libgdx 1.5.4. Normally to modify screen size, I have done this:
public class DesktopLauncher {
public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
int temp = 3; //scale viewport
config.width = temp *160;
config.height = temp *144;
new LwjglApplication(new MyGame(), config);
}
}
The temp var above will scale the desktop application window size by some number (3 in this case). In my render() loop, I have a batch (floatingBatch = new SpriteBatch();) that isn't modified by a camera, to draw my 'ui' elements:
floatingBatch.begin();
//bunch of floatingBatch.draw()'s...
floatingBatch.end();
I have noticed, though, that the coordinates in floatingBatch don't scale with my screen size. For example, the point (200,200) in floating batch will be offscreen if temp above = 1, but will be onscreen if temp = 3. This isn't right, I want floatingBatch to scale with the window. If I start with temp = 1, and just manually drag the window out to about 3x it's starting size, everything is as I want obviously.
One thing that I have tried is have the temp = 1 above, and then do this in my create() function:
Gdx.graphics.setDisplayMode(160*3, 144*3, false);
The above is fine, and works, but it looks a little awkward visually (the screen first starts out as 144x160, then resizes to 3x that size.) Is there a better way to do what I am describing? Can I change floatingBatch somehow so that it scales with the window?

You can use
Gdx.graphics.getWidth();
and
Gdx.graphics.getHeight();
to obtain the pixel count of the device then adjust your scene2d
Refer to the api for more help

Related

Custom actor for BitmapFont (libgdx)

I've spent several frustrating hours trying to implement (what I thought would be) a simple FontActor class.
The idea is to just draw text at a specific position using a provided BitmapFont. That much, I've managed to accomplish. However, I'm struggling to compute my actor's width/height based on the rendered text.
(Using a FitViewport for testing)
open class FontActor<T : BitmapFont>(val font: T, var text: CharSequence = "") : GameActor() {
val layout = Pools.obtain(GlyphLayout::class.java)!!
companion object {
val identity4 = Matrix4().idt()
val distanceFieldShader: ShaderProgram = DistanceFieldFont.createDistanceFieldShader()
}
override fun draw(batch: Batch?, parentAlpha: Float) {
if (batch == null) return
batch.end()
// grab ui camera and backup current projection
val uiCamera = Game.context.inject<OrthographicCamera>()
val prevTransform = batch.transformMatrix
val prevProjection = batch.projectionMatrix
batch.transformMatrix = identity4
batch.projectionMatrix = uiCamera.combined
if (font is DistanceFieldFont) batch.shader = distanceFieldShader
// the actor has pos = x,y in local coords, but we need UI coords
// start by getting group -> stage coords (world)
val coords = Vector3(localToStageCoordinates(Vector2(0f, 0f)), 0f)
// world coordinate destination -> screen coords
stage.viewport.project(coords)
// screen coords -> font camera world coords
uiCamera.unproject(coords,
stage.viewport.screenX.toFloat(),
stage.viewport.screenY.toFloat(),
stage.viewport.screenWidth.toFloat(),
stage.viewport.screenHeight.toFloat())
// adjust position by cap height so that bottom left of text aligns with x, y
coords.y = uiCamera.viewportHeight - coords.y + font.capHeight
/// TODO: use BitmapFontCache to prevent this call on every frame and allow for offline bounds calculation
batch.begin()
layout.setText(font, text)
font.draw(batch, layout, coords.x, coords.y)
batch.end()
// viewport screen coordinates -> world coordinates
setSize((layout.width / stage.viewport.screenWidth) * stage.width,
(layout.height / stage.viewport.screenHeight) * stage.height)
// restore camera
if (font is DistanceFieldFont) batch.shader = null
batch.projectionMatrix = prevProjection
batch.transformMatrix = prevTransform
batch.begin()
}
}
And in my parent Screen class implementation, I rescale my fonts on every window resize so that they don't become "smooshed" or stretched:
override fun resize(width: Int, height: Int) {
stage.viewport.update(width, height)
context.inject<OrthographicCamera>().setToOrtho(false, width.toFloat(), height.toFloat())
// rescale fonts
scaleX = width.toFloat() / Config.screenWidth
scaleY = height.toFloat() / Config.screenHeight
val scale = minOf(scaleX, scaleY)
gdxArrayOf<BitmapFont>().apply {
Game.assets.getAll(BitmapFont::class.java, this)
forEach { it.data.setScale(scale) }
}
gdxArrayOf<DistanceFieldFont>().apply {
Game.assets.getAll(DistanceFieldFont::class.java, this)
forEach { it.data.setScale(scale) }
}
}
This works and looks great until you resize your window.
After a resize, the fonts look fine and automatically adjust with the relative size of the window, but the FontActor has the wrong size, because my call to setSize is wrong.
Initial window:
After making window horizontally larger:
For example, if I then scale my window horizontally (which has no effect on the world size, because I'm using a FitViewport), the font looks correct, just as intended. However, the layout.width value coming back from the draw() changes, even though the text size hasn't changed on-screen. After investigation, I realized this is due to my use of setScale, but simply dividing the width by the x-scaling factor doesn't correct the error. And again, if I remove my setScale calls, the numbers make sense, but the font is now squished!
Another strategy I tried was converting the width/height into screen coordinates, then using the relevant project/unproject methods to get the width and height in world coordinates. This suffers from the same issue shown in the images.
How can I fix my math?
Or, is there a smarter/easier way to implement all of this? (No, I don't want Label, I just want a text actor.)
One problem was my scaling code.
The fix was to change the camera update as follows:
context.inject<OrthographicCamera>().setToOrtho(false, stage.viewport.screenWidth.toFloat(), stage.viewport.screenHeight.toFloat())
Which causes my text camera to match the world viewport camera. I was using the entire screen for my calculations, hence the stretching.
My scaleX/Y calculations were wrong for the same reason. After correcting both of those miscalculations, I have a nicely scaling FontActor with correct bounds in world coordinates.

Libgdx: text inside the game screen / render - fix size and position

I want to add text on the top of the enemies body.
Downscale issue starts here:
https://gamedev.stackexchange.com/questions/73688/why-is-my-text-is-too-large-even-when-scaled-to-05f-in-libgdx
The issue is that the text is very very big and i can't scale it smaller.
There is a solution for this, to add another camera / viewport.
This is OK - for HUD screen.
But i need the text on the enemy head.
From font class:
viewport = new FitViewport(Application.V_WIDTH * 4 , Application.V_HEIGHT * 4, new OrthographicCamera());
stage = new Stage(viewport, SpriteBatch);
stage.addActor(tableText);
Game main class:
viewport = new FitViewport(Application.V_WIDTH / Application.PPM, Application.V_HEIGHT / Application.PPM, orthographicCamera);
AND THE GAME. . .
What should i do? Do i need to find the right ratio betwin the game and the font class?
Danke
There is another way that works for me now:
bitmapFont.getData().setScale(0.008f);
bitmapFont.setUseIntegerPositions(false);
From libgdx API:
/** Specifies whether to use integer positions. Default is to use them so filtering doesn't kick in as badly. */
public void setUseIntegerPositions (boolean integer) {
this.integer = integer;
cache.setUseIntegerPositions(integer);
}

How to draw square pixel with different colors using SWT application

I am working on a fluid simulation application using SWT and want to draw the final calculated density of fluid back to UI. Now I am thinking about using SWT canvas and GC, but GC seems only for drawing shapes and lines, without the ability to draw colored pixels
There are many simulation app in the website but none is implemented by SWT. Below is the expected result for this application:
Use GC.drawPoint to draw a single pixel, the colour will be that set with GC.setForeground.
Jonah's answer is the way to do it if you want to draw everything yourself.
I'm just posting this as an alternative:
There's a library called "JHC (Java Heat Map Control)" that you could use to draw your data.
It would look something like this:
The data is provided in the form of an int[][].
// Define scales
JHCScale<String> xScale = new JHCScale.String(new String[] { "X1", "X2", "X3" });
JHCScale<String> yScale = new JHCScale.String(new String[] { "Y1", "Y2", "Y3" });
// Configure
JHCLayout layout = new JHCLayout(false, 20, true, 20);
JHCConfiguration config = new JHCConfiguration("x-label", "y-label",
JHCGradient.GRADIENT_HEAT, layout);
// Create array
int[][] array = {{0,1,0}, {1,2,1}, {0,1,0}};
// Create data object
JHCData data = JHCData.create(array, Orientation.ROW, xScale, yScale);
// Create JHC widget
JHC jhc = new JHC(shell, SWT.NONE);
jhc.setData(data, config);

libGdx array<TextureRegion> fails to assign to Sprite

After a long search on StackOverflow and Google I decided to write a question myself as I did not find a solution for my problem. I am learning libgdx from a 'learning LibGdx game development' book. I confirmed that my code is exactly the same as the code provided in the book. Everything runs fine until it reaches this part:
Sprite spr = new Sprite(regions.random());
Region.randoms() is an Array<TextureRegion>. It works fine if I add direct instance of an asset from a Asset class but crashes if I want to add a random asset from Array<TextureRegion>. Why?
private void initTestObj () {
// creates new array for 5 sprite's
testSprites = new Sprite[5];
// Create a list of texture regions
Array<TextureRegion> regions = new Array<TextureRegion>();
regions.add(Assets.instance.bunny.head);
regions.add(Assets.instance.ballon.ballon);
regions.add(Assets.instance.spider.spider);
System.out.println("xxxxxxxxxxxxxxxxx test before Sprite xxxxxxxxxxxxxxxxxxxxx");
System.out.println(regions.size);
//create new sprite's using a random texture region
for( int i = 0; i < testSprites.length ; i++) {
Sprite spr = new Sprite(regions.random()); //<<<< Main problem
System.out.println(".....................test after sprite...............");
//define sprite size to be 1x1 in game world
spr.setSize(1 , 1);
//set origin to sprite's center
spr.setOrigin(spr.getWidth() / 2.0f, spr.getHeight() / 2.0f);
//calculate random position for sprite
float randomX = MathUtils.random(-2.0f, 2.0f);
float randomY = MathUtils.random(-2.0f, 2.0f);
spr.setPosition(randomX, randomY);
// put new sprite into array
testSprites[i] = spr;
}
//set first sprite as selected one
selectedSprite = 0;
I got it working eventually there was nothing wrong with texture region array there was problem with assets, apparently you cant name them like item_1 because after packing texture packer discards the _1 a the end and you left with just item.

LibGDX - Drawing to a FrameBuffer does not work

So I'm trying to make custom buttons, for which I need to combine different parts of the button background. To do this I figured using a FrameBuffer would work, however it did not give viable results. Therefore I attempted to test my FrameBuffer drawing method, by writing a simple test method, which returns a texture that is drawn to the display at every render() call. This method is here (note that it is a test method, so it may be a little poorly optimized):
private Texture test()
{
BitmapFont f = ReverseBlade.fontTitle;
f.setColor(Color.LIGHT_GRAY);
FrameBuffer fbo = new FrameBuffer(Format.RGBA8888, (int)f.getBounds("Hi").width, (int)f.getBounds("Hi").height, false);
Batch b = ReverseBlade.batch;
OrthographicCamera c = new OrthographicCamera(fbo.getWidth(), fbo.getHeight());
c.setToOrtho(false);
c.update();
b.setProjectionMatrix(c.combined);
fbo.begin();
b.begin();
f.draw(b, "Hi", 0, 0);
b.end();
fbo.end();
Texture t = fbo.getColorBufferTexture();
fbo.dispose();
b.setProjectionMatrix(ReverseBlade.camera.combined);
return t;
}
However, nothing is displayed. The screen is dark... I have tried without the camera and multiple other variations that I can no longer remember. What am I doing wrong?
Half Solution
What I ended up having to do is to make a new Matrix4 object for the FrameBuffer like this:
Matrix4 m = new Matrix4();
m.setToOrtho2D(0, 0, fbo.getWidth(), fbo.getHeight());
batch.setProjectionMatrix(m);
However, this makes everything that is drawn be upside down, like this:
I think the fbo.dispose() call is destroying more than you want.
See the source and notice where it destroys the colorTexture, which is the result of getColorBufferTexture().
I think this could be considered a bug in Libgdx. The color texture is generally something that should have a very different lifetime than the FBO, so cleaning up the texture seems a bit too aggressive. However, trying to figure out which cases to clean the texture up is probably complicated.....
So following what I added with the Half Solution, all I had to do was create a new Sprite object with the texture from the FBo and call flip(false, true)!
May be this is a workaround to dispose() the framebuffer and keeping the texture alive. I do the following:
public class TextureSaveFBO extends FrameBuffer {
static final Texture DUMMY = new Texture(1, 1, Format.RGB565) {
public void dispose() {
};
};
public TextureSaveFBO(Format format, int width, int height,
boolean hasDepth) {
super(format, width, height, hasDepth);
}
#Override
public void dispose() {
// prevents the real texture of dispose()
Texture t = colorTexture;
colorTexture = DUMMY;
super.dispose();
colorTexture = t;
}
}
Just a precisation:
OrthographicCamera c = new OrthographicCamera(fbo.getWidth(), fbo.getHeight());
c.setToOrtho(false);
This is potentially harmful unless you know what you are doing: c.setOrtho(false) does the following:
Sets this camera to an orthographic projection using a viewport fitting the screen resolution, centered at (Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/2), with the y-axis pointing up or down.
So even if you specified in the OrthographicCamera's constructor that you want the viewport to be of the frame buffer size, you are overwriting that with the following call to a viewport covering the screen size and centered to the screen center.
You should probably do:
camera.setToOrtho(false, fbo.getWidth(), fbo.getHeight());
Issue solved since LibGDX 1.6.5.
It's now possible to override disposeColorBuffer method to not dispose rendered texture.

Categories