Lighting Bolts Effect

Some time ago I made a lighting bolt effect in Java, using Slick2D library, following the tutorial of Drilian’s House of Game Development. My results:

If you are looking to make a nice lighting bolt effect for your game, I recommend you to follow the original tutorial.

Here is my code if you want to use it and/or modify it:

  
package com.gemserk.commons.tests;

import static org.lwjgl.opengl.GL11.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.Callable;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Line;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.opengl.SlickCallable;

public class LightingBoltTestTemporal extends BasicGame {

	public LightingBoltTestTemporal(String title) {
		super(title);
	}

	public static void main(String[] arguments) throws SlickException {
		AppGameContainer app = new AppGameContainer(new LightingBoltTestTemporal("Name");
		app.setDisplayMode(320, 240, false);
		app.setAlwaysRender(true);
		app.setMinimumLogicUpdateInterval(1);
		app.setShowFPS(true);
		app.start();
		return app;
	}

	public static class LightingBoltEffect {

		Collection segments;

		int totalTime;

		int currentTime;

		private float lineWidth;

		public LightingBoltEffect(int time, Collection segments, float lineWidth) {
			this.totalTime = time;
			this.segments = segments;
			this.currentTime = time;
			this.lineWidth = lineWidth;
		}

		public void update(int delta) {
			currentTime -= delta;
			if (currentTime <= 0)
 				currentTime = 0;
 		}
 
 		public void render() {
 			float alpha = (float) currentTime / (float) totalTime;
 			glPushMatrix();
 			glColor4f(alpha, alpha, alpha, alpha);
 			glLineWidth(lineWidth);
 			glBegin(GL_LINES);
 			{
 				for (Line segment : segments) {
 					glVertex(segment.getStart());
 					glVertex(segment.getEnd());
 				}
 			}
 			glEnd();
 			glPopMatrix();
 		}
 
 		public boolean isDone() {
 			return currentTime <= 0;
 		}
 		
 
 		public void glVertex(Vector2f v) {
 			glVertex3f(v.x, v.y, 0);
 		}
 		
 	}
 
 	@Override
 	public void init(GameContainer container) throws SlickException {
 		generateLightingBolt(new Vector2f(50, 240), new Vector2f(290, 240), 100);
 	}
 
 	protected void generateLightingBolt(Vector2f p0, Vector2f p1, int duration) {
 		Collection segments = new ArrayList();
		
		segments.add(new Line(p0, p1));
		
		float offset = 200f;
		double probability = 0.3; // probability to generate new partitions
		float height = 50.0f; 
		
		Random random = new Random();
		
		int partitions = 4;
		
		for (int i = 0; i < partitions; i++) {
 		
 			Collection newSegments = new ArrayList();
		
			for (Line segment : segments) {
		
				Vector2f midPoint = segment.getStart().copy().add(segment.getEnd()).scale(0.5f);
		
				Vector2f perpendicular = midPoint.copy().add(90);
				perpendicular.normalise().scale(random.nextFloat() * offset - (offset / 2));
				midPoint.add(perpendicular);
				
				if (random.nextFloat() < probability) {
 					// generate new branch
 					Vector2f direction = midPoint.copy().sub(segment.getStart());
 					direction.add(random.nextFloat() * height);
 					newSegments.add(new Line(midPoint.copy(), midPoint.copy().add(direction)));
 				}
 		
 				newSegments.add(new Line(segment.getStart().copy(), midPoint.copy()));
 				newSegments.add(new Line(midPoint.copy(), segment.getEnd().copy()));
 			}
 		
 			segments = newSegments;
 		
 			offset /= 2;
 		}
 		lightingBoltEffect = new LightingBoltEffect(duration, segments, 2.0f);
 	}
 
 	private LightingBoltEffect lightingBoltEffect;
 
 	@Override
 	public void update(GameContainer container, int delta) throws SlickException {
 		lightingBoltEffect.update(delta);
 		if (!lightingBoltEffect.isDone())
 			return;
 
 		Input input = container.getInput();
 		if (!input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON))
 			return;
 
 		int mouseX = input.getMouseX();
 		int mouseY = input.getMouseY();
 
 		Random random = new Random();
 		int duration = random.nextInt() % 600 + 100;
 		generateLightingBolt(new Vector2f(mouseX, mouseY), new Vector2f((mouseX + 300), mouseY), duration);
 	}
 
 	@Override
 	public void render(GameContainer container, Graphics g) throws SlickException {
 		SlickCallable.enterSafeBlock();
 		lightingBoltEffect.render();
 		SlickCallable.leaveSafeBlock();
 	}
 }
VN:F [1.9.22_1171]
Rating: 4.0/5 (2 votes cast)
Lighting Bolts Effect, 4.0 out of 5 based on 2 ratings

Tags: , , ,

  • PunkKi11a

    I love this. Have you tried it on C# XNA. I have been trying to do it on it but have no luck what so ever.

  • Ariel Coppes

    Hi PunkKi11a, I never tried it on XNA, however you could try it by using quads and mapping a texture on it since you can't render directly lines or rectangles on XNA. Take a look at the original article at http://drilian.com/2009/02/25/lightning-bolts/ to get more information about it. The main logic to create the lighting bolt should be almost the same.

  • PunkKi11a

    I know that a quad is made of for vetecies and is made of two triangles using the triangle strip primitive type. What I cant figure out is how two implement it in too a generic collection list. And take in a Quad.Add(First quad(starting position), Second quad(ending position)) When I do it as  list of my class type Quad I can only pass in one argument through the .add property.

  • Ariel Coppes

    First of all, I know too little about XNA. I was taking a look at some Quad examples in the web and my suggestion is, first build a list of segments defining the Lighting Bolt skeleton, and then build quads from each segment.

    For example, given a segment s1 with points p0 and p1, you can build four vertex for the quad in this way (pseudo code):

    vector_direction = p1 - p0
    vector_normal = vector_direction.rotate(90)
    vector_normal.normalize()
    vector_normal.mult(desiredQuadWidth)

    v0 =p0.add(vector_normal)
    v1 =p0.add(-vector_normal)

    v2 =p1.add(vector_normal)

    v3 =p1.add(-vector_normal)

    You can build the Quad specifying four points instead. Can I see the code of your Quad class, where are you getting it?

  • PunkKi11a

    using System;
    using System.Collections.Generic;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Content;
    using System.Collections;

    namespace Glock
    {
        public class Line
        {
            private Vector3 start, end, mid;
            public int cout = 0;

            public Vector3 Start
            {
                get { return start; }
                set { start = value; }
            }

            public Vector3 End
            {
                get { return end; }
                set { end = value; }
            }

            public Vector3 Mid
            {
                get { return mid; }
                set { mid = value; }
            }

            public Line(Vector3 start, Vector3 end)
            {
                this.start = start;
                this.end = end;
            }

            public virtual void Initialize()
            {
                GenerateLines(new Vector3(50, 300, 0), new Vector3(300, 300, 0), 100);
            }

            protected void GenerateLines(Vector3 p0, Vector3 p1, int time)
            {
                List lines = new List();

                lines.Add(new Line(p0, p1));
                Random rnd = new Random();
                float offset = 100f;
                int links = 5;

                for (int i = 0; i < links; i++)
                {
                    List newLine = new List();

                    foreach (Line Line in lines)
                    {
                        Vector3 middle = (p0 + p1) / 2;
                        Vector3 p = p1 - p0;
                        p.Normalize();
                        middle += p * (float)MathHelper.Lerp(-offset, offset, (float)rnd.NextDouble());

                        newLine.Add(new Line(p0, middle));
                        newLine.Add(new Line(middle, end));
                    }

                    lines = newLine;
                    cout = lines.Count;
                    offset /= 2;
                }
            }
        }
    }

    This is what I got so far. This is just a class in a separate .cs file. I call a constructor that takes in a vector3 for the start and end of the line. I than go through all the code to created the other points of the line by deviding it and changing the status of it.

  • Ariel Coppes

    Hi, I believe you should build the quads as I suggested now that you have the collection of lines, however, as I said before, I know too little about XNA and stuff so I suggest you to visit the original author of the article at http://drilian.com/2009/02/25/lightning-bolts/ and ask him directly, they could give you more ideas.

    Thanks for visiting the blog and good luck with the effect.

  • Anonymous

    Inside your foreach loop you are still using the positions that you passed into the generateLines method?Wouldnt that draw the same line over and over again?