In many android applications I faced the "Sticky Header For List" title and we love it. https://github.com/search?q=header+sticky+android, Now, I am trying to do it in libGDX framework using Scrollpane and another scene2dui actors.
I achieve that about 60% or more because I think its very simple to do it, But I am missing something to complete it, So, I need some help!
The full example when amountY of scrollpane1 (parent) arrive into specific height, so, please stop scrolling for your child :
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Align;
public class MyGdxGame extends ApplicationAdapter {
private Stage stage;
private ScrollPane scrollPane1;
private ScrollPane scrollPane2;
#Override
public void create() {
stage = new Stage();
Table baseContainer = new Table();
Table container = new Table();
scrollPane1 = new ScrollPane(container);
Table list = new Table();
scrollPane2 = new ScrollPane(list);
Table mainHeader = new Table();
mainHeader.background(new TextureRegionDrawable(this.getPixel()).tint(Color.GRAY));
Table stickyHeader = new Table();
container.add(mainHeader).height(100).growX();
container.row();
container.add(stickyHeader).height(200).growX();
container.row();
container.add(scrollPane2).grow();
baseContainer.setFillParent(true);
baseContainer.add(scrollPane1).grow();
stage.addActor(baseContainer);
Gdx.input.setInputProcessor(stage);
this.fillDummyDataInHeader(stickyHeader);
this.fillDummyDataInList(list);
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height);
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
// to fix sticky header
if (scrollPane1.getScrollY() >= 100) {
// stop scrolling in scrollpane 1
scrollPane1.setScrollingDisabled(true, true);
scrollPane1.cancel();
// run scrollpane 2
scrollPane2.setScrollingDisabled(true, false);
}
}
#Override
public void dispose() {
stage.dispose();
}
private void fillDummyDataInHeader(Table stickyHeader) {
stickyHeader.background(new TextureRegionDrawable(this.getPixel()).tint(new Color(0.1960F, 0.3921F, 0.7843F, 0.8F)));
Image ic = new Image(new Texture("badlogic.jpg"));
Label label = new Label("Hi, I am sticky header!", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
label.setAlignment(Align.center);
stickyHeader.add(ic).pad(10).center().size(50);
stickyHeader.row();
stickyHeader.add(label).pad(10).center().growX();
}
private void fillDummyDataInList(Table list) {
for (int i = 0; i < 10; i++) {
Table row = new Table().background(new TextureRegionDrawable(this.getPixel()).tint(new Color(1F, 0.63921F, 0, 0.6F)));
Image ic = new Image(new Texture("badlogic.jpg"));
Label label = new Label("Row Item [" + (i + 1) + "]", new Label.LabelStyle(new BitmapFont(), Color.DARK_GRAY));
row.add(ic).pad(20).size(40);
row.add(label).height(100).growX();
list.add(row).pad(20).padTop(0).growX();
list.row();
}
}
private Texture getPixel() {
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(1F, 1F, 1F, 1F);
pixmap.fill();
Texture texture = new Texture(pixmap);
pixmap.dispose();
return texture;
}
}
Using Stack is the solution as a #second mention as the following code:
package com.mygdx.game;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Cell;
import com.badlogic.gdx.scenes.scene2d.ui.Container;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Stack;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Align;
public class MyGdxGame extends ApplicationAdapter {
private Stage stage;
private ScrollPane scrollPane1;
private Stack stack;
private Table stickyHeader;
private Container<Table> tableContainer;
private Cell<Container<Table>> oldCell;
#Override
public void create() {
stage = new Stage();
Table baseContainer = new Table();
Table container = new Table();
scrollPane1 = new ScrollPane(container) {
#Override
protected void scrollY(float pixelsY) {
super.scrollY(pixelsY);
if (pixelsY >= 100F) {
stack.add(tableContainer);
} else {
oldCell.setActor(tableContainer);
}
}
};
Table list = new Table();
Table mainHeader = new Table();
mainHeader.background(new TextureRegionDrawable(this.getPixel()).tint(Color.GRAY));
stickyHeader = new Table();
tableContainer = new Container<Table>(stickyHeader);
container.add(mainHeader).height(100).growX();
container.row();
oldCell = container.add(tableContainer.fill().top().height(100)).height(100).growX();
container.row();
container.add(list).grow();
baseContainer.setFillParent(true);
stack = new Stack();
stack.setFillParent(true);
stack.add(scrollPane1);
stage.addActor(stack);
Gdx.input.setInputProcessor(stage);
this.fillDummyDataInHeader(stickyHeader);
this.fillDummyDataInList(list);
}
#Override
public void resize(int width, int height) {
stage.getViewport().update(width, height);
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
stage.draw();
}
#Override
public void dispose() {
stage.dispose();
}
private void fillDummyDataInHeader(Table stickyHeader) {
stickyHeader.background(new TextureRegionDrawable(this.getPixel()).tint(new Color(0.1960F, 0.3921F, 0.7843F, 0.8F)));
Image ic = new Image(new Texture("badlogic.jpg"));
Label label = new Label("Hi, I am sticky header!", new Label.LabelStyle(new BitmapFont(), Color.WHITE));
label.setAlignment(Align.left);
stickyHeader.add(ic).pad(10).left().size(50);
stickyHeader.add(label).pad(10).left().growX();
}
private void fillDummyDataInList(Table list) {
for (int i = 0; i < 10; i++) {
Table row = new Table().background(new TextureRegionDrawable(this.getPixel()).tint(new Color(1F, 0.63921F, 0, 0.6F)));
Image ic = new Image(new Texture("badlogic.jpg"));
Label label = new Label("Row Item [" + (i + 1) + "]", new Label.LabelStyle(new BitmapFont(), Color.DARK_GRAY));
row.add(ic).pad(20).size(40);
row.add(label).height(100).growX();
list.add(row).pad(20).padTop(0).growX();
list.row();
}
}
private Texture getPixel() {
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
pixmap.setColor(1F, 1F, 1F, 1F);
pixmap.fill();
Texture texture = new Texture(pixmap);
pixmap.dispose();
return texture;
}
}
Related
So I'm trying to create a very basic photo editor program in Java, using JavaFX. I got a brush and eraser working pretty well so far the following way:
package application;
import java.io.File;
import javax.imageio.ImageIO;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.fxml.FXML;
import javafx.geometry.Point2D;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
public class EditorController {
private boolean eraser = false;
#FXML
private Canvas canvas;
#FXML
private ColorPicker colorPicker;
#FXML
private TextField brushSize;
#FXML
private TextField selectedTool;
private Point2D last = null;
public void initialize() {
GraphicsContext gc = canvas.getGraphicsContext2D();
canvas.setOnMouseReleased(e -> {last = null;});
canvas.setOnMouseClicked(e -> {
if (!eraser) {
double size = Double.parseDouble(brushSize.getText());
float mouseX = (float) e.getX();
float mouseY = (float) e.getY();
gc.fillOval(mouseX-(size/2), mouseY-(size/2), size, size);
}
});
canvas.setOnMouseDragged(e -> {
System.out.println(eraser);
double size = Double.parseDouble(brushSize.getText());
gc.setLineCap(StrokeLineCap.ROUND);
gc.setLineWidth(size);
float mouseX = (float) e.getX();
float mouseY = (float) e.getY();
if (last != null && !eraser) {
gc.strokeLine(last.getX(), last.getY(), mouseX, mouseY);
} else if (eraser) {
gc.clearRect(mouseX, mouseY, size, size);
}
last = new Point2D(mouseX, mouseY);
});
}
public void onSave() {
try {
Image snapshot = canvas.snapshot(null, null);
ImageIO.write(SwingFXUtils.fromFXImage(snapshot, null), "png", new File("paint.png"));
} catch (Exception e) {
System.out.println("Failed to save image: " + e);
}
}
public void onLoad() {
// not implemented yet
}
// not implemented yet
public void onUndo() { }
public void onRedo() { }
public void onSmaller() { }
public void onBigger() { }
public void onResetView() { }
public void onFitView() { }
public void onFillView() { }
public void onNewLayer() { }
public void onDeleteLayer() { }
public void onDuplicateLayer() { }
public void onGroupLayers() { }
public void onMergeLayers() { }
public void onAddMask() { }
public void onBrush() { eraser = false; selectedTool.setText("Brush"); }
public void onEraser() { eraser = true; selectedTool.setText("Eraser"); }
public void onExit() {
Platform.exit();
}
}
Now I want to have a feather/hardness value for the brush (like in photoshop) where I can draw a softer-looking line, but I'm not sure how to achieve it with JavaFX? Are there any tools within it for things like this?
So with a visual example: the brush on the left would be a feathered brush, the one on the right isn't (and that's what I have currently)
Simple drawing app.
It uses a radial gradient rendered to an image that is drawn on the canvas, but you could just draw the gradient directly onto the canvas. A gradient is a paint so you can set it directly as an argument to setFill on a graphics context.
The solution in my example probably won't exactly give you the solution you are looking for, but perhaps you could tweak it for what you need.
It was a quick app I put together for demo purposes, it could be structured better if a more functional drawing app was required.
The code which creates the "feathered brush" is based on a gradient:
private RadialGradient createSoftBrushGradient(Color primaryColor) {
return new RadialGradient(
0, 0,
.5, .5,
.5,
true,
CycleMethod.NO_CYCLE,
new Stop(0, primaryColor),
new Stop(1, Color.TRANSPARENT)
);
}
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.*;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class SprayPaint extends Application {
private final IntegerProperty brushDiameter = new SimpleIntegerProperty();
private final ObjectProperty<Image> brushImage = new SimpleObjectProperty<>();
private final ToggleGroup brushHardnessSelection = new ToggleGroup();
private final RadioButton hardBrushSelection = new RadioButton();
private final RadioButton softBrushSelection = new RadioButton();
private final Circle hardBrush = new Circle();
private final Circle softBrush = new Circle();
private final SnapshotParameters snapshotParams = new SnapshotParameters();
private final Canvas canvas = new Canvas(600, 450);
#Override
public void start(Stage stage) {
snapshotParams.setFill(Color.TRANSPARENT);
Pane controls = createControls();
StackPane canvasHolder = new StackPane(canvas);
canvasHolder.setStyle("-fx-border-color: gray;");
canvasHolder.setMaxSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
VBox layout = new VBox(
10,
controls,
canvasHolder
);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
enableDrawing(canvas);
}
private void enableDrawing(Canvas canvas) {
EventHandler<MouseEvent> drawHandler = event -> {
Image brush = snapshotBrushImage();
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.drawImage(
brush,
event.getX() - brushDiameter.doubleValue() / 2,
event.getY() - brushDiameter.doubleValue() / 2
);
};
canvas.setOnMousePressed(drawHandler);
canvas.setOnMouseDragged(drawHandler);
}
private Image snapshotBrushImage() {
if (brushImage.get() == null) {
if (hardBrushSelection == brushHardnessSelection.getSelectedToggle()) {
brushImage.set(snapshot(hardBrush));
} else { // soft brush selected
brushImage.set(snapshot(softBrush));
}
}
return brushImage.get();
}
private Image snapshot(Circle brushNode) {
return brushNode.snapshot(snapshotParams, null);
}
private Pane createControls() {
hardBrush.radiusProperty().bind(
brushDiameter.divide(2.0)
);
softBrush.radiusProperty().bind(
brushDiameter.divide(2.0)
);
hardBrushSelection.getStyleClass().addAll("toggle-button", "left-pill");
hardBrushSelection.getStyleClass().remove( "radio-button");
StackPane hardBrushGraphic = new StackPane(hardBrush);
hardBrushGraphic.setMinSize(40, 40);
hardBrushSelection.setGraphic(hardBrushGraphic);
hardBrushSelection.setToggleGroup(brushHardnessSelection);
softBrushSelection.getStyleClass().addAll( "toggle-button", "right-pill");
softBrushSelection.getStyleClass().remove( "radio-button");
StackPane softBrushGraphic = new StackPane(softBrush);
softBrushGraphic.setMinSize(40, 40);
softBrushSelection.setGraphic(softBrushGraphic);
softBrushSelection.setToggleGroup(brushHardnessSelection);
hardBrushSelection.setSelected(true);
HBox brushSelectionPanel = new HBox(hardBrushSelection, softBrushSelection);
Slider brushDiameterSlider = new Slider(8, 40, 20);
brushDiameterSlider.setMajorTickUnit(4);
brushDiameterSlider.setMinorTickCount(0);
brushDiameterSlider.setShowTickMarks(true);
brushDiameter.setValue((int) Math.round(brushDiameterSlider.getValue()));
brushDiameterSlider.valueProperty().addListener((observable, oldValue, newValue) ->
brushDiameter.setValue((int) Math.round(newValue.doubleValue()))
);
Label diameterLabel = new Label();
diameterLabel.textProperty().bind(
brushDiameter.asString()
);
ColorPicker colorPicker = new ColorPicker();
hardBrush.fillProperty().bind(
colorPicker.valueProperty()
);
colorPicker.valueProperty().addListener((observable, oldColor, newColor) ->
softBrush.setFill(
createSoftBrushGradient(newColor)
)
);
colorPicker.setValue(Color.NAVY);
brushDiameter.addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
colorPicker.valueProperty().addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
brushHardnessSelection.selectedToggleProperty().addListener((observable, oldValue, newValue) ->
brushImage.set(null)
);
Button clear = new Button("Clear");
clear.setOnAction(e ->
canvas.getGraphicsContext2D().clearRect(
0, 0, canvas.getWidth(), canvas.getHeight()
)
);
HBox controlPanel = new HBox(
10,
colorPicker,
brushSelectionPanel,
new Label("Diameter: "),
brushDiameterSlider,
diameterLabel,
clear
);
controlPanel.setMinWidth(450);
controlPanel.setMinHeight(Pane.USE_PREF_SIZE);
return controlPanel;
}
private RadialGradient createSoftBrushGradient(Color primaryColor) {
return new RadialGradient(
0, 0,
.5, .5,
.5,
true,
CycleMethod.NO_CYCLE,
new Stop(0, primaryColor),
new Stop(1, Color.TRANSPARENT)
);
}
}
I'd like to implement the reveal highlight effect for JavaFX that can be seen in various parts of Windows 10, particularly the Settings and Calculator apps.
The effect seems to be composed for two parts, a border highlight (seen here) and a background highlight (seen here, though admittedly looks better in person due to compression).
My first instinct was to see if this could be done in some sort of pixel shader but after googling around for that it seems JavaFX does provide a public API for anything like that?
Would it be possible to create this effect without resorting to a canvas and drawing the whole UI by hand?
First I'd like to say I have no idea how Windows implements that style. But one idea I had is to have multiple layers:
A black background.
A circle with a radial gradient going from white to transparent that moves with the mouse.
A region with a black background and a shape that has holes wherever the option nodes are.
The option nodes with a layered background.
When the mouse is not hovering:
Transparent background with no insets.
Black background with a slight inset.
When the mouse is hovering:
Low opacity white background with no insets.
Black background with a slight inset.
White-to-transparent radial gradient background that's centered on the mouse.
Unfortunately that means a lot of the styling has to be done in the code even though I'd prefer to put most of it in CSS. Here's a proof-of-concept I quickly mocked up. It's not fully functional but shows the look you want is possible.
OptionsPane.java
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.TilePane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.RadialGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Font;
public class OptionsPane extends Region {
public static class Option {
private final String title;
private final String subtitle;
private final Node graphic;
public Option(String title, String subtitle, Node graphic) {
this.title = title;
this.subtitle = subtitle;
this.graphic = graphic;
}
public String getTitle() {
return title;
}
public String getSubtitle() {
return subtitle;
}
public Node getGraphic() {
return graphic;
}
}
private final ObservableList<Option> options = FXCollections.observableArrayList();
private final TilePane topTiles = new TilePane();
private final Region midCover = new Region();
private final Circle underGlow = new Circle();
public OptionsPane() {
setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
underGlow.setManaged(false);
underGlow.setRadius(75);
underGlow.visibleProperty().bind(hoverProperty());
underGlow.setFill(
new RadialGradient(
0, 0,
0.5, 0.5,
1.0,
true,
null,
new Stop(0.0, Color.WHITE),
new Stop(0.35, Color.TRANSPARENT)));
addEventFilter(
MouseEvent.MOUSE_MOVED,
e -> {
underGlow.setCenterX(e.getX());
underGlow.setCenterY(e.getY());
});
midCover.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
topTiles.setMinSize(0, 0);
topTiles.setVgap(20);
topTiles.setHgap(20);
topTiles.setPadding(new Insets(20));
topTiles.setPrefTileWidth(250);
topTiles.setPrefTileHeight(100);
topTiles.setPrefColumns(3);
options.addListener(
(InvalidationListener)
obs -> {
topTiles.getChildren().clear();
options.forEach(opt -> topTiles.getChildren().add(createOptionRegion(opt)));
});
getChildren().addAll(underGlow, midCover, topTiles);
}
public final ObservableList<Option> getOptions() {
return options;
}
#Override
protected void layoutChildren() {
double x = getInsets().getLeft();
double y = getInsets().getTop();
double w = getWidth() - getInsets().getRight() - x;
double h = getHeight() - getInsets().getBottom() - y;
layoutInArea(midCover, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
layoutInArea(topTiles, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
Shape coverShape = new Rectangle(x, y, w, h);
for (Node optionNode : topTiles.getChildren()) {
Bounds b = optionNode.getBoundsInParent();
Rectangle rect = new Rectangle(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
coverShape = Shape.subtract(coverShape, rect);
}
midCover.setShape(coverShape);
}
private Region createOptionRegion(Option option) {
Label titleLabel = new Label(option.getTitle());
titleLabel.setTextFill(Color.WHITE);
titleLabel.setFont(Font.font("System", 13));
Label subtitleLabel = new Label(option.getSubtitle());
subtitleLabel.setTextFill(Color.DARKGRAY);
subtitleLabel.setFont(Font.font("System", 10));
VBox textBox = new VBox(5, titleLabel, subtitleLabel);
HBox.setHgrow(textBox, Priority.ALWAYS);
HBox container = new HBox(10, textBox);
container.setPadding(new Insets(10));
if (option.getGraphic() != null) {
container.getChildren().add(0, option.getGraphic());
}
setNonHoverBackground(container);
container
.hoverProperty()
.addListener(
(obs, ov, nv) -> {
if (!nv) {
setNonHoverBackground(container);
}
});
container.setOnMouseMoved(e -> setHoverBackground(container, e.getX(), e.getY()));
return container;
}
private void setNonHoverBackground(Region region) {
BackgroundFill fill1 = new BackgroundFill(Color.TRANSPARENT, null, null);
BackgroundFill fill2 = new BackgroundFill(Color.BLACK, null, new Insets(2));
region.setBackground(new Background(fill1, fill2));
}
private void setHoverBackground(Region region, double x, double y) {
RadialGradient gradient =
new RadialGradient(
0, 0,
x, y,
400,
false,
null,
new Stop(0.0, new Color(1, 1, 1, 0.2)),
new Stop(0.35, Color.TRANSPARENT));
BackgroundFill fill1 = new BackgroundFill(new Color(1, 1, 1, 0.3), null, null);
BackgroundFill fill2 = new BackgroundFill(Color.BLACK, null, new Insets(2));
BackgroundFill fill3 = new BackgroundFill(gradient, null, null);
region.setBackground(new Background(fill1, fill2, fill3));
}
}
Main.java
import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
OptionsPane pane = new OptionsPane();
List<OptionsPane.Option> options = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Rectangle graphic = new Rectangle(20, 20, Color.DARKGRAY);
options.add(
new OptionsPane.Option("Option Title #" + (i + 1), "Description #" + (i + 1), graphic));
}
pane.getOptions().addAll(options);
primaryStage.setScene(new Scene(pane));
primaryStage.show();
}
}
And this is what it looks like:
It's not exactly the same but you can experiment yourself and change things as you want.
I'm pulling out my hair trying to figure out why this isn't working.
I'm just trying to put together a basic Options menu and for some reason the Stage doesn't seem to be drawing the actors.
I've tried putting the same actor creation code into another project with a working stage and it draws fine, and I've gone over both this and the working project with a fine tooth comb looking for anything I'm missing and as far as I can tell everything stage-related in the working code is in this one too, yet all I get with this OptionsScreen.java is a blank black screen.
Here's the java file in question, OptionsScreen.java
package com.kittykazoo.screens;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer.ShapeType;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox.CheckBoxStyle;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.utils.viewport.StretchViewport;
import com.kittykazoo.gamehelpers.ScreenHandler;
public class OptionsScreen implements Screen {
private ScreenHandler sh;
private Stage stage;
private Skin skin;
private OrthographicCamera cam;
private ShapeRenderer shapeRenderer;
private SpriteBatch batch;
private Label sfxVolValue;
private Label musicVolValue;
public OptionsScreen(ScreenHandler sh) {
Gdx.app.log("OptionsScreen", "Attached");
this.sh = sh;
cam = new OrthographicCamera();
cam.setToOrtho(true, 960, 600);
shapeRenderer = new ShapeRenderer();
shapeRenderer.setProjectionMatrix(cam.combined);
batch = new SpriteBatch();
batch.setProjectionMatrix(cam.combined);
stage = new Stage();
Gdx.input.setInputProcessor(stage);
createOptions();
}
private void createOptions() {
skin = new Skin();
Pixmap pixmap = new Pixmap(100, 100, Format.RGBA8888);
pixmap.setColor(Color.GREEN);
pixmap.fill();
skin.add("white", new Texture(pixmap));
BitmapFont bfont = new BitmapFont();
bfont.scale(1);
skin.add("default", bfont);
CheckBoxStyle checkBoxStyle = new CheckBoxStyle();
checkBoxStyle.checkboxOff = skin.newDrawable("white", Color.WHITE);
checkBoxStyle.checkboxOffDisabled = skin.newDrawable("white",
Color.DARK_GRAY);
checkBoxStyle.checkboxOn = skin.newDrawable("white", Color.WHITE);
checkBoxStyle.checkboxOnDisabled = skin.newDrawable("white",
Color.DARK_GRAY);
checkBoxStyle.checked = skin.newDrawable("white", Color.WHITE);
checkBoxStyle.font = skin.getFont("default");
skin.add("default", checkBoxStyle);
TextButtonStyle textButtonStyle = new TextButtonStyle();
textButtonStyle.up = skin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.down = skin.newDrawable("white", Color.DARK_GRAY);
textButtonStyle.checked = skin.newDrawable("white", Color.BLUE);
textButtonStyle.over = skin.newDrawable("white", Color.LIGHT_GRAY);
textButtonStyle.font = skin.getFont("default");
skin.add("default", textButtonStyle);
final CheckBox checkBox = new CheckBox("Checkbox here", checkBoxStyle);
checkBox.setPosition(100, 100);
stage.addActor(checkBox);
final TextButton textButton = new TextButton("UPDATE", textButtonStyle);
textButton.setPosition(200, 200);
stage.addActor(textButton);
textButton.addListener(new ChangeListener() {
public void changed(ChangeEvent event, Actor actor) {
textButton.setText("Submitting...");
sh.hideOptions();
}
});
}
#Override
public void render(float delta) {
// Black background
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
Gdx.app.log("OptionsScreen", "resizing");
stage.setViewport(new StretchViewport(width, height));
}
#Override
public void show() {
Gdx.app.log("OptionsScreen", "show called");
}
#Override
public void hide() {
Gdx.app.log("OptionsScreen", "hide called");
}
#Override
public void pause() {
Gdx.app.log("OptionsScreen", "pause called");
}
#Override
public void resume() {
Gdx.app.log("OptionsScreen", "resume called");
}
#Override
public void dispose() {
stage.dispose();
skin.dispose();
}
}
I assume I must be missing something super obvious. If anyone can tell me where I'm going wrong I would be very grateful!
public void resize(int width, int height) {
stage.setViewport(new StretchViewport(width, height));
}
This is completely wrong in many ways. First of all, you do not want to create a new viewport on every resize, because of the Garbage Collector.
Furthermore creating a StretchViewport with the new screen width and height renders the StretchViewport useless, because it will basically behave like a ScreenViewport instead. Make sure to read the Viewports wiki article to understand how the different viewports work.
And last but not least, the reason why your stage is not drawn is also hidden within that. A UI Stage needs to have the camera centered, otherwise it won't be rendered correctly.
So what you want to do instead is the following:
public void resize(int width, int height) {
stage.getViewport().update(width, height, true);
}
I'm trying to do a menu for the option of the game i'm creating.
For this screen I use a tablelayout but I can't get it work.
All I get is a black screen.
See yourself :
package com.me.mygdxgame;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.CheckBox;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
public class BlueToothOptionScreen implements Screen{
final Stage stage;
Skin skin;
boolean hote = false;
Table table;
public BlueToothOptionScreen() {
this.stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getWidth(), true);
FileHandle skinFile1 = Gdx.files.internal("uiskin.json");
skin = new Skin(skinFile1);
}
#Override
public void render(float delta) {
stage.act(delta);
Gdx.gl.glClear( GL20.GL_COLOR_BUFFER_BIT );
stage.draw();
Table.drawDebug(stage);
}
#Override
public void resize(int width, int height) {
stage.setViewport(width, height, true);
}
#Override
public void show() {
Gdx.input.setInputProcessor( stage );
table = new Table(skin);
stage.addActor(table);
table.setFillParent(true);
table.defaults().spaceBottom(30);
table.add("Options").colspan(3);
final CheckBox hoteCheckBox = new CheckBox( "", skin );
hoteCheckBox.setChecked(true);
hoteCheckBox.addListener(new ChangeListener() {
#Override
public void changed(
ChangeEvent event,
Actor actor )
{
boolean enabled = hoteCheckBox.isChecked();
hote = enabled;
}
} );
table.row();
table.add("Hote");
table.add(hoteCheckBox);
}
}
I will be glad, if you can help me.
My 3rd person floats in the air and the camera should zoom out:
This is the program
package adventure;
import java.applet.Applet;
import com.jme3.math.Quaternion;
import com.jme3.math.FastMath;
import java.applet.AudioClip;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.TextArea;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import com.jme3.material.RenderState.FaceCullMode;
import javax.swing.JFrame;
import javax.swing.JPanel;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.animation.LoopMode;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.BlenderKey;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.util.CollisionShapeFactory;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.system.AppSettings;
import com.jme3.system.JmeCanvasContext;
import com.jme3.terrain.geomipmap.TerrainLodControl;
import com.jme3.terrain.geomipmap.TerrainQuad;
import com.jme3.terrain.heightmap.AbstractHeightMap;
import com.jme3.terrain.heightmap.ImageBasedHeightMap;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.util.SkyFactory;
public class MountainWorld extends SimpleApplication implements ActionListener,
PhysicsCollisionListener, AnimEventListener, Playable {
private static World world;
private static Person person;
private static Player dplayer;
private static TextArea textarea;
private BulletAppState bulletAppState;
private AnimChannel channel;
private AnimControl control;
// character
CharacterControl character;
Node model;
// temp vectors
Vector3f walkDirection = new Vector3f();
// terrain
TerrainQuad terrain;
RigidBodyControl terrainPhysicsNode;
// Materials
Material matRock;
Material matBullet;
// animation
AnimChannel animationChannel;
AnimChannel shootingChannel;
AnimControl animationControl;
float airTime = 0;
// camera
boolean left = false, right = false, up = false, down = false;
ChaseCamera chaseCam;
// bullet
Sphere bullet;
SphereCollisionShape bulletCollisionShape;
// explosion
ParticleEmitter effect;
// brick wall
Box brick;
float bLength = 0.8f;
float bWidth = 0.4f;
float bHeight = 0.4f;
FilterPostProcessor fpp;
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
AppSettings settings = new AppSettings(true);
settings.setWidth(850);
settings.setHeight(440);
MountainWorld canvasApplication = new MountainWorld();
canvasApplication.setSettings(settings);
canvasApplication.createCanvas(); // create canvas!
JmeCanvasContext ctx = (JmeCanvasContext) canvasApplication
.getContext();
ctx.setSystemListener(canvasApplication);
Dimension dim = new Dimension(640, 480);
ctx.getCanvas().setPreferredSize(dim);
JFrame window = new JFrame("Mountain World");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel(new BorderLayout()); // a panel
world = new DungeonWorld(canvasApplication);
person = new Person(world, "You", null);
dplayer = new Player(world, person);
Commands commands = new Commands(person);
textarea = new TextArea("", 10, 60,
TextArea.SCROLLBARS_VERTICAL_ONLY);
textarea.append("You are in a mountain. The trolls live here.\n");
textarea.setEditable(false);
panel.add("West", ctx.getCanvas());
panel.add("East", commands);
panel.add("South", textarea);
window.add(panel);
window.pack();
window.setVisible(true);
canvasApplication.startCanvas();
}
});
}
#Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
stateManager.attach(bulletAppState);
setupKeys();
prepareBullet();
prepareEffect();
createLight();
createSky();
createTerrain();
createWall();
createCharacters();
setupChaseCamera();
setupAnimationController();
setupFilter();
}
private void setupFilter() {
FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
fpp.addFilter(bloom);
viewPort.addProcessor(fpp);
}
private PhysicsSpace getPhysicsSpace() {
return bulletAppState.getPhysicsSpace();
}
private void setupKeys() {
inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
inputManager.addListener(this, "wireframe");
inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
inputManager.addMapping("CharSpace",
new KeyTrigger(KeyInput.KEY_RETURN));
inputManager
.addMapping("CharShoot", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addListener(this, "CharLeft");
inputManager.addListener(this, "CharRight");
inputManager.addListener(this, "CharUp");
inputManager.addListener(this, "CharDown");
inputManager.addListener(this, "CharSpace");
inputManager.addListener(this, "CharShoot");
}
private void createWall() {
float xOff = -144;
float zOff = -40;
float startpt = bLength / 4 - xOff;
float height = 6.1f;
brick = new Box(Vector3f.ZERO, bLength, bHeight, bWidth);
brick.scaleTextureCoordinates(new Vector2f(1f, .5f));
for (int j = 0; j < 15; j++) {
for (int i = 0; i < 4; i++) {
Vector3f vt = new Vector3f(i * bLength * 2 + startpt, bHeight
+ height, zOff);
addBrick(vt);
}
startpt = -startpt;
height += 1.01f * bHeight;
}
}
private void addBrick(Vector3f ori) {
Geometry reBoxg = new Geometry("brick", brick);
reBoxg.setMaterial(matBullet);
reBoxg.setLocalTranslation(ori);
reBoxg.addControl(new RigidBodyControl(1.5f));
reBoxg.setShadowMode(ShadowMode.CastAndReceive);
this.rootNode.attachChild(reBoxg);
this.getPhysicsSpace().add(reBoxg);
}
private void prepareBullet() {
bullet = new Sphere(32, 32, 0.4f, true, false);
bullet.setTextureMode(TextureMode.Projected);
bulletCollisionShape = new SphereCollisionShape(0.4f);
matBullet = new Material(getAssetManager(),
"Common/MatDefs/Misc/Unshaded.j3md");
matBullet.setColor("Color", ColorRGBA.Green);
// matBullet.setColor("m_GlowColor", ColorRGBA.Green);
getPhysicsSpace().addCollisionListener(this);
}
private void prepareEffect() {
int COUNT_FACTOR = 1;
float COUNT_FACTOR_F = 1f;
effect = new ParticleEmitter("Flame", Type.Triangle, 32 * COUNT_FACTOR);
effect.setSelectRandomImage(true);
effect.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f,
(float) (1f / COUNT_FACTOR_F)));
effect.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
effect.setStartSize(1.3f);
effect.setEndSize(2f);
effect.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
effect.setParticlesPerSec(0);
effect.setGravity(0, -5, 0);
effect.setLowLife(.4f);
effect.setHighLife(.5f);
effect.setInitialVelocity(new Vector3f(0, 7, 0));
effect.setVelocityVariation(1f);
effect.setImagesX(2);
effect.setImagesY(2);
Material mat = new Material(assetManager,
"Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture",
assetManager.loadTexture("Effects/Explosion/flame.png"));
effect.setMaterial(mat);
// effect.setLocalScale(100);
rootNode.attachChild(effect);
}
private void createLight() {
Vector3f direction = new Vector3f(-0.1f, -0.7f, -1).normalizeLocal();
DirectionalLight dl = new DirectionalLight();
dl.setDirection(direction);
dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
rootNode.addLight(dl);
}
private void createSky() {
rootNode.attachChild(SkyFactory.createSky(assetManager,
"Textures/Sky/Bright/BrightSky.dds", false));
}
private void createTerrain() {
matRock = new Material(assetManager,
"Common/MatDefs/Terrain/TerrainLighting.j3md");
matRock.setBoolean("useTriPlanarMapping", false);
matRock.setBoolean("WardIso", true);
matRock.setTexture("AlphaMap",
assetManager.loadTexture("Textures/Terrain/splat/alphamap.png"));
Texture heightMapImage = assetManager
.loadTexture("Textures/Terrain/splat/mountains512.png");
Texture grass = assetManager
.loadTexture("Textures/Terrain/splat/grass.jpg");
grass.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap", grass);
matRock.setFloat("DiffuseMap_0_scale", 64);
Texture dirt = assetManager
.loadTexture("Textures/Terrain/splat/dirt.jpg");
dirt.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_1", dirt);
matRock.setFloat("DiffuseMap_1_scale", 16);
Texture rock = assetManager
.loadTexture("Textures/Terrain/splat/road.jpg");
rock.setWrap(WrapMode.Repeat);
matRock.setTexture("DiffuseMap_2", rock);
matRock.setFloat("DiffuseMap_2_scale", 128);
Texture normalMap0 = assetManager
.loadTexture("Textures/Terrain/splat/grass_normal.jpg");
normalMap0.setWrap(WrapMode.Repeat);
Texture normalMap1 = assetManager
.loadTexture("Textures/Terrain/splat/dirt_normal.png");
normalMap1.setWrap(WrapMode.Repeat);
Texture normalMap2 = assetManager
.loadTexture("Textures/Terrain/splat/road_normal.png");
normalMap2.setWrap(WrapMode.Repeat);
matRock.setTexture("NormalMap", normalMap0);
matRock.setTexture("NormalMap_1", normalMap2);
matRock.setTexture("NormalMap_2", normalMap2);
AbstractHeightMap heightmap = null;
try {
heightmap = new ImageBasedHeightMap(heightMapImage.getImage(),
0.25f);
heightmap.load();
} catch (Exception e) {
e.printStackTrace();
}
terrain = new TerrainQuad("terrain", 65, 513, heightmap.getHeightMap());
List<Camera> cameras = new ArrayList<Camera>();
cameras.add(getCamera());
TerrainLodControl control = new TerrainLodControl(terrain, cameras);
terrain.addControl(control);
terrain.setMaterial(matRock);
terrain.setLocalScale(new Vector3f(2, 2, 2));
terrainPhysicsNode = new RigidBodyControl(
CollisionShapeFactory.createMeshShape(terrain), 0);
terrain.addControl(terrainPhysicsNode);
rootNode.attachChild(terrain);
getPhysicsSpace().add(terrainPhysicsNode);
}
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
model.setLocalScale(0.15f);
//ninja.setViewDirection( new Vector3f( 1, 0, 1 ) );
//model.rotate(0.0f, -3.0f, 0.0f);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(-140f, 9.9f, -9f));
character.setViewDirection(new Vector3f(1, 0, 0));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
//BlenderKey blenderKey = new BlenderKey(
// "objects/creatures/alien/alien.mesh.xml");
//Node alien = (Node) assetManager.loadModel(blenderKey);
//alien.setLocalTranslation(new Vector3f(-145, 21, -10));
//rootNode.attachChild(alien);
BlenderKey blenderKey2 = new BlenderKey(
"objects/creatures/minotaur/minotaur.mesh.xml");
Spatial man = (Spatial) assetManager.loadModel(blenderKey2);
man.setLocalTranslation(new Vector3f(-140, 15, -10));
rootNode.attachChild(man);
}
private void setupChaseCamera() {
flyCam.setEnabled(false);
chaseCam = new ChaseCamera(cam, model, inputManager);
}
private void setupAnimationController() {
animationControl = model.getControl(AnimControl.class);
animationControl.addListener(this);
animationChannel = animationControl.createChannel();
//shootingChannel = animationControl.createChannel();
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "uparm.right"));
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "arm.right"));
//shootingChannel.addBone(animationControl.getSkeleton().getBone(
// "hand.right"));
}
#Override
public void simpleUpdate(float tpf) {
Vector3f camDir = cam.getDirection().clone().multLocal(0.1f);
Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
camDir.y = 0;
camLeft.y = 0;
walkDirection.set(0, 0, 0);
if (left) {
walkDirection.addLocal(camLeft);
}
if (right) {
walkDirection.addLocal(camLeft.negate());
}
if (up) {
walkDirection.addLocal(camDir);
}
if (down) {
walkDirection.addLocal(camDir.negate());
}
if (!character.onGround()) {
airTime = airTime + tpf;
} else {
airTime = 0;
}
if (walkDirection.length() == 0) {
if (!"Idle1".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Idle1", 1f);
}
} else {
character.setViewDirection(walkDirection);
if (airTime > .3f) {
if (!"stand".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("stand");
}
} else if (!"Walk".equals(animationChannel.getAnimationName())) {
animationChannel.setAnim("Walk", 0.7f);
}
}
character.setWalkDirection(walkDirection);
}
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals("CharLeft")) {
if (value) {
left = true;
} else {
left = false;
}
} else if (binding.equals("CharRight")) {
if (value) {
right = true;
} else {
right = false;
}
} else if (binding.equals("CharUp")) {
if (value) {
up = true;
} else {
up = false;
}
} else if (binding.equals("CharDown")) {
if (value) {
down = true;
} else {
down = false;
}
} else if (binding.equals("CharSpace")) {
character.jump();
} else if (binding.equals("CharShoot") && !value) {
bulletControl();
}
}
private void bulletControl() {
shootingChannel.setAnim("Dodge", 0.1f);
shootingChannel.setLoopMode(LoopMode.DontLoop);
Geometry bulletg = new Geometry("bullet", bullet);
bulletg.setMaterial(matBullet);
bulletg.setShadowMode(ShadowMode.CastAndReceive);
bulletg.setLocalTranslation(character.getPhysicsLocation().add(
cam.getDirection().mult(5)));
RigidBodyControl bulletControl = new BombControl(bulletCollisionShape,
1);
bulletControl.setCcdMotionThreshold(0.1f);
bulletControl.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletControl);
rootNode.attachChild(bulletg);
getPhysicsSpace().add(bulletControl);
}
public void collision(PhysicsCollisionEvent event) {
if (event.getObjectA() instanceof BombControl) {
final Spatial node = event.getNodeA();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
} else if (event.getObjectB() instanceof BombControl) {
final Spatial node = event.getNodeB();
effect.killAllParticles();
effect.setLocalTranslation(node.getLocalTranslation());
effect.emitAllParticles();
}
}
public void onAnimCycleDone(AnimControl control, AnimChannel channel,
String animName) {
if (channel == shootingChannel) {
channel.setAnim("stand");
}
}
public void onAnimChange(AnimControl control, AnimChannel channel,
String animName) {
}
// Load an image from the net, making sure it has already been
// loaded when the method returns
public Image loadPicture(String imageName) {
return null;
}
// Load and play a sound from /usr/local/hacks/sounds/
public void playSound(String name) {
URL u = null;
try {
u = new URL("file:" + "/usr/local/hacks/sounds/" + name + ".au");
} catch (MalformedURLException e) {
}
AudioClip a = Applet.newAudioClip(u);
a.play();
}
}
I want the green person to stay on the ground but I can't seem to change the physicslocation to appropriate. Do you have any idea? I took this program and just switched the character from oto to ninja:
http://code.google.com/p/jmonkeyengine/source/browse/trunk/engine/src/test/jme3test/bullet/TestWalkingChar.java
Do you have any idea how I can make the camera zoom out so that the whole green person is displayed and change the location of the green person so that he is on the ground?
I've noticed that I can change the location of the main char with this
character.setPhysicsLocation(new Vector3f(-140f, 9.9f, -9f));
but when I put in lower y coordinates the main char drops through the ground and then floats way up in the sky like he ended up on the top of everything when he went through the ground. I could run the example with Oto perfectly, it's changing the main char to ninja that doesn't work for me.
Update
When using setlocaltranslationinstead of setphysicallocationmy main char gets located in the air:
Update 2
After scaling the main char:
model.scale(0.25f);
as suggested in the answer, the rendering is much better:
Update 2
After some more manipulation, it looks more like the way I want:
private void createCharacters() {
CapsuleCollisionShape capsule = new CapsuleCollisionShape(3f, 4f);
character = new CharacterControl(capsule, 0.01f);
model = (Node) assetManager.loadModel("Models/Ninja/Ninja.mesh.xml");
model.setLocalScale(0.15f);
// ninja.setViewDirection( new Vector3f( 1, 0, 1 ) );
// model.rotate(0.0f, -3.0f, 0.0f);
float scale = 0.25f;
model.scale(scale, scale, scale);
model.addControl(character);
character.setPhysicsLocation(new Vector3f(-141f, 10.5f, -9f));
model.setShadowMode(ShadowMode.CastAndReceive);
// model.setLocalTranslation(0, 50, 0);
character.setViewDirection(new Vector3f(1, 0, 0));
rootNode.attachChild(model);
getPhysicsSpace().add(character);
// BlenderKey blenderKey = new BlenderKey(
// "objects/creatures/alien/alien.mesh.xml");
// Node alien = (Node) assetManager.loadModel(blenderKey);
// alien.setLocalTranslation(new Vector3f(-145, 21, -10));
// rootNode.attachChild(alien);
BlenderKey blenderKey2 = new BlenderKey("Models/Oto/Oto.mesh.xml");
Spatial man = (Spatial) assetManager.loadModel(blenderKey2);
man.setLocalTranslation(new Vector3f(-140, 10, -10));
// man.setShadowMode(ShadowMode.CastAndReceive);
rootNode.attachChild(man);
}
I suspect there are 3 things that could make your character appear to be hovering:
Your character is hovering. In this case you'd want to use character.setLocalTranslation(0, -20, 0) to adjust where your character is located (may have to adjust the -20 to some other negative number)
Your character's scale is too big compared to the ground's scale (it sounds like this is a problem you want to fix anyways). To fix this, you'd want to use character.scale(.25) (where the .25 can be adjusted to whatever number works for you
You don't have shadows on the ground. This really links your character to the ground and gives the feeling that they're connected in some way. For this, you'll probably want to use object.setShadowMode(RenderQueue.ShadowMode.Receive) for all your terrain objects, and character.setShadowMode(RenderQueue.ShadowMode.CastAndReceive) on your character object.
The JMonkeyEngine javadoc's are pretty good - If you haven't already I would suggest looking there to help figure out what you're capable of doing with your engine. http://jmonkeyengine.org/javadoc/index.html