I have a problem with size of ImageButton(s), buttons are not strectched/scaled because screen size of device its 1920x1080 and button picture is 342x129. (it is viewed smaller than original picture seems).
When I draw it through this game.batch.draw(playBtn, ...);
It works fine, but I need ImageButtons. What is wrong ?
Note: WIDTH = 480, HEIGHT = 800
picture: https://imgur.com/IJs4uPB
public MenuScreen(PlumberGame game) {
this.game = game;
camera = new OrthographicCamera();
viewport = new FitViewport(WIDTH,HEIGHT, camera);
stage =new Stage(viewport, spriteBatch);
background = new Texture("background.png");
title = new Texture("title.png");
camera.setToOrtho(false, WIDTH, HEIGHT);
}
#Override
public void show() {
stage = new Stage(); //Set up a stage for the ui
Gdx.input.setInputProcessor(stage); //Start taking input from the ui
atlas = new TextureAtlas("ui/buttons.pack");
skin = new Skin(atlas);
table = new Table(skin);
table.setBounds(0,0, WIDTH, HEIGHT);
ImageButton.ImageButtonStyle playBtnStyle = new ImageButton.ImageButtonStyle();
playBtnStyle.up = skin.getDrawable("playBtn");
playBtnStyle.down = skin.getDrawable("playBtnOn");
playBtn = new ImageButton(playBtnStyle);
playBtn.setSize(playBtn.getWidth(), playBtn.getHeight());
playBtn.getImage().setScaling(Scaling.fit);
playBtn.invalidateHierarchy();
playBtn.setPosition((float)(WIDTH * 0.15 ), (float) (HEIGHT * 0.5));
stage.addActor(playBtn);
}
#Override
public void render(float delta) {
stage.getBatch().setProjectionMatrix(camera.combined);
renderer.setProjectionMatrix(stage.getBatch().getProjectionMatrix());
renderer.setTransformMatrix(stage.getBatch().getTransformMatrix());
renderer.translate(WIDTH, HEIGHT, 0);
stage.act(Gdx.graphics.getDeltaTime()); //Perform ui logic
stage.getBatch().begin();
stage.getBatch().draw(background, 0, 0, WIDTH,HEIGHT);
stage.getBatch().draw(title, (float)(WIDTH * 0.12), (float) (HEIGHT * 0.75));
stage.getBatch().end();
stage.draw(); //Draw the ui
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
table.invalidateHierarchy();
table.setSize(width, height);
camera.update();
}
You are setting playBtn size wrong way.
This line doesn't make sense:
playBtn.setSize(playBtn.getWidth(), playBtn.getHeight());
I would recommend using Table which is really convinient when designing UIs. You specify the size of the actor when adding it to the table.
I can't try it out now since I'm not at home but it would look something like:
#Override
public void show() {
stage = new Stage(); //Set up a stage for the ui
Gdx.input.setInputProcessor(stage); //Start taking input from the ui
atlas = new TextureAtlas("ui/buttons.pack");
skin = new Skin(atlas);
table = new Table(skin);
table.setBounds(0,0, WIDTH, HEIGHT);
ImageButton.ImageButtonStyle playBtnStyle = new ImageButton.ImageButtonStyle();
playBtnStyle.up = skin.getDrawable("playBtn");
playBtnStyle.down = skin.getDrawable("playBtnOn");
playBtn = new ImageButton(playBtnStyle);
playBtn.setPosition((float)(WIDTH * 0.15 ), (float) (HEIGHT * 0.5));
Table table = new Table();
table.setFillParent(true);
table.add(playBtn).expand().fillX().height(yourHeight);
stage.addActor(table);
}
EDIT:
Another alternative is to set min size of the drawables you put in the image button style:
ImageButton.ImageButtonStyle playBtnStyle = new ImageButton.ImageButtonStyle();
playBtnStyle.up = skin.getDrawable("playBtn");
playBtnStyle.down = skin.getDrawable("playBtnOn");
playBtnStyle.up.setMinWidth(yourWidth);
playBtnStyle.up.setMinHeight(yourHeight);
playBtnStyle.down.setMinWidth(yourWidth);
playBtnStyle.down.setMinHeight(yourHeight);
Related
I am trying to pause a progress bar. How would I go about it? I have a listener on a Pause button where I could do the pause but I cannot stop the animation.
Here is the code that currently runs the progress bar.
Pixmap pixmap = new Pixmap(100, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.CYAN);
pixmap.fill();
TextureRegionDrawable drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
ProgressBar.ProgressBarStyle progressBarStyle = new ProgressBar.ProgressBarStyle();
progressBarStyle.background = drawable;
pixmap = new Pixmap(0, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.RED);
pixmap.fill();
drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
progressBarStyle.knob = drawable;
pixmap = new Pixmap(100, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.RED);
pixmap.fill();
drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
progressBarStyle.knobBefore = drawable;
ProgressBar bar = new ProgressBar(0.0f, 1.0f, 0.01f, false, progressBarStyle);
bar.setBounds(100, 1600, 875, 50);
bar.setAnimateDuration(25)
bar.setValue(1f);
stage.addActor(bar);
One option is to bypass the animation completely, update the progress bar from your render callback (by default libgdx combines these callbacks into one), and rely on the pause + resume callbacks at the application-level. Here's an example based on your initial code. Note the bar has no animation and a max value of 25, just to make the progress updates a bit more intuitive.
public class ProgressBarTest implements ApplicationListener {
boolean active;
SpriteBatch batch;
Stage stage;
int width = 380;
int height = 180;
ProgressBar bar;
#Override
public void create () {
active = true;
batch = new SpriteBatch();
stage = new Stage(new FitViewport(width, height));
Pixmap pixmap = new Pixmap(100, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.CYAN);
pixmap.fill();
TextureRegionDrawable drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
ProgressBar.ProgressBarStyle progressBarStyle = new ProgressBar.ProgressBarStyle();
progressBarStyle.background = drawable;
pixmap = new Pixmap(0, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.RED);
pixmap.fill();
drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
progressBarStyle.knob = drawable;
pixmap = new Pixmap(100, 50, Pixmap.Format.RGBA8888);
pixmap.setColor(Color.RED);
pixmap.fill();
drawable = new TextureRegionDrawable(new TextureRegion(new Texture(pixmap)));
pixmap.dispose();
progressBarStyle.knobBefore = drawable;
bar = new ProgressBar(0f, 25f, 0.01f, false, progressBarStyle);
bar.setBounds(100, 1600, 875, 50);
stage.setRoot(new Table() {
{
add(bar);
setFillParent(true);
setSize(width, height);
}
});
}
#Override
public void resize(int x, int y) {
}
#Override
public void render () {
if (active && bar.getValue() < bar.getMaxValue()){
bar.setValue(bar.getValue() + Gdx.graphics.getDeltaTime());
if (bar.getValue() >= bar.getMaxValue()){
System.out.println("Filled up!");
}
}
stage.act();
ScreenUtils.clear(0, 0, 0, 1);
batch.begin();
stage.draw();
batch.end();
}
#Override
public void pause() {
System.out.println("Paused!");
active = false;
}
#Override
public void resume() {
System.out.println("Resumed!");
active = true;
}
#Override
public void dispose () {
batch.dispose();
stage.dispose();
}
}
Maybe something like this works for your use case?
EDIT: Also, if you want to keep the animation, you can probably get away with conditionally calling stage.act() when active is true.
I am creating a basic space real time strategy game that needs a very large map, but will only show a small part of the map at a time. I have found that this is very easy to do with a viewport using imageview. Unfortunately, imageview needs an image to be passed to it and I have been using a canvas to draw all the sprites onto the background. I have been using snapshot to take an image of the fully rendered scene and passing that to imageview so I can use a viewport, but that has been causing a severe drop in performance. Without the use of snapshot, the game runs at 60+ fps, however when using snapshot the fps is always 15 or lower. Using snapshot was not a problem when the scene was 1024x512, but the scene is now 4096x2048. Is there a better way to use imageview and viewport 60 times a second without using snapshot? Example code of what I'm doing below:
Group trueRoot = new Group(); //shown image
Group root = new Group(); //invisible unedited image
Canvas canvas = new Canvas(4096, 2048); //unedited canvas
root.getChildren().add(canvas);
GraphicsContext gc = canvas.getGraphicsContext2D();
Scene scene2 = new Scene(trueRoot);
primaryStage.setScene(scene2);
WritableImage sceneImage = root.snapshot(new SnapshotParameters(), null); //snapshot of fully rendered root scene
ImageView imageView = new ImageView(sceneImage); //imageView can be used to resize or crop
Rectangle2D viewportRect = new Rectangle2D(0, 0, 1024, 512); //for cropping
imageView.setViewport(viewportRect); //for cropping
trueRoot.getChildren().add(imageView); //also for resizing / cropping
new AnimationTimer() {
public void handle(long currentNanoTime) {
//simple animated background
if (cloudTimer == 16384)
cloudTimer = 0;
else
cloudTimer++;
gc.drawImage(stars, 0, 0);
gc.drawImage(clouds1, cloudTimer % 16384, 0);
gc.drawImage(clouds1, (cloudTimer % 16384) - 8192, 0);
gc.drawImage(clouds2, (cloudTimer / 2) % 16384, 0);
gc.drawImage(clouds2, ((cloudTimer / 2) % 16384) - 8192, 0);
p1.update(p2, root);
p2.update(p1, root);
p1.render(gc, root);
p2.render(gc, root);
root.snapshot(new SnapshotParameters(), sceneImage); //takes root scene and hands it to trueRoot
}
}.start();
primaryStage.show();
full code at https://github.com/WiredOverload/ObliterateEverything
I know that I could use asynchronous snapshot to avoid delaying the rest of the code while the snapshot is taking place, but that would still leave the game at 15 fps. Any suggestions would be appreciated.
Just write your own viewport property with a listener that adjusts the GraphicsContext's transform to apply a transform & scale that fits the viewport to the Canvas's size:
#Override
public void start(Stage primaryStage) {
Canvas canvas = new Canvas();
GraphicsContext context = canvas.getGraphicsContext2D();
ObjectProperty<Rectangle2D> viewport = new SimpleObjectProperty<>(Rectangle2D.EMPTY);
Pane root = new Pane(canvas);
root.setPrefSize(400, 400);
canvas.setManaged(false);
root.layoutBoundsProperty().addListener((observable, oldBound, newBounds) -> {
canvas.setWidth(newBounds.getWidth());
canvas.setHeight(newBounds.getHeight());
Rectangle2D vp = viewport.get();
viewport.set(new Rectangle2D(vp.getMinX(), vp.getMinY(), newBounds.getWidth(), newBounds.getHeight()));
});
viewport.addListener((observable, oldViewport, newViewport) -> {
// combined transform & scale transfromation
context.setTransform(canvas.getWidth() / newViewport.getWidth(), 0,
0, canvas.getHeight() / newViewport.getHeight(),
-newViewport.getMinX(), -newViewport.getMinY());
redraw(context);
});
// sample movement animation
Transition scale = new Transition() {
{
setCycleDuration(Duration.seconds(2));
setCycleCount(2);
setAutoReverse(true);
}
#Override
protected void interpolate(double frac) {
double scale = 1 + 2 * frac;
Rectangle2D vp = viewport.get();
viewport.set(new Rectangle2D(vp.getMinX(), vp.getMinY(), canvas.getWidth() / scale, canvas.getHeight() / scale));
}
};
Transition move = new Transition() {
{
setCycleDuration(Duration.seconds(2));
setCycleCount(2);
setAutoReverse(true);
}
#Override
protected void interpolate(double frac) {
double posX = 300 * frac;
Rectangle2D vp = viewport.get();
viewport.set(new Rectangle2D(posX, 0, canvas.getWidth(), canvas.getHeight()));
}
};
SequentialTransition animation = new SequentialTransition(scale, move);
animation.setCycleCount(Animation.INDEFINITE);
animation.play();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
private static Color[] colors = new Color[]{
Color.RED,
Color.BEIGE,
Color.GREEN,
Color.ORANGE,
Color.BLACK,
Color.BLUE,
Color.WHEAT,
Color.CORAL,
Color.CRIMSON,
Color.PURPLE,
Color.AQUAMARINE,
Color.YELLOW,
Color.CHOCOLATE
};
public static void redraw(GraphicsContext gc) {
Bounds bounds;
try {
bounds = gc.getTransform().inverseTransform(gc.getCanvas().getLayoutBounds());
} catch (NonInvertibleTransformException ex) {
throw new IllegalStateException();
}
gc.clearRect(bounds.getMinX(), bounds.getMinY(), bounds.getWidth(), bounds.getHeight());
for (int i = 0; i < colors.length; i++) {
gc.setFill(colors[i]);
gc.fillRect(i * 50, 0, 50, 50);
}
}
I am trying to create a stage where i am going to draw Textbuttons on.
I can launch the program fine without any errors. But there is no textbuttons that appear.
The issue might be that i cannot set the size of the stage i am using.
Here is the code for the initializing:
#Override
public void resize(int width, int height)
{
if(stage == null)
stage = new Stage();
stage.clear();
Gdx.input.setInputProcessor(stage);
TextButtonStyle style = new TextButtonStyle();
style.up = skin.getDrawable("Button");
style.down = skin.getDrawable("Button");
style.font = buttonFont;
startGameBtn = new TextButton("Start Game", style);
startGameBtn.setWidth(300);
startGameBtn.setHeight(150);
startGameBtn.setX(Gdx.graphics.getWidth() / 2 - startGameBtn.getWidth() / 2);
startGameBtn.setY((float) (Gdx.graphics.getHeight() / 1.5));
startGameBtn.addListener(new InputListener()
{
});
stage.addActor(startGameBtn);
}
And here is where i draw the button:
public void render(float delta)
{
Gdx.gl20.glClearColor(0, 0, 0, 1);
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(delta);
sb.begin();
stage.draw();
sb.end();
}
Thank you for any help! :)
Solved problem!
I accidently had a black background and a black button so it was actually drawing it, i just couldn't see it.
Cheers!
Table table;
Stage stage;
Skin skin;
TextureAtlas atlas;
private BitmapFont black;
OrthographicCamera camera;
private static final int VIRTUAL_WIDTH = 1280;
private static final int VIRTUAL_HEIGHT = 720;
private static final float ASPECT_RATIO = (float)VIRTUAL_WIDTH/(float)VIRTUAL_HEIGHT;
private Rectangle viewport;
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1,1,1,1);
camera.update();
try {
camera.apply(Gdx.gl10);
} catch(Exception e) {
}
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
// clear previous frame
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
Table.drawDebug(stage);
stage.draw();
}
#Override
public void resize(int width, int height) {
// calculate new viewport
float aspectRatio = (float)width/(float)height;
float scale = 1f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ASPECT_RATIO)
{
scale = (float)height/(float)VIRTUAL_HEIGHT;
crop.x = (width - VIRTUAL_WIDTH*scale)/2f;
}
else if(aspectRatio < ASPECT_RATIO)
{
scale = (float)width/(float)VIRTUAL_WIDTH;
crop.y = (height - VIRTUAL_HEIGHT*scale)/2f;
}
else
{
scale = (float)width/(float)VIRTUAL_WIDTH;
}
float w = (float)VIRTUAL_WIDTH*scale;
float h = (float)VIRTUAL_HEIGHT*scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
}
#Override
public void show() {
stage = new Stage();
camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
black = new BitmapFont(Gdx.files.internal("data/font.fnt"), false);
atlas = new TextureAtlas("data/button.pack");
skin = new Skin(atlas);
table = new Table(skin);
table.setPosition(50, Gdx.graphics.getHeight()-(Gdx.graphics.getHeight()-50));
table.setSize(Gdx.graphics.getWidth()-100, Gdx.graphics.getHeight()-100);
LabelStyle style = new LabelStyle();
style.font = black;
table.align(Align.top);
Label label = new Label("ORTHO", style);
label.setWrap(true);
label.setAlignment(Align.left);
Label label1 = new Label("A", style);
label1.setWrap(true);
label1.setAlignment(Align.center);
Label label2 = new Label("B", style);
label2.setWrap(true);
label2.setAlignment(Align.center);
Label label3 = new Label("C", style);
label3.setWrap(true);
label3.setAlignment(Align.center);
table.add(label).width(Gdx.graphics.getWidth()-100).colspan(3).align(Align.left).padBottom(50);
table.row();
table.add(label1).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label2).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label3).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.row();
table.add(label1).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label2).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label3).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.row();
table.add(label1).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label2).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.add(label3).width((Gdx.graphics.getWidth()-100)/3).height((Gdx.graphics.getWidth()-100)/3);
table.debug();
stage.addActor(table);
TextButtonStyle textButtonStyle = new TextButtonStyle();
textButtonStyle.up = skin.getDrawable("button.up");
textButtonStyle.down = skin.getDrawable("button.down");
textButtonStyle.pressedOffsetX = 1;
textButtonStyle.pressedOffsetY = -1;
textButtonStyle.font = black;
textButtonStyle.font.setScale(3);
TextButton button = new TextButton("game", textButtonStyle);
button.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
System.out.println("hello");
return false;
}
});
}
i'm expecting the table i'm creating with this code to fit the screen, and it does perfectly on my Galaxy S3, i've added the
private Rectangle viewport;
....
....
Gdx.gl.glClearColor(1,1,1,1);
camera.update();
try {
camera.apply(Gdx.gl10);
} catch(Exception e) {
}
....
....
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
....
....
float aspectRatio = (float)width/(float)height;
float scale = 1f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ASPECT_RATIO)
{
scale = (float)height/(float)VIRTUAL_HEIGHT;
crop.x = (width - VIRTUAL_WIDTH*scale)/2f;
}
else if(aspectRatio < ASPECT_RATIO)
{
scale = (float)width/(float)VIRTUAL_WIDTH;
crop.y = (height - VIRTUAL_HEIGHT*scale)/2f;
}
else
{
scale = (float)width/(float)VIRTUAL_WIDTH;
}
float w = (float)VIRTUAL_WIDTH*scale;
float h = (float)VIRTUAL_HEIGHT*scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
....
....
camera = new OrthographicCamera(VIRTUAL_WIDTH, VIRTUAL_HEIGHT);
to scale it properly to other devices like a 480-320 emulator i have running, but it doesn't at all, it scales it fine, but puts the table way lower then where it should be...
I'm writing a new program in LibGDX handling Backgroundtextures and have just begun to implement the screens. But when I test it, it just shows me a black cleared screen with the given resolution.
I call the screen with the setScreen(screen)- Method in an implemented Game class.
So here is the code:
public class MenuScreen implements Screen{
Table table;
Stage stage;
TextButton button;
TextField textField;
Texture texture;
Pic2Box2d game;
public MenuScreen(Pic2Box2d gameH)
{
this.game = gameH;
stage = new Stage(0, 0, true);
table = new Table();
}
public void create()
{
final TextFieldStyle fieldStyle = new TextFieldStyle(new BitmapFont(), Color.WHITE, new BitmapFont(), Color.GRAY, null, null, null);
textField = new TextField("path", fieldStyle);
final TextButtonStyle buttonStyle = new TextButtonStyle();
buttonStyle.font = new BitmapFont();
buttonStyle.fontColor = Color.WHITE;
buttonStyle.pressedOffsetY = 1f;
buttonStyle.downFontColor = new Color(0.8f, 0.8f, 0.8f, 1f);
button = new TextButton("Übernehmen", buttonStyle);
button.setClickListener(new ClickListener() {
#Override
public void click(Actor actor, float x, float y) {
if(textField.getText() != "" && textField.getText() != "path")
{
texture = new Texture(textField.getText());
game.setScreen(new Workscreen(texture));
}
}
});
table.row();
table.add(textField);
table.add(button);
stage.addActor(table);
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(0, 0, 0, 1);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
}
#idaNakav is correct about using show() and setting the Stage dimensions. However, that will still give you a black screen because your table doesn't have any size. Try changing your show() code to include table.setFillParent(true).
It should look something like this (code based on the example that you provided).
#Override
public void show() {
final TextFieldStyle fieldStyle = new TextFieldStyle(new BitmapFont(), Color.WHITE, null, null, null);
textField = new TextField("path", fieldStyle);
final TextButtonStyle buttonStyle = new TextButtonStyle();
buttonStyle.font = new BitmapFont();
buttonStyle.fontColor = Color.WHITE;
buttonStyle.pressedOffsetY = 1f;
buttonStyle.downFontColor = new Color(0.8f, 0.8f, 0.8f, 1f);
button = new TextButton("Übernehmen", buttonStyle);
button.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
if (textField.getText() != "" && textField.getText() != "path") {
texture = new Texture(textField.getText());
game.setScreen(new Workscreen(texture));
}
}
});
table.row();
table.add(textField);
table.add(button);
// Make the table fill the stage.
table.setFillParent(true);
stage.addActor(table);
}
When screen is becoming the current screen show method is called (not create like in your case), try changing your create method to show
also you are init the stage wrong , you should send it width and height , you sent 0,0
try something like
stage = new Stage(Gdx.graphics.getWidth(),Gdx.graphics.getHeight(),false);