public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
stage.act(delta);
batch.setProjectionMatrix(camera.combined);
batch.begin();
if (lifeCount > 0) {
/*
When stage.draw is called here, it only displays the exit button.
The game still operates, but everything is invisible.
If I leave the game on, the game over screen is shown,
*/
stage.draw();
text.setColor(1.0f, 1.0f, 1.0f, 1.0f);
text.draw(batch, score + scoreCount, 25, 100);
text.draw(batch, lives + lifeCount, 25, 120);
text.draw(batch, speed + raindropSpeed, 25, 80);
batch.draw(bucketTexture, bucket.x, bucket.y);
for (Rectangle clearDrop : clearDrops) {
batch.draw(clearDropTexture, clearDrop.x, clearDrop.y);
}
for (Rectangle healthDrop : healthDrops) {
batch.draw(healthDropTexture, healthDrop.x, healthDrop.y);
/*
If I place stage.draw here, health drops are invisible.
This also happens if I place it in the raindrop for-loop and the
cleardrop for-loop
*/
}
for (Rectangle raindrop : raindrops) {
batch.draw(raindropTexture, raindrop.x, raindrop.y);
}
/*
If I place stage.draw here, the bucket, score, life, and speed
display correctly. The drops are still invisible.
*/
} else {
pause();
raindrops.clear();
game.setScreen(new GameOver(game));
}
batch.end();
What I have been trying to do is have an exit button in the top right corner of the GameScreen, although drawing the stage which the button resides in gives me difficulties (see comments in code).
Here is my code for the exit button and stage (resize()):
if (stage == null)
stage = new Stage(width, height, true);
stage.clear();
Gdx.input.setInputProcessor(stage);
TextButtonStyle styleQuit = new TextButtonStyle();
styleQuit.up = skin.getDrawable("buttonnormal");
styleQuit.down = skin.getDrawable("buttonpressed");
styleQuit.font = text;
quitButton = new TextButton(" ", styleQuit);
quitButton.setWidth(128);
quitButton.setHeight(128);
quitButton.setX(800 - 128);
quitButton.setY(480 - 100);
quitButton.addListener(new InputListener() {
public boolean touchDown(InputEvent event, float x, float y,
int pointer, int button) {
return true;
}
public void touchUp(InputEvent event, float x, float y,
int pointer, int button) {
Gdx.app.log(RainCatcher.LOG, "Quit Button Pressed");
game.setScreen(new MainMenu(game));
}
});
stage.addActor(quitButton);
And the rest (in show())
atlas = new TextureAtlas("gamebuttons.pack");
skin = new Skin();
skin.addRegions(atlas);
text = new BitmapFont();
Is there any special trick to allow a stage to be rendered alongside with the falling raindrops, bucket, and text? My friends and I have been stumped and couldn't find a solution anywhere.
Move stage.draw() after batch.end() or before batch.begin()
This is not the right approach you're taking in my opinion. If you're using a stage in libgdx, then you should benefit from other components of Scene2d. So, rather than drawing your raindrops and other game entities separately (and making your job complicated), you should make them actors and add them to stage wherever you need and then draw the stage in the render.
For example:
public class RaindDrop extends Actor {
TextureRegion region;
public RaindDrop () {
region = new TextureRegion(...);
}
public void draw (SpriteBatch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(...);
}
}
So on for other entities. Initialise and add them to your stage.
Here's the official wiki to read more:
https://code.google.com/p/libgdx/wiki/scene2d
Related
Currently I have 2 screens, a main menu and a battle screen. When i move directly from the main menu screen to the battle screen without resizing, everything works normally, but when i resize my main menu and then move to my battlescreen, the particles (and only the particles) get rendered incorrectly.
I have tried :
-updating the viewport in the show method of the battlescreen
-setting the projectionmatrix of the spritebatch to the camera's combined of either the mapcamera or the unitcamera.
-applying every viewport (there are 3) before rendering.
THE MAIN MENU SCEEN
https://snag.gy/cJCnRt.jpg
private Stage _stage;
public MainMenuScreen(Object... params){
_stage = new Stage();
backgroundbatch = new SpriteBatch();
}
#Override
public void render(float delta) {
backgroundbatch.begin();
backgroundbatch.draw(_currentFrame, 0, 0,_stage.getViewport().getWorldWidth(),_stage.getViewport().getWorldHeight()); // Draw current frame at (0, 0)
backgroundbatch.end();
_stage.act(delta);
_stage.draw();
}
#Override
public void resize(int width, int height) {
_stage.getViewport().setScreenSize(width, height);
//_stage.getViewport().update(width, height);
}
THE BATTLESCREEN CLASS
private void initializeHUD() {
_hudCamera = new OrthographicCamera();
_hudCamera.setToOrtho(false, VIEWPORT.physicalWidth, VIEWPORT.physicalHeight);
}
#Override
public void show() {
_camera = new OrthographicCamera();
_camera.setToOrtho(false, map.getMapWidth(), map.getMapHeight());
_mapRenderer = new OrthogonalTiledMapRenderer(_mapMgr.getCurrentTiledMap(), Map.UNIT_SCALE);
_mapRenderer.setView(_camera);
spritebatch = new SpriteBatch();
map.makeSpawnParticles();
}
#Override
public void render(float delta) {
_camera.update();
_hudCamera.update();
//draw particles
ParticleMaker.drawAllActiveParticles(spritebatch, delta);
}
#Override
public void resize(int width, int height) {
Player.getInstance().getEntityStage().getViewport().update(width, height, true);
_playerBattleHUD.resize(width, height);
map.getTiledMapStage().getViewport().update(width, height, true);
}
PARTICLE MANAGER CLASS
public static void drawAllActiveParticles(SpriteBatch spriteBatch, float delta) {
for (ArrayList<Particle> particleTypeList : allParticles.values()) {
spriteBatch.begin();
for(Particle particle : particleTypeList) {
if(particle.isActive()) {
particle.update(delta);
particle.draw(spriteBatch,delta);
}
if (particle.isComplete()) {
particle.delete();
//particles.remove(particle);
}
}
spriteBatch.end();
}
}
PARTICLE CLASS
public void draw(SpriteBatch spriteBatch, float delta) {
Gdx.app.debug("Particle : ", "spritebatch position = " + spriteBatch.getProjectionMatrix().getScaleX() + " , " + spriteBatch.getProjectionMatrix().getScaleY() + ")");
particleEffect.draw(spriteBatch, delta);
}
expected results :
https://snag.gy/q2txr5.jpg (particles are the yellow dots)
results (after resizing the main screen):
https://snag.gy/g81ZJ7.jpg
noteworthy : after resizing, the spritebatch projectionmatrix shows a different value in comparison to not having resized the main menu.
For anyone else with similar issues, I fixed my problem by using my tiledmaprenderer's batch to render my particles.
How can I detect a click on a text using LibGdx? For example, I want the user to be able to click on the String Show Highscore. The problem I'm having is that I already implemented an if-statement, which states that if anything is clicked on, the game starts. What kind of method do I have to write now, so that the user can click on Show Highscore? I can change the if statement which uses the whole screen, if necessary. Thanks :)
public class MainMenuScreen implements Screen {
final FlappyWizardGame game;
OrthographicCamera camera;
String buttonHighscore = "Show Highscore";
public MainMenuScreen(FlappyWizardGame game) {
this.game = game;
camera = new OrthographicCamera();
camera.setToOrtho(false, 1280, 720);
screenHeight = Gdx.graphics.getHeight();
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
game.batch.draw(game.hermine, 0, 0, (int)(game.hermine.getWidth() * 0.25), (int)(game.hermine.getHeight() * 0.25));
game.font.draw(game.batch, "Welcome to Flappy Wizard!!! ", camera.viewportWidth / 3, camera.viewportHeight - 100);
game.font.draw(game.batch, "Tap anywhere to begin!", camera.viewportWidth / 3, camera.viewportHeight - 150);
game.font.draw(game.batch, buttonHighscore, camera.viewportWidth / 3, camera.viewportHeight - 200);
if (Gdx.input.isTouched()) {
game.setScreen(new GameScreen(game));
dispose();
}
}
You must create a Rectangle around the Text:
Rectangle rectangle = new Rectangle(x, y, width, height);
And then in your if statement check if the touch event is in the Rectangle:
if(Gdx.input.isTouched()){
if(rectangle.contains(Gdx.input.getX(), Gdx.input.getY())){
openHighscore();
} else{
game.setScreen(new GameScreen(game));
dispose();
}
}
Answer by Morchul is correct although for UI Screen you probably want to consider using LibGDX's Scene2D component. It allows you to easily create Label, button, menu and detect click event by adding listener to them.
I cant seem to find an answer to this. Why does my start button freeze after a certain amount of clicks(3)? It works for the first 3 clicks and then decides to stop working. I can still see that the screen is responding(the button becomes red when clicked), but the text on it doesn't change. It's like the click listener stopped responding.
public class MainMenu implements Screen{
private Game game;
private Stage stage;
private TextButton Start_btn;
private TextButton LocalWifi_btn;
private TextButton Internet_btn;
private TextButton Settings_btn;
private boolean Start_clicked = false;
private boolean LocalWifi_clicked = false;
private boolean Internet_clicked = false;
private boolean Settings_clicked = false;
public MainMenu(Game g){
game = g; //The Wasteland
stage = new Stage(new ExtendViewport(Gdx.graphics.getWidth(),
Gdx.graphics.getHeight())); //create a new stage with viewport to draw 2d stuff on
Gdx.input.setInputProcessor(stage); //all input set to stage
Skin skin = new Skin(Gdx.files.internal("gui/uiskin.json"), new TextureAtlas(Gdx.files.internal("gui/uiskin.atlas"))); //need this before you can make a gui
Start_btn = new TextButton("" + Start_clicked, skin);
Start_btn.setPosition(500, 500);
Start_btn.setSize(200, 200);
Start_btn.getLabel().setFontScale(10, 10); //change text size
Start_btn.addListener(new ClickListener(){
#Override
public void touchUp(InputEvent e, float x, float y, int point, int button){
onStartClicked();
}
});
stage.addActor(Start_btn);
}
private void onStartClicked(){
if(!Start_clicked){
Start_clicked = true;
Start_btn.setText("" + Start_clicked);
Gdx.app.log("Button", "" + Start_clicked);
}
else{
Start_clicked = false;
Start_btn.setText("" + Start_clicked);
Gdx.app.log("Button", "" + Start_clicked);
}
}
#Override
public void render(float delta) {
//this has to be before anything or else it will be drawn on top of everything else
Gdx.gl.glClearColor(0, 0, 0, 1); //set background color
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); //clears the screen
stage.act(delta); //send ammount of time since last render call, tells it to keep a steady fps
stage.draw();
}
#Override
public void resize(int width, int height) {
// use true here to center the camera
// that's what you probably want in case of a UI
stage.getViewport().update(width, height, true);
}
}
What am I doing wrong?!?!?!
Don't override touchUp() without calling super.touchUp(), because its messing up the functionality of ClickListener. But that's not what you want to override anyway. You should override clicked() so it only triggers if you release the click while still over the button. Or better yet, use a ChangeListener. Buttons already have a ClickListener built in that fires a change event when the button is clicked. Adding another ClickListener is redundant when you can just use a ChangeListener.
I'm new to LibGDX and I am taking it slowly. I'm still trying to understand most things which is why typically google searches don't help due to the fact that their too complicated. I have a main menu that has text that I want centered no matter what the screen size is. Here is the code that I have for that menu.
public class Menu implements Screen {
SlingshotSteve game;
OrthographicCamera camera;
public Menu(final SlingshotSteve gam) {
this.game = gam;
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
game.font.draw(game.batch, "Welcome to Slingshot Steve!!! ", 100, 150);
game.font.draw(game.batch, "Tap anywhere to begin!", 100, 100);
game.batch.end();
if (Gdx.input.isTouched()) {
game.setScreen((Screen) new GameScreen(game));
dispose();
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
I'm here to save you!
To get width / height from a String drawn with your BitmapFont you can utilize this super nice method:
game.font.getBounds(String string)
And to use it in your case, it would be something like this:
game.font.getBounds("Tap anywhere to begin!").width / 2;
Cheers!
It is possible to do it the way Nine Magics suggested, however one would usually do it via a Stage, which is part of scene2d.
More specifically one would use scene2d.ui which is a bunch of Actors like Button, Image, Label etc. You can attach a ClickListener to a Button for example and react on this event.
Furthermore, for layouting there is one very powerful Actor, namely Table which you can very easily use to center things on the screen.
Some very minimalistic code:
// do this once, in create() or show()
Skin skin = new Skin("uiskin.json"); // get the demo skin from the libgdx test resources
Stage stage = new Stage();
Table table = new Table(skin);
table.add("Welcome to Slingshot Steve!!!");
table.row();
table.add("Tap anywhere to begin!");
stage.addActor(table);
// do this in your render loop
stage.act();
stage.draw();
You can find the "default" skin resources here. You need to get all the uiskin.* files.
I'm having a hard time getting events working with my Actor in libgdx. I'm using nightly builds.
My stage is setup in the show() method of a Screen subclass:
stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
Gdx.input.setInputProcessor(stage);
TestActor actor = new TestActor();
stage.addActor(actor);
And my actor class looks like:
class TestActor extends Actor {
private Sprite sprite;
private TextureAtlas atlas;
public TestActor() {
atlas = new TextureAtlas(Gdx.files.internal("textures/images-packed.atlas"));
sprite = atlas.createSprite("logo-96");
setTouchable(Touchable.enabled);
addListener(new InputListener() {
public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.debug(TestGame.TAG, "TestActor.touchDown()");
return true; // must return true for touchUp event to occur
}
public void touchUp (InputEvent event, float x, float y, int pointer, int button) {
Gdx.app.debug(TestGame.TAG, "TestActor.touchUp()");
}
});
}
#Override
public void draw(SpriteBatch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
batch.draw(sprite, getX(), getY());
}
}
The events don't seem to fire. Oddly enough, I've used built-in UI widgets like the TextButton and can get those events to fire just fine. Can anybody see what I'm doing wrong?
You should also setBounds to your actor.
best way to do it (if you want the same size as your texture)
add these lines to your constructor :
setWidth(sprite.getWidth());
setHeight(sprite.getHeight());
setBounds(0, 0, getWidth(), getHeight());
notice you can also set the location of the bounds with the first 2 parameters.