Page 1 of 2

How to do lightning

Posted: Tue Jul 12, 2022 10:13 pm
by Gunroar:Cannon()
Like the zap zap kind. Not a misspelling.
The closest thing I've found is this and I'm not really sire how it applies to love2d.
I'm guessing a crude way be to get 2 points (start and destination) and then create a bunch of lines (that fit into the width of end minus start) of different lengths and offsets to each other. But that's crude I think.

Any known nice way of implementing it. (Examples Nuclear throne, MDickie games (Extra Lives, BackWars))
Image
Not that sporadic but this is the gif I could find :)

Re: How to do lightning

Posted: Wed Jul 13, 2022 3:26 am
by togFox
While not at destination
Determine random segment length
Determine random angle
Draw segment
End

There is probably some recursion in there somewhere.

Re: How to do lightning

Posted: Wed Jul 13, 2022 7:11 am
by darkfrei

Re: How to do lightning

Posted: Wed Jul 13, 2022 10:27 am
by Gunroar:Cannon()
So I tried with the posts I got + some ideas and now I have this...

Code: Select all

local function distance(x,y,xx,yy)
    return math.sqrt(((x-xx)^2)+(y-yy)^2)
end

rand  = function(x)
    return math.random(-x, x)
end
ldraw = function(x,y,xx,yy)
    local drawLine = love.graphics.line
    local len =  distance(x,y,xx,yy)
    local nlen = len
    
    local lines = {}
    local nn = 15
    
    local ox, oy = x, y
    
    local min, max = 3,13
    local mx = math.random(min,max)
 
    while nlen>0 do
        local t = len/(math.random(min,max))
        if t > nlen then
            t = nlen
        end
        
        nlen = nlen - t
        
        local done = nlen<=0
        local line = {
            x = ox,
            
            y = oy,
            
            x2 = ox+(done and 0 or rand(nn)),
            y2 = oy+t,0+(done  and 0 or rand(nn))
        }
        ox,oy=line.x2, line.y2
        
        lines[#lines+1] = line
         
    end
     
    
    for xi = 1, #lines do
        local l = lines[xi]
        drawLine(l.x, l.y, l.x2, l.y2)
    end
end

love.draw = function()
    ldraw(100,10,100,300)--startx, starty, endx, endy
    --love.graphics.line(200,10,200,300)
end
It's a little fast but I think it's pretty good. Not fancy but could work.
Though problems that I couldn't seem to fix:
1) As I see it I can only go one axis/direction at a time. Either a vertical path or a horizontal one whereas I'd like there to also be diagonal.

2) The last segment doesn't end at the intended destination.

Re: How to do lightning

Posted: Wed Jul 13, 2022 10:48 am
by darkfrei
Gunroar:Cannon() wrote: Wed Jul 13, 2022 10:27 am 2) The last segment doesn't end at the intended destination.
This.
Make the line between start and end points.
Take a point between them and move it slightly. So you have two lines now, that are not on the straight line between start and end points.
For each line take any point plus/minus near the middle and move it slightly.
Ignore too short lines and repeat until result.

The animation is just random moving this all new middle points, respecting the sequence of it creation.

Re: How to do lightning

Posted: Wed Jul 13, 2022 11:09 am
by Gunroar:Cannon()
darkfrei wrote: Wed Jul 13, 2022 10:48 am
Take a point between them and move it slightly.
Is that possible in love2d. Won't I need a mesh or something similar for that? My implementation consists of multiple lines starting at the end of the line that came before them(or the starting point if the line is the first one).

Re: How to do lightning

Posted: Wed Jul 13, 2022 12:02 pm
by pgimeno
Gunroar:Cannon() wrote: Wed Jul 13, 2022 11:09 am Is that possible in love2d. Won't I need a mesh or something similar for that?
A table. Think of lines in abstract, not as in love.graphics.line(). You'll get to the rendering part later.

Re: How to do lightning

Posted: Wed Jul 13, 2022 10:38 pm
by Gunroar:Cannon()
pgimeno wrote: Wed Jul 13, 2022 12:02 pm
Gunroar:Cannon() wrote: Wed Jul 13, 2022 11:09 am Is that possible in love2d. Won't I need a mesh or something similar for that?
A table. Think of lines in abstract, not as in love.graphics.line().
Ahhh, yes! I'll try this.

Re: How to do lightning

Posted: Thu Jul 14, 2022 10:30 am
by darkfrei
(not tested)

Code: Select all

local source ={x=0, y=0}
local target ={x=800, y=600}

local lightning= {
  source=source,
  target=target,
  mainLine={source, target},
}

function addPoint (lightning, index) -- index = math.random(#lightning.mainLine -1)
  local x1=lightning.mainLine[index].x
  local y1=lightning.mainLine[index].y
  local x2=lightning.mainLine[index+1].x
  local y2=lightning.mainLine[index+1].y
  local t = 0.25 + 0.5*math.random()
  local x = x1+ t*(x2 - x1)
  local y = y1+ t*(y2 - y1)
  x = x + 0.25*(y2 - y1)* (math.random()-1)
  y = y + 0.25*(x2 - x1)* (math.random()-1)
  table.insert (lightning.mainLine, index+1, {x=x,y=y})
end

Re: How to do lightning

Posted: Fri Jul 22, 2022 10:02 pm
by Gunroar:Cannon()
A little too fast (no delay implemented) but here's what I scrounged up (still haven't tested darkfrei's one yet, just wanted to see if this idea worked first).

Code: Select all

local function choose(t)
    return t[math.random(#t)]
end

local function point_distance(x,y,xx,yy)
    return math.sqrt(((x-xx)^2)+(y-yy)^2)
end

local function point_direction(x,y,xx,yy)
    return -math.atan2(yy-y, xx-x)
end

random_range  = function(x,y)
    return math.random((y and x or -x), y or x)
end

array_length = function(t)
    return #t
end


function lengthdir_x(spd, ang)
    return spd*math.cos(ang)
end

function lengthdir_y(spd, ang)
    return spd*-math.sin(ang)
end

function draw_set_alpha(a)
    local r,g,b = love.graphics.getColor()
    love.graphics.setColor(r,g,b,a)
end


function set_color(rr,gg,bb,aa)
    
    local r,g,b,a = love.graphics.getColor()
    
    if not rr then
        return r, g, b, a
    end
    
    love.graphics.setColor(rr,gg,bb,aa)
    return r,g,b,a
end

function draw_line_width_colour(x1, y1, x2, y2,size,color1,color2)
    local r,g,b,a = set_color(color1)
    local p = love.graphics.getLineWidth()
    love.graphics.setLineWidth(sizge or p)
    love.graphics.line(x1,y1,x2,y2)
    set_color(r,g,b,a)
    love.graphics.setLineWidth(p)
end

function draw_circle_colour(x,y,r,c1,c2)
    local r,g,b,a = set_color(c1)
    love.graphics.circle("fill",x,y,r)
    set_color(r,g,b,a)
end

local c_white = {1,1,1}
function draw_lightning(x1, y1, x2, y2, branches, size, color)
    --draw_lightning(x, y, x2, y2, branches, size, colour)
    --
    --draws a lightning bolt from the given starting location to the given end location
    --
    --x = x of the bolt's start
    --y = y of the bolt's start
    --x2 = x of the bolt's end
    --y2 = y of the bolt's end
    --branches = true or false, if the lightning bolt branches into multiple smaller ones
    --size = pixel width of the lightning
    --colour = colour of the glow
    --
    --amusudan 23/5/2016
    --
    --feel free to use this in your project!
    --
    local dir = point_direction(x1,y1,x2,y2)
    local length = point_distance(x1,y1,x2,y2) --error(length)
    local colour = color;
    local _size = size;
    --make different segments
    local point = {};
    point[1] = 0;
    local i2 = 2;
    for i = 1, length do
        if (math.random() < .06) then
            point[i2] = i;
            i2 = i2+1;
        end
    end
    point[i2] = length;-- error(point[#point-0])
    local points = array_length(point);
    --draw segments
    local i2 = 2
    local difx = 0;
    local difx2 = 0;
    local dify = 0;
    local ii =0
    local dify2 = 0;    --error(points)
    local dis = 7
    for i2 = 2, points do
        local i2 = i2+ii
        difx = random_range(dis)
        dify = random_range(dis)
        local xx = x1 + lengthdir_x(point[i2 - 1],dir);
        local yy = y1 + lengthdir_y(point[i2 - 1],dir);
        local xx2 = x1 + lengthdir_x(point[i2],dir);
        local yy2 = y1 + lengthdir_y(point[i2],dir); --error(xx2..","..x1..",y2:"..yy2..","..y1..","..dir..","..point[i2])
        --create a branch
        if (math.random() < .15 and branches) then
            local bdir = dir + choose({random_range(-45,-25),random_range(45,25)});
            local blength = random_range(5,30);
            draw_lightning(xx + difx2, yy + dify2, xx + difx2 + lengthdir_x(blength,bdir), yy + dify2 + lengthdir_y(blength,bdir), false, _size, colour)
        end
        --draw the glow of the lightning
        set_color(1,1,1,.1)
        
        draw_line_width_colour(xx + difx2,yy + dify2,xx2 + difx,yy2 + dify, size + 5,colour,colour);
        draw_line_width_colour(xx + difx2,yy + dify2,xx2 + difx,yy2 + dify, size + 3,c_white,c_white);
        draw_line_width_colour(xx + difx2,yy + dify2,xx2 + difx,yy2 + dify, size + 1,c_white,c_white);
        draw_set_alpha(1)
        --draw the white center of the lightning
        draw_line_width_colour(xx + difx2,yy + dify2,xx2 + difx,yy2 + dify, size,c_white,c_white);
        --ii = ii+1;
        difx2 = difx;
        dify2 = dify;
    end
    --draw a glowing circle
    if (branches) then
        draw_set_alpha(.91);
        draw_circle_colour(x1,y1,size + 2.5,colour,colour,false);
        draw_circle_colour(x1,y1,size + 1.5,colour,colour,false);
        draw_circle_colour(x1,y1,size + .5,colour,colour,false);
        draw_set_alpha(1);
        draw_circle_colour(x1,y1,size,c_white,c_white,false);
    end
end

love.draw = function()
    love.graphics.scale(.5)
    --love.graphics.translate(0,500)
    local x = 100
    local y = 100
    local xx = 150
    local yy = 300

    --draw_line_width_colour(x,y,xx,yy,1,{0,1,0})
    draw_lightning(x,y,xx,yy,true,10)
end
But it works, can even branch :P