1
Graphics / Re: Tilemap cell ID
« on: April 12, 2021, 12:57:13 pm »
Yes Paul!
It works! So I'm still copying all tiles from vertices into std::vector<sf::ConvexShape> tileShapes;
Meanwhile I made a function void checkTiles which checks mouse position and calculates region of 3 tiles based on 32x32 tile size. It selects closest tile to mouse and 1 up from it and 1 down from it. I didn't check to left and right because tile sizes on X doesn't change and is always 32. This function saves int variable representing tile ID into std::vector<unsigned int> vec; and it matches the tile sequence of std::vector<sf::ConvexShape> tileShapes;
Then based on what ID's I'm having currently I'm checking for tileShapes[vec] and saving all 4 corner positions into std::vector<sf::Vector2f> v1, v2 and v3. Then I'm running your algorithm on these 3 vectors and checking which v is true in a separate function - unsigned int currentTile(mousePosView). And then in gamestate rendering it like that: this->window->draw(this->map.tileShapes[map.currentTile(mousePosView)]);
Only thing I'm struggling with now is that when mouse position on X axis is right on the edge of 2 tiles it's calling tileShape[0] for some reason :/
Any ideas?
Here is my code:
It works! So I'm still copying all tiles from vertices into std::vector<sf::ConvexShape> tileShapes;
Meanwhile I made a function void checkTiles which checks mouse position and calculates region of 3 tiles based on 32x32 tile size. It selects closest tile to mouse and 1 up from it and 1 down from it. I didn't check to left and right because tile sizes on X doesn't change and is always 32. This function saves int variable representing tile ID into std::vector<unsigned int> vec; and it matches the tile sequence of std::vector<sf::ConvexShape> tileShapes;
Then based on what ID's I'm having currently I'm checking for tileShapes[vec] and saving all 4 corner positions into std::vector<sf::Vector2f> v1, v2 and v3. Then I'm running your algorithm on these 3 vectors and checking which v is true in a separate function - unsigned int currentTile(mousePosView). And then in gamestate rendering it like that: this->window->draw(this->map.tileShapes[map.currentTile(mousePosView)]);
Only thing I'm struggling with now is that when mouse position on X axis is right on the edge of 2 tiles it's calling tileShape[0] for some reason :/
Any ideas?
Here is my code:
#pragma once
#include <SFML/Graphics.hpp>
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
std::vector<sf::ConvexShape> tileShapes;
std::vector<unsigned int> vec;
std::vector<sf::Vector2f> v;
std::vector<sf::Vector2f> v1;
std::vector<sf::Vector2f> v2;
std::vector<sf::Vector2f> v3;
void checkTiles(sf::Vector2f mousePosView, sf::Vector2i tileSize, unsigned int width, unsigned int height)
{
vec[0] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if((int)mousePosView.y < (height * 32) - 32)
vec[1] = unsigned int(((int)mousePosView.x / tileSize.x + (((int)mousePosView.y / tileSize.y) + 1) * width));
else
vec[1] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if ((int)mousePosView.y > 32)
vec[2] = unsigned int(((int)mousePosView.x / tileSize.x + (((int)mousePosView.y / tileSize.y) - 1) * width));
else
vec[2] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if ((int)mousePosView.y < height * 32 && (int)mousePosView.x < width * 32)
{
v1 = { {tileShapes[vec[0]].getPoint(0)}, {tileShapes[vec[0]].getPoint(1)}, {tileShapes[vec[0]].getPoint(2)}, {tileShapes[vec[0]].getPoint(3)} };
v3 = { {tileShapes[vec[2]].getPoint(0)}, {tileShapes[vec[2]].getPoint(1)}, {tileShapes[vec[2]].getPoint(2)}, {tileShapes[vec[2]].getPoint(3)} };
v2 = { {tileShapes[vec[1]].getPoint(0)}, {tileShapes[vec[1]].getPoint(1)}, {tileShapes[vec[1]].getPoint(2)}, {tileShapes[vec[1]].getPoint(3)} };
}
}
int PointInPolygon(const sf::Vector2f& p, const std::vector<sf::Vector2f>& v)
{
int i, j, c = 0;
for (i = 0, j = v.size() - 1; i < v.size(); j = i++)
{
if (((v[i].y > p.y) != (v[j].y > p.y)) &&
(p.x < (v[j].x - v[i].x) * (p.y - v[i].y) / (v[j].y - v[i].y) + v[i].x))
c = !c;
}
return c;
}
unsigned int currentTile(sf::Vector2f mousePosView)
{
if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v1))
{
return vec[0];
}
else if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v2))
{
return vec[1];
}
else if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v3))
{
return vec[2];
}
}
bool load(const std::string& tileset, sf::Vector2i tileSize, const int* tiles, unsigned int width, unsigned int height, std::vector<std::vector<int>> m_heightmap, sf::Color gridColor)
{
// load the tileset texture
if (!m_tileset.loadFromFile(tileset))
return false;
// resize the vertex array to fit the level size
m_vertices.setPrimitiveType(sf::Quads);
m_vertices.resize(width * height * 4);
m_gridX.setPrimitiveType(sf::Lines);
m_gridX.resize(width * height * 4);
m_gridY.setPrimitiveType(sf::Lines);
m_gridY.resize(width * height * 4);
tileShapes.resize(width * height);
v.resize(4);
vec.resize(3);
for (auto i = 0; i < tileShapes.size(); i++) {
tileShapes[i].setPointCount(4);
tileShapes[i].setOrigin(0, 0);
}
// populate the vertex array, with one quad per tile
for (unsigned int x = 0; x < width; ++x)
for (unsigned int y = 0; y < height; ++y)
{
// get the current tile number
int tileNumber = tiles[x + y * width];
// find its position in the tileset texture
int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
// get a pointer to the current tile's quad
sf::Vertex* quad = &m_vertices[(x + y * width) * 4];
sf::Vertex* gridX = &m_gridX[(x + y * width) * 4];
sf::Vertex* gridY = &m_gridY[(x + y * width) * 4];
// define its 4 corners
quad[0].position = sf::Vector2f(x * tileSize.x, y * tileSize.y + m_heightmap[x][y]);
quad[1].position = sf::Vector2f((x + 1) * tileSize.x, y * tileSize.y + m_heightmap[x + 1][y]);
quad[2].position = sf::Vector2f((x + 1) * tileSize.x, (y + 1) * tileSize.y + m_heightmap[x + 1][y + 1]);
quad[3].position = sf::Vector2f(x * tileSize.x, (y + 1) * tileSize.y + m_heightmap[x][y + 1]);
tileShapes[x + y * width].setPoint(0, quad[0].position);
tileShapes[x + y * width].setPoint(1, quad[1].position);
tileShapes[x + y * width].setPoint(2, quad[2].position);
tileShapes[x + y * width].setPoint(3, quad[3].position);
gridX[0].position = quad[0].position;
gridX[1].position = quad[1].position;
gridX[2].position = quad[3].position;
gridX[3].position = quad[2].position;
gridY[0].position = quad[0].position;
gridY[1].position = quad[3].position;
gridY[2].position = quad[1].position;
gridY[3].position = quad[2].position;
gridX[0].color = gridColor;
gridX[1].color = gridColor;
gridX[2].color = gridColor;
gridX[3].color = gridColor;
gridY[0].color = gridColor;
gridY[1].color = gridColor;
gridY[2].color = gridColor;
gridY[3].color = gridColor;
// define its 4 texture coordinates
quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
}
return true;
}
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// apply the transform
states.transform *= getTransform();
// apply the tileset texture
states.texture = &m_tileset;
// draw the vertex array
target.draw(m_vertices, states);
target.draw(m_gridX);
target.draw(m_gridY);
}
sf::VertexArray m_vertices;
sf::VertexArray m_gridX;
sf::VertexArray m_gridY;
sf::Texture m_tileset;
};
#include <SFML/Graphics.hpp>
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
std::vector<sf::ConvexShape> tileShapes;
std::vector<unsigned int> vec;
std::vector<sf::Vector2f> v;
std::vector<sf::Vector2f> v1;
std::vector<sf::Vector2f> v2;
std::vector<sf::Vector2f> v3;
void checkTiles(sf::Vector2f mousePosView, sf::Vector2i tileSize, unsigned int width, unsigned int height)
{
vec[0] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if((int)mousePosView.y < (height * 32) - 32)
vec[1] = unsigned int(((int)mousePosView.x / tileSize.x + (((int)mousePosView.y / tileSize.y) + 1) * width));
else
vec[1] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if ((int)mousePosView.y > 32)
vec[2] = unsigned int(((int)mousePosView.x / tileSize.x + (((int)mousePosView.y / tileSize.y) - 1) * width));
else
vec[2] = unsigned int(((int)mousePosView.x / tileSize.x + ((int)mousePosView.y / tileSize.y) * width));
if ((int)mousePosView.y < height * 32 && (int)mousePosView.x < width * 32)
{
v1 = { {tileShapes[vec[0]].getPoint(0)}, {tileShapes[vec[0]].getPoint(1)}, {tileShapes[vec[0]].getPoint(2)}, {tileShapes[vec[0]].getPoint(3)} };
v3 = { {tileShapes[vec[2]].getPoint(0)}, {tileShapes[vec[2]].getPoint(1)}, {tileShapes[vec[2]].getPoint(2)}, {tileShapes[vec[2]].getPoint(3)} };
v2 = { {tileShapes[vec[1]].getPoint(0)}, {tileShapes[vec[1]].getPoint(1)}, {tileShapes[vec[1]].getPoint(2)}, {tileShapes[vec[1]].getPoint(3)} };
}
}
int PointInPolygon(const sf::Vector2f& p, const std::vector<sf::Vector2f>& v)
{
int i, j, c = 0;
for (i = 0, j = v.size() - 1; i < v.size(); j = i++)
{
if (((v[i].y > p.y) != (v[j].y > p.y)) &&
(p.x < (v[j].x - v[i].x) * (p.y - v[i].y) / (v[j].y - v[i].y) + v[i].x))
c = !c;
}
return c;
}
unsigned int currentTile(sf::Vector2f mousePosView)
{
if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v1))
{
return vec[0];
}
else if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v2))
{
return vec[1];
}
else if (PointInPolygon(static_cast<sf::Vector2f>(mousePosView), v3))
{
return vec[2];
}
}
bool load(const std::string& tileset, sf::Vector2i tileSize, const int* tiles, unsigned int width, unsigned int height, std::vector<std::vector<int>> m_heightmap, sf::Color gridColor)
{
// load the tileset texture
if (!m_tileset.loadFromFile(tileset))
return false;
// resize the vertex array to fit the level size
m_vertices.setPrimitiveType(sf::Quads);
m_vertices.resize(width * height * 4);
m_gridX.setPrimitiveType(sf::Lines);
m_gridX.resize(width * height * 4);
m_gridY.setPrimitiveType(sf::Lines);
m_gridY.resize(width * height * 4);
tileShapes.resize(width * height);
v.resize(4);
vec.resize(3);
for (auto i = 0; i < tileShapes.size(); i++) {
tileShapes[i].setPointCount(4);
tileShapes[i].setOrigin(0, 0);
}
// populate the vertex array, with one quad per tile
for (unsigned int x = 0; x < width; ++x)
for (unsigned int y = 0; y < height; ++y)
{
// get the current tile number
int tileNumber = tiles[x + y * width];
// find its position in the tileset texture
int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
// get a pointer to the current tile's quad
sf::Vertex* quad = &m_vertices[(x + y * width) * 4];
sf::Vertex* gridX = &m_gridX[(x + y * width) * 4];
sf::Vertex* gridY = &m_gridY[(x + y * width) * 4];
// define its 4 corners
quad[0].position = sf::Vector2f(x * tileSize.x, y * tileSize.y + m_heightmap[x][y]);
quad[1].position = sf::Vector2f((x + 1) * tileSize.x, y * tileSize.y + m_heightmap[x + 1][y]);
quad[2].position = sf::Vector2f((x + 1) * tileSize.x, (y + 1) * tileSize.y + m_heightmap[x + 1][y + 1]);
quad[3].position = sf::Vector2f(x * tileSize.x, (y + 1) * tileSize.y + m_heightmap[x][y + 1]);
tileShapes[x + y * width].setPoint(0, quad[0].position);
tileShapes[x + y * width].setPoint(1, quad[1].position);
tileShapes[x + y * width].setPoint(2, quad[2].position);
tileShapes[x + y * width].setPoint(3, quad[3].position);
gridX[0].position = quad[0].position;
gridX[1].position = quad[1].position;
gridX[2].position = quad[3].position;
gridX[3].position = quad[2].position;
gridY[0].position = quad[0].position;
gridY[1].position = quad[3].position;
gridY[2].position = quad[1].position;
gridY[3].position = quad[2].position;
gridX[0].color = gridColor;
gridX[1].color = gridColor;
gridX[2].color = gridColor;
gridX[3].color = gridColor;
gridY[0].color = gridColor;
gridY[1].color = gridColor;
gridY[2].color = gridColor;
gridY[3].color = gridColor;
// define its 4 texture coordinates
quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
}
return true;
}
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// apply the transform
states.transform *= getTransform();
// apply the tileset texture
states.texture = &m_tileset;
// draw the vertex array
target.draw(m_vertices, states);
target.draw(m_gridX);
target.draw(m_gridY);
}
sf::VertexArray m_vertices;
sf::VertexArray m_gridX;
sf::VertexArray m_gridY;
sf::Texture m_tileset;
};