Java/LibGDX — отслеживать собранные предметы из комнаты в комнату

Я пишу игру на основе Jet Set Willy для личного проекта. Как вы уже знаете, персонаж может перемещаться из комнаты в комнату, собирая предметы по пути.

Я использую LibGDX и редактор Tiled Map.

В настоящее время я загружаю свои элементы на основе плиток объектов на моей карте, которые находятся на слое под названием «Элементы», как показано ниже:

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));
    }
}

Элементы хранятся в массиве на моем игровом экране следующим образом:

public static Array<Item> items;

Когда предметы собраны, я просто убираю их с экрана.

Чтобы переключить комнаты, я, по сути, загружаю новую карту, извлекаю предметы этого уровня и т. Д. Проблема в том, что если я вернусь в исходную комнату, мне нужно снова извлечь предметы, что снова отрисовывает их все.

//////////////////////////////////////////////////////////////////////////////
/**
 * 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);
}

Мой класс Item выглядит следующим образом:

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));
}

}

Я хотел бы хранить каждый предмет, который я собираю, в каком-то массиве, который включает номер комнаты и положение предмета X/Y. Когда я перерисовываю элементы, он пропускает любые элементы, находящиеся в собранном списке. Проблема в том, что я не уверен, как добиться этого на Java.

Есть ли у кого-нибудь предложения о том, как я могу этого добиться?

С Уважением

Джеймс


person James Stoddern    schedule 01.09.2016    source источник
comment
В зависимости от количества уровней, элементов и данных, которые вы хотите сохранить, настройки libGDX могут быть самыми простыми. Думаю, это зависит от вашего определения небольших сохранений состояния игры и / или от того, сможете ли вы найти умный способ, возможно, закодировать ваши roomItemStates в строковом или числовом представлении - github.com/libgdx/libgdx/wiki/Preferences   -  person Peter R    schedule 01.09.2016
comment
Спасибо, я думал, что это может быть простой массив [room_number, item_number]. Когда я загружаю элементы, я мог бы сравнить, возможно, с этим массивом и отображать только те, которых нет в собранном массиве. Нужно только помнить их во время игры - я не собираюсь делать никаких игровых сохранений и т. д. Что вы думаете?   -  person James Stoddern    schedule 01.09.2016
comment
Ах хорошо. Я неправильно понял. Я думал, ты ищешь какой-то настойчивости. Если это только в памяти, то это более просто. Проще всего использовать хэш-карту, где ключом является номер комнаты, а значение будет таким, каким вы хотите хранить собранные вами элементы - список, массив,...   -  person Peter R    schedule 01.09.2016


Ответы (1)


Есть много способов сделать это. Вот одно предложение:

Храните все списки предметов ваших комнат в объекте карты и считывайте их с карты в loadItems(), если это необходимо. Кроме того, я бы избегал использования static, если это действительно необходимо - это может легко привести к скрытым ошибкам, если вы все еще немного новичок в Java, и обычно это не является хорошей практикой объектно-ориентированного программирования.

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);
    }
}

Затем вы можете безопасно удалить предметы из items, и список отразит это при следующем входе в комнату.

person Tenfour04    schedule 01.09.2016
comment
Вы также можете использовать Java HashMap<Integer, Array<Item>>, но LibGDX IntMap лучше оптимизирован для игры. Не нужно упаковывать целые числа в экземпляры Integer. - person Tenfour04; 01.09.2016
comment
Спасибо, Tenfour04, отличная идея. Я точно в этом разберусь. Еще раз спасибо, что нашли время. - person James Stoddern; 01.09.2016
comment
Это отлично работает с точки зрения перерисовки спрайтов, но когда они перезагружаются при повторном входе в комнату, все они теряют свои тела Box2D, и поэтому их больше нельзя собирать. Я вызову для элемента метод redefineItem(), который воссоздает тела box2D, но он все равно не работает. Любые идеи, как я могу это решить? - person James Stoddern; 02.09.2016
comment
Не зная всей вашей структуры, я могу только догадываться. Вы создаете новый мир, меняя комнаты? Если это так, вам нужно передать новый мир в ваш метод redefineItem() и назначить его соответствующим образом. - person Tenfour04; 02.09.2016
comment
Ура, наконец-то разобрался Tenfour04. Метод обновления в классе Items не получал правильный мир после того, как комната была построена. Итак, я передал мир в Update(World world), и тела Box2D были правильно применены к остальным элементам. Спасибо за всю вашу помощь, я никогда раньше не использовал intMaps и не был уверен в использовании Final. Вы меня немного больше просветили. Вот результат, если вам интересно: youtu.be/36cF6wdmOWQ - person James Stoddern; 02.09.2016