Code of Poem
Unknown programmer's programming note.

pcat (3) タイルマップ のコード

pcat (3) タイルマップで出来たコード片を集めたものです。製作の途中段階であり、不完全であることに注意してください。

CMakeLists.txt

cmake_minimum_required(VERSION 3.3)

project(pcat)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 17)

# setup ctest
enable_testing()
add_test(NAME test_mapitem COMMAND test_mapitem)
add_executable(test_mapitem test_mapitem.cpp mapitem.cpp)
target_link_libraries(test_mapitem gtest gtest_main sfml-graphics)

add_executable(pcat main.cpp mapitem.cpp)
target_link_libraries(pcat sfml-system sfml-window sfml-graphics)

main.cpp

#include <SFML/Graphics.hpp>
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Window/Keyboard.hpp>
#include <SFML/Window/VideoMode.hpp>
#include <iostream>

#include "mapitem.h"

const int TileWidth = 32;
const int TileHeight = 32;
const int MapWidth = 16;
const int MapHeight = 15;

int main() {
  sf::RenderWindow window{sf::VideoMode{512, 480}, "pcat"};

  sf::Texture tileset;
  tileset.loadFromFile("pcat3_tileset.png");
  MapItemFactory map_factory{tileset, {32, 32}};
  auto map_items = make_map_from_csv_file("pcat3_tilemap.csv", map_factory);
  if (!map_items.has_value()) {
    return 1;
  }

  while (window.isOpen()) {
    sf::Event event;
    while (window.pollEvent(event)) {
      if (event.type == sf::Event::Closed) {
        window.close();
      } else if (event.type == sf::Event::KeyPressed) {
        if (event.key.code == sf::Keyboard::Escape) {
          window.close();
        }
      }
    }

    window.clear(sf::Color{0, 0, 255});

    for (const auto &item : map_items.value()) {
      window.draw(item);
    }

    window.display();
  }
}

mapitem.h

#ifndef MAPITEM_H
#define MAPITEM_H

#include <SFML/Graphics.hpp>
#include <SFML/Graphics/Drawable.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/System/NonCopyable.hpp>
#include <SFML/System/Vector2.hpp>
#include <optional>
#include <string>
#include <vector>

class MapItem : public sf::Drawable, sf::NonCopyable {
 public:
  MapItem(int map_x, int map_y);
  MapItem(MapItem &&m);
  MapItem &operator=(MapItem &&m);

  sf::Vector2i map_position() const;
  void set_map_position(int map_x, int map_y);
  int tile_index() const;
  bool init_sprite(int tile_index, const sf::Texture &tileset,
                    sf::Vector2i tile_size);

  void draw(sf::RenderTarget &target, sf::RenderStates states) const override;

 private:
  sf::Vector2i map_position_;
  int tile_index_;
  sf::Sprite sprite_;
};

class MapItemFactory : sf::NonCopyable {
 public:
  MapItemFactory(const sf::Texture &tileset, sf::Vector2i tile_size);
  MapItem create_map_item(int tile_index, int map_x, int map_y) const;

 private:
  const sf::Texture &tileset_;
  sf::Vector2i tile_size_;
};

std::optional<std::vector<MapItem>> make_map_from_csv_file(
    const std::string &filename, const MapItemFactory &factory);

#endif

mapitem.cpp

#include "mapitem.h"

#include <SFML/Graphics/Rect.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/lexical_cast/bad_lexical_cast.hpp>
#include <fstream>
#include <string>
#include <iostream>

MapItemFactory::MapItemFactory(const sf::Texture &tileset,
                                sf::Vector2i tile_size)
    : tileset_{tileset}, tile_size_{tile_size} {}

MapItem MapItemFactory::create_map_item(int tile_index, int map_x,
                                        int map_y) const {
  MapItem item{map_x, map_y};
  item.init_sprite(tile_index, tileset_, tile_size_);
  return item;
}

MapItem::MapItem(int map_x, int map_y) : map_position_{map_x, map_y} {}

// this function does not work as expected
MapItem::MapItem(MapItem &&other)
    : map_position_{other.map_position_},
      tile_index_{other.tile_index_},
      sprite_{std::move(other.sprite_)} {
  // other.map_position_ = {0, 0};
  // other.tile_index_ = -1;
}

// this function dows not work as expected
MapItem &MapItem::operator=(MapItem &&other) {
  map_position_ = other.map_position_;
  tile_index_ = other.tile_index_;
  sprite_ = std::move(other.sprite_);
  // other.map_position_ = {0, 0};
  // other.tile_index_ = -1;
  return *this;
}

sf::Vector2i MapItem::map_position() const { return map_position_; }

void MapItem::set_map_position(int map_x, int map_y) {
  map_position_ = {map_x, map_y};
}

int MapItem::tile_index() const { return tile_index_; }

// gimme boolean result
bool MapItem::init_sprite(int tile_index, const sf::Texture &tileset,
                          sf::Vector2i tile_size) {
  std::clog << map_position_.y << '\n';
  auto texture_size = tileset.getSize();
  int tiles_in_row = texture_size.x / tile_size.x;
  int row = tile_index / tiles_in_row;
  int col = tile_index % tiles_in_row;

  sprite_.setTexture(tileset);
  sprite_.setTextureRect(sf::IntRect{col * tile_size.x, row * tile_size.y,
                                      tile_size.x, tile_size.y});
  sprite_.setPosition(tile_size.x * map_position_.x,
                      tile_size.y * map_position_.y);


  tile_index_ = tile_index;
  return true; // by now returns always true
}

void MapItem::draw(sf::RenderTarget &target, sf::RenderStates states) const {
  target.draw(sprite_, states);
}

std::optional<std::vector<MapItem>> make_map_from_csv_file(
    const std::string &filename, const MapItemFactory &factory) {
  std::ifstream is{filename};
  if (!is) {
    return std::nullopt;
  }

  std::vector<MapItem> mapItems;
  int row = 0;
  std::string line;
  while (std::getline(is, line)) {
    std::vector<std::string> fields;
    boost::split(fields, line, boost::is_any_of(","));
    try {
      for (size_t col = 0; col < fields.size(); ++col) {
        auto v = boost::lexical_cast<int>(fields[col]);
        if (v >= 0) {
          auto item = factory.create_map_item(v, col, row);
          mapItems.push_back(std::move(item));
        }
      }
    } catch (boost::bad_lexical_cast &e) {
      return std::nullopt;
    }
    ++row;
  }
  return mapItems;
}

test_mapitem.cpp

#include <gtest/gtest.h>
#include <SFML/Graphics.hpp>
#include "mapitem.h"

TEST(TestMapItem, TestMapItem) {
  sf::Texture tileset;
  tileset.loadFromFile("../pcat3_tileset.png");

  MapItem mi{2, 3};
  ASSERT_EQ(mi.map_position(), sf::Vector2i(2, 3));

  mi.set_map_position(100, 200);
  ASSERT_EQ(mi.map_position(), sf::Vector2i(100, 200));

  bool init_ok = mi.init_sprite(1, tileset, {32, 32});
  ASSERT_TRUE(init_ok);
  ASSERT_EQ(mi.tile_index(), 1);
}

TEST(TestMapItem, TestMapItemFactory) {
  sf::Texture tileset;
  tileset.loadFromFile("../pcat3_tileset.png");

  MapItemFactory f{tileset, sf::Vector2i{32, 32}};

  auto m1 = f.create_map_item(1, 10, 20);
  ASSERT_EQ(m1.map_position(), sf::Vector2i(10, 20));
  ASSERT_EQ(m1.tile_index(), 1);
}

TEST(TestMapItem, TestMakeMapFromCsvFile) {
  sf::Texture tileset;
  tileset.loadFromFile("../pcat3_tileset.png");

  MapItemFactory f{tileset, {32, 32}};

  auto o = make_map_from_csv_file("../pcat3_tilemap.csv", f);

  ASSERT_TRUE(o.has_value());
  ASSERT_EQ(o.value().size(), 23);
}

タイルマップデータ

-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,4,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,4,-1,-1,-1,2,4,2,4,2,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

タイルセット画像

pcat3 tileset