So, I have created a texture, and then a sprite.
On my render() method, I am check for user input. If the user has touched/clicked, then I want my sprite to rotate 90 degrees ONCE.
Right now the rotation works. However, it rotates multiple times per click!
How can I make it rotate only once per touch? I have a feeling that I might have to use delta time, and that occurs because the render method is being called frequently, but I don't know how to fix it... Thanks!
public class MyGame extends ApplicationAdapter {
SpriteBatch batch;
Texture img;
Sprite sprite;
#Override
public void create () {
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
sprite = new Sprite(img);
}
#Override
public void render () {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
sprite.draw(batch);
batch.end();
if (Gdx.input.isTouched()) {
rotateRight();
}
}
private void rotateRight() {
sprite.setRotation(sprite.getRotation() - 90);
}
}
Right now you are polling input inside of your render method. Polling simply checks the status of the input (is it touched or not) and does not care for any actual "event" occurred.
Instead of this you need to look for input events via event handling as this will give you access to the actual event of the screen being touched or untouched. You can do this by implementing InputProcessor which will give you access to override a bunch of touch event methods so you can execute your rotate method only on the event of the touch.
Example:
public void update()
{
Gdx.input.setInputProcessor(new InputProcessor() {
#Override
public boolean TouchDown(int x, int y, int pointer, int button) {
if (button == Input.Buttons.LEFT) {
rotateRight();
return true;
}
return false
}
});
}
Don't forget to call your update() method from your render method. I put it in a separate function just for readability and not to clog up your rendering code.
You can also have your class implement InputProcessor and override the methods in your class so you do not have to do it inline as I did above.
if (Gdx.input.justTouched() && Gdx.input.isTouched()) {
rotateRight();
}
Related
I'm currently making a 2d infinite runner esque game that has a release date of Halloween. I've nearly finished the game itself and I've just added a main menu and am currently in the process of making a pause menu. I'm having a big issue with setting the screens though. Setting the screen from my main menu into the game works perfectly fine but when I set the screen back to the main menu a few weird things happen:
Nothing in the spriteBatch renders, everything is just a black box (MainMenuScreen)
If I choose to go back to the game I find that not everything is reset like when the screen is initially set to GameScreen
Here's a video of the issue
Here's the code for how I switch screens:
From when the game is first opened to main menu:
public class RadiationPigeon extends Game {
public static final float PPM = 100;
public static SpriteBatch batch;
#Override
public void create () {
batch = new SpriteBatch();
setScreen(new MainMenuScreen(this));
}
#Override
public void render () {
super.render();
}
#Override
public void dispose () {
batch.dispose();
}}
From MainMenuScreen to GameScreen:
private RadiationPigeon radiationPigeon;
public MainMenuScreen(RadiationPigeon radiationPigeon){
this.radiationPigeon = radiationPigeon;
}
playButton.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) {
radiationPigeon.setScreen(new GameScreen(radiationPigeon));
//I give GameScreen radiationpigeon so that the screen can be set back to MainMenu
}
});
From GameScreen to MainMenu:
private RadiationPigeon radiationPigeon;
public GameScreen(RadiationPigeon radiationPigeon){
world.setContactListener(new ContactListener());
Timer timer = new Timer();
timer.schedule(new BatAttackChanceCounter(), 0, 1000);
this.radiationPigeon = radiationPigeon;
}
pauseButton.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) {
//paused = true;
radiationPigeon.setScreen(new MainMenuScreen(radiationPigeon));
}
});
Just for reference, here's my render method of both screens:
MainMenuScreen:
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stage.act();
RadiationPigeon.batch.begin();
stage.draw();
RadiationPigeon.batch.end();
}
GameScreen:
#Override
public void render(float delta) {
update(delta);
Gdx.gl.glClearColor(0, 0,0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
RadiationPigeon.batch.setProjectionMatrix(pigeoncam.combined);
RadiationPigeon.batch.begin();
//RadiationPigeon.batch.draw(pausedScreen, pigeoncam.position.x - (250 / RadiationPigeon.PPM), pigeoncam.position.y - pigeoncam.viewportHeight / 2, 5, 5);
RadiationPigeon.batch.end();
b2dr.render(world, pigeoncam.combined);
stage.draw();
hud.stage.draw();
}
After lots of trial and error I finally resolved my issue. There was nothing wrong with how I set my screens (for anyone having trouble with setting screens the code I provided works). The problem was with the actual buttons and images. I had made some of them static because I needed to use the same buttonstyles and fonts in GameScreen. I thought that would save memory, however, once they were accessed outside the screen in which they were created (MainMenuScreen) they would lose all their values essentially making them empty without making them null.
A lack of understanding on how buttons worked is what caused the issue
EDIT:
Turns out a lack of understanding on how static variables work was the real issue. On android when you minimize an android app the java cycle doesn't stop and as such the items are "kept alive"
it's my first time posting and I'm self taught so be please gentle!
I've been building a bomberman replica game in libGDX using Game and Screen classes:
public class Main extends Game {
...
#Override
public void create() {
levelScreen = new LevelScreen(playerCount, new int[playerCount]);
levelScreen.level.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(2f)));
this.setScreen(levelScreen);
}
However when the game launches there is no fade effect.
public class LevelScreen implements Screen {
...
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1, 0.1f, 0.5f, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
batch.begin();
level.act();
level.draw();
batch.end();
}
I want this levelScreen to fade in from black but it just doesn't!
When the round is over I want to fadeOut of this levelScreen to black, then fadeIn to a trophyScreen from black:
(From Main Class)
#Override
public void render() {
super.render();
if (endRoundTimer <= 0) {
trophyScreen = new TrophyScreen(playerCount, levelScreen.getScore());
levelScreen.level.addAction(Actions.sequence(Actions.fadeOut(1), Actions.run(new Runnable() {
#Override
public void run() {
setScreen(trophyScreen);
}
})));
}
}
And I've tried using the show() method in the TrophyScreen:
public class TrophyScreen implements Screen {
...
#Override
public void show() {
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(1)));
}
I've done loads of searching and tried various things but no joy. I'm sure I'm missing something somewhere in a draw() or render() method that is preventing the fade Action from taking place.
UPDATE1
#Override public void draw() {
super.draw();
if (roundOver) {
this.getBatch().begin(); String s = String.format("%s", message);
font_text.draw(this.getBatch(), s, (90 + (2 * 30)), (this.getHeight() / 2));
this.getBatch().end();
}
For fading to work on actors, they must properly apply their own color's alpha in the draw method. And for an entire hierarchy of objects to fade at once, they must all also apply the parentAlpha parameter from the draw method signature.
So your draw method in any custom Actor subclass should look like this:
public void draw (Batch batch, float parentAlpha) {
Color color = getColor();
batch.setColor(color.r, color.g, color.b, color.a * parentAlpha);
//..do drawing
}
If you are using a Sprite in your Actor instead of a TextureRegion (which I don't recommend due to redundancies) you must apply the color to the Sprite instead of Batch.
Note that this method of fading the whole game is not a "clean" fade. Any actors that are overlapping other actors will show through each other when the parent alpha is less than 1 during the fade. An alternative that would provide a clean-looking fade would be to draw a copy of your background (or black) over your entire scene and fade that instead.
I assume that level is an object of class that extends Stage and you are creating a control inside the stage, which is weird. You are not appling color to your font_text which I assume it is a BitmapFont
Solution, the weird way
If you want to do it in this way you will need something like that:
#Override public void draw() {
super.draw();
if (roundOver) {
getBatch().begin();
String s = String.format("%s", message);
font_text.setColor(getRoot().getColor())
font_text.draw(this.getBatch(), s, (90 + (2 * 30)), (this.getHeight() / 2));
getBatch().end();
}
}
getRoot() gets Group from Stage, we do it, because every action applied to Stage is actually applied to this Group root element. We get color (which has alpha channel) and we copy the color to the bitmapFont.
This solution is weird, because you are actually creating an Label inside Stage. It is pointless, actors plays on stage, not inside.
Solution, the good way
You want to draw text, right? So just use Label which is an actor, who shows a text. Actors do jobs for you:
stage = new Stage();
Label.LabelStyle labelStyle = new Label.LabelStyle(bitmapFont, Color.WHITE);
Label label = new Label("Hi, I am a label!", labelStyle);
stage.addActor(label);
Then you can apply actions and they will work fine (and every actor can have own actions applied).
stage.addAction(Actions.sequence(Actions.alpha(0), Actions.fadeIn(5)));
label.addAction(Actions.moveBy(0, 300, 15));
There is a lot of different actors like TextButton, Image, ScrollPane. They are customizable, easy to manage and they can be integrated in groups and tables.
Output:
A better way would be to just start by drawing a black image over everything, so you don't have to mess with every scene object's alpha. Use layering to do that. This post may be helpful.
Then you can control it's alpha channel, change it's rendering to 0 right before unpausing the game action to get it's drawing cycles back. Reactivate it on stage ending for your fade out effect.
Thank you cray, it's way better like this.
I have a problem, I think its related to Screen render and its lifecycle.
Basically I have two screens (Menu and Game). In GameScreen render method i call World.update and after that my Render. In hide method (of GameScreen) i dispose of the SpriteBatch from Redner class.
So when I change the screen from Game to Menu (within World.update) Java crashes. As far as I can tell, the dispose is making the crash.
So my question is, when i set a new screen in the middle of the render cycle, is that render cycle still going to finish with its old screen? Meaning, am I calling batch.dispose before the rendering was finished, and that is why i get the problem?
Thank you for all the help
public class GameScreen extends AbstractGameScreen {
private static final String TAG = GameScreen.class.getName();
private WorldController worldController;
private WorldRenderer worldRenderer;
private boolean paused;
public GameScreen(Game game) {
super(game);
}
#Override
public void render(float deltaTime) {
// Do not update game world when paused
if (!paused) {
// Update game world by the time that has passed since last render time
worldController.update(deltaTime);
}
// Sets the clear screen color to: Cornflower Blue
Gdx.gl.glClearColor(0x64 / 255.0f, 0x95 / 255.0f, 0xed / 255.0f, 0xff / 255.0f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Render game world to screen
worldRenderer.render();
}
#Override
public void resize(int width, int height) {
worldRenderer.resize(width, height);
}
#Override
public void show() { // Similar as create method
worldController = new WorldController(game);
worldRenderer = new WorldRenderer(worldController);
Gdx.input.setCatchBackKey(true);
}
#Override
public void hide() { // Similar to dispose method
worldRenderer.dispose();
Gdx.input.setCatchBackKey(false);
}
#Override
public void pause() {
paused = true;
}
#Override
public void resume() {
super.resume();
// Only called on Android
paused = false;
}
}
That's basically correct. The screen that calls setScreen from within its render method will have hide called on itself and then will continue through the rest of its code in its render method. So you are killing your sprite batch right before trying to draw with it.
So don't call dispose from within your hide method. In fact, it is probably bad practice for a Screen to ever call dispose on itself. You can reserve that for the Game class that owns it. For example, you could do something like this in your game class:
#Override
public void render() {
super.render();
if (getScreen() != gameScreen && gameScreen != null) {
gameScreen.dispose();
gameScreen = null;
}
}
By the way, you should probably put the SpriteBatch in your Game subclass and let all the different screens share it. It's a fairly big object to be allocating and deallocating for no reason.
I'm doing a simple game and I have a class that I call GameLoop and inside the run() method the code is passing the reference of the canvas to a method that draws a circle. This code is just some testcode.
My question is how do I pass the reference of the canvas if I want to create several sprite objects(classes) with a circle and put them in a list when the game starts? I have done this before, but then I used bitmap in each sprite object and just passed a reference of the image, but in this case I'm not sure how I would do since the canvas is inside the run() method, and I want to create my sprite objects in a method like initializeGameObjects() once in the beginning. I hope my question isn't unclear!? Preciate some help! Thanks!
// Game loop ---------------------------------------
#Override
public void run() {
// TODO Auto-generated method stub
while (gameRunning) {
if (!surfaceHolder.getSurface().isValid())
continue;
canvas = surfaceHolder.lockCanvas();
// Call method to draw objects on screen
drawObjects(canvas);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
// End game loop ---------------------------------------
// Method that draw everything on canvas
private void drawObjects(Canvas canvas) {
// Test
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
// Clear screen with black color
canvas.drawRGB(0, 0, 0);
// Draw a circle
canvas.drawCircle(100, 100, 100, paint);
}
I would implement Runnable and construct a Thread. This way you can have a "setup" method which ultimately calls a "start" method. The "start" method would draw to the canvas before starting the thread.
This is actually better practice for this kind of stuff. You may want to init stuff before starting a thread.
public class Game implements Runnable {
private Thread thisThread = new Thread(this);
private boolean gameRunning = false;
public void startGame() {
gameRunning = true;
//Draw to canvas
thisThread.start();
}
#Override
public void run() {
while(gameRunning) {
//Draw other objects
}
}
}
Define an interface:
public interface Renderable{
public void renderToCanvas(Canvas c);
}
Define a List of Renderables:
private List<Renderable> sprites = new ArrayList<Renderable>();
create methods to add/remove sprites, make sure they are synchronized:
public synchronized void addSprite(Renderable r){
sprites.add(r);
}
public synchronized void removeSprite(Renderable r){
sprites.remove(r);
}
This is drawing method, also synchronized, so you don't end up drawing and modifying sprite list at same time. This is either called in a loop by other thread, or a view's onDraw() method:
public synchronized void draw(Canvas c){
// lock canvas if needed
// common drawing code
for(Renderable r :sprites){
r.renderToCanvas(c);
}
// release canvas if locked
}
Now, you are free to define any type of sprite, Circle, Square or whatever,
public class Circle implements Renderable{
private Paint mPaint;
private float mRadius;
public Circle(Paint p, float r){
mPaint = p;
mRadius = r;
}
#Override
public void renderToCanvas(Canvas c){
c.drawCircle(100, 100, mRadius, mPaint);
}
}
and add it to sprite list:
addSprite(new Circle(new Paint(),20f));
Note: I used synchronized for simplicity, but there are better alternatives to it like atomic flags.These are required only if you want to add/remove sprites while game loop is running. If you setup all sprites before starting game loop, this is not a problem.
I am adding bodies with fixtures to a box2d world in libgdx.
I want to detect if the user has touched (clicked) an object.
How do I do this? thanks
You should use libgdx Stage in order to detect touch events on Actors (what you are referring to them as Objects here). The best practice is to map a box2d body to a stage actor which makes it quite simple to do things like that.
To detect touch:
Implement touchDown method of the InputProcessor interface such that:
You have to transform your screen coordinates to stage coordinates using stage.toStageCoordiantes(...) method.
Use the transformed coordinates to detect hit on the Actor (Object) on stage using stage.hit(x, y).
stage.hit(x, y) will return you the actor if a hit is detected.
Hope that helps.
User touches the body only if he touches some of Fixture's contained into this body. This mean, you can check every Fixture of Body using testPoit() method:
public class Player {
private Body _body;
public boolean isPointOnPlayer(float x, float y){
for(Fixture fixture : _body.getFixtureList())
if(fixture.testPoint(x, y)) return true;
return false;
}
}
Next, you need to create InputAdapter like this:
public class PlayerControl extends InputAdapter {
private final Camera _camera;
private final Player _player;
private final Vector3 _touchPosition;
public PlayerControl(Camera camera, Player player) {
_camera = camera;
_player = player;
// create buffer vector to make garbage collector happy
_touchPosition = new Vector3();
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
// don't forget to unproject screen coordinates to game world
_camera.unproject(_touchPosition.set(screenX, screenY, 0F));
if (_player.isPointOnPlayer(_touchPosition.x, _touchPosition.y)) {
// touch on the player body. Do some stuff like player jumping
_player.jump();
return true;
} else
return super.touchDown(screenX, screenY, pointer, button);
}
}
And the last one - setup this processor to listen user input:
public class MyGame extends ApplicationAdapter {
#Override
public void create () {
// prepare player and game camera
Gdx.input.setInputProcessor(new PlayerControl(cam, player));
}
Read more about touch handling here