C++ 中的 3D 运动场

Posted

技术标签:

【中文标题】C++ 中的 3D 运动场【英文标题】:3D playing field in C++ 【发布时间】:2020-03-15 21:44:07 【问题描述】:

我对 C++ 非常陌生,并且很难创建 3D 游戏板。 棋盘的大小可以在整个游戏过程中增加

我的第一个想法是使用嵌套向量。 vector<vector<vector<int>>> board

想法是,它很容易访问 (board[z][y][x]),而且动态分配不会有问题。但是我听说嵌套向量不是一个好主意,因为它破坏了“缓存友好性”。相反,我应该使用一维向量和使用数学的“假”维度。

我认为这意味着创建大小为 (x * y * z) 的向量。在这种情况下,当我想“转到”nested_vector_board[0][1][0] 时,我使用 board[x]。如果我想去nested_vector_board[1][1][0],我使用board[x * y + x]。

我的理解正确吗?板子大小调整时我该怎么办?例如,如果我想让板子更宽,似乎我需要手动移动所有数据。 更重要的是,有没有更好、更简单的方法呢?

我会欢迎任何建议,并在此先感谢您!

【问题讨论】:

'但是我听说嵌套向量不是一个好主意,因为它破坏了“缓存友好性”。'在您的程序正常运行之前,不要担心这样的微优化。 @JosephSible-ReinstateMonica 使用适当的数据结构不是微优化。 在几乎所有时间都在等待用户输入的程序中,不必担心 CPU 性能。 所有答案都暗示了一些东西。我的建议是确保无论您使用什么,都应该将细节隐藏在接口后面,这意味着当您交换板如何实现的细节时,您不需要更改程序其余部分的任何内容。最终,您将拥有一个工作程序,并希望了解板如何存储在内存中,并且您不想更改整个程序来执行此操作。您可能想要使用它的原因是 a) 访问速度、重新分配速度、每个分配的最大大小 【参考方案1】:

如果您不知道您的访问模式是什么,您就无法选择最佳解决方案。此外,如果您没有可行的解决方案,您将无法衡量您的表现,只能猜测最佳解决方案,您可能会猜错。

我建议用清晰的界面实现您自己的 3D Board 类,并检查您的简单解决方案是否足够好。如果速度太慢,您可以将实现换成更合适的实现。

这里有一个例子,你可以如何实现你的板子:

#include <iostream>
#include <unordered_map>
#include <map>

template <class ValueType>
class board_3d 
public:
    using key_t = std::tuple<size_t, size_t, size_t>;
    using value_t = ValueType;

    ValueType& operator[](key_t key) 
        return m_data[key];
     

    const ValueType& operator[](key_t key) const 
        return m_data[key];
     

private:
    std::map<key_t, ValueType> m_data;
;

int main()

    board_3d<int> my_board;
    my_board[0,0,0] = 5;
    return 0;

std::map 最终可能不是你想要的。访问是 O(log n)。要进行优化,您可以使用具有 O(1) 访问权限的 std::unordered_map 或使用向量的任何实现。您还可以添加一个调整大小的函数,该函数负责将数据从旧存储复制到新存储。

【讨论】:

【参考方案2】:

我认为你可以使用稀疏表示:

std::map<int, std::map<int, std::map<int, int> > > board;

您可以使用方括号来引用此映射中的值:

board[x][y][z] = 13;

但是,这种结构可能混乱且效率低下,因此您可以将 x、y 和 z 打包成一个复合键并在 std::unordered_map 中使用它。

struct XYZ_Key 
  int32_t  x;
  int32_t  y;
  int32_t  z;
  bool operator==(const XYZ_Key& o) const 
    return (x == o.x) && (y == o.y) && (z == o.z);
  

  size_t hashCode() const 
    return ((((11 + x) * 7) + y) * 5) + z) * 3;
  
;

std::unordered_map<XYZ_Key, int, decltype(std::mem_fn(&XYZ_Key::hashCode)) >  board;

【讨论】:

【参考方案3】:

为了避免嵌套向量,如何在堆栈上构建一个 2D 指针网格,大小固定(设置为电路板大小的上限),单个向量保存所有数据?像这样的东西(这里用智能指针实现):

#include <iostream>
#include <vector>
#include <memory>

int main() 
    const int MAXSIZE = 512
    int width = 5;
    int height = 5;
    int depth = 5; 
    std::unique_ptr<std::vector<int>> grid[MAXSIZE][MAXSIZE];
    for (int i = 0; i < width; ++i) 
       for (int j = 0; j < height; ++j) 
          grid[i][j] = std::make_unique<std::vector<int>>();
          for (int k = 0; k < depth; ++k) 
              grid[i][j]->push_back(k);
          
       
    
    //To iterate over all the contents:
    for (int i = 0; i < width; ++i) 
        for (int j = 0; j < height; ++j) 
            for (int k = 0; k < depth; ++k) 
                std::cout << grid[i][j]->at(k) << " ";
            
            std::cout << " // ";
        
        std::cout << std::endl;
    
    return 0;

您可能需要使用std::shared_ptr,具体取决于游戏的运行方式。我的理解是这种结构允许比嵌套向量方法更有效的内存访问;也许缺点是必须一次分配更大的网格?

【讨论】:

以上是关于C++ 中的 3D 运动场的主要内容,如果未能解决你的问题,请参考以下文章

为 3D 空间中的点生成随机运动

使用加速度计、陀螺仪和指南针计算设备在 3D 世界中的运动

Unity3D UGUI组件跟随鼠标运动

在 C++ 中的 OpenGL 中将坐标从 3D 透视投影映射到 2D 正交投影

如何仅使用 C++ 中的迭代器正确迭代 3D 向量? [关闭]

是啥导致 Unity 3D 中的“WrongThreadException”?