【问题标题】:Using a 2D array for a game map in C++在 C++ 中使用 2D 数组作为游戏地图
【发布时间】:2021-07-14 23:59:39
【问题描述】:

软件:Visual Studio 2017 社区

大家好,

我正在用 C++ 制作一个简单的 2d 控制台游戏(如果你知道的话,可能是非常简化的矮人要塞)。

我希望在控制台中使用 ASCII 显示地图。

类似这样的:

我在头文件(简化版)中声明了一个 WorldMap 类。 我在里面声明了一个二维数组。

#pragma once
#include <iostream>

class WorldMap
{
public:
    WorldMap();
    virtual ~WorldMap();

private:
    int worldWidth;
    int worldHeight;
    char worldMap;     // Declare a variable that will hold all the characters for the map
};

然后在.cpp文件中定义:

#include "WorldMap.h"
#include <algorithm>

WorldMap::WorldMap()
{
    worldWidth = 50;
    worldHeight = 50;
    worldMap[50][50];       // Define the map array
    // And then here I will also somehow need to be able to fill the whole map with '.' symbols, and so on
}

这就是我想要实现的基本理念。我之所以不能立即定义数组大小是因为我希望在创建地图时能够选择地图的大小。

我已经试过了:

  1. 上面的代码。

错误:

error C2109: subscript requires array or pointer type
  1. 将二维数组声明为char worldMap[][];,然后将其定义为worldMap[50][50];

错误:

error C2087: 'worldMap': missing subscript
warning C4200: nonstandard extension used: zero-sized array in struct/union
message : This member will be ignored by a defaulted constructor or copy/move assignment operator
  1. 将二维数组声明为char worldMap[worldWidth][worldHeight];,期望在创建对象时先定义宽度和高度变量,然后再定义数组。

错误:

error C2327: 'WorldMap::worldWidth': is not a type name, static, or enumerator
error C2065: 'worldWidth': undeclared identifier
error C2327: 'WorldMap::worldHeight': is not a type name, static, or enumerator
error C2065: 'worldHeight': undeclared identifier
  1. 使用char* worldMap;char** worldMap,但到目前为止我什至无法理解双指针是如何工作的,但char* worldMap 实际上可以毫无错误地使用一维数组,直到我开始访问数组中元素的值.

我想一种解决方法是使用字符串或一维字符数组,并且在显示它时只需使用 mapWidth 来结束每 50 个字符的行,例如,这将给出相同的结果。但我觉得这不是实现这一目标的好方法,因为我需要访问这张地图的 x 和 y 坐标等等。

我想我要问的是:

  1. 为类声明二维数组然后在对象中定义它的最佳方式是什么?
  2. 为这样的主机游戏存储地图的最佳方式是什么? (不一定使用数组)

感谢您的阅读。我将非常感谢任何帮助,即使只是想法和提示也可能将我推向正确的方向:)

【问题讨论】:

  • 对于在编译时大小未知的数组,您使用动态内存分配。在 C++ 中,您实际上不需要做太多事情,因为标准库为您提供了 std::vector,它已经为您完成了所有复杂的事情。了解此功能后,您可能会认为 std::vector&lt;std::vector&lt;char&gt;&gt; 可能是您正在搜索的内容。这可行,但在性能方面并不理想。为了获得最佳性能,人们通常使用词法索引(例如idx = y * width + x),这几乎就是您自己提出的。然后可以将其包装到类或函数中。
  • @TedLyngmo 抱歉,我已经删除了该评论,因为该部分可能被解释为具有构造函数的参数,该参数可能仍然是静态的。但我想这有点牵强......
  • Here's an example of a wrapper class 就是 PaulG 所说的。
  • This 深入探讨了我提到的性能注意事项。
  • 最后,您的尝试无效的原因: 1. WorldMapchar 不是字符数组。这永远行不通。 2. 如果num_elements 在编译时已知,char array[num_elements] 声明仅适用于标准 C/C++。有一些编译器(gcc)允许您以不同的方式使用它(甚至可能像您尝试过的那样,我不知道),这意味着“非标准扩展”。 3. 从指针开始是向动态内存方向迈出的一步,但你仍然需要分配内存让它工作。但是使用像这样的“原始”指针是 C 的做事方式。

标签: c++ arrays oop visual-c++


【解决方案1】:
  1. 为类声明二维数组然后在对象中定义它的最佳方式是什么?
  2. 为这样的主机游戏存储地图的最佳方式是什么? (不一定使用数组)

这不是“最好的方法”,而是一种的方法。

  • 创建一个 class 包装一维 std::vector&lt;char&gt;
  • 添加operator()s 以访问各个元素。
  • 添加杂项。 class 的其他支持功能,例如 save()restore()

我以您的class 为基础,并尝试记录它在代码中的作用:如果我使用的某些功能不熟悉,我建议您在https://en.cppreference.com/ 查找它们,这是一个很好的wiki,通常有如何使用您读到的特定功能的示例。

#include <algorithm>   // std::copy, std::copy_n
#include <filesystem>  // std::filesystem::path
#include <fstream>     // std::ifstream, std::ofstream
#include <iostream>    // std::cin, std::cout
#include <iterator>    // std::ostreambuf_iterator, std::istreambuf_iterator
#include <vector>      // std::vector

class WorldMap {
public:
    WorldMap(unsigned h = 5, unsigned w = 5) : // colon starts the initializer list
        worldHeight(h),      // initialize worldHeight with the value in h
        worldWidth(w),       // initialize worldWidth with the value in w
        worldMap(h * w, '.') // initialize the vector, size h*w and filled with dots.
    {}

    // Don't make the destructor virtual unless you use polymorphism
    // In fact, you should probably not create a user-defined destructor at all for this.
    //virtual ~WorldMap(); // removed

    unsigned getHeight() const { return worldHeight; }
    unsigned getWidth() const { return worldWidth; }

    // Define operators to give both const and non-const access to the
    // positions in the map.
    char operator()(unsigned y, unsigned x) const { return worldMap[y*worldWidth + x]; }
    char& operator()(unsigned y, unsigned x) { return worldMap[y*worldWidth + x]; }

    // A function to print the map on screen - or to some other ostream if that's needed
    void print(std::ostream& os = std::cout) const {
        for(unsigned y = 0; y < getHeight(); ++y) {
            for(unsigned x = 0; x < getWidth(); ++x)
                os << (*this)(y, x); // dereference "this" to call the const operator()
            os << '\n';
        }
        os << '\n';
    }

    // functions to save and restore the map
    std::ostream& save(std::ostream& os) const {
        os << worldHeight << '\n' << worldWidth << '\n'; // save the dimensions

        // copy the map out to the stream
        std::copy(worldMap.begin(), worldMap.end(), 
                  std::ostreambuf_iterator<char>(os));
        return os;
    }

    std::istream& restore(std::istream& is) {
        is >> worldHeight >> worldWidth;            // read the dimensions
        is.ignore(2, '\n');                         // ignore the newline
        worldMap.clear();                           // empty the map
        worldMap.reserve(worldHeight * worldWidth); // reserve space for the new map

        // copy the map from the stream
        std::copy_n(std::istreambuf_iterator<char>(is),
                    worldHeight * worldWidth, std::back_inserter(worldMap));
        return is;
    }

    // functions to save/restore using a filename
    bool save(const std::filesystem::path& filename) const {
        if(std::ofstream ofs(filename); ofs) {
            return static_cast<bool>(save(ofs)); // true if it suceeded
        }
        return false;
    }

    bool restore(const std::filesystem::path& filename) {
        if(std::ifstream ifs(filename); ifs) {
            return static_cast<bool>(restore(ifs)); // true if it succeeded
        }
        return false;
    }

private:
    unsigned worldHeight;
    unsigned worldWidth;

    // Declare a variable that will hold all the characters for the map
    std::vector<char> worldMap;
};

Demo

【讨论】:

  • 哇!万分感谢!对我来说,这看起来像是一个非常专业的代码,至少考虑到我是一个初学者。这很复杂,但很清楚。我花了几个小时来分析这一切,我从中学到了很多东西。已经在我的代码中实现了这个的修改版本。所以这非常有帮助:) 我唯一还没有找到相关信息的是(*this)(y,x)。具体来说,为什么*this 用括号括起来?也许这种使用括号的方式有一个唯一的名称?
  • @YamikoHikari 很高兴它对您有所帮助,不客气!可以写operator()(y, x); 而不是(*this)(y,x) - 这只是访问用户定义的operator()(s) 的一种方式。括号是因为operator precedence。如果没有括号,它将与*(this(y,x)) 相同(尝试将this 指针作为函数调用(然后取消引用结果),而不是*this 导致的WorldMap&amp;
【解决方案2】:

没有最好的方法来做任何事情*。这是最适合您的方法。

据我了解,您想制作一个动态二维数组来保存您的世界地图字符。你有很多选择来做到这一点。你可以拥有一个世界地图类,这没有什么问题。如果你想要动态 2D 数组,只需使用这种逻辑创建函数即可。

#include <iostream>
#include <vector>

int main() {
    int H = 10, W = 20;
    char** map = NULL; //This would go in your class.H

    //Make a function to allocate 2D array 
    map = new char* [H];
    for (int i = 0; i < H; i++) {
        map[i] = new char[W];
    }
    //FILL WITH WHATEVER 
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            map[i][j] = 'A';
        }
    }
    //do what ever you want like normal 2d array 
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            std::cout << map[i][j] << " ";
        }
        std::cout << std::endl;
    }
    //Should always delete when or if you want to make a new one run time  
    for (int i = 0; i < H; i++)    
        delete[] map[i];
    delete[] map;              
    map = NULL;

    //Also you can use vectors
    std::cout << "\n\n With vector " << std::endl;
    std::vector<std::vector<char>> mapV; 

    //FILL WITH WHATEVER 
    for (int i = 0; i < H; i++) {
        std::vector<char> inner;
        for (int j = 0; j < W; j++) {
            inner.push_back('V');
        }
        mapV.push_back(inner);
    }
    //do what ever you want kind of like a normal array 
    //but you should look up how they really work 
    for (int i = 0; i < H; i++) {
        for (int j = 0; j < W; j++) {
            std::cout << mapV[i][j] << " ";
        }
        std::cout << std::endl;
    }

    mapV.clear();

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多