Search code examples
javalibgdxplatforminventory

Java/LibGDX - Track collected items from room to room


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


Solution

  • 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.