I've been learning about libgdx recently. In the process of following the instructions on their libgdx wiki I ran into some problems.
Specifically in the GameScreen class at the 99th line I changed the code inside so that it goes back to the previous screen (MainMenuScreen class) and yes you see when the mouse is pressed it worked (I mean go back to the screen before ) but a very very short time after, the screen AUTOMATICALLY switches to the GameScreen class (like I click the mouse once but it makes me 1 more redundant task). I guess when I click on the GameScreen screen it did the code in the if statement on line 99 to go to MainMenuScreen screen. In that screen at line 32 I guess it was true after I got to this screen because when I change the key is listened then it works fine (only converts once). I was intending to try implementing InputProcessor on each screen class but now I'm avoiding it for some reason. Can someone give me some advice recommend.Thank you
Here is the source code for the MainMenuScreen class.
package com.mygdx.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.utils.ScreenUtils;
//import com.mygdx.game.Drop;
public class MainMenuScreen implements Screen {
final Drop game;
OrthographicCamera camera;
public MainMenuScreen(final Drop gam) {
game = gam;
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
}
#Override
public void render(float delta) {
ScreenUtils.clear(0, 0, 0.2f, 1);
camera.update();
game.batch.setProjectionMatrix(camera.combined);
game.batch.begin();
game.font.draw(game.batch, "Welcome to Drop!!! ", 100, 150);
game.font.draw(game.batch, "Tap anywhere to begin!", 100, 100);
game.batch.end();
if (Gdx.input.isTouched()) { //I guess right after switching to this screen this conditional sentence was true before
game.setScreen(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() {
}
}
Here is source code for the GameScreen class
package com.mygdx.game;
import java.util.Iterator;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.utils.TimeUtils;
public class GameScreen implements Screen {
final Drop game;
Texture dropImage;
Texture bucketImage;
//Sound dropSound;
//Music rainMusic;
OrthographicCamera camera;
Rectangle bucket;
Array<Rectangle> raindrops;
long lastDropTime;
int dropsGathered;
public GameScreen(final Drop gam) {
this.game = gam;
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("drop.png"));
bucketImage = new Texture(Gdx.files.internal("bucket.png"));
// load the drop sound effect and the rain background "music"
//dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
//rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
//rainMusic.setLooping(true);
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 800, 480);
// create a Rectangle to logically represent the bucket
bucket = new Rectangle();
bucket.x = 800 / 2 - 64 / 2; // center the bucket horizontally
bucket.y = 20; // bottom left corner of the bucket is 20 pixels above
// the bottom screen edge
bucket.width = 64;
bucket.height = 64;
// create the raindrops array and spawn the first raindrop
raindrops = new Array<Rectangle>();
spawnRaindrop();
}
private void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800 - 64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
#Override
public void render(float delta) {
// clear the screen with a dark blue color. The
// arguments to clear are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
ScreenUtils.clear(0, 0, 0.2f, 1);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
game.batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
game.batch.begin();
game.font.draw(game.batch, "Drops Collected: " + dropsGathered, 0, 480);
game.batch.draw(bucketImage, bucket.x, bucket.y);
for (Rectangle raindrop : raindrops) {
game.batch.draw(dropImage, raindrop.x, raindrop.y);
}
game.batch.end();
// process user input
if (Gdx.input.justTouched()) { //This conditional works fine
/* Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
*/
game.setScreen(new MainMenuSceen(game)); //Screen switch
}
if (Gdx.input.isKeyPressed(Keys.LEFT))
bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Keys.RIGHT))
bucket.x += 200 * Gdx.graphics.getDeltaTime();
// make sure the bucket stays within the screen bounds
if (bucket.x < 0)
bucket.x = 0;
if (bucket.x > 800 - 64)
bucket.x = 800 - 64;
// check if we need to create a new raindrop
if (TimeUtils.nanoTime() - lastDropTime > 1000000000)
spawnRaindrop();
// move the raindrops, remove any that are beneath the bottom edge of
// the screen or that hit the bucket. In the later case we play back
// a sound effect as well.
Iterator<Rectangle> iter = raindrops.iterator();
while (iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if (raindrop.y + 64 < 0)
iter.remove();
if (raindrop.overlaps(bucket)) {
dropsGathered++;
//dropSound.play();
iter.remove();
}
}
}
#Override
public void resize(int width, int height) {
}
#Override
public void show() {
// start the playback of the background music
// when the screen is shown
//rainMusic.play();
}
#Override
public void hide() {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
dropImage.dispose();
bucketImage.dispose();
//dropSound.dispose();
//rainMusic.dispose();
}
}
Here is source for Drop class
package com.mygdx.game;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
public class Drop extends Game {
SpriteBatch batch;
BitmapFont font;
public void create() {
batch = new SpriteBatch();
// Use LibGDX's default Arial font.
font = new BitmapFont();
this.setScreen(new MainMenuScreen(this));
}
public void render() {
super.render(); // important!
}
public void dispose() {
batch.dispose();
font.dispose();
}
}
Your analysis of the problem seems correct to me. The method Gdx.input.isTouched() will immediately return true, if the screen is still being touched after you changed to the main menu.
Also you the solution that you already tried seems correct:
I was intending to try implementing InputProcessor on each screen class
When using an InputProcessor you will get one event (the method call to touchDown or touchUp) when the screen is touched, and don't need to pull the touch event using the isTouched method.
A problem when implementing InputProcessor with both classes probably is, that you can only set one to be the input processor of the game using the method Gdx.input.setInputProcessor. (When setting the second input processor, the first one is removed).
A solution to this problem is the InputMultiplexer. You can add this multiplexer as the input processor of the game (using Gdx.input.setInputProcessor(multiplexer)) and then add your input processors (the main menu and game objects) to this multiplexer: multiplexer.addProcessor(mainMenu) or ((InputMultiplexer) Gdx.input.getInputProcessor()).addProcessor(game).
This way you can handle touch events instead of pulling the touched state in both of your classes.
Related
I want a camera to follow an Actor. I've watched a lot of tutorials on how to do this but I get a weird bug.
The Actor i created called "skeleton" seems to move away from the camera and not on the X axis.
The camera moves over static sprites fine.
I used 3 different types of moving the actor. None of them seem to work.
Sry for the bad code aswell.
Playscreen:
public class PlayScreen implements Screen {
OrthographicCamera camera;
Table table;
Stage stage;
Viewport viewport;
private MyGdxGame game;
skeleton skeleton;
public PlayScreen(MyGdxGame game){
this.game = game;
camera = new OrthographicCamera(1f, (Gdx.graphics.getHeight()/Gdx.graphics.getWidth()));
viewport =new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),camera);
skeleton = new skeleton();
stage = new Stage(new ScreenViewport());
skeleton.setPosition((Gdx.graphics.getWidth()/2)-(skeleton.getWidth()/2),((Gdx.graphics.getHeight()/1.75f)-(skeleton.getHeight()/1.4f)));
stage.addActor(skeleton);
Gdx.input.setInputProcessor(stage);
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(1,0,0,1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
skeleton.moveRight();
camera.position.x = skeleton.getX() + skeleton.getOriginX();
camera.position.y = skeleton.getY() + skeleton.getOriginY();
camera.update();
game.batch.setProjectionMatrix(camera.combined);
Gdx.input.setInputProcessor(stage);
stage.act(Gdx.graphics.getDeltaTime());
stage.draw();
}
#Override
public void resize(int width, int height) {
viewport.update(width,height);
}
Skeleton Actor:
public class skeleton extends Actor {
SpriteBatch batch;
Texture img;
int frame = 0;
int zeile = 0;
TextureRegion[][] regions;
public Sprite sprite;
public skeleton(){
batch = new SpriteBatch();
img = new Texture(Gdx.files.internal("Warrior Skeleton Animations/Walking/walk.png"));
//ANIMATION
regions = TextureRegion.split(img,572,953);
sprite = new Sprite(regions[0][0]);
code to make it an animation...
(works fine don't wont to enlarge the code here any further)
setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
}
public void moveRight(){
//First way of moving
MoveByAction mba = new MoveByAction();
mba.setAmount(10f,0f);
mba.setDuration(1/10f);
skeleton.this.addAction(mba);
//Second way of moving
//this.setPosition(this.getX()+10,this.getY());
//setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
//Third way of moving
//sprite.translateX(1);
//setBounds(sprite.getX(),sprite.getY(),sprite.getWidth(),sprite.getHeight());
}
#Override
protected void positionChanged() {
sprite.setPosition(getX(),getY());
super.positionChanged();
}
#Override
public void draw(Batch batch, float parentAlpha){
sprite.draw(batch);
}
#Override
public void act(float delta) {
super.act(delta);
}
}
I've tried moving the camera independently from the skeleton actor like this: camera.position.x = camera.position.x + 100; And the actor still moves away from the camera even if the camera moves faster than the actor.
I also tried moving the camera with the coordinates of the sprite itself from the skeleton actor. Same error though.
Thanks in advance.
I found a way to fix it for me.
If i move the camera itself and not the actor, I get the effect of the actor being moved. This proofs that my actor is bound to my Camera.
I would still love to know why that is and how to fix it though.
so I am doing what is suppose to be a simple game but I think I might be complicating things. I have a camera for the GameScreen and a viewPort which follow the position of the player and when it reaches to points on the sides, the camera stops following the player and stays in one point.
This by itself works fine, but then I wanted to add the pause menu and some other features in the game, creating a hud class with its own camera and viewport as well as a Stage and a shapeRenderer.
The problem comes when I create the instance of this hud inside my gameScreen, the camera that I am looking while I am playing looks like is the hudCam, which does not follow the player and basically does not let me see the player when it reaches the edges of the screen.
This is my GameScreen Class:
public class GameScreen implements Screen {
WowInvasion game;
ScrollingBackground background;
private OrthographicCamera gameCam;
private Viewport gameViewPort;
/*
Basically I wanna keep the same sprites running while in the menu, playing and till dead
therefore, I'll have a switch statement with cases on where the games at, inside the functions needed. That way I'll keep
the game has a background for the menu and there's no need for running a second screen.
*/
public static final int MAIN_MENU = 0;
public static final int GAME = 1;
private static int state = 1; //current state. starts with MAIN_MENU //DEBUGGING GAME SCREEN
//STAGES
private GameStage gameStage; //game ui
private menuStage mainMenu; //Main menu of the game
private Hud hud;
//Resources
private TextureAtlas atlas; //for the textures most
private Skin skin; //for the styles and fonts
//Sprites
private Player player;
//Shapes
private float progressPower; //for the power to build up
private final float POWER_CHARGED = 1000; //limit to get power
private final float DECREASING_POWER = 20; //limit to get power
public GameScreen(WowInvasion game){
this.game = game;
gameCam = new OrthographicCamera();
gameCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
gameViewPort = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, gameCam);
progressPower = 0f;
game.wowAssetManager.loadTexturesGameScreen(); // tells our asset manger that we want to load the images set in loadImages method
game.wowAssetManager.loadSkins(); //load the needed skins
game.wowAssetManager.manager.finishLoading(); // tells the asset manager to load the images and wait until finsihed loading.
skin = game.wowAssetManager.manager.get("ui/menuSkin.json");
}
#Override
public void show() {
game.batch.setProjectionMatrix(gameCam.combined);
background = new ScrollingBackground();
atlas = game.wowAssetManager.manager.get(WowAssetManager.GAME_ATLAS); //declaring atlas
mainMenu = new menuStage(gameViewPort, game.batch, skin, game); //pass it so that we only use one batch and one same viewport
gameStage = new GameStage(gameViewPort, game.batch, skin, game);
hud = new Hud(game.batch, skin);
player = new Player(atlas.findRegion("player"), (int) gameCam.viewportWidth / 2, (int) gameCam.viewportHeight / 2);
switch(state){
case MAIN_MENU:
Gdx.input.setInputProcessor(mainMenu);
break;
case GAME:
background.setFixedSpeed(false); //does not work in here
}
}
#Override
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if(state == GAME) {
background.setFixedSpeed(false);
player.update(delta, gameCam.viewportWidth, gameCam.viewportHeight); //updating player for movement
//really cheap way to charge power with velocity
if(progressPower != POWER_CHARGED) {
progressPower += Math.abs(player.getVelocity().x) + Math.abs(player.getVelocity().y);
progressPower -= DECREASING_POWER;
}
else
progressPower = POWER_CHARGED / 4;
}
mainMenu.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f)); //updating while making sure delta won't be more than 1/30f.
gameStage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
game.batch.begin();
background.updateAndRender(delta, game.batch); //updating scrolling background
player.draw(game.batch);
game.batch.end();
mainMenu.draw(); //draw the menu stage
gameStage.draw(); //draw the ui stage for the game
hud.getStage().draw();
hud.renderRotateMeter();
updateCamera(0, WowInvasion.WIDTH);
System.out.println(player.getPosition().x);
}
public void updateCamera(float startX, float endX){
Vector3 position = gameCam.position;
//linear interpolation : a + (b - a) * lerp
//b = player position
//a = current camera position
//lerp = interpolation factor
position.x = gameCam.position.x + (player.getPosition().x - gameCam.position.x) * .1f;
//making the camera stay when the player gets to close to the sides
if(position.x < startX) {
position.x = startX;
}
if(position.x > endX){
position.x = endX;
}
gameCam.position.set(position);
gameCam.update();
}
#Override
public void resize(int width, int height) {
gameViewPort.update(width, height);
//hud.getViewport().update(width, height);
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
dispose();
}
#Override
public void dispose() {
mainMenu.dispose();
gameStage.dispose();
game.dispose();
hud.dispose();
}
public static void setState(int state) {
GameScreen.state = state;
}
}
And this is my HUD:
public class Hud implements Disposable{
private Stage stage;
private Viewport viewport;
Button buttonPause, buttonResume;
private OrthographicCamera hudCam;
private ShapeRenderer sp; //like a batch for shapes
public Hud(SpriteBatch sb, Skin skin){
hudCam = new OrthographicCamera();
hudCam.setToOrtho(false, WowInvasion.WIDTH, WowInvasion.HEIGHT);
viewport = new StretchViewport(WowInvasion.WIDTH, WowInvasion.HEIGHT, hudCam);
stage = new Stage(viewport, sb);
sp = new ShapeRenderer();
Table table = new Table();
table.top();
//this makes the table the size of the stage
table.setFillParent(true);
buttonPause = new Button(skin, "pause");
buttonPause.setTransform(true);
buttonPause.addListener(new ClickListener(){ //listener to handle event
#Override
public void clicked(InputEvent event, float x, float y) {
}
});
buttonResume = new Button(skin, "resume");
buttonResume.setTransform(true);
buttonResume.setScale(0.5f);
buttonResume.addListener(new ClickListener(){
#Override
public void clicked(InputEvent event, float x, float y) {
buttonResume.setVisible(false);
}
});
table.add(buttonPause);
table.row();
table.add(buttonResume);
stage.addActor(table);
}
public void renderRotateMeter(){
sp.setProjectionMatrix(hudCam.combined);
sp.begin(ShapeRenderer.ShapeType.Filled);
sp.setColor(Color.YELLOW);
sp.rect(hudCam.position.x,hudCam.position.y, WowInvasion.WIDTH / 2, 20);
sp.end();
}
public Viewport getViewport() {
return viewport;
}
public Stage getStage() {
return stage;
}
#Override
public void dispose() {
stage.dispose();
sp.dispose();
}
}
thanks in advance!
EDIT
so I tried passing the gameCam has a parameter to the hud and instead of making a new OrthographicCamera I used that one has the hudCamara as well and well, the movement with the player is perfect except now the thins from the Hud do not move at all..
It looks like you only set projectionMatrix to only HUD camera as seen in
sp.setProjectionMatrix(hudCam.combined);
Try to set it the same to other stuff outside of the HUD class prior to draw call too.
Another thing to keep in mind is that, when you involve using multiple Viewport and Camera in the game as most of the time it will be 1 Viewport matching with 1 Camera and work with another set as in your case. In draw call, you need to call apply() or apply(true) of Viewport class too to tell the system that you will draw based on which viewport thus in turn it will adhere to screen coordinate that was set up by viewport's attaching camera.
So assume you have 2 objects that needed to be called in different viewport consecutively, do it like the following code. The methods call is correct according to libgdx API but variable names are fictional.
// draw objA adhering to viewportA (thus cameraA) <-- assume it's player cam
sb.setProjectionMatrix(cameraA.combined);
viewportA.apply();
objA.draw();
// draw objB adhering to viewportB (thus cameraB) <-- assume it's HUD cam
sb.setProjectionMatrix(cameraB.combined);
viewportB.apply(true); // send in true as for HUD, we always want to center the screen
objB.draw();
In summary, 2 things to keep in mind when drawing objects that use multiple of camera and viewport in consecutive draw call.
Set projection matrix to either SpriteBatch or ShapeRenderer.
Call apply() or apply(true) of Viewport class to let it know you work with this viewport.
I am creating a game and trying to set up a splash screen.
Whenever I render the sprite that i want to tween to by using the sprite.draw method which looks like this:
#Override
public void render(float delta)
{
Gdx.gl20.glClearColor(0.2F, 0.5F, 1F, 1F);
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
tm.update(delta);
cam.update();
sb.setProjectionMatrix(cam.combined);
sb.begin();
Assets.splash_spr_bg.draw(sb);
sb.end();
}
The tweening works great except i can only see 1/4 of the picture on my screen, it is completely out of position.
And whenever I try to use this code in order to render the sprite by using the spritebatch to draw it, which looks like this:
#Override
public void render(float delta)
{
Gdx.gl20.glClearColor(0.2F, 0.5F, 1F, 1F);
Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT);
tm.update(delta);
cam.update();
sb.setProjectionMatrix(cam.combined);
sb.begin();
sb.draw(Assets.splash_spr_bg, 0, 0);
sb.end();
}
I can see the background great, great quality, correct size and position and all that. However, the tweening doesn't work at all; nothing happens.
Why does this not work? How can I fix it?
Here is some other code.
Initializion of the TweenHandler class:
package com.heavenapps.jumpdodge.handlers;
import com.badlogic.gdx.graphics.g2d.Sprite;
import aurelienribon.tweenengine.TweenAccessor;
public class TweenHandler implements TweenAccessor<Sprite>
{
public static final int ALPHA = 1;
#Override
public int getValues(Sprite target, int tweenType, float[] returnValues)
{
switch(tweenType)
{
case ALPHA:
returnValues[0] = target.getColor().a;
return 1;
default:
return 0;
}
}
#Override
public void setValues(Sprite target, int tweenType, float[] newValues)
{
switch(tweenType)
{
case ALPHA:
target.setColor(1, 1, 1, newValues[0]);
break;
}
}
}
Initializion of the sprite/texture:
package com.heavenapps.jumpdodge.handlers;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
public class Assets
{
public static Texture splash_tex_bg;
public static Sprite splash_spr_bg;
public static void init()
{
// Splash Screen
splash_tex_bg = new Texture(Gdx.files.internal("Splash Screen/Background.png"));
splash_tex_bg.setFilter(TextureFilter.Linear, TextureFilter.Linear);
splash_spr_bg = new Sprite(splash_tex_bg);
splash_spr_bg.setOrigin(splash_spr_bg.getWidth() / 2, splash_spr_bg.getHeight() / 2);
splash_spr_bg.setPosition(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
splash_spr_bg.setColor(1, 1, 1, 0);
}
}
Usage of the tweening:
public void fadeSplashScreen()
{
Tween.to(Assets.splash_spr_bg, TweenHandler.ALPHA, 2F).target(1).ease(TweenEquations.easeInBounce).start(tm);
}
You need to set the sprite's position if you want to draw it using your first method. (sprite.draw(spriteBatch)). And you need to use your first method if you want to use the sprite's color, which is being controlled by the tween.
It looks like you did give the background sprite an initial position, but you put it off screen. So change
splash_spr_bg.setPosition(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
to
splash_spr_bg.setPosition(0, 0);
The reason the second method you showed (spriteBatch.draw(sprite, x, y)) draws it in the correct position is that this method of drawing ignores the fact that it's a sprite and just draws the texture region owned by the sprite in whatever position you give it. And this method doesn't fade the sprite because it's ignoring the fact that it is a sprite (which has a color).
I want to make a simple flappy bird clone while I'm in vacation(The firt time I try game programming since high school) so after looking at ligdx wiki and sort of adapting the code to fit my needs I had something like this:
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Rectangle;
public class FlappyBall implements ApplicationListener {
Texture ballImage;
Texture chaoImage;
Texture canoImage;
OrthographicCamera camera;
SpriteBatch batch;
Rectangle ball;
int score = 0;
final float gravity = 9.0f;
#Override
public void create() {
ballImage = new Texture(Gdx.files.internal("crystalball_small.png"));
//chaoImage = new Texture(Gdx.files.internal("Flappy-Ground.png"));
camera = new OrthographicCamera(800,480);
camera.setToOrtho(false, 800, 480);
batch = new SpriteBatch();
ball = new Rectangle();
ball.x = 128;
ball.y = 20;
ball.width = 64;
ball.height = 64;
}
#Override
public void dispose() {
ballImage.dispose();
//chaoImage.dispose();
}
#Override
public void render() {
// cleans the screen
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// rendenring ball
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(ballImage, ball.x, ball.y);
batch.end();
// jump(not tested)
if (Gdx.input.isTouched()) {
ball.y += 10;
}
// apply gravity(not tested)
while (ball.y < 480)
ball.y -= gravity * Gdx.graphics.getDeltaTime();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
however this code ends up showing a blank screen that can't even be closed when I press the X button(in order to close it I need to terminate its process in eclipse or use the xkill command :/)
Ps.:That's my Main.java file:
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
public class Main {
public static void main(String[] args) {
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
cfg.title = "Flappy Ball";
cfg.useGL20 = false;
cfg.width = 800;
cfg.height = 480;
new LwjglApplication(new FlappyBall(), cfg);
}
}
Ps².:The compiler shows no errors when this code is being executed
Thanks :D
Your render method will probably never finish it's first frame. The while loop will keep going until ball.y becomes greater than or equal to 480. This will never happen because you are subtracting a positive number.
while (ball.y < 480)
ball.y -= gravity * Gdx.graphics.getDeltaTime();
I would rather change it in something like this:
float change = gravity * Gdx.graphics.getDeltaTime();
if(ball.y - change >= 0)
ball.y -= change;
I'm currently trying to make a breakout clone using java and libgdx. I'm currently experiencing trouble getting the ball to bounce off of the blocks at the appropriate angle. In short the problem I'm having is that the ball moves 12 pixels every frame and doesn't always line up with the edge of a brick. If anyone has any suggestions on a better way to move the ball or a different way to check collision it would be much appreciated!
Main game class
package com.kyleparker.breakout;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;
public class BreakoutGameScreen implements ApplicationListener {
Texture dropImage;
Sound dropSound;
Music rainMusic;
SpriteBatch batch;
OrthographicCamera camera;
Rectangle bucket;
Paddle paddle;
//Brick bricks[];
Array<Brick> bricks;
Ball ball;
#Override
public void create() {
// load the images for the droplet, 64x64 pixels
dropImage = new Texture(Gdx.files.internal("droplet.png"));
// load the drop sound effect and the rain background "music"
dropSound = Gdx.audio.newSound(Gdx.files.internal("drop.wav"));
rainMusic = Gdx.audio.newMusic(Gdx.files.internal("rain.mp3"));
// start the playback of the background music immediately
rainMusic.setLooping(true);
rainMusic.play();
// create the camera and the SpriteBatch
camera = new OrthographicCamera();
camera.setToOrtho(false, 1280, 720);
batch = new SpriteBatch();
paddle = new Paddle(new Texture(Gdx.files.internal("bucket.png")));
bricks = new Array<Brick>();
populateBricks();
ball = new Ball(new Texture(Gdx.files.internal("bucket.png")), paddle, bricks);
}
private void populateBricks() {
bricks.add(new Brick(200,100));
for (int i = 0; i < 5; i++) {
for (int j = 0; j <= 7; j++) {
bricks.add(new Brick (j * 144 + 76, i * 80 + 300)); //Offsets each new brick
}
}
}
#Override
public void render() {
// clear the screen with a dark blue color. The
// arguments to glClearColor are the red, green
// blue and alpha component in the range [0,1]
// of the color to be used to clear the screen.
Gdx.gl.glClearColor(0, 0, 0.2f, 1);
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
// tell the camera to update its matrices.
camera.update();
// tell the SpriteBatch to render in the
// coordinate system specified by the camera.
batch.setProjectionMatrix(camera.combined);
// begin a new batch and draw the bucket and
// all drops
batch.begin();
paddle.render(batch, camera);
ball.move();
ball.render(batch, camera);
for (int x = bricks.size - 1; x > 0; x--) {
bricks.get(x).render(batch,camera);
}
batch.end();
}
#Override
public void dispose() {
// dispose of all the native resources
dropImage.dispose();
dropSound.dispose();
rainMusic.dispose();
batch.dispose();
paddle.dispose();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
}
Ball class
package com.kyleparker.breakout;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.utils.Array;
public class Ball{
Texture ballImage;
Rectangle ball;
private int xdir;
private int ydir;
Paddle paddle;
Array<Brick> bricks;
final int BALL_SPEED = 12;
public Ball(Texture ballImage, Paddle paddle, Array<Brick> bricks) {
// load the ball image
this.ballImage = ballImage;
xdir = 1;
ydir = -1;
// create a Rectangle for the balls collision
ball = new Rectangle();
ball.x = 1280 / 2 - 64 / 2; // center the ball
ball.y = 100; // put the ball 200px away from the bottom of the screen
ball.width = 64;
ball.height = 64;
this.paddle = paddle;
this.bricks = bricks;
}
public void render(SpriteBatch batch, OrthographicCamera camera) {
// draw the paddle onto the batch of the level
batch.draw(ballImage, ball.x, ball.y);
}
public void move() {
ball.x += xdir * BALL_SPEED;
ball.y += ydir * BALL_SPEED;
if (ball.x <= 0) {
setXDir(1);
}
if (ball.x >= 1280 - 64) {
setXDir(-1);
}
if (ball.y <= 0) {
setYDir(1);
}
if (ball.y >= 720 - 64) {
setYDir(-1);
}
if (ball.overlaps(paddle.getRect())) {
setYDir(1);
}
for (int i = 0; i < bricks.size; i++) {
if (ball.overlaps(bricks.get(i).getRect())) {
if ((ball.x == (bricks.get(i).getRect().x + 128)))
{
setXDir(1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision RIGHT");
}
if (((ball.x + 64) == bricks.get(i).getRect().x))
{
setXDir(-1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision LEFT");
}
if ((ball.y == (bricks.get(i).getRect().y + 64)))
{
setYDir(1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision TOP");
}
if (((ball.y + 64) == bricks.get(i).getRect().y))
{
setYDir(-1);
bricks.get(i).setDestroyed(true);
System.out.println("Collision BOTTOM");
}
}
}// end of for
}
public void setXDir(int x) {
xdir = x;
}
public void setYDir(int y) {
ydir = y;
}
public int getYDir() {
return ydir;
}
public int getXDir() {
return xdir;
}
public Rectangle getRect() {
// return the collision rectangle for checking overlaps
return ball;
}
public void dispose() {
// dispose of all the native resources
ballImage.dispose();
}
}// end of class
Brick code just in case
package com.kyleparker.breakout;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
public class Brick{
Texture brickImage;
Rectangle brick;
boolean destroyed;
public Brick(int x, int y) {
brickImage = new Texture(Gdx.files.internal("brick.png"));
// create a Rectangle for the bricks collision
brick = new Rectangle();
brick.x = x;
brick.y = y;
brick.width = 128;
brick.height = 64;
destroyed = false;
}
public void render(SpriteBatch batch, OrthographicCamera camera) {
// draw the brick onto the batch of the level
batch.draw(brickImage, brick.x, brick.y);
}
public boolean isDestroyed() {
// return the collision rectangle for checking overlaps
return destroyed;
}
public void setDestroyed(boolean destroyed)
{
this.destroyed = destroyed;
if (this.destroyed == true) {
dispose();
brick.x = -1000;
brick.y = -1000;
}
}
public Rectangle getRect() {
return brick;
}
public void dispose() {
// dispose of all the native resources
brickImage.dispose();
}
}
Don't worry about the fact that the ball doesn't always line up with an object for which the collision needs handled -- that's not actually relevant. You can (and should) handle your collisions less 'precisely.' That is, the ball's path is fixed, so you can calculate its position at any future point. Check its position, calculate its position in the next frame (which you have to do to draw it anyway), and add some code to handle the collision that is going to happen, rather than trying to detect and handle the collision which has happened. You can slow down the ball if you really want a clean reflection, or you can speed up your framerate, or you can let the ball be partially 'absorbed' by the object before it reflects:
public class Ball {
. . .
public void move() {
. . .
if (collisionObject.overlaps(new Rectangle(ball.x + xdir, ball.y + ydir, ball.width, ball.height))) {
//a collision will have occurred in the next frame
//handle the collision however you please
}
}
}
I also note that your BALL_SPEED field is inaccurately named. As presently coded, the ball always moves at a 45° angle, with a speed of about 17 pixels per frame (in that direction). You've coded its x- and y-offset as 12 pixels, but if (when?) you change the ball's direction, you'll find that the speed fluctuates wildly depending on what values are placed in for the xdir and ydir fields. For example, if you were to (somewhat) randomize these, but keep the rest of your code as-is, you might find that xdir = 2 and ydir = 4 on one instance, and xdir = 6 and ydir = 12 on another. Note these describe the same direction, but the second version will move three times as fast.
To properly handle the ball's direction and speed, assign an angle, and calculate the xdir and ydir values through the appropriate trigonometric functions (xdir = BALL_SPEED * Math.cos(ballAngle) and ydir = BALL_SPEED * Math.sin(ballAngle)).
I would use box2d for the whole thing. That you haven't used box2d probably means you have no experience in it, so that would be a little hurdle, but I'm sure you'll be able to wrap your head around it quickly. Here's a link for that: http://code.google.com/p/libgdx/wiki/PhysicsBox2D