Hi Folks,
I am trying to setup a hexGrid type game and I am currently playing with different ways to do it.
Right now I got a simple class which has a sf::CircleShape that has 6 points to make it a Hexagon. I also have a vector of the sf::CircleShape for the grid.
Now I got the grid drawing fine and I am now trying to detect the hex that the mouse is in. I've written a simple method that is called each time the mouse is moved. It basically checks the globalBounds of each sf::Circleshape to see if the mouse position is contained within that circleShape. If yes then it changes the fillColour of the sf::Circleshape.
Now this is working ok, except when the mouse is hovering near the edge of some grids, then both get highlighted white, i.e. the mouse is within both circleshapes.
Is this because there is a bounding box around the sf::CircleShape which is larger that the displayed shape? Is there a way to resolve this?
I have put down a full working example that should be able to be compile and run on it's own.
hexGrid.h
#ifndef HEXGRID_HPP
#define HEXGRID_HPP
#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
using namespace std;
class hexgrid : public sf::Drawable
{
public:
hexgrid(sf::RenderWindow& window);
~hexgrid();
void draw(sf::RenderTarget& target, sf::RenderStates states) const;
bool chkHexGridWithMousePos(sf::Vector2f passed_mousePos);
bool isMouseContainsHexagaon(sf::CircleShape *hexPtr, sf::Vector2f passed_mousePos);
enum hexStyle
{
translucent,
colorful,
green,
cyan
};
private:
sf::CircleShape hexagon;
std::vector<sf::CircleShape> hexaGrid;
};
#endif // HEXGRID_HPP
hexGrid.cpp
#include "hexgrid.h"
hexgrid::hexgrid(sf::RenderWindow& window)
{
//set up background elements
hexagon.setRadius(window.getSize().x / 16.f);
hexagon.setPointCount(6);
hexagon.setFillColor(sf::Color(150, 50, 250));
hexagon.setOutlineThickness(2);
hexagon.setOutlineColor(sf::Color(250, 150, 100));
hexagon.setOrigin(hexagon.getGlobalBounds().width / 2.f, hexagon.getGlobalBounds().height / 2.f);
std::vector<sf::ConvexShape>::iterator hexit;
float xpos = 0, ypos = 0;
for (int y = 0; y < 12; y++)
{
if (y == 0)
{
ypos = y * hexagon.getGlobalBounds().height;
}
else
{
ypos = ypos + (hexagon.getGlobalBounds().height - (hexagon.getGlobalBounds().height * 0.25f));
}
for (int x = 0; x < 12; x++)
{
if (y % 2 == 0)
{
xpos = x * hexagon.getGlobalBounds().width;
}
else
{
xpos = (x * hexagon.getGlobalBounds().width) + (hexagon.getGlobalBounds().width * 0.5f);
}
hexagon.setPosition(xpos, ypos);
hexaGrid.push_back(hexagon);
}
}
}
hexgrid::~hexgrid()
{
}
void hexgrid::draw(sf::RenderTarget& target, sf::RenderStates states) const
{
std::vector<sf::CircleShape>::const_iterator hexit;
for (hexit = hexaGrid.begin(); hexit != hexaGrid.end(); ++hexit)
{
target.draw(*hexit, states);
}
}
bool hexgrid::chkHexGridWithMousePos(sf::Vector2f passed_mousePos)
{
bool returnVal = false;
for (int i = 0; i < hexaGrid.size(); i++)
{
if (isMouseContainsHexagaon(&hexaGrid[i], passed_mousePos))
{
returnVal = true;
}
}
return returnVal;
}
bool hexgrid::isMouseContainsHexagaon(sf::CircleShape *hexPtr, sf::Vector2f passed_mousePos)
{
if (hexPtr->getGlobalBounds().contains(passed_mousePos))
{
hexPtr->setFillColor(sf::Color(255, 255, 255));
return true;
}
else
{
hexPtr->setFillColor(sf::Color(150, 50, 250));
return false;
}
}
main
#include <iostream>
#include <SFML/Graphics.hpp>
#include "hexgrid.h"
int main()
{
sf::ContextSettings settings;
settings.antialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Hexgrid Example", sf::Style::Default, settings);
hexgrid grid(window);
sf::Event e;
bool running = true;
while (running)
{
while (window.pollEvent(e))
{
if (e.type == sf::Event::Closed)
{
window.close();
return 0;
}
if (e.type == sf::Event::MouseMoved)
{
grid.chkHexGridWithMousePos(sf::Vector2f(e.mouseMove.x, e.mouseMove.y));
}
}
window.clear();
window.draw(grid);
window.display();
}
return 0;
}
Implement your own "point in hexagon" test. There are two common ways of finding if a point is inside a convex polygon, you should easily find them with Google ;)
Ok thanks. I see the easiest one is the ray casting algorithm, i.e. check how many times a straight line from that point intersects the polygon's edge.
And there is the first stumbling block that I hit, how can I determine the edges of a sf::Circleshape? I am assuming there is no getEdges method in sf::circleshape or even getAllPixels method? (I could not see anything in documentation).
I've gone through each point of the shape and worked out the angle between each connecting point as below.
However I am confused as to how I then determine all of the the points along the edge. Without being able to determine that then I do not see how I can implement a ray casting algorithm
bool hexgrid::isMouseWithinHexagon(sf::CircleShape *hexPtr, sf::Vector2f passed_mousePos)
{
bool returnVal = true;
sf::Vector2f point, nextPoint;
const sf::Transform transformedVals = hexPtr->getTransform();
float angle = 0;
for (int i = 0; i < hexPtr->getPointCount(); i++)
{
point = transformedVals.transformPoint(hexPtr->getPoint(i));
if (i + 1 < hexPtr->getPointCount())
{
nextPoint = transformedVals.transformPoint(hexPtr->getPoint(i + 1));
}
else
{
nextPoint = transformedVals.transformPoint(hexPtr->getPoint(0));
}
angle = atan2(point.y - nextPoint.y, point.x - nextPoint.x);
angle = (angle * 180) / PI;
}
return returnVal;
}
Are you testing to see if a point is inside a circle, not a hexagon? Testing a circle is incredibly simple: test the distance between the point and the circle's centre - if that distance is greater than the circle's radius, it's outside.
I am trying to test if the point is inside a hexagon. So code from first post you see I have a sf::CircleShape defined with 6 points.
sf::CircleShape hexagon;
....
....
....
hexagon.setPointCount(6);
Are you splitting an edge into multiple points? You only need two points (which you can get from the circle) to define a line and you use this line to see if the testing line crosses that line.
No the edges are not split into multiple points. Yeah I am going through the output of sf::CircleShape->getPoint method to get points of the hexagon. (see below)
I get the points that are next to each other and work out the angle and distance between those two points.
My question/problem is I don't know how to get all of the x,y co-ordinates of the line/edge between those two points. So how can I determine the edge between those two points when I have the distance and angle?
bool hexgrid::isMouseWithinHexagon(sf::CircleShape *hexPtr, sf::Vector2f passed_mousePos)
{
bool returnVal = true;
sf::Vector2f point, nextPoint;
const sf::Transform transformedVals = hexPtr->getTransform();
float angle = 0;
for (int i = 0; i < hexPtr->getPointCount(); i++)
{
point = transformedVals.transformPoint(hexPtr->getPoint(i));
if (i + 1 < hexPtr->getPointCount())
{
nextPoint = transformedVals.transformPoint(hexPtr->getPoint(i + 1));
}
else
{
nextPoint = transformedVals.transformPoint(hexPtr->getPoint(0));
}
angle = atan2(point.y - nextPoint.y, point.x - nextPoint.x);
angle = (angle * 180) / PI;
}
return returnVal;
}
It might be worth considering specific hexagon detection algorithms. One of those being splitting the hexagon into triangles as they're pretty easy to test if a point is inside it.
Hmm ok, won't I have the same issue? how to determine the edge of the sf::CircleShape that has 3 points ?
Oh ok, I see what you mean, I guess I should have googled more before replying. :) So I will try to to do something like the below link to calc the area of the triangle?
http://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/