I'm going to need a force field in future project and since I suck at making graphics, I decided to make a simple class that draws lightning (not a lightning rod
I thought I'd share the code incase someone else need some lightning.
#include <SFML/Graphics.hpp>
#include <vector>
class Lightning : public sf::Drawable
{
public:
Lightning(unsigned long noSegments, float thickness = 1.0f, sf::Color fadeColor = sf::Color(0, 0, 0))
: noSegments(noSegments), thickness(thickness), fadeColor(fadeColor) {segments.reserve(noSegments);}
void generate ();
void setSize (float width, float height) {size.x = width; size.y = height;}
virtual void Render (sf::RenderTarget& target, sf::Renderer& renderer) const;
private:
unsigned long noSegments;
float thickness;
sf::Color fadeColor; // Outer color of the lightning
std::vector<float> segments; // Contains the x-position of each segment (-1.0 - +1.0), multiply with width to get final position
sf::Vector2f size; // Size of the lightning
};
void Lightning::generate()
{
sf::Vector2f start;
sf::Vector2f end(0.0f, 0.0f);
segments.clear();
segments.push_back(0.0f); // The start point of the lightning is centered
for(unsigned long c = 1; c < noSegments - 1; ++c)
segments.push_back(sf::Randomizer::Random(-1.0f, 1.0f));
segments.push_back(0.0f); // The end point of the lightning is also centered
}
void Lightning::Render(sf::RenderTarget& target, sf::Renderer& renderer) const
{
sf::Vector2f start, end;
sf::Color color, colorDifference, colorStep;
// If the thickness is 1.0 then draw using only the main color. Otherwise calculate the amount to fade the fade color each iteration
if(thickness == 1.0f)
color = GetColor();
else
{
color = fadeColor;
// sf::Color doesn't provide the minus or division operators, so we'll have to make the calculations component by component
colorDifference = sf::Color(GetColor().r - fadeColor.r,
GetColor().g - fadeColor.g,
GetColor().b - fadeColor.b,
GetColor().a - fadeColor.a);
colorStep = sf::Color(colorDifference.r / static_cast<sf::Uint8> (thickness - 1.0f),
colorDifference.g / static_cast<sf::Uint8> (thickness - 1.0f),
colorDifference.b / static_cast<sf::Uint8> (thickness - 1.0f),
colorDifference.a / static_cast<sf::Uint8> (thickness - 1.0f)); // Increase the fade color by this each iteration.
}
float segmentHeight = size.y / (noSegments - 1.0f);
float halfWidth = size.x / 2;
float halfThickness = thickness / 2;
// Draw multiple lightnings, decreasing the thinkness by 1.0 until we reach thickness 1.0 (or less)
for(float t = thickness; t >= 1.0f; --t, color += colorStep)
for(unsigned long c = 0; c < noSegments - 1; ++c)
{
start.x = segments[c] * (halfWidth - halfThickness) + halfWidth ; start.y = segmentHeight * c;
end.x = segments[c + 1] * (halfWidth - halfThickness) + halfWidth; end.y = segmentHeight * (c + 1);
sf::Shape line = sf::Shape::Line(start, end, t, color);
target.Draw(line);
}
}
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "Lightning test");
sf::Event evnt;
bool paused=false;
Lightning lightning(15, 3.0f, sf::Color::Blue);
lightning.SetPosition(0.0f, 0.0f);
lightning.setSize(15.0f, 150.0f);
lightning.SetColor(sf::Color::White);
while(window.IsOpened())
{
while(window.GetEvent(evnt))
{
if(evnt.Type == sf::Event::Closed)
window.Close();
if(evnt.Type == sf::Event::KeyPressed && evnt.Key.Code == sf::Key::Escape)
window.Close();
if(evnt.Type == sf::Event::KeyPressed && evnt.Key.Code == sf::Key::Space)
paused=true;
if(evnt.Type == sf::Event::KeyReleased && evnt.Key.Code == sf::Key::Space)
paused=false;
}
if(!paused)
{
window.Clear();
lightning.generate();
window.Draw(lightning);
window.Display();
}
}
return EXIT_SUCCESS;
}
I've included a simple main() to test it. Hold SPACE to pause, ESCAPE to quit.
The corners don't look good if the size and/or thinkness gets to large. The start and end points of the lightning are fixed. But it shouldn't be too hard to change that if you need eg. a lightning gun.
I've only tried it with SFML2.
edit: Fixed a small bug when calculating segmentHeight.