I'm trying to draw a curve line using BSpline and ShapeRenderer.
With perspective camera I found myself able to do that.
If I try to use OrthographicCamera, instead, nothing gets rendered on the screen.
My goal is to draw a BSPline with ShapeRenderer and a Sprite that follow the same design as the Shape.
Can anyone tell me how to fix this / what I am doing wrong?
thanks a lot
public class MyGdxGame extends GdxTest implements ApplicationListener {
private SpriteBatch spriteBatch;
ParticleEffect effect;
int emitterIndex;
Array<ParticleEmitter> emitters;
int particleCount = 10;
float fpsCounter;
InputProcessor inputProcessor;
int CAMERA_WIDTH = 640; //Gdx.graphics.getWidth(); if use this function an exception is arose
int CAMERA_HEIGHT = 480; //Gdx.graphics.getHeight(); if use this function an exception is arose
BspHbCached hb;
ShapeRenderer renderer;
int nPoints;
private OrthographicCamera camera;
#Override
public void create () {
hb = new BspHbCached(CAMERA_WIDTH/4, CAMERA_WIDTH/4); // caching points for curve to draw
renderer = new ShapeRenderer();
nPoints = hb.getNumSamplePoints();
camera = new OrthographicCamera(CAMERA_WIDTH, CAMERA_HEIGHT);
camera.position.set(CAMERA_WIDTH / 2f, CAMERA_HEIGHT / 2f, 0);
camera.update();
spriteBatch = new SpriteBatch();
effect = new ParticleEffect();
effect.load(Gdx.files.internal("data/test.p"), Gdx.files.internal("data"));
effect.setPosition(Gdx.graphics.getWidth() / 2, Gdx.graphics.getHeight() / 2);
// Of course, a ParticleEffect is normally just used, without messing around with its emitters.
emitters = new Array(effect.getEmitters());
effect.getEmitters().clear();
effect.getEmitters().add(emitters.get(0));
inputProcessor = new InputProcessor() {
public boolean touchUp (int x, int y, int pointer, int button) {
return false;
}
Here are my other important methods:
public void render () {
float delta = Gdx.graphics.getDeltaTime();
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
renderer.setProjectionMatrix(camera.combined);
spriteBatch.setProjectionMatrix(camera.combined);
for(int i = 0; i < nPoints - 1; i++) // all points contained in bspline to draw
{
renderer.begin(ShapeType.Line);
renderer.setColor(Color.RED);
renderer.line(p1,p2);
renderer.end();
}
spriteBatch.begin();
effect.draw(spriteBatch, delta);
spriteBatch.end();
fpsCounter += delta;
if (fpsCounter > 3) {
fpsCounter = 0;
int activeCount = emitters.get(emitterIndex).getActiveCount();
Gdx.app.log("libgdx", activeCount + "/" + particleCount + " particles, FPS: " + Gdx.graphics.getFramesPerSecond());
}
}
}
NB: my starting point was Path Interface Splines
Related
I'm new to LibGDX and I'm trying to make a simple RPG game.
I've implemented a basic movement & combat system.
Now I would like to add a sidebar with inventory, character information etc. (like in Tibia).
Then I would like to add a bottom bar too.
However, I don't know how to accomplish that. I've read that adding 2nd stage could be a solution, but I don't know how implement it into my code.
screen how that supposed to look like
Here is my current code with render method:
public class Game extends ApplicationAdapter {
private final static int TURN_DURATION_IN_MILLIS = 2000;
TiledMap tiledMap;
OrthographicCamera gameCamera;
OrthographicCamera guiCamera;
private Map renderer;
Player player;
Monster rat;
private Map.Drawing playerDrawing;
private Map.Drawing ratDrawing;
private SpriteBatch sb;
BitmapFont font;
BitmapFont font2;
Stage stage;
float guiToCameraRatioX;
float guiToCameraRatioY;
long lastTurn;
#Override
public void create() {
sb = new SpriteBatch();
tiledMap = new TmxMapLoader().load("map.tmx");
renderer = new Map(tiledMap, 1 / TILE_CELL_IN_PX, sb);
gameCamera = new OrthographicCamera(NUMBER_OF_TILES_HORIZONTALLY, NUMBER_OF_TILES_VERTICALLY);
gameCamera.update();
guiCamera = new OrthographicCamera(GAME_WINDOW_WIDTH, GAME_WINDOW_HEIGHT);
guiCamera.update();
guiToCameraRatioX = guiCamera.viewportWidth / gameCamera.viewportWidth;
guiToCameraRatioY = guiCamera.viewportHeight / gameCamera.viewportHeight;
player = new Player();
playerDrawing = new Map.Drawing(true, null, player.positionX - 0.5f, player.positionY + 0.5f, player.width, player.height, gameCamera, guiCamera);
player.initHealthPointsBar();
renderer.addCreature(player);
stage = new TiledMapStage(tiledMap, player);
Gdx.input.setInputProcessor(stage);
stage.getViewport().setCamera(gameCamera);
rat = new Monster(MonsterType.RAT);
ratDrawing = new Map.Drawing(false, null, rat.positionX, rat.positionY, rat.width, rat.height, gameCamera, guiCamera);
renderer.addDrawing(ratDrawing);
renderer.addDrawing(playerDrawing);
rat.initHealthPointsBar();
renderer.addCreature(rat);
initFont();
}
private void initFont() {
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("martel.ttf"));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 10;
parameter.color = new Color(0, 0.75f, 0.15f, 1);
parameter.borderWidth = 1.2f;
font = generator.generateFont(parameter);
parameter.size = 12;
parameter.color = new Color(0.8f, 0.1f, 0.1f, 1);
font2 = generator.generateFont(parameter);
generator.dispose();
}
#Override
public void render() {
Gdx.gl.glClearColor(1, 0, 0, 1);
Gdx.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float deltaTime = Gdx.graphics.getDeltaTime();
player.updateState(deltaTime);
rat.updateState(deltaTime);
gameCamera.position.set(player.positionX + 0.5f, player.positionY + 0.5f, 0);
gameCamera.update();
renderer.setView(gameCamera);
renderer.render();
stage.act();
playerDrawing.x = player.positionX;
playerDrawing.y = player.positionY;
ratDrawing.x = rat.positionX;
ratDrawing.y = rat.positionY;
//TODO implement real combat system
if (System.currentTimeMillis() - lastTurn >= TURN_DURATION_IN_MILLIS) {
lastTurn = System.currentTimeMillis();
if (rat.state != Creature.State.DEAD && Math.abs(rat.positionX - player.positionX) < 2 && Math.abs(rat.positionY - player.positionY) < 2) {
player.givenHit = (int) (Math.random() * rat.attack + 1) * 2 - player.defence / 2;
player.currentHealthPoints -= player.givenHit;
rat.givenHit = (int) (Math.random() * player.attack + 1) * 2 - rat.defence / 2;
rat.currentHealthPoints -= rat.givenHit;
if (rat.currentHealthPoints <= 0) {
rat.currentHealthPoints = 0;
rat.state = Creature.State.DEAD;
rat.moveDestinationX = -1;
rat.moveDestinationY = -1;
}
} else {
player.givenHit = 0;
rat.givenHit = 0;
}
}
// used solution below https://stackoverflow.com/questions/20595558/libgdx-sprite-batch-font-bad-scale-rendering
player.renderPlayer(font, font2, playerDrawing, sb, guiCamera.position.x - (gameCamera.position.x - player.positionX) * guiToCameraRatioX, guiCamera.position.y - (gameCamera.position.y - player.positionY) * guiToCameraRatioY, player.givenHit, System.currentTimeMillis() - lastTurn);
rat.renderMonster(font, font2, ratDrawing, sb, guiCamera.position.x - (gameCamera.position.x - rat.positionX) * guiToCameraRatioX, guiCamera.position.y - (gameCamera.position.y - rat.positionY) * guiToCameraRatioY, rat.givenHit, System.currentTimeMillis() - lastTurn);
}
#Override
public void dispose() { // SpriteBatches and Textures must always be disposed
sb.dispose();
}
I appreciate any help :)
You already have a guiCamera in your Game class. This one can be used to draw the sidebar (or anything else that doesn't move with the rest of the map).
If this camera is already used (e.g. for a menu), you could create a new one, so it can be changed if needed (without changing the menu, or whatever it is used for).
Basically you just need to set the camera's projection matrix to you SpriteBatch (maybe better use a new SpriteBatch for this) and start drawing.
A solution could look like this:
private SpriteBatch sb2; // better use a different sprite batch here (and initialize it in your create method, just like the other one)
// ...
#Override
public void render() {
// ... other rendering stuff ...
// set the projection matrix
sb2.setProjectionMatrix(guiCamera.combined);
sb2.begin();
// draw whatever you want on your sidebar to appear
// all things you draw here will be static on the screen, because the guiCamera doesn't move with the player, but stays in it's position
// when you are ready drawing, end the SpriteBatch, so everything is actually drawn
}
sb2.end();
I am creating a top-down shooter game, and whenever I move the camera, or zoom, black likes appear like a grid
I am using Tiled to create the map, and I have the camera following my centered box2d body. I have found that making the camera position equal the position of the box2d body with an int cast results in the black lines disappearing like this:
The problem though, is that because I have the game scaled down, the player will move for a second or two and then when the player reaches the next whole number on either axis, the camera snaps to the player, which is not what I want for the game as it's jarring. The player's movement is granular, but, while rounded, the camera's is not. I do not know if this is a problem with my tile sheet or if it's something I can fix by altering some code. I have tried all different kinds of combinations of padding, and values of spacing and margins. So ultimately, how can I have the camera match the player's position smoothly and not cause the black lines? I'd greatly appreciate any help or recommendations. Thank you in advance!
Where I am type casting the player's float position to an int in game class:
public void cameraUpdate(float delta) {
//timeStep = 60 times a second, velocity iterations = 6, position iterations = 2
world.step(1/60f, 6, 2); //tells game how many times per second for Box2d to make its calculations
cam.position.x = (int)playerOne.b2body.getPosition().x;
cam.position.y = (int)playerOne.b2body.getPosition().y;
cam.update();
}
Majority of player class:
public class PlayerOne extends Sprite implements Disposable{
public World world; // world player will live in
public Body b2body; //creates body for player
private BodyDef bdef = new BodyDef();
private float speed = 1f;
private boolean running;
TextureAtlas textureAtlas;
Sprite sprite;
TextureRegion textureRegion;
private Sound runningSound;
public PlayerOne(World world) {
this.world = world;
definePlayer();
textureAtlas = new TextureAtlas(Gdx.files.internal("sprites/TDPlayer.atlas"));
textureRegion = textureAtlas.findRegion("TDPlayer");
sprite =new Sprite(new Texture("sprites/TDPlayer.png"));
sprite.setOrigin((sprite.getWidth() / 2) / DunGun.PPM, (float) ((sprite.getHeight() / 2) / DunGun.PPM - .08));
runningSound = Gdx.audio.newSound(Gdx.files.internal("sound effects/running.mp3"));
}
public void definePlayer() {
//define player body
bdef.position.set(750 / DunGun.PPM, 400 / DunGun.PPM);
bdef.type = BodyDef.BodyType.DynamicBody;
//create body in the world
b2body = world.createBody(bdef);
FixtureDef fdef = new FixtureDef();
CircleShape shape = new CircleShape();
shape.setRadius(12 / DunGun.PPM);
fdef.shape = shape;
b2body.createFixture(fdef);
}
public void renderSprite(SpriteBatch batch) {
float posX = b2body.getPosition().x;
float posY = b2body.getPosition().y;
float posX2 = (float) (posX - .14);
float posY2 = (float) (posY - .1);
sprite.setSize(32 / DunGun.PPM, 32 / DunGun.PPM);
sprite.setPosition(posX2, posY2);
float mouseX = Level1.mouse_position.x; //grabs cam.unproject x vector value
float mouseY = Level1.mouse_position.y; //grabs cam.unproject y vector value
float angle = MathUtils.atan2(mouseY - getY(), mouseX - getX()) * MathUtils.radDeg; //find the distance between mouse and player
angle = angle - 90; //makes it a full 360 degrees
if (angle < 0) {
angle += 360 ;
}
float angle2 = MathUtils.atan2(mouseY - getY(), mouseX - getX()); //get distance between mouse and player in radians
b2body.setTransform(b2body.getPosition().x, b2body.getPosition().y, angle2); //sets the position of the body to the position of the body and implements rotation
sprite.setRotation(angle); //rotates sprite
sprite.draw(batch); //draws sprite
}
public void handleInput(float delta) {
setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2 + (5 / DunGun.PPM));
this.b2body.setLinearVelocity(0, 0);
if(Gdx.input.isKeyPressed(Input.Keys.W)){
this.b2body.setLinearVelocity(0f, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.S)){
this.b2body.setLinearVelocity(0f, -speed);
}if(Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, 0f);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, speed);
}if(Gdx.input.isKeyPressed(Input.Keys.W) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, speed);
}
if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.A)){
this.b2body.setLinearVelocity(-speed, -speed );
}if(Gdx.input.isKeyPressed(Input.Keys.S) && Gdx.input.isKeyPressed(Input.Keys.D)){
this.b2body.setLinearVelocity(speed, -speed);
}
Where I declare the pixels per meter scale:
public class DunGun extends Game{
public SpriteBatch batch;
//Virtual Screen size and Box2D Scale(Pixels Per Meter)
public static final int V_WIDTH = 1500;
public static final int V_HEIGHT = 800;
public static final float PPM = 100; //Pixels Per Meter
Game render and resize methods:
#Override
public void render(float delta) {
cameraUpdate(delta);
playerOne.handleInput(delta);
//clears screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (Gdx.input.isButtonPressed(Input.Buttons.LEFT)) {
cam.zoom -= .01;
}
if (Gdx.input.isButtonPressed(Input.Buttons.RIGHT)) {
cam.zoom += .01;
}
mapRenderer.render();
b2dr.render(world, cam.combined); //renders the Box2d world
mapRenderer.setView(cam);
//render our game map
//mapRenderer.render(); // renders map
//mapRenderer.render(layerBackround); //renders layer in Tiled that p1 covers
game.batch.setProjectionMatrix(cam.combined); //keeps player sprite from doing weird out of sync movement
mouse_position.set(Gdx.input.getX(), Gdx.input.getY(), 0);
cam.unproject(mouse_position); //gets mouse coordinates within viewport
game.batch.begin(); //starts sprite spriteBatch
playerOne.renderSprite(game.batch);
game.batch.end(); //starts sprite spriteBatch
//mapRenderer.render(layerAfterBackground); //renders layer of Tiled that hides p1
}
#Override
public void resize(int width, int height) {
viewport.update(width, height, true); //updates the viewport camera
}
I solved it by fiddling around with the padding of the tilesets in GDX Texture Packer. I added 5 pixels of padding around the 32x32 tiles. I set the margins to 2, and spacing to 4 in Tiled. I had tried a lot of different combinations of padding/spacing/margins that didn't work which made me think it was a coding problem, but those settings worked, and I didn't have to round the floats.
I'm trying to load an explosion animation. The animations consists of 16 frames, all saved in the file Explosion.png. In my game, all the images are stored in a texture atlas pack.
So first i got the region that i needed from the class Assets.java
public class Explosion {
public final AtlasRegion explosion;
public Explosion (TextureAtlas atlas){
explosion = atlas.findRegion(Constants.EXPLOSION);
}
}
Then in my class which will create the explosion, I have the following code:
public Particles(Vector2 position){
this.position = position;
startTime = TimeUtils.nanoTime();
Array<TextureRegion> explosionAnimationTexture = new Array<TextureRegion>();
TextureRegion region = Assets.instance.explosion.explosion;
Texture explosionTexture = region.getTexture();
int ROW = 4; // rows of sprite sheet image
int COLUMN = 4;
TextureRegion[][] tmp = TextureRegion.split(explosionTexture, explosionTexture.getWidth() / COLUMN, explosionTexture.getHeight() / ROW);
TextureRegion[] frames = new TextureRegion[ROW * COLUMN];
int elementIndex = 0;
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COLUMN; j++) {
explosionAnimationTexture.add(tmp[i][j]);
frames[elementIndex++] = tmp[i][j];
}
}
explosion = new Animation(EXPLOSION_FRAME_DURATION,explosionAnimationTexture , Animation.PlayMode.LOOP_PINGPONG);
}
I'm using 4x4 since I have 16 frame. And inside the render method i got the following:
public void render(SpriteBatch batch){
float elapsedTime = MathUtils.nanoToSec * (TimeUtils.nanoTime() - startTime);
TextureRegion walkLoopTexture = explosion.getKeyFrame(elapsedTime);
batch.draw(
walkLoopTexture.getTexture(),
position.x,
position.y,
0,
0,
walkLoopTexture.getRegionWidth(),
walkLoopTexture.getRegionHeight(),
0.3f,
0.3f,
0,
walkLoopTexture.getRegionX(),
walkLoopTexture.getRegionY(),
walkLoopTexture.getRegionWidth(),
walkLoopTexture.getRegionHeight(),
false,
false);
}
The animation is working, however the images are loading from the whole atlas file, and not only Explosion.png as specified in step 1.
Code inside your Particles class :
TextureRegion region = Assets.instance.explosion.explosion;
TextureRegion[][] tmp = TextureRegion.split(explosionTexture, explosionTexture.getWidth() / COLUMN, explosionTexture.getHeight() / ROW);
replace with :
TextureRegion[][] tmp = region.split(region.getRegionWidth()/COLUMN,region.getRegionHeight()/ROW);
And
draw your Animation using textureRegion instead of his texture so choose appropriate method signature of SpriteBatch for drawing textureRegion.
I'm teaching myself LibGdx and was following the simple game tutorial, unfortunately majority of the code is in one class. I want to refactor the code so I can use multiple textures for the rain that falls based on a random number.
I'll attach the Code for the main program and then the class I got started on.
So far everything worked except the Rain texture/img does not show on the screen.
public class GameScreen implements Screen {
public static FruitHarvest game;
protected final Texture dropImage;
//protected final Texture dropImage2;
private final Texture bucketImage;
public static Rectangle bucket;
public static Sound dropSound;
//private static Music rainMusic;
private final OrthographicCamera camera;
public static Array<Rectangle> raindrops;
private long lastDropTime;
public static int dropsGathered;
// private int random = MathUtils.random(0,1);
private Drops drop;
//Iterator<Rectangle> iterator = raindrops.iterator();
public GameScreen(final FruitHarvest game) {
this.game = game;
// load the images for the droplet and the bucket, 64x64 pixels each
dropImage = new Texture(Gdx.files.internal("droplet.png"));
//dropImage2 = new Texture(Gdx.files.internal("droplet1.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 SpriteBatcher
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>();
long delta = 0;
drop = new Drops(dropImage, 64, 64, raindrops, delta);
}
#Override
public void render(float delta) {
// 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, .5f, 1);
Gdx.gl.glClear(GL20.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.
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, bucket.width, bucket.height);
// Draws the Items Falling
for (Rectangle raindrop : raindrops) {
game.batch.draw(dropImage, raindrop.x, raindrop.y);
}
game.batch.end();
// process user input
if (Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos);
bucket.x = touchPos.x - 64 / 2;
}
if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) bucket.x -= 200 * Gdx.graphics.getDeltaTime();
if (Gdx.input.isKeyPressed(Input.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() - drop.getLastDropTime() > 1000000000) {
drop.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 increase the value our drops counter
// and add a sound effect.
Iterator<Rectangle> iter = raindrops.iterator();
drop.update(delta);
// 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();
// }
// }
}
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();
}
// public void randomDrop(int value, float dropX, float dropY) {
// switch (value) {
// case 0:
// game.batch.draw(dropImage, dropX, dropY);
// break;
// case 1:
// //game.batch.draw(dropImage2, dropX, dropY);
// break;
// default:
// game.batch.draw(dropImage, dropX, dropY);
// break;
// }
// }
#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();
}
}
Heres my class for the drops
public class Drops {
private Rectangle raindrop;
private int imageHeight, imageWidth, x, y;
private Array<Rectangle> raindrops;
private long lastDropTime;
private Texture dropImage = new Texture(Gdx.files.internal("droplet.png"));
Iterator<Rectangle> iter = GameScreen.raindrops.iterator();
private float runTime = 0;
public Drops(Texture img, int imageHeight, int imageWidth, Array<Rectangle> drop, float delta) {
this.imageHeight = imageHeight;
this.imageWidth = imageWidth;
this.raindrops = drop;
this.dropImage = img;
}
public void update(float delta) {
while (iter.equals(true)) {
raindrop = iter.next();
raindrop.y -= 200 * Gdx.graphics.getDeltaTime();
if (raindrop.y + 64 < 0) iter.remove();
onCollision();
}
}
public void onCollision() {
if (raindrop.overlaps(bucket)) {
GameScreen.dropsGathered++;
GameScreen.dropSound.play();
iter.remove();
}
}
public void spawnRaindrop() {
Rectangle raindrop = new Rectangle();
raindrop.x = MathUtils.random(0, 800 - 64);
raindrop.y = 480;
raindrop.width = imageWidth;
raindrop.height = imageHeight;
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
public long getLastDropTime() {
return lastDropTime;
}
}
By drop.spawnRaindrop(); you add drops to Array<Rectangle> raindrops; in your Drops class but for drawing you use
for (Rectangle raindrop : raindrops) {
game.batch.draw(dropImage, raindrop.x, raindrop.y);
}
Which will loop trough raindrop array list in your GameScreen which is empty.
So either draw the array list in drops or populate array list in GameScreen.
You need to be more careful as you refactor. You left behind your original Array of drop rectangles in your screen class, and you're drawing that (which is now empty). Then in your Drops class you are referencing the iterator for the now useless array in the screen class. And you're updating that empty array in the screen's render method.
Basically, the drops need to be handled in one place, but you're handling redundant arrays of drops in two different classes and getting them all mixed up.
It's not clear to me why you even have a class called Drops that tries to handle collisions with a bucket. There's no reason to move top-level game logic into a separate class, as that just complicates the code. If you had a more complicated game, it might make sense to have separate classes for tracking and updating various aspects of the game.
Incidentally, you're leaking a texture you load in this line:
private Texture dropImage = new Texture(Gdx.files.internal("droplet.png"));
because you never dispose of it before replacing the reference with another one in the constructor. In LibGDX, any object that implements Disposable must be disposed before its reference is lost, or it will leak native memory.
The straight-forward way to allow multiple drop images:
1) Go back to your original single class with all the game logic in the screen class.
2) Load your drop images into an array for easier access.
private final Array<Texture> dropImages = new Array<Texture>(); // replaces your dropImage declaration
//...
// in constructor:
dropImages.add(new Texture(Gdx.files.internal("droplet.png")));
dropImages.add(new Texture(Gdx.files.internal("droplet1.png")));
// etc. as many variations as you like
// don't forget to dispose of them:
#Override
public void dispose() {
for (Texture dropImage : dropImages) dropImage.dispose();
bucketImage.dispose();
dropSound.dispose();
}
3) Create a class Drop that extends Rectangle and has an additional parameter for the image type. You probably also want to make these sortable by image index to avoid swapping between Textures multiple times as you draw them, which causes batch flushes since you're not using a TextureAtlas.
public class Drop extends Rectangle implements Comparable<Drop>{
public int imageIndex;
public Drop (){
super();
}
public int compareTo(Drop otherDrop) {
return (int)Math.signum(imageIndex - otherDrop.imageIndex);
}
}
4) Change your Array<Rectangle> to Array<Drop>. When you spawn a drop, also give it a random image index:
private void spawnRaindrop() {
Drop raindrop = new Drop ();
raindrop.x = MathUtils.random(0, 800 - 64);
raindrop.y = 480;
raindrop.width = 64;
raindrop.height = 64;
raindrop.imageIndex = MathUtils.random(dropImages.size); // <-- HERE
raindrops.add(raindrop);
lastDropTime = TimeUtils.nanoTime();
}
5) When drawing your drops, use the drop's imageIndex to pull the correct texture. You can sort them first to avoid swapping the Texture back and forth:
// Draws the Items Falling
raindrops.sort();
for (Drop raindrop : raindrops) {
game.batch.draw(dropImages.get(raindrop.imageIndex), raindrop.x, raindrop.y);
}
I am trying to fill the resize function that comes with Libgdx to fit all of the android screens.
The best solution I have seen so far is this tutorial: http://www.java-gaming.org/index.php?topic=25685.0 but when I run it on my Galaxy S3 the buttons are really tiny and just overall the whole game is tiny where on the desktop version it is big and the way I want it to be. My resize code right now is:
#Override
public void resize(int width, int height) {
float aspectRatio = (float)width/(float)height;
float scale = 2f;
Vector2 crop = new Vector2(0f, 0f);
if(aspectRatio > ASPECT_RATIO)
{
scale = (float)height/(float)VIRTUAL_HEIGHT;
crop.x = (width - VIRTUAL_WIDTH*scale)/2f;
}
else if(aspectRatio < ASPECT_RATIO)
{
scale = (float)width/(float)VIRTUAL_WIDTH;
crop.y = (height - VIRTUAL_HEIGHT*scale)/2f;
}
else
{
scale = (float)width/(float)VIRTUAL_WIDTH;
}
float w = (float)VIRTUAL_WIDTH*scale;
float h = (float)VIRTUAL_HEIGHT*scale;
viewport = new Rectangle(crop.x, crop.y, w, h);
}
My render code is:
#Override
public void render() {
// update camera
camera.update();
// set viewport
Gdx.gl.glViewport((int) viewport.x, (int) viewport.y,
(int) viewport.width, (int) viewport.height);
// clear previous frame
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
ScreenManager.updateScreen();
sb.begin();
ScreenManager.renderScreen(sb);
sb.end();
}
And my variables are:
private static final int VIRTUAL_WIDTH = 600;
private static final int VIRTUAL_HEIGHT = 800;
private static final float ASPECT_RATIO = (float)VIRTUAL_WIDTH/(float)VIRTUAL_HEIGHT;
public static BitmapFont FONT;
private OrthographicCamera camera;
private Rectangle viewport;
private SpriteBatch sb;
Like I said, when I run the desktop version it works fine but when I run it on my Galaxy S3 (Screen Dim: 1,280 x 720), the game is zoomed out a lot, and it crops out the tops and sides of the game which is not what I want at all.