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);
}
}
Related
I am using anchorpane in my javafx application. I want to draw a rectangle and fill it with gradient color like this: the left side of the rectangle is blue and the right side is red and I want it to seem that from left to right, the blue color decreases and red color increases.
I know how to put a rectangle ( how to use the Rectangle class in javafx ) but I don't know how to fill it this way. Any ideas?
See docs on linear gradients.
public class Gradient extends Application {
public static final double S = 100;
#Override
public void start(Stage stage) {
Stop[] stops = new Stop[] {
new Stop(0, Color.BLUE),
new Stop(1, Color.RED)
};
LinearGradient gradient = new LinearGradient(
0, 0,
1, 0,
true,
CycleMethod.NO_CYCLE,
stops
);
Rectangle rectangle = new Rectangle(S, S, gradient);
stage.setScene(new Scene(new Group(rectangle)));
stage.show();
}
public static void main(String[] args) {
launch();
}
}
I need a function.
import javafx.scene.image.Image;
private Image cropImage(Image image) {
// Cut Image to max Circle or square
return Image;
}
The important thing is that I don't want to change the scale of the image but cut the maximum circle or square in the middle of the image.
I have been trying to do this for two days now.
Can you help me with this?
Thank you very much. Rene
Something like this:
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Image img = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Osias_Beert_the_Elder_-_Dishes_with_Oysters%2C_Fruit%2C_and_Wine_-_Google_Art_Project.jpg/350px-Osias_Beert_the_Elder_-_Dishes_with_Oysters%2C_Fruit%2C_and_Wine_-_Google_Art_Project.jpg");
Image img2 = crop(img, false);
Image img3 = crop(img, true);
HBox box = new HBox(8, new ImageView(img),new ImageView(img2),new ImageView(img3));
Scene scene = new Scene(box);
stage.setScene(scene);
stage.show();
}
private Image crop(Image img, boolean toCircle) {
double d = Math.min(img.getWidth(),img.getHeight());
double x = (d-img.getWidth())/2;
double y = (d-img.getHeight())/2;
Canvas canvas = new Canvas(d, d);
GraphicsContext g = canvas.getGraphicsContext2D();
if (toCircle) {
g.fillOval(0, 0, d, d);
g.setGlobalBlendMode(BlendMode.SRC_ATOP);
}
g.drawImage(img, x, y);
return canvas.snapshot(null, null);
}
}
You can do that via the setClip method of the ImageView.
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);
I have a grid.
Now the background of the grid is lightGray.
But how can I get the background of the grid with random colors? So not one color.
How can I make this?
Can someone help me?
thanks in advance.
the code is:
public class Grid2 extends Application {
double gridSize = 20;
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(new Group(), 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
scene.setFill(createGridPattern());
}
public ImagePattern createGridPattern() {
double w = gridSize;
double h = gridSize;
Canvas canvas = new Canvas(w, h);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setStroke(Color.BLACK);
gc.setFill(Color.LIGHTGRAY.deriveColor(1, 1, 1, 0.2));
gc.fillRect(0, 0, w, h);
gc.strokeRect(0, 0, w, h);
Image image = canvas.snapshot(new SnapshotParameters(), null);
ImagePattern pattern = new ImagePattern(image, 0, 0, w, h, false);
return pattern;
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
You can probably add a new Jpanel() object of each cell of your grid to get a different color. By making different component of group of some specific cells or just even one cell, you can have different background color for the same.
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!