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();
Related
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 writing a game based on Jet Set Willy for a personal project. As you will know, the character can move from room to room, collecting items as he goes.
I am using LibGDX and the Tiled Map editor.
I currently load my items based on Object Tiles in my map, which are on a layer called 'Items', as per below:
public void loadItems() {
//////////////////////////////////////////////////////////////////////////
//create all Items
for(MapObject object : map.getLayers().get(4).getObjects().getByType(RectangleMapObject.class)){
Rectangle rect = ((RectangleMapObject) object).getRectangle();
//new Item(screen, object);
items.add(new Item(this, object, (rect.getX() + rect.getWidth() / 2) / Engine.PPM, (rect.getY() + rect.getHeight() / 2) / Engine.PPM));
}
}
The items are stored in an Array on my Playscreen as follows:
public static Array<Item> items;
When the items are collected, I simply remove them from the screen.
To switch rooms I essentially load a new map, fetch that level's Items etc. The problem is, that if I move back to the original room I need to fetch the items again, which draws them all again.
//////////////////////////////////////////////////////////////////////////////
/**
* Load the next Level
*/
public void changeMap(int roomNumber, float x, float y) {
map.dispose();
loadMap(roomNumber);
this.current_level = roomNumber;
renderer.getMap().dispose(); //dispose the old map
renderer.setMap(map); //set the map in your renderer
world = new World(new Vector2(0,-4 ), true);
world.setContactListener(new WorldContactListener());
creator = new B2WorldCreator(this);
loadItems();
//Reposition Player
player = new Player(world, this, x * Engine.TILE_WIDTH, y * Engine.TILE_HEIGHT);
}
My Item class is as follows:
public class Item extends Sprite {
protected World world;
protected PlayScreen screen;
private float stateTime;
protected TiledMap map;
protected MapObject object;
private Animation animation;
private Array<TextureRegion> frames;
private boolean setToDestroy;
private boolean destroyed;
float angle;
public Body b2body;
FixtureDef fdef;
private Texture tex;
private Texture blank_texture;
private int item_number;
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Constructor
* #param screen
* #param object
* #param x
* #param y
*/
public Item(PlayScreen screen, MapObject object, float x, float y){
this.world = screen.getWorld();
this.screen = screen;
this.map = screen.getMap();
//this.item_number = item_number;
setPosition(x, y);
Random rn = new Random();
int max = 2;
int min = 1;
int random = rn.nextInt(5) + 1;
tex = new Texture(Gdx.files.internal("sprites/item" + random + ".png"));
frames = new Array<TextureRegion>();
for(int i = 0; i < 4; i++) {
frames.add(new TextureRegion(tex, i * 16, 0, 16, 16));
}
animation = new Animation(0.1f, frames);
blank_texture = new Texture(Gdx.files.internal("sprites/blank_item.png"));
setBounds(getX(), getY(), 15 / Engine.PPM, 15 / Engine.PPM);
setToDestroy = false;
destroyed = false;
angle = 0;
stateTime = 0;
define_item();
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
*Define the Box2D body for the item
*/
public void define_item() {
BodyDef bdef = new BodyDef();
bdef.position.set(getX(), getY());
bdef.type = BodyDef.BodyType.StaticBody;
b2body = world.createBody(bdef);
fdef = new FixtureDef();
fdef.filter.categoryBits = Engine.ITEM_BIT;
fdef.filter.maskBits = Engine.PLAYER_BIT;
PolygonShape shape = new PolygonShape();
shape.setAsBox(7 / Engine.PPM, 7 / Engine.PPM);
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
b2body.setGravityScale(0);
b2body.setActive(true);
}
public void redefineItem() {
Gdx.app.log("redefineItem", "Item");
Vector2 position = b2body.getPosition();
world.destroyBody(b2body);
BodyDef bdef = new BodyDef();
bdef.position.set(position);
bdef.type = BodyDef.BodyType.StaticBody;
b2body = world.createBody(bdef);
fdef = new FixtureDef();
fdef.filter.categoryBits = Engine.ITEM_BIT;
fdef.filter.maskBits = Engine.PLAYER_BIT;
PolygonShape shape = new PolygonShape();
shape.setAsBox(7 / Engine.PPM, 7 / Engine.PPM);
fdef.shape = shape;
b2body.createFixture(fdef).setUserData(this);
b2body.setGravityScale(0);
b2body.setActive(true);
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Draw Method
* #param batch
*/
#Override
public void draw(Batch batch) {
if(!destroyed) {
super.draw(batch);
}
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Update the Items
* #param dt
*/
public void update(float dt){
setRegion(getFrame(dt));
stateTime += dt;
if(setToDestroy && !destroyed){
world.destroyBody(b2body);
destroyed = true;
setRegion(blank_texture);
stateTime = 0;
}
else if(!destroyed) {
setRegion(animation.getKeyFrame(stateTime, true));
setPosition(b2body.getPosition().x - getWidth() / 2, b2body.getPosition().y - getHeight() / 2);
}
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Get the Texture
* #param dt
* #return
*/
public TextureRegion getFrame(float dt){
TextureRegion region;
region = animation.getKeyFrame(stateTime, true);
return region;
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Item has been collected
* #param player
*/
public void collected(Player player) {
if(Engine.bPlaySounds) {
Sound sound = Gdx.audio.newSound(Gdx.files.internal("audio/sounds/collect_item.wav"));
sound.play(1.0f);
}
//Change the Category Bit, so that it is no longer collidable
fdef.filter.categoryBits = Engine.COLLECTED_BIT;
this.setToDestroy = true;
Gdx.app.log("Collected Item ", "" + this.item_number + " from room " + screen.getCurrentLevel() );
//Increment the counter on the HUD
screen.incrementItemCounter();
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Set the category Filter
* #param filterBit
*/
public void setCategoryFilter(short filterBit){
Filter filter = new Filter();
filter.categoryBits = filterBit;
}
////////////////////////////////////////////////////////////////////////////////////////////
/**
* Get the Tilemap cell
* #return
*/
public TiledMapTileLayer.Cell getCell(){
TiledMapTileLayer layer = (TiledMapTileLayer) map.getLayers().get(0);
return layer.getCell((int)(b2body.getPosition().x * Engine.PPM / 16), (int)(b2body.getPosition().y * Engine.PPM / 16));
}
}
I'd like to store each item I collect in some kind of array, which includes the room number, and the item's X/Y position. When I redraw the items, it will skip any of the items which are in the collected list. Problem is, I'm not sure how to achieve this in Java.
Does anyone have any suggestions on how I might achieve this?
Regards
James
There are many ways you can do this. Here's one suggestion:
Store all your rooms' item lists in a map object and read from the map in loadItems() if appropriate. Also, I would avoid the use of static unless it is really necessary--that can easily lead to sneaky bugs if you're still a bit new to Java, and they aren't usually good object-oriented practice.
private final IntMap<Array<Item>> roomsToItems = new IntMap();
private Array<Item> items;
//...
public void loadItems(int roomNumber) {
items = roomsToItems.get(roomNumber); //get this room's previous list if it exists
if (items == null) { //this room hasn't been loaded yet
items = new Array<>();
//TODO: Load the items into "items"
//store the items list so it can be retrieved instead of loaded next time:
roomsToItems.put(roomNumber, items);
}
}
Then you can safely remove items from items and the list will reflect that the next time you enter the room.
I'm learning libgdx (java) game programming and I just finished my first game : a simple Brick Destroyer.
I made a desktop and android project. After some hours of "hard" work, my game finally works fine on desktop. I'm not using scene2D or box2D or anything else for this game (I just implemented the interface "screen" on my classes : GameScreen, MainMenuScreen etc...).
Here is the problem : When I export my game as an .APK file and then install it on my android phone (Sony Xperia Z1 compact), the screen is absolutely different. The ball still reacts well with all other elements (bricks and sides of the screen) but it seems not to be the same zoom. I've tried so much things, I don't know what to do anymore, please help me :).
I'm using a FitViewport and an Orthographic Camera.
See below the screenshots :
1 - The constructor of GameScreen class.
2 - Desktop programm.
3 - Android app.
Thank you all for your answers and sorry for my english if I made mistakes(I'm not an english native speaker).
final Bricks game;
Texture ball_img, plate_img, brique_img_rouge, brique_img_orange, brique_img_vert;
Vector2 posb, posp;
Vector2 vitb, vitp;
OrthographicCamera camera;
Rectangle ball, plate;
Array<Brique> briques;
FitViewport viewport;
final int plate_width = 60, plate_height = 10;
final int brique_width = 35, brique_height = 15;
final int ball_width = 15, ball_height = 14;
boolean left_p, right_p, up_p;
public GameScreen(final Bricks gam) {
this.game = gam;
ball_img = new Texture("ball_bl.PNG");
plate_img = new Texture("plate_bl.PNG");
brique_img_rouge = new Texture("brique_bl_rouge.PNG");
brique_img_orange = new Texture("brique_bl_orange.PNG");
brique_img_vert = new Texture("brique_bl_vert.PNG");
camera = new OrthographicCamera();
viewport = new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), camera);
viewport.apply();
ball = new Rectangle();
plate = new Rectangle();
briques = new Array<Brique>();
briques.add(new Brique(2));
briques.first().x = 50;
briques.first().y = Gdx.graphics.getHeight()-60;
briques.first().width = brique_width;
briques.first().height = brique_height;
posb = new Vector2(Gdx.graphics.getWidth()/2, Gdx.graphics.getHeight()/4);
vitb = new Vector2(0, 400*Gdx.graphics.getDeltaTime());
posp = new Vector2(Gdx.graphics.getWidth()/2 - plate_width/2, 20);
vitp = new Vector2(400*Gdx.graphics.getDeltaTime(), 0);
for (int i = 1; i < 65; i++) {
if( i % 13 == 0) {
briques.add(new Brique(1));
briques.get(i).x = briques.first().x;
briques.get(i).y = briques.get(i-1).y - 35;
briques.get(i).width = brique_width;
briques.get(i).height = brique_height;
} else {
briques.add(new Brique(1));
briques.get(i).x = briques.get(i-1).x + briques.get(i-1).width + 20;
briques.get(i).y = briques.get(i-1).y;
briques.get(i).width = brique_width;
briques.get(i).height = brique_height;
}
if(i > 51 || i == 19 || i == 32) {
briques.get(i).setType(3);
}
if((i < 13 || i %13 == 0 || i == 25 || i >= 38) && i < 52) {
briques.get(i).setType(2);
}
}
ball.x = posb.x;
ball.y = posb.y;
ball.width = ball_width;
ball.height = ball_height;
plate.x = posp.x;
plate.y = posp.y;
plate.width = plate_width;
plate.height = plate_height;
I think the problem comes from here, but it works on desktop.
camera = new OrthographicCamera();
viewport = new FitViewport(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), camera);
viewport.apply();
Desktop : Desktop programm
Android : android app from .APK file
This happens, since you use a FitViweport with a virtual screen size of the actual screen size. Since your desktop app and android phone have a different resolution, you get different viewports.
So for your desktop app the camera code might actually "look" like this:
camera = new OrthographicCamera();
viewport = new FitViewport(800, 600, camera);
viewport.apply();
And for you android it might actually "look" like this:
camera = new OrthographicCamera();
viewport = new FitViewport(400, 700, camera);
viewport.apply();
To solve that define an inner virtual world size, so that for both the world size is for example VIRTUAL_WIDTH x VIRTUAL_HEIGHT. This size must not be in pixels:
// Global static field in your game class
public static int VIRTUAL_WIDTH = XXX;
public static int VIRTUAL_HEIGHT = XXX;
// ...
camera = new OrthographicCamera();
viewport = new FitViewport(VIRTUAL_WIDTH, VIRTUAL_HEIGHT, camera);
viewport.apply();
This means that instead of using Gdx.graphics.getWidth() or Gdx.graphics.getHeight() you use these values, respectively, since you now work in your virtual screen size.
For example here in your code:
posb = new Vector2(VIRTUAL_WIDTH / 2f, VIRTUAL_HEIGHT / 4f);
For your out of screen problem:
Check how you draw your blocks, do you use Gdx.graphics.getXXX() or values that exceed your new size limit?
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