【问题标题】:Drawing sprite causes Segmentation Fault绘制精灵导致分段错误
【发布时间】:2018-02-07 03:34:48
【问题描述】:

我正在用 SFML2 编写一个基于等距图块的简单游戏。地图中的每个瓦片都由瓦片类表示。该类包含 sf::Sprite 对象和 draw() 函数,该函数应该在屏幕上绘制精灵。问题是调用window.draw() 会导致分段错误。我读过它通常是由关联的 sf::Texture 指针无效引起的,但我已经检查过,事实并非如此。

(使用ResourceManager<sf::Texture>对象将纹理引用传递给Tile构造函数)

tile.hpp:

#pragma once

#include <SFML/Graphics.hpp>
#include "resource_manager.hpp"

class Tile
{
public:
  Tile(const sf::Texture& texture);

  void draw(sf::RenderWindow& window, const float dt);

private:
  sf::Sprite _sprite;
};

tile.cpp:

#include "tile.hpp"

Tile::Tile(const sf::Texture& texture)
{
  _sprite.setTexture(texture);
}

void Tile::draw(sf::RenderWindow& window, const float dt)
{
  std::cout << _sprite.getTexture()->getSize().x << std::endl; //Texture pointer works fine
  window.draw(_sprite); //Segmentation Fault here
}

map.hpp:

#pragma once

#include <vector>
#include <SFML/Graphics.hpp>
#include "resource_holder.hpp"
#include "tile.hpp"

class Map
{
public:
  Map(ResourceHolder& resources, int width, int height);

  void draw(sf::RenderWindow& window, const float dt);

private:
  ResourceHolder& _resources;
  int _width, _height;

  std::vector<Tile> _tiles;
};

map.cpp:

#include "map.hpp"
#include <iostream>

Map::Map(ResourceHolder& resources, int width, int height)
  : _resources(resources), _width(width), _height(height)
{
  _tiles.reserve(width * height);

  for(int y = 0; y < _height; ++y)
  {
    for(int x = 0; x < _width; ++x)
    {
      _tiles[x + y * _width] = Tile(_resources.textures["groundTile_NE"]);
    }
  }
}

void Map::draw(sf::RenderWindow& window, const float dt)
{
  for(int x = 0; x < _width; ++x)
  {
    for(int y = 0; y < _height; ++y)
    {
      _tiles[x + y * _width].draw(window, dt);
    }
 }
}

resource_manager.hpp:

#pragma once

#include <unordered_map>
#include <iostream>

template<typename Resource>
class ResourceManager
{
public:
  ResourceManager(const std::string& directory, const std::string& extension)
    : _directory("assets/" + directory + "/"), _extension("." + extension)
  {}

  Resource& operator[](const std::string& name)
  {
    return get(name);
  }

  Resource& get(const std::string& name)
  {
    if(!exists(name))
      load(name);

    return _resources.at(name);
  }

  bool exists(const std::string& name) const
  {
    return _resources.find(name) != _resources.end();
  }

  void load(const std::string& name)
  {
    Resource res;
    if(!res.loadFromFile(_directory + name + _extension))
    {
      res.loadFromFile(_directory + "_fail_" + _extension);
    }

    _resources.insert({name, res});
  }

private:
  const sf::String _directory;
  const sf::String _extension;

  std::unordered_map<std::string, Resource> _resources;
};

【问题讨论】:

  • @Eddge 它确实返回指针,我的错误
  • @meowgoesthedog 资源是一个模板类型名。在这种情况下是 sf::Texture
  • 抱歉,我没有看到模板声明。评论已删除
  • 感谢您修复@Hadenir,您能否向我们展示您如何加载纹理以及如何验证它是否正确加载?
  • 我已经编辑了问题并添加了执行加载的 map.cpp 和 map.hpp。 _sprite.getTexture()-&gt;getSize().x 返回正确的宽度,所以我假设纹理加载正确

标签: c++ pointers segmentation-fault sfml


【解决方案1】:

从 SFML 源代码 (Graphics/Texture.cpp) 来看,析构函数导致 OpenGL 纹理缓冲区被删除:

Texture::~Texture()
{
    // Destroy the OpenGL texture
    if (m_texture)
    {
        TransientContextLock lock;

        GLuint texture = static_cast<GLuint>(m_texture);
        glCheck(glDeleteTextures(1, &texture));
    }
}

在您的load 方法中,您在堆栈上分配对象res。这意味着当方法返回时,Resource 的析构函数将在res 对象上被调用。您的 OpenGL 句柄已被删除,但仍存储在您插入到 _resources 中的 res副本中。

要解决此问题,请改为存储资源类的指针

std::unordered_map<std::string, Resource*> _resources;

...

void load(const std::string& name)
{
   Resource* res = new Resource();
   if (!res->loadFromFile(_directory + name + _extension))
   {
      res->loadFromFile(_directory + "_fail_" + _extension);
   }

   _resources.insert({name, res});
}

Resource* operator[](const std::string& name)
{
   return get(name);
}

Resource* get(const std::string& name)
{
   if (!exists(name))
      load(name); // or return nullptr

   return _resources.at(name);
}

但是当ResourceManager对象被销毁时,你也需要销毁这些指针:

~ResourceManager()
{
   for (auto& pair : _resources)
      delete pair.second;
}

注意:您可能想将 references 存储在 _resources 中。但是请参阅 this link 了解为什么这是不可能的。

【讨论】:

  • 它解释了很多,谢谢。我的经理基于此代码:github.com/Hopson97/SFML-Game-Framework/blob/master/Source/… 我想知道为什么它对他有用,但对我来说不是。
  • @Hadenir 嗯,不要对知名博主过度怀疑,但您确定它对他有效吗?我的意思是,您的代码(以及他的)编译 很好,但我在存储库中的任何地方都找不到他实际使用 ResourceManager/Holder 的位置。也许我们应该问那个人本人?
  • 我也是这么想的,但在视频中,他的纹理效果很好。不过感谢 meowgoesthedog 的帮助!
猜你喜欢
  • 1970-01-01
  • 2012-06-15
  • 2023-03-31
  • 2014-04-10
  • 2011-11-22
  • 2017-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多