【问题标题】:Game inventory system游戏库存系统
【发布时间】:2020-07-17 17:50:44
【问题描述】:

我正在尝试用 C++ 为我正在开发的游戏构建一个库存系统。但是,当我调用Inventory::AddItem(Item i) 时,库存系统中有一个错误,没有添加任何物品,并且该插槽仍然保持空白。目前,我通过std::vector<Item>处理库存,其中Item是一个包含类型的结构,如果它是可堆叠的,堆栈中的最大块数,堆栈中的当前块数,以及几个对象动画。此外,我自动在库存中填充了 40 个空气块插槽,其 ID 为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;
        }
    }
}

【问题讨论】:

  • 我认为问题在于您没有将Item 设置为最后一个if 语句中的下一个空插槽,您只需更改Item 的属性即可那里。你如何初始化你的vector?你用空白项目填充它吗?或者你只是reserve 一些空间?
  • @melk,我刚刚编辑了问题,向您展示了我如何初始化空插槽。另外,关键是我有一个独特的Item,每个插槽都有一个特定项目的数量。这样做是为了节省内存
  • 那为什么不直接做this-&gt;items[next_empty_slot] = item呢?另外,我会确保Item 的构造函数强制执行不变量num &gt; max_num,因此您不必稍后再检查。此外,根据您构建循环的方式,您将始终在 vector 末尾添加 Items。

标签: c++ game-development


【解决方案1】:

我知道你找到了错误,但是你的代码中有很多问题导致了这个错误,我想帮助你了解如何编写更好的代码,这样下次你会更容易找到它(甚至可能避免它!)。

  1. 您应该将逻辑分成尽可能小的部分 - 模块化是更清晰、更简洁的代码的关键,这有助于您更快地理解错误。
  2. 您没有创建一个清晰的流程,而是创建了两个不同的流程打开和关闭。当您用尽一个可能的流程时,代码会更加清晰,然后才启动另一个(查看函数 add_item_to_existing_stack_if_possibleadd_item_to_new_stack_if_possible
  3. 您的变量/函数/类名称必须代表它们所代表的含义,原始代码并非如此!现在看看Item 结构——每个成员代表什么就更清楚了,没有cmets! (就我个人而言,我根本没有使用 cmets)
  4. C++ 不是带有类的 C - 诸如 typedef 之类的东西不应出现在您的代码中,您应该使用 operator&lt;&lt;std::cout 而不是 printf 等等。
  5. 确保尽可能添加 const 说明符,它可以帮助在编译时发现许多错误(并使您的程序运行得更快)。
  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;
}

【讨论】:

  • 哇!非常感谢您花在这个答案上的时间。对此,我真的非常感激。我一定会在整个代码库中进行这些更改,以生成更好、更无错误的代码。干杯,伙计!
【解决方案2】:

好的,我想通了。在第二个 for 循环结束时必须有一个中断,它会在其中查找下一个空槽,否则,它将检测下一个空槽作为库存中的最后一个槽,假设您正在添加第一个项目在存货。因此,该项目没有显示在 hopbar 中。

这是正确的解决方案:

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;

}

【讨论】:

  • 这是您应该将代码分成更小的函数的原因之一!
  • 在代码中添加了我的修复,我认为现在更清晰,更不容易出错。
猜你喜欢
  • 2011-10-16
  • 1970-01-01
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
相关资源
最近更新 更多