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?