So I've recently started tinkering with LibGDX (desktop only, at the moment) in Java. I'm mildly experienced with other type of OGL libraries in other languages (namely SFML in C++) and quite experienced with the Java language itself, so I tackled LibGDX pretty confidently; and while the results were mostly positive, there's a bug that I just can't figure out.
What I want to achieve is simple: draw a map (Tiled .tmx), draw a Sprite (soon-to-be "player") inbetween layers 1 and 2, and render a couple of GUI widgets on top of all that, using a scene2D stage. I've managed to achieve this partially.
Here's my Game class, it's pretty messy from so much switching things around just to try, but it's pretty clear what it attempts to do:
public class Game extends ApplicationAdapter
{
private SpriteBatch batch;
private Texture img;
private Sprite playerSprite;
private TiledMap tiledMap;
private OrthographicCamera camera;
private OrthogonalTiledMapRenderer tiledMapRenderer;
private Stage stage;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("trchar000.png");
playerSprite = new Sprite(img, 0, 0, 32, 48);
playerSprite.setPosition(32, 192);
InputHandler.init();
Gdx.input.setInputProcessor(new InputHandler());
stage = new TestStage(playerSprite);
// inputs
InputMultiplexer im = new InputMultiplexer(stage, new InputHandler());
Gdx.input.setInputProcessor(im);
// tiles
camera = new OrthographicCamera();
camera.setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
camera.update();
tiledMap = new TmxMapLoader().load("map2.tmx");
tiledMapRenderer = new OrthogonalTiledMapRenderer(tiledMap);
}
#Override
public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
tiledMapRenderer.setView(camera);
int[] groundLayers = {0};
int[] topLayers = {1};
// render layer1 -> player -> layer 2
// the .tmx map doesn't have any other layers (just those 2)
tiledMapRenderer.render(groundLayers);
// removing these 3 lines solves the issue, but doesn't render the player
// I've also tried using my regular SpriteBatch instance; same results
// Also, I tried creating a class that extends OrthogonalTiledMapRenderer and overriding the render() method, so that it would draw the player inbetween layers, but again, same results.
tiledMapRenderer.getBatch().begin();
tiledMapRenderer.getBatch().draw(playerSprite, playerSprite.getX(), playerSprite.getY());
tiledMapRenderer.getBatch().end();
tiledMapRenderer.render(topLayers);
batch.begin();
// just to try, I'm rendering the stage here (not calling it's draw() method). Removing this for loop solves the issue, but doesn't render the GUI
for(Actor a : stage.getActors())
a.draw(batch, 1);
batch.end();
}
#Override
public void dispose () {
batch.dispose();
img.dispose();
tiledMapRenderer.dispose();
stage.dispose();
}
}
Also, here's my TestStage class:
public class TestStage extends Stage {
private Skin skin;
private BitmapFont newDefaultFont;
public TestStage(Sprite player)
{
super(new ScreenViewport());
// skin
FreeTypeFontGenerator freeTypeFontGenerator = new FreeTypeFontGenerator(Gdx.files.internal("segoeui.ttf"));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 14;
newDefaultFont = freeTypeFontGenerator.generateFont(parameter);
skin = new Skin();
skin.add("default-font", newDefaultFont, BitmapFont.class);
FileHandle fileHandle = Gdx.files.internal("uiskin.json");
FileHandle atlasFile = fileHandle.sibling("uiskin.atlas");
if (atlasFile.exists()) {
skin.addRegions(new TextureAtlas(atlasFile));
}
skin.load(fileHandle);
final TextButton button = new TextButton("This is a Button", skin, "default");
button.setPosition(250, 250);
button.setWidth(150);
button.setHeight(40);
final Label lbl = new Label("Test", skin);
this.addActor(lbl);
this.addActor(button);
}
}
Now here's the deal; this is what it looks like if a try to render everything (as shown in my Game class' render() method):
Image 1
The player, GUI, and layer 2 are rendered correctly, but the layer 1 is all bugged out, and I can't figure out why.
Now, here's how it looks like if a don't render the player, removing the line
tiledMapRenderer.getBatch().draw(playerSprite, playerSprite.getX(), playerSprite.getY());
Image 2
Everything renders fine.
Also, here's what it looks like if I decide not to render the stage actors, commenting this loop:
for(Actor a : stage.getActors())
a.draw(batch, 1);
Image 3
Again, everything renders fine.
The only other classes in my project are the default DesktopLauncher class, and an InputProcessor which I'm sure has nothing to do with this.
This is my first question here ever; I just can't figure this out.
Thanks in advance.
Related
I am working on an game for Android. I want the user to be able to "scroll" to the left and the right side by dragging the finger. That worked perfectly till now. Right now I am trying to implement the Gui with some buttons.
It should look kinda like this:
https://i.stack.imgur.com/jf0uZ.png
Code:
public class MainGameScreen implements Screen, InputProcessor {
Texture background, background_left, background_right;
public SpriteBatch batch;
//Graphical user interface
private Stage GUIStage;
private InputMultiplexer multiplexer;
//Camera
OrthographicCamera camera;
//Font
private BitmapFont font;
private String message = "Touch me";
//Buttons
private Stage button;
private Texture myTexture;
private TextureRegion myTextureRegion;
private TextureRegionDrawable myTexRegionDrawable;
private ImageButton imageButton;
public MainGameScreen (Trench_Warfare game) {
this.game = game;
batch = new SpriteBatch();
button = new Stage();
//GUI - Graphical user interface
GUIStage = new Stage(new ScreenViewport());
Image gui_background = new Image(new Texture("gui/GUI.png"));
gui_background.setPosition(0,0);
gui_background.setTouchable(Touchable.disabled);
GUIStage.addActor(gui_background);
//Buttons
myTexture = new Texture(Gdx.files.internal("gui/button/paper_riflemen.png"));
myTextureRegion = new TextureRegion(myTexture);
myTexRegionDrawable = new TextureRegionDrawable(myTextureRegion);
imageButton = new ImageButton(myTexRegionDrawable); //Set the button up
button = new Stage(new ScreenViewport()); //Set up a stage for the ui;
imageButton.isTouchable();
imageButton.setBounds(0,500,194,200);
button.addActor(imageButton); //Add the button to the stage to perform rendering and take input.
//Font
font = new BitmapFont();
font.setColor(Color.BLACK);
font.getData().scale(5);
//Background
background = new Texture("level1.png");
background_left = new Texture("level1_seiten.png");
background_right = new Texture("level1_seiten.png");
//Camera
camera = new OrthographicCamera(Gdx.graphics.getWidth()/*4000*/, Gdx.graphics.getHeight()/*2200*/);
camera.update();
camera.setToOrtho(false, Gdx.graphics.getWidth()*1.5f, Gdx.graphics.getHeight()*1.5f);
button.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
message = "Touch";
event.handle();//the Stage will stop trying to handle this event
}
});
InputMultiplexer multiplexer = new InputMultiplexer(button, this);
multiplexer.addProcessor(button);
multiplexer.addProcessor(this);
Gdx.input.setInputProcessor(multiplexer);
}
The Camera movement happens in the "#Override touchDragged" stuff. I don't think I have to show it here.
I am now trying to implement the buttons for two days now but I can't get them to work.
The problem is right at the bottom:
InputMultiplexer multiplexer = new InputMultiplexer(this, button);
Gdx.input.setInputProcessor(multiplexer);
In this order I can move the camera around but I cant touch the button. If I write (...)(button, this); I can't move the camera around anymore but can click the button. I can also activate the button's function when I click anywhere on the screen.
Hopefully you can help me with this problem!
According to the LibGDX Wiki on Event-Handling with InputMultiplexer:
The InputMultiplexer will hand any new events to the first InputProcessor that was added to it. If that processor returns false from the method invoked to handle the event, this indicates the event was not handled and the multiplexer will hand the event to the next processor in the chain. Through this mechanism, the MyUiInputProcessor can handle any events that fall inside one of its widgets and pass on any other events to the MyGameInputProcessor.
Your problem can be solved by setting the returning value of your overriden methods in your InputAdapters' children classes to false, here's an example:
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
[Some Logic]
return false; // Input has not been processed, next processor in the multiplexer will try to handle this
}
I'm trying to draw a stage and a string with a specific font, but when I draw the stage, the string disappears, but the images do not. I suppose it's a simple thing, but I have no idea. I've already tried to change the order to render, but that doesn't seem to be the problem. Any help would be aprecciated.
#Override
public void create() {
stage = new Stage();
Gdx.input.setInputProcessor(stage);
batch = new SpriteBatch();
textatlas = new TextureAtlas("Agorafunfa.txt");
TextureAtlas.AtlasRegion a = textatlas.findRegion("spider");
spider = new Sprite(a);
img = new Texture("Captura.PNG");
yesa = new BitmapFont(Gdx.files.internal("yesa.fnt"));
font = "Escape Planet";
img3 = new Texture("saturno.png");
funciona = new BitmapFont(Gdx.files.internal("yesa.fnt"));
starte = "Começar";
opcoes = "Opções";
Skin skin = new Skin();
skin.addRegions(ta);
final TextButton.TextButtonStyle tbs = new TextButton.TextButtonStyle();
tbs.font = yesa;
tbs.checked = skin.getDrawable("comecaversao2");
tbs.up = skin.getDrawable("comeca");
b = new TextButton("Começar",tbs);
b.setHeight(250);
b.setWidth(300);
b.setPosition(-10, 50);
b.addListener(new ClickListener() {
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
stage.addActor(b);
Gdx.input.setInputProcessor(stage);
}
public void render() {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
batch.draw(img,0,0);
batch.draw(img3, 200,170, 250, 170);
yesa.setColor(Color.WHITE);
yesa.draw(batch,font,430,300);
stage.draw();//if I comment this line, the string appears
batch.end();
}
You're on right track, stage.draw() is causing the problem. You're calling it without closing the spritebatch before. If you look into stage's source code you'll see it has its own spritebatch. Call batch.end() before drawing the stage.
Rendering a batch inside another opened batch will result in all kind of weird behaviour that makes you think the fault lies in everything but the batch.
As these type of problems pop up frequently nowadays I checked out the stage docs, there is clear information that it uses its own batch, you can even set your own with the constructor stage(Viewport viewport, Batch batch). If you use that constructor you don't need to use the solution above. However the docs doesn't mention batches needs to be closed, not even in the batch docs. This is something that probably should be added so we can avoid the same issues popping up.
Possible duplicate of this and this.
This is my code:
game init:
playerAtlas = new TextureAtlas(Gdx.files.internal("spineboy.atlas"));
json = new SkeletonJson(playerAtlas);
playerSkeletonData = json.readSkeletonData(Gdx.files.internal("spineboy.json"));
playerAnimationData = new AnimationStateData(playerSkeletonData);
batch = new SpriteBatch();
skeletonRenderer = new SkeletonRenderer();
skeleton = new Skeleton(playerSkeletonData);
animationState = new AnimationState(playerAnimationData);
animationState.setAnimation(0, "walk", true); // trackIndex, name, loop
render:
animationState.update(Gdx.graphics.getDeltaTime());
animationState.apply(skeleton);
batch.begin();
skeletonRenderer.draw(batch, skeleton);
batch.end();
Gdx.app.log("rendering", "x" +skeleton.getX());
Gdx.app.log("rendering", "y"+skeleton.getY());
skeleton.setPosition(300, 300);
And for some reason, I can't get my animation to show up.
I got this code from here and copied it:
https://gamedev.stackexchange.com/questions/98890/implementing-spine-animations-in-libgdx
I cant figure out why the animation isn't rendering!!
Can anyone please help?
Try this Test Class :
public class SimpleTest1 extends ApplicationAdapter {
OrthographicCamera camera;
SpriteBatch batch;
SkeletonRenderer renderer;
SkeletonRendererDebug debugRenderer;
TextureAtlas atlas;
Skeleton skeleton;
AnimationState state;
public void create () {
camera = new OrthographicCamera();
batch = new SpriteBatch();
renderer = new SkeletonRenderer();
renderer.setPremultipliedAlpha(true); // PMA results in correct blending without outlines.
debugRenderer = new SkeletonRendererDebug();
debugRenderer.setBoundingBoxes(false);
debugRenderer.setRegionAttachments(false);
atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy.atlas"));
SkeletonJson json = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless.
json.setScale(0.6f); // Load the skeleton at 60% the size it was in Spine.
SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("spineboy/spineboy.json"));
skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc).
skeleton.setPosition(250, 20);
AnimationStateData stateData = new AnimationStateData(skeletonData); // Defines mixing (crossfading) between animations.
stateData.setMix("run", "jump", 0.2f);
stateData.setMix("jump", "run", 0.2f);
state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc).
state.setTimeScale(0.5f); // Slow all animations down to 50% speed.
// Queue animations on track 0.
state.setAnimation(0, "run", true);
state.addAnimation(0, "jump", false, 2); // Jump after 2 seconds.
state.addAnimation(0, "run", true, 0); // Run after the jump.
}
public void render () {
state.update(Gdx.graphics.getDeltaTime()); // Update the animation time.
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
state.apply(skeleton); // Poses skeleton using current animations. This sets the bones' local SRT.
skeleton.updateWorldTransform(); // Uses the bones' local SRT to compute their world SRT.
// Configure the camera, SpriteBatch, and SkeletonRendererDebug.
camera.update();
batch.getProjectionMatrix().set(camera.combined);
debugRenderer.getShapeRenderer().setProjectionMatrix(camera.combined);
batch.begin();
renderer.draw(batch, skeleton); // Draw the skeleton images.
batch.end();
debugRenderer.draw(skeleton); // Draw debug lines.
}
public void resize (int width, int height) {
camera.setToOrtho(false); // Update camera with new size.
}
public void dispose () {
atlas.dispose();
}
}
I can't add comments, but there is a some additional info for Abhishek Aryan's post.
Now (oct, 22)you should use:
PolygonSpriteBatch batch;
..
batch = new PolygonSpriteBatch ();
to make his code work.
I want to rotate the ring image constantly in anticlockwise direction here is my code
public class SpriteSheet extends ApplicationAdapter {
Stage stage;
#Override
public void create () {
stage=new Stage(new ScreenViewport());
Group group=new Group();
Image background =new Image(new Texture(Gdx.files.internal("background.png")));
Image button=new Image(new Texture(Gdx.files.internal("btn.png")));
Image ring=new Image(new Texture(Gdx.files.internal("ring2.png")));
background.setName("background");
button.setName("button");
ring.setName("ring");
group.addActor(background);
group.addActor(button);
group.addActor(ring);
stage.addActor(group);
background.setPosition(Gdx.graphics.getWidth()/2-background.getWidth()/2,Gdx.graphics.getHeight()/2-background.getHeight()/2);
button.setPosition(Gdx.graphics.getWidth()/2-button.getWidth()/2,Gdx.graphics.getHeight()/2-button.getHeight()/2);
ring.setPosition(255,105);
ring.setOrigin(255f,105f);
ring.rotateBy(2f); // I need continuous rotation here
}
#Override
public void render () {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
}
I guess the Actions are what you are looking for.
An Action can be added to Actors (and subclasses) and they will be performed inside the act(delta) method, of the Actor.
In your case you could use the Actions.rotateBy(float rotationAmount, float duration) and let it repeat forever by using Actions.repeat(RepeatAction.FOREVER, rotateAction).
So your final code should look like this:
ring.addAction(Actions.repeat(RepeatAction.FOREVER, Actions.rotateBy(rotation, duration)));
Where rotation is the rotation amount (i guess in degrees, but i am not sure) and duration is the time it should take to rotate by the given amount (given in seconds).
I'm developing a Tower Defense game using libGDX. I've just started and I am able to display the path, the "environment" and the enemies walking along the path.
I displayed the environment using a SpriteBatch-Object, like this:
public LevelController(Level level) {
spriteBach = new SpriteBach();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
}
public void setSize() {
spriteBatch.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteBatch.begin();
drawTowerBases();
spriteBatch.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteBatch.draw(towerBaseTexture, x, y, 1f, 1f);
}
}
This is working properly and the textures are displayed well: Tower Defense using spriteBatch
Now, I was wondering if it is possible to cache the background. Because it stays the same, there is no need to calculate it every time. I've found the SpriteCache through Google-Search. So I changed the code as follows:
public LevelController(Level Level) {
spriteCache= new SpriteCache();
atlas = new TextureAtlas(Gdx.files.internal("images/textures/textures.pack"));
spriteCache.beginCache();
this.drawTowerBases();
this.spriteCacheEnvironmentId = spriteCache.endCache();
}
public void setSize() {
this.cam.update();
spriteCache.setProjectionMatrix(this.cam.combined);
}
public void render() {
spriteCache.begin();
spriteCache.draw(this.spriteCacheEnvironmentId);
spriteCache.end();
}
private void drawTowerBases() {
// for each tower base (=environment square)
TextureRegion towerBaseTexture = atlas.findRegion("TowerBase");
if(towerBaseTexture != null) {
spriteCache.add(towerBaseTexture, x, y, 1f, 1f);
}
}
And now the game looks like this: Tower Defense using spriteCache
For me it seems as if the transparency is not rendered properly. If I take images without transparency, everything works fine. Does anyone have an idea, why this happens and how I can fix this?
Thank you in advance.
Taken from the SpriteCache documentation:
Note that SpriteCache does not manage blending. You will need to enable blending (Gdx.gl.glEnable(GL10.GL_BLEND);) and set the blend func as needed before or between calls to draw(int).
I guess there is not much more to say.