Система инвентаря игры - PullRequest
2 голосов
/ 05 апреля 2020

Я пытаюсь создать систему инвентаризации в C ++ для игры, над которой я работаю. Однако в системе инвентаризации есть ошибка, когда я звоню Inventory::AddItem(Item i), элемент не добавляется, и этот слот остается пустым. В настоящее время я выполняю инвентаризацию через std::vector<Item>, где Item - это структура, содержащая тип, если он является наращиваемым, максимальное количество блоков в стеке, текущее количество блоков в стеке и пару объектов. для анимации. Кроме того, я автоматически заполняю инвентарь 40 слотами воздушных блоков, которые имеют идентификатор INVENTORY_EMTPY_SLOT_ID.

. Вот код:

    typedef struct item {
        int type;    // this is whether the block is a foreground of background tile
        int id;      // use this for the id of objects
        bool stackable;   // true indicates that the block can be stacked
        int max_num;      // maximum number of blocks in a stack
        int num;          // the current number of blocks in the stack
        Animation* use_animation;    // the animation of the block or item when it is being used
        Animation* other_animation;  // secondary animation of item in case it is necessary
    } Item;

Как инициализировать пустые слоты:

    for (size_t x = 0; x < INVENTORY_MAX_SLOTS; x++) {
        Item i = {0, INVENTORY_EMPTY_SLOT_ID, true, 1, 1, NULL, NULL};
        this->items.push_back(i);
    }

Добавление предметов

/*********BUG HERE:******************/
void Inventory::AddItem(Item item) {
    // find all indexes with the same item.id
    std::vector<size_t> indexes_w_same_item;
    for (size_t i = 0; i < this->items.size(); i++) {
        if (this->items[i].id == item.id) {
            indexes_w_same_item.push_back(i);
        }
    }

    // find the next empty slot
    int next_empty_slot = -1;
    for (size_t i = 0; i < this->items.size(); i++) {
        if (this->items[i].id == INVENTORY_EMPTY_SLOT_ID) {
            next_empty_slot = i;
        }
    }

    // go through all the indexes with the same item.id
    // and see if at least one of them is empty.
    // if one is empty and has sufficient capacity,
    // add the item and return. if it isn't, keep moving forward
    for (size_t x = 0; x < indexes_w_same_item.size(); x++) {
        if (item.id == this->items[indexes_w_same_item[x]].id) {
            if (this->items[indexes_w_same_item[x]].num + item.num <= this->items[indexes_w_same_item[x]].max_num) {
                this->items[indexes_w_same_item[x]].num += item.num;
                return;
            }
        }
    }

    // if there is an empty slot, make a new stack
    if (next_empty_slot >= 0) {
        this->items[next_empty_slot].id = item.id;
        this->items[next_empty_slot].max_num = item.max_num;
        // clamp item.num so it doesn't exceed item.max_num
        if (item.max_num > item.num) {
            this->items[next_empty_slot].num = item.num;
        } else {
            this->items[next_empty_slot].num = item.max_num;
        }
    }
}

Ответы [ 2 ]

1 голос
/ 05 апреля 2020

Я знаю, что вы нашли ошибку, но есть много проблем в вашем коде, которые приводят к этой ошибке, и я хотел помочь вам понять, как писать лучший код, так что в следующий раз вам будет легче найти его (и, возможно, даже избегать этого!).

  1. Вы должны разделить логи c на как можно более мелкие кусочки - модульность - это ключ к более ясному и чистому коду, который помог вам понять ошибка намного быстрее.
  2. Вместо того, чтобы делать чистый поток, вы делали два отдельных потока включением и выключением. Код становится намного понятнее, когда вы исчерпываете один возможный поток и только потом запускаете другой (посмотрите на функции add_item_to_existing_stack_if_possible и add_item_to_new_stack_if_possible.
  3. Ваши имена переменных / функций / классов должны представлять то, что они стоят ведь с исходным кодом это было не так! Посмотрите на структуру Item сейчас - гораздо понятнее, что представляет каждый член, без комментариев! (лично я вообще не использую комментарии)
  4. C ++ не C с классами - такие вещи, как typedef не должны появляться в вашем коде, вы должны использовать operator<< до std::cout вместо printf и т. Д.
  5. Убедитесь, что вы Если возможно, добавьте константные спецификаторы, это поможет найти много ошибок во время компиляции (и ускорить выполнение вашей программы).
  6. Производительность связана с тем, что вы должны передавать объекты как ссылки настолько, насколько это возможно, гораздо быстрее uint64 (ячейка памяти), чем копируется весь ваш Item объект.
#include <vector>
#include <array>
#include <iostream>

struct Animation;

struct Item {
    int type;
    int id;
    bool is_stackable;
    int max_num_blocks_in_stack;
    int curr_num_of_blocks_in_stack;
    Animation* used_animation; // if it is non nullable, you should consider to use it without a pointer (possibly a reference)
    Animation* secondary_animation; // nullable - can be a pointer or std::optional
};

class Inventory
{
public:
    bool add_item(Item&& item);

private:
    bool is_slot_empty(size_t idx) const { return items[idx].id == INVENTORY_EMPTY_SLOT_ID; }
    std::vector<size_t> find_indexes_of(const Item& item) const;
    size_t find_next_empty_slot() const;

    bool add_item_to_existing_stack_if_possible(const Item& item);
    bool add_item_to_new_stack_if_possible(Item&& item);

    void print() const;

    static constexpr size_t MAX_INV_SIZE = 40; // can transform into a class template!

    std::array<Item, MAX_INV_SIZE> items;

    static constexpr int INVENTORY_EMPTY_SLOT_ID = -1;
};

std::vector<size_t> Inventory::find_indexes_of(const Item& item) const
{
    std::vector<size_t> indexes{};

    for (size_t idx = 0; idx < MAX_INV_SIZE; ++idx) 
    {
        if (items[idx].id == item.id) 
        {
            indexes.push_back(idx);
        }
    }

    return indexes;
}

size_t Inventory::find_next_empty_slot() const
{
    for (size_t idx = 0; idx < MAX_INV_SIZE; ++idx) 
    {
        if (is_slot_empty(idx)) 
        {
            return idx;
        }
    }

    return MAX_INV_SIZE; // invalid value!
}

void Inventory::print() const
{
    for (size_t i = 0; i < MAX_INV_SIZE; ++i) 
    {
        if (this->items[i].id != INVENTORY_EMPTY_SLOT_ID)
        {
            std::cout << "Inventory slot: " << i << "\n"
                      << "Item ID: " << items[i].id << "\n"
                      << "Item Num: " << items[i].curr_num_of_blocks_in_stack << "\n"
                      << "Item Max Num: " << items[i].max_num_blocks_in_stack << std::endl;
                      //<< "Item Texture: " << textures[items[i].id] << std::endl;
        }
    }
}

bool Inventory::add_item_to_existing_stack_if_possible(const Item& item)
{
    auto indexes_with_same_item = find_indexes_of(item);

    for (auto idx : indexes_with_same_item) 
    {
        if (item.id == items[idx].id) 
        {
            if (items[idx].curr_num_of_blocks_in_stack + item.curr_num_of_blocks_in_stack <= 
                items[idx].max_num_blocks_in_stack) 
            {
                items[idx].curr_num_of_blocks_in_stack += item.curr_num_of_blocks_in_stack;
                return true;
            }
        }
    }

    return false;
}

bool Inventory::add_item_to_new_stack_if_possible(Item&& item)
{
    size_t next_empty_slot = find_next_empty_slot();

    if (next_empty_slot >= 0) 
    {
        this->items[next_empty_slot] = std::move(item);

        return true;
    }

    return false;
}

bool Inventory::add_item(Item&& item) 
{
    bool was_possible_to_add_to_existing_stack = add_item_to_existing_stack_if_possible(item);

    if (!was_possible_to_add_to_existing_stack)
    {
        return add_item_to_new_stack_if_possible(std::move(item));
    }

    return false;
}
0 голосов
/ 05 апреля 2020

Хорошо, я понял это. Должен быть разрыв в конце секунды для l oop, где он ищет следующий пустой слот, в противном случае он обнаружит следующий пустой слот как последний слот в инвентаре, предполагая, что вы добавляете первый предмет в инвентаре. Следовательно, этот элемент не отображался в панели переходов.

Вот правильное решение:

void Inventory::AddItem(Item item) {
// find all indexes with the same item.id
std::vector<size_t> indexes_w_same_item;
for (size_t i = 0; i < this->items.size(); i++) {
    if (this->items[i].id == item.id) {
        indexes_w_same_item.push_back(i);
    }
}

// find the next empty slot
int next_empty_slot = -1;
for (size_t i = 0; i < this->items.size(); i++) {
    if (this->items[i].id == INVENTORY_EMPTY_SLOT_ID) {
        next_empty_slot = i;
        break;
    }
}

// go through all the indexes with the same item.id
// and see if at least one of them is empty.
// if one is empty and has sufficient capacity,
// add the item and return. if it isn't, keep moving forward
for (size_t x = 0; x < indexes_w_same_item.size(); x++) {
    if (item.id == this->items[indexes_w_same_item[x]].id) {
        if (this->items[indexes_w_same_item[x]].num + item.num <= this->items[indexes_w_same_item[x]].max_num) {
            this->items[indexes_w_same_item[x]].num += item.num;
            return;
        }
    }
}

// if there is an empty slot, make a new stack
if (next_empty_slot >= 0) {
    this->items[next_empty_slot] = item;
}

for (size_t i = 0; i < INVENTORY_MAX_SLOTS; i++) {
    if (this->items[i].id != '.') {
        printf("\nInventory slot: %d\n", i);
        printf("Item ID: %c\n", this->items[i].id);
        printf("Item Num: %d\n", this->items[i].num);
        printf("Item Max Num: %d\n", this->items[i].max_num);
        printf("Item Texture: %x\n", this->textures[this->items[i].id]);
    }
}


return;

}

...