$10 Explosion Competition

Its simple. Its cheap. Hopefully it is enough to inspire you to dig up your explosion code and post it here. It’s $10 sent via payapl to the best explosion code out there.

I have always been fascinated by explosions. I almost took a job in Amarillo Texas just so I could build hydrogen bombs, the biggest man made explosions I know of. Thankfully my altruistic self won out and I decided it would be better for humanity if I used my skills to produce power to bless the lives of others, rather than destroy them.

The competition will run through July 7th. With the winner announced by July 14th.
The criteria is as follows:

  1. Attitude. How do you treat others? Are you nice and helpful (10 pts) or a troll, arrogant, and unhelpful (0 pts).
  2. Code Sharing. Is it able to be used and modified without restriction (10 pts), displayed so that others can see, but all rights retained (5 pts), or not shared at all (0 pts).
  3. Usability. Is it simple to implement and use in code (10 pts), or does it take a nuclear science degree to figure out how to set it up to use in code (0 pts)?
  4. Readibiltiy. Is the code simple to read with lots of comments (10 pts)? Or does it require prophetic skills to interpret and decipher the technique used (0 pts)?
  5. Awesomeness. Does it make you go, “Wow, I wish I was able to code stuff like that?” (up to 10 pts).
  6. Frames per second. Can it run at a full 60 fps (up to 10 pts)? Or does it lag out with stuttery frames on older iPads?
  7. Judging slush fund. 10 points that the judge (currently only me) or judges (if someone would like to volunteer to help make it more fair, please do so), that the judge can use any way he/she sees fit. If you are a judge you will be not be allowed to receive the $10. Feel free to participate, you can still win, but realize the $10 will go to the person with the most points who is not a judge.

Hopefully that covers everything. It’s meant to be fun, enjoyable, and help facilitate learning about how to make explosions in games.

Please post all entries, comments, and code in this thread.

To be considered for judging you must include somewhere in your entry post the following word spelled backwards, “aibohphobia”. Thus the judges can do a search and gather all the entries and look at them one by one.

I have started my entry. It works! But the way I set up initial angles/velocity of particles made it so that if I use enough particles, the explosion is a square shape xD Going to restructure those calculations so it will be more circular, and more random.

Hi there! Are you looking for a visual/partical animation, physics effect, or both?

I am looking for whatever you would enjoy coding the most. It is meant to be an enjoyable, leisurely, competition. Feel free to do multiple explosions.

I’m working on my own explosion entry, and I’ll have done it a little bit.

(In case if you wondered how many people would enter)

Here’s a particle based example using the ship parts which come with Codea, though could be easily customised with your own parts

-- Particle Explosion by West
displayMode(FULLSCREEN)
-- Use this function to perform your initial setup
function setup()
    --particle based explosion
    --use doParticles() in draw() to draw the particles
    --explodeStar() and explodeShip() create explosions
    
    --Feel free to use, modify improve as you like
    
    particles={} -- a table to hold the particle
    touches={} --a table to track individual touches
    gravity=0.2 --a constant value for gravity
    ground=HEIGHT/7 --a constant for the ground level
    shipparts={"Space Art:Part Red Hull 1","Space Art:Part Red Hull 2","Space Art:Part Red Hull 3","Space Art:Part Red Hull 4",
    "Space Art:Part Red Hull 5","Space Art:Part Red Wing 1","Space Art:Part Red Wing 2","Space Art:Part Red Wing 3","Space Art:Part Red Wing 4"} --a table for the graphical components of the explosion
end

-- This function gets called once every frame
function draw()
    -- set up the scene
    background(27, 27, 49, 255)
    strokeWidth(3)
    line(0,ground,WIDTH,ground) --draw a ground line
    doParticles()
end
function doParticles()
        --loop through the particle table
    for i,p in pairs(particles) do
        pushMatrix()
        tint(255,255,255,p.fade)  --set the transparency of the particle based on its fade property
        translate(p.x,p.y) --- move it to the correct position
        rotate(p.rotation) --rotate it by the coorect amount
        sprite(p.img,0,0,p.w,p.h) --draw the particle
        noTint() -- remove transparencies
        popMatrix()
        --if the particle is a star or smoke then fade it out
        if p.type<3 then
            p.fade = p.fade -1
            --if the star or smoke has faded to invisible then remove it
            if p.fade<0 then
                table.remove(particles,i)
            end
        end
        --rotate the particle
        p.rotation = p.rotation + p.rotspd
        --adjust the position of the particle
        p.x = p.x + p.xspd
        p.y = p.y + p.yspd
        
        --if the particle is smoke then increase the size of it
        if p.type==2 then
            p.w = p.w + 0.1
            p.h = p.h + 0.1
        end
        
        --if the particle is a "solid" object
        if p.type>2 then
            --add a smoke particle at a random time
            if math.random(14)==1 then
                local s=math.random(10)
                table.insert(particles,{img="Cargo Bot:Smoke Particle",x=p.x,y=p.y,w=5+s,h=5+s,angle=a,fade=255,rotation=0, rotspd=-4+math.random(7),xspd=0,yspd=1,type=2})
            end
            --check to see if there is collision with the ground or walls and iff so, bounce it off
            if p.y<ground then
                p.y = p.y -p.yspd
                p.yspd = p.yspd *-0.7
                p.xspd = p.xspd *0.95
            end
            if p.x<0 or p.x>WIDTH then
                p.x = p.x -p.xspd
                p.xspd = p.xspd *-0.9
            end
            --slow the rotation of the particle if it is coming to rest
            if math.abs(p.xspd)+math.abs(p.yspd)<2 then
                if p.rotspd<0 then p.rotspd = p.rotspd + 0.2 else p.rotspd = p.rotspd -0.2 end
                if math.abs(p.rotspd)<2 then p.rotspd=0 end
                if math.abs(p.xspd)+math.abs(p.yspd)<0.2 then
                    p.xspd=0
                    p.yspd=0
                    p.timer = p.timer + 1
                    if p.timer>200 then
                        explodeStar(p.x,p.y,15+math.random(25))
                        table.remove(particles,i)
                    end
                end
            end
            --appy the effect of gravity to the particle
            p.yspd = p.yspd - gravity
        end
    end
end
function explodeStar(xin,yin,sizein)
    --generate an explosion at position xin,yin with sizein indicating the density of the explosion.
    --stars
    for a=1,360,sizein do
        local spd=7+math.random(40)/10
        table.insert(particles,{img="Cargo Bot:Star Filled",x=xin,y=yin,w=25,h=25,angle=a,fade=255,rotation=0, rotspd=-4+math.random(7),xspd=spd*math.sin(math.rad(a)),yspd=spd*math.cos(math.rad(a)),type=1})
    end
end
function explodeShip(xin,yin)
    --solid bits of ship
    for i=1,#shipparts do
        local spd=2+math.random(20)/10
        a=math.random(360)
        table.insert(particles,{img=shipparts[i],x=xin,y=yin,w=25,h=25,angle=a,fade=255,rotation=0, rotspd=-4+math.random(7),xspd=spd*math.sin(math.rad(a)),yspd=spd*math.cos(math.rad(a)),type=3,timer=0})
    end
end

function touched(touch)
    if touch.state==ENDED or touch.state==CANCELLED then
        --draw an explosion when a touch event is finished
        if touch.y>ground then
            explodeStar(touch.x,touch.y,math.random(10))
            explodeShip(touch.x,touch.y)
        end
        touches[touch.id] = nil
    else
        touches[touch.id] = touch
    end
end


Can I share my source for a critique and for advice before submitting it?

@riverforge absolutely. The judging won’t start until after July 7th.

Okay, so it’s definitely sloppy/disorganized. I was really just trying to figure out what approach to take, and I’ve modified a lot of it. That being said, I set it up to make totally custom explosions based on the parameters. So you should be able to paste in just this main and then tweak the parameters while running it.

Here’s how they work:

gN → the number of rectangles drawn in the explosion

gT → This adjusts the speed of the explosion and the time before the
rectangles start to fall (gravity) and the rate at which the alpha level changes. Larger numbers = slow motion

gR_min → the minimum random number for speed/angle

gR_max → the maximum random number for speed/angle

gDone → 0 means the animation is occurring. Do not adjust the parameter gN unless gDone = 1 ----important note: drag gDone back to 0 to start the new explosion----

Some of the explosions look good, some look bad. Play with the parameters and tell me what you think :slight_smile:

It’s very pixel-like. I made it this way because I’m using spritely in my projects so a square based explosion works well with it.

-- Explosion

-- Use this function to perform your initial setup
function setup()
    print("Hello World!")
    parameter.integer("gN", 1, 500, 20)
    parameter.integer("gT", 2, 25, 10)
    parameter.number("gR_min", -1000, 0, -100)
    parameter.number("gR_max", 0, 1000, 100)
    parameter.integer("gDone", 0, 1, 0, InitExplosion)
    InitExplosion()
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color
    background(0, 0, 0, 255)

    -- This sets the line thickness
    strokeWidth(5)
    -- Do your drawing here
    Explosion()
    DrawExplosion()

end

function InitExplosion()
    rectMode(CENTER)
    fill(255, 155, 0, 255)
    stroke(255, 206, 0, 255)
    if x == nil then
        x = WIDTH/2
    end
    if y == nil then
        y = HEIGHT/2
    end
    if w == nil then
        w = WIDTH/75
    end
    if h == nil then
        h = HEIGHT/75
    end
    lR_max = gR_max/gT
    lR_min = gR_min/gT
    Rect = {}
    Rect.stime = ElapsedTime
    Rect.timestep = 0.000000001
    Rect.alpha = 255
    Rect.done = 0
    for eN = 0, gN do
        Rect[eN] = {}
        Rect[eN].x = x
        Rect[eN].y = y
        Rect[eN].w = w
        Rect[eN].h = h
        Rect[eN].stepx = math.random(lR_min, lR_max)/gT
        Rect[eN].stepy = math.random(lR_min, lR_max)/gT
        Rect[eN].decx = Rect[eN].stepx/gT
        Rect[eN].decy = Rect[eN].stepy/gT
    end
end

function Explosion()
    if gDone == 0 then
      --  if ElapsedTime >= Rect.stime + Rect.timestep then
            for eN = 0, gN do
                dec_max = math.ceil(gR_max/(gT*gT))
                Rect[eN].stepy = Rect[eN].stepy - (math.random(1, dec_max)/(gT*gT*2))
                if Rect[eN].stepx >= 0 then
                    stepx = Rect[eN].stepx - (Rect[eN].decx)
                    else
                    stepx = Rect[eN].stepx - (Rect[eN].decx)
                end
                if Rect[eN].stepy >= 0 then
                    stepy = Rect[eN].stepy - (Rect[eN].decy)
                    else
                    stepy = Rect[eN].stepy - (Rect[eN].decy)
                end
                Rect[eN].x = Rect[eN].x + stepx
                Rect[eN].y = Rect[eN].y + stepy
            end
            Rect.stime = ElapsedTime
            Rect.alpha = Rect.alpha - (30/gT)
            if Rect.alpha <= 0 then
                gDone = 1
            end
            fill(255, 155, 0, Rect.alpha)
            stroke(255, 206, 0, Rect.alpha)
        -- end
    end
end

function DrawExplosion()
    if gDone ==  0 then
        for eN = 0, gN do
            rect(Rect[eN].x, Rect[eN].y, Rect[eN].w, Rect[eN].h)
        end
    end
end

Here is my entry so far: aibohphobia

Updated again, and again,and again… And again!

Try playing around with the parameters
Particles now can bounce of the edges


--# Main
-- explosions

function setup()
    img=readImage("Planet Cute:Star")
    --_save("explosions","3.0",true)
    
    --displayMode(FULLSCREEN)
    parameter.watch("FPS")
    parameter.watch("#explosions")
    parameter.integer("numSparks",10,500,50)
    parameter.number("damping",1.01,1.10,1.05)
    parameter.number("velocity",0.1,1,0.5)
    parameter.integer("MODE",1,3,2)--img,sq,circ
    parameter.boolean("constant",false)
    parameter.boolean("atTouch",false)
    parameter.boolean("Rebound",true)
    parameter.number("GRAVITY",0,5)
    parameter.integer("Xwind",-10,10,0)
    parameter.integer("Ywind",-10,10,0)
    
    
    explosions = {}
    print("\
tap for an explosion\
")
    print("scroll down to see all the parameters")
    print("MODES:\
1=image\
2=square\
3=circle")
end
function draw()    
    gravity = vec2(0,-GRAVITY)     
    wind = vec2(Xwind,Ywind)
    FPS=1/DeltaTime
    background(0)
    blendMode(ADDITIVE)
    for i =1, #explosions do
        explosions[i]:draw(gravity,wind)
    end
    cullexplosions()
    if constant then
        explosions[#explosions+1] = Sparks(origin.x,origin.y,numSparks/10,col,nil,30)
    end
    if not atTouch then origin=vec2(WIDTH/2,HEIGHT/2)end
end
--sprite()

function touched(touch)
    if atTouch then origin=vec2(touch.x,touch.y)end
    if touch.state == BEGAN and not constant then
explosions[#explosions+1] = Sparks(origin.x,origin.y,numSparks,col,nil,30)
    end
end



function cullexplosions()
    for i=#explosions,1,-1 do
        if not explosions[i].alive then
            
            table.remove(explosions,i)
            
        end
    end
end





--# Spark
Spark = class()

function Spark:init(x,y,c,t,size)
    self.texture = img
    self.size = math.random(size-20,size+20)
    self.pos = vec2(x,y)
    local vel = vec2(0,self.size*velocity)
    self.velocity = vel:rotate(math.random(360))
    self.col = color(255, 255, 0, 255)
    self.alive = true 
    self.angle = math.random(360)
    self.rotationdirection = math.random(-2,2)
    self.damping=damping
    --[[
    self.IsSquare=Square
    self.IsImg=Image
      ]]
    self.type=MODE
end

function Spark:draw(g,w)
    local _gravity = g or vec2(0,-1)
    local _wind = w or vec2(1,0)
    self:move(_gravity,_wind)
    pushMatrix()
    translate(self.pos.x,self.pos.y)
    rotate(self.angle)
    if self.type==1 then
        tint(self.col)
        sprite(self.texture,0,0, self.size)
    else
        fill(self.col)
        if self.type==2 then
            rectMode(CENTER)
            rect(0,0, self.size,self.size)
        else
            ellipse(0,0, self.size)
        end
    end
    popMatrix()
end

function Spark:move(g,w)
    self.size = self.size / self.damping--math.random(1.1,1.5)
    self.col.a = self.col.a /(self.damping)
    self.col.r = self.col.r / self.damping
    self.col.g = self.col.g / (self.damping^3)
    self.velocity = self.velocity / self.damping
    self.velocity = self.velocity + g
    self.pos = self.pos + self.velocity
    self.pos = self.pos + w 
    self.pos = self.pos + g
    if self.col.a <= 100 then self.alive = false end
    self.angle = self.angle + self.rotationdirection*self.velocity:len()
    if Rebound then
        if self.pos.y<=0 then self.velocity.y=math.max(self.velocity.y,-self.velocity.y)end
        if self.pos.y>=HEIGHT then self.velocity.y=math.min(self.velocity.y,-self.velocity.y)end
        if self.pos.x<=0 then self.velocity.x=math.max(self.velocity.x,-self.velocity.x)end
        if self.pos.x>=WIDTH then self.velocity.x=math.min(self.velocity.x,-self.velocity.x)end
    end
end

Sparks = class()

function Sparks:init(x,y,amount,col,img,size)
    self.alive = true
    self.sparktable={}
    for i = 1, amount do
self.sparktable[i]=Spark(x,y,col or color(math.random(200,255), math.random(0,200), 0,255),img,size)
    end
    
end

function Sparks:draw(grav,wind)
    for i = 1, #self.sparktable do
        self.sparktable[i]:draw(grav,wind)
    end
    local i = 1
    while i <= #self.sparktable do
        if not self.sparktable[i].alive then 
            table.remove(self.sparktable,i)
            i = i - 1
        end
        i = i + 1
    end
    if #self.sparktable == 0 then self.alive = false end
end

aibohphobia

My entry shouldn’t win cos it’s just a tweak of an old particle effect shader I did yonks ago.

Run it and touch the screen. Can stutter with a lot of simultaneous touches, but smooth if touched gently.

--# Main
-- Particle Effect

-- Use this function to perform your initial setup
function setup()
    displayMode(FULLSCREEN)
    --to get ghost trails
    backingMode(RETAINED)
    --b=Backup("Particle Effect Ver 007")
    --sprite("Tyrian Remastered:Explosion Huge")
    particles = Particles(readImage("Tyrian Remastered:Explosion Huge"), 100)
end

function touched(touch)
    if touch.state == BEGAN then
        x,y = touch.x,touch.y
        for i=1,100 do
            particles:addParticle(x, y, math.random(WIDTH)-x, math.random(HEIGHT)-y, math.random(10), 0, math.random(4000)-2000, math.random(50)+70, math.random(200)-100, 1, -1, 2^(math.random(5)-1) + (math.random(2)-1) * 32, 2^(math.random(5)-1), 2^(math.random(5)-1), 2^(math.random(5)-1), color(255,255,255, 255))
        end
    end
end

-- This function gets called once every frame
function draw()
    output.clear()
    --debug info
    print(1/DeltaTime)
    print(particles.numParticles)

    --background(0, 0, 0, 5)
    fill(0, 0, 0, 20)
    --fade to black
    rect(0, 0 , WIDTH, HEIGHT)

    --draw the particles
    particles:draw()
end


--# Particles
Particles = class()

function Particles:init(particleImage, gravity)
    self.particleMesh = mesh()
    self.particleMesh.shader = shader(ParticleShader.vertexShader, ParticleShader.fragmentShader)
    self.particleMesh.texture = particleImage
    self.particleMesh.shader.gravity = gravity
    self.particleBuffers = {}
    --x,y is the start location z,w is the movement to the end location
    self.particleBuffers["startAndEnd"] = self.particleMesh:buffer("startAndEnd")
    --x,y is the start time and time to live z,w is the start rotation and delta rotation
    self.particleBuffers["timeAndRotation"] = self.particleMesh:buffer("timeAndRotation")
    --x,y is the start size and delta size, z,w is the start alpha and alpha delta
    self.particleBuffers["sizeAndAlpha"] = self.particleMesh:buffer("sizeAndAlpha")
    --easing mode x is position, y is rotation, z is size, w is alpha
    --modes
    --type 1 linear, 2 quadin, 4 quadout, 8 expoin, 16 expoout
    --apply gravity (only applies to position) add 32
    self.particleBuffers["easing"] = self.particleMesh:buffer("easing")

    self.particleDeath = {}
    self.numParticles = 0
end

function Particles:draw()
    self.particleMesh.shader.time = ElapsedTime
    self.particleMesh:draw()
end

function Particles:addParticle(startX,startY,endDeltaX, endDeltaY, timeToLive, startAngle, deltaAngle, startSize, deltaSize, startAlpha, deltaAlpha, easePos, easeSize, easeRot, easeAlpha, particleColor)
    etime = ElapsedTime
    firstVertex = 0
    for i=1,self.numParticles do
        if self.particleDeath[i] < etime then
            firstVertex = i*6-5
            break
        end
    end
    if firstVertex == 0 then
        firstVertex = self.particleMesh.size + 1
        self.particleMesh:addRect(0,0,1,1)
        self.particleBuffers["startAndEnd"]:resize(firstVertex + 5)
        self.particleBuffers["timeAndRotation"]:resize(firstVertex + 5)
        self.particleBuffers["sizeAndAlpha"]:resize(firstVertex + 5)
        self.particleBuffers["easing"]:resize(firstVertex + 5)
        self.numParticles = self.numParticles + 1
    end
    self.particleMesh:setRectColor((firstVertex+5)/6, particleColor)
    self.particleDeath[(firstVertex+5)/6] = etime+timeToLive
    for i=firstVertex,firstVertex+5 do
        self.particleBuffers["startAndEnd"][i] = vec4(startX,startY,endDeltaX, endDeltaY)
        self.particleBuffers["timeAndRotation"][i] = vec4(etime, timeToLive, math.rad(startAngle), math.rad(deltaAngle))
        self.particleBuffers["sizeAndAlpha"][i] = vec4(startSize, deltaSize, startAlpha, deltaAlpha)
        self.particleBuffers["easing"][i] = vec4(easePos, easeSize, easeRot, easeAlpha)
    end
end



ParticleShader = {
vertexShader = [[
//
// A basic vertex shader
//

//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;
uniform float time;
uniform float gravity;

//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 color;
attribute vec4 position;
attribute vec2 texCoord;
attribute vec4 startAndEnd;
attribute vec4 timeAndRotation;
attribute vec4 sizeAndAlpha;
attribute vec4 easing;

//This is an output variable that will be passed to the fragment shader
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
    //Pass the mesh color to the fragment shader
    vColor = easing;
    vColor = color;
    vTexCoord = texCoord;
    vec4 lPosition = position;
    if (time < (timeAndRotation.x + timeAndRotation.y)) {
        //type 1 linear, 2 quadin, 4 quadout, 8 expoin, 16 expoout
        float timeDelta = (time - timeAndRotation.x)/timeAndRotation.y;
        float positionDelta = timeDelta;
        float rotationDelta = timeDelta;
        float sizeDelta = timeDelta;
        float alphaDelta = timeDelta;
        if (mod(easing.x, 4.0) > 1.5) {
            positionDelta = pow(timeDelta, 2.0);
        }
        if (mod(easing.x, 8.0) > 3.5) {
            positionDelta = 1.0 - pow((1.0-timeDelta),2.0);
        }
        if (mod(easing.x, 16.0) > 7.5) {
            positionDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
        }
        if (mod(easing.x, 32.0) > 15.5) {
            positionDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
        }

        if (mod(easing.y, 4.0) > 1.5) {
            rotationDelta = pow(timeDelta, 2.0);
        }
        if (mod(easing.y, 8.0) > 3.5) {
            rotationDelta = 1.0 - pow((1.0-timeDelta),2.0);
        }
        if (mod(easing.y, 16.0) > 7.5) {
            rotationDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
        }
        if (mod(easing.y, 32.0) > 15.5) {
            rotationDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
        }

        if (mod(easing.z, 4.0) > 1.5) {
            sizeDelta = pow(timeDelta, 2.0);
        }
        if (mod(easing.z, 8.0) > 3.5) {
            sizeDelta = 1.0 - pow((1.0-timeDelta),2.0);
        }
        if (mod(easing.z, 16.0) > 7.5) {
            sizeDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
        }
        if (mod(easing.z, 32.0) > 15.5) {
            sizeDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
        }

        if (mod(easing.w, 4.0) > 1.5) {
            alphaDelta = pow(timeDelta, 2.0);
        }
        if (mod(easing.w, 8.0) > 3.5) {
            alphaDelta = 1.0 - pow((1.0-timeDelta),2.0);
        }
        if (mod(easing.w, 16.0) > 7.5) {
            alphaDelta = pow(2.0, 10.0 * (timeDelta - 1.0)) - 0.001;
        }
        if (mod(easing.w, 32.0) > 15.5) {
            alphaDelta = 1.001 * (-pow(2.0, -10.0 * timeDelta) + 1.0);
        }

        float sinAngle = sin(timeAndRotation.z + rotationDelta * timeAndRotation.w);
        float cosAngle = cos(timeAndRotation.z + rotationDelta * timeAndRotation.w);
        lPosition.xy = vec2(dot(lPosition.xy, vec2(cosAngle, -sinAngle)),
                dot(lPosition.xy, vec2(sinAngle, cosAngle)));
        lPosition.xy = lPosition.xy * (sizeAndAlpha.x + sizeDelta * sizeAndAlpha.y);
        lPosition.xy = lPosition.xy + startAndEnd.xy + (startAndEnd.zw * positionDelta);

        if (mod(easing.x,64.0) > 31.5) {
            //adjust position by gravity
            lPosition.y = lPosition.y - (0.5 * gravity * pow((time - timeAndRotation.x), 2.0));
        }

        //not sure why just alpha channel isn't working...
        //vColor.a = sizeAndAlpha.z + (alphaDelta * sizeAndAlpha.w);
        vColor = vColor * sizeAndAlpha.z + (alphaDelta * sizeAndAlpha.w);
    }
    else {
        lPosition = vec4(-1000.0,-1000.0, -1000.0, -1000.0);
    }
    //Multiply the vertex position by our combined transform
    gl_Position = modelViewProjection * lPosition;
}
]],
fragmentShader = [[
//
// A basic fragment shader
//

//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform lowp sampler2D texture;

//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

void main()
{
    //Sample the texture at the interpolated coordinate
    lowp vec4 col = texture2D( texture, vTexCoord ) * vColor;

    //Set the output color to the texture color
    gl_FragColor = col;
}
]]
}

@spacemonkey That’s a lot of conditionals! I wonder if it couldn’t be improved with a little mathematics …

@Andrew_Stacey possibly, the conditionals are mostly types of tweening which I took almost verbatim from the tweening lua that I believe Codea uses. And all the If’s are pulling out bit’s from the parameters to see which modes you want the particle to run in. It’s good old fashioned generic, poor performing code :wink:

aibohphobia

Here’s another one. It’s based on http://www.clicktorelease.com/blog/vertex-displacement-noise-3d-webgl-glsl-three-js fixed for Codea. You need http://www.clicktorelease.com/blog/vertex-displacement-webgl-glsl-perlin-noise-three-js/explosion.png in your images for the readImage on line 8.

Grab code here: https://gist.github.com/sp4cemonkey/7caadf6d098be9e0d865

Here’s another version. It puts the explosion into 2d space via a tweaked projection matrix and will go bang on touch points. at 4 explosions simultaneously runs smooth, but degrades from there if you touch too fast.

code: https://gist.github.com/sp4cemonkey/b6cd5c718a7598717700 this also has some comments to explain usage

I got my entry in over here, it’s a 3D explosion with a particle system into a small and easy class.

These entries are amazing. You are all fantastic programmers.

@spacemonkey, I see no reason why your entry cannot be part of the competition. There is no reason you cannot submit code you have worked on previously.

@skythecoder and @riverforge, great work. Keep it up. Take your time, there is plenty of it left :slight_smile:

These are all fantastic entries!

Less than a week left! :slight_smile:

I wasn’t going to enter but I had to make an explosion (2d) for my game. It’s as close as I’ve gotten all day, has a stupid name due to having a class called Explosion already (Updated, set size and draw with smaller explosions, big ones can lag if theres more than 2):


--# Explozun
Explozun = class()

function Explozun:init(pos,...)
    -- you can accept and set parameters here
    self.pos = pos
    self.m = mesh()
    self.m.texture = readImage("Documents:circgrad")
    self.ex = {}
    self.finished = false
    if ... then self.size = ... else self.size = 50 end
    for i=1,self.size do
        self.ex[i] = {}
        local mr = math.random(50,70)
        self.ex[i].r = self.m:addRect(pos.x,pos.y,mr,mr)
        self.ex[i].pos = pos
        local bias = vec2(math.random(-50,50),math.random(-50,50))*0.1
        self.ex[i].vel = (vec2(math.sin(((math.pi*2)/self.size)*i),math.cos(((math.pi*2)/self.size)*i))*math.random(10,100))/250
        self.ex[i].vel = self.ex[i].vel*self.size*0.5
        self.ex[i].size = vec2(mr,mr)*(self.size*0.03+1.5)
        self.ex[i].time = mr*0.05+7
        self.ex[i].sets = mr*0.05+6
    end
    self.time = 7
end

function Explozun:draw()
    local alpha = math.max(self.time*50,50)
    for k,v in pairs(self.ex) do
        self.ex[k].pos = v.pos + v.vel
        self.ex[k].vel = self.ex[k].vel*(0.93-(v.pos:dist(self.pos)/900))
        --[[local size = v.size-(1-self.time)*v.size*2
        size.x = math.max(size.x,0)
        size.y = math.max(size.y,0)]]
        size = v.size*((v.sets)-self.time)/v.sets
        if self.time>1 then
            
        else
            alpha = self.time*50
        end
        self.m:setRect(v.r,v.pos.x,v.pos.y,size.x,size.y)
    end
    self.time = self.time-0.04*(DeltaTime*60)
    if self.time<0 then self.finished = true end
    local time = 7-self.time
    pushStyle()
    self.m:setColors(50,50,50,alpha)
    blendMode(SRC_COLOR,ONE_MINUS_SRC_COLOR,ZERO,ONE)
    self.m:draw()
    if self.time>0.8 then
        self.m:setColors(math.max(255-time*40,40),math.max(150-time*20,30),time*5,math.max(self.time*50+50,0))
        blendMode(SRC_COLOR,ONE,ONE_MINUS_SRC_ALPHA,ONE)
        self.m:draw()
    end
    popStyle()
end

function Explozun:touched(touch)
    -- Codea does not automatically call this method
end

--# Main
-- tapCount

-- Use this function to perform your initial setup
function setup()
    p = {}
    id = 0
    explozuns = {}
    parameter.watch("1/DeltaTime")
    Sizes = 30
    parameter.integer("Sizes",1,100,30)
end

function touched(t)
    if t.state == BEGAN then
        id = id + 1
        table.insert(explozuns,Explozun(vec2(t.x,t.y),Sizes))
    end
    if t.state == MOVING then
        id = id + 1
        if id%2 == 0 then
            table.insert(explozuns,Explozun(vec2(t.x,t.y),Sizes))
        end
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a edark background color 
    background(61, 61, 156, 255)

    -- This sets the line thickness
    strokeWidth(5)

    -- Do your drawing here
    for k,v in pairs(explozuns) do
        v:draw()
        if v.finished then table.remove(explozuns,k) end
    end
end