Coding a bitmap to flash briefly

Hi, so in my game engine, I’m looking to code a basic 8bit/16bit flash of the object when it hits. Is it possible to change the RGB value of the image or to fill it with white and retain transparency (I use PNGs).

Think of the big boss ships getting hit in this clip:
https://vimeo.com/35259959

thanks,
Major

Will tint(255) before the sprite draw and noTint() after the sprite work? :smiley:

@TheSolderKing, tint(255) doesn’t do what it seems it would.

@Majormorgan, try this:

-- Flash

-- Use this function to perform your initial setup
function setup()
    highlightAmount = 0
    parameter.action("Flash", function()
        tween(0.15, _G, { highlightAmount = 1 }, nil, function() 
            tween(0.15, _G, { highlightAmount = 0 })
        end)
    end)
end

function draw()
    background(40, 40, 50)
    
    blendMode(NORMAL)
    sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
    
    local flash = color(255,255,255,255*highlightAmount)
    pushStyle()
    blendMode(ADDITIVE)
    fill(flash) tint(flash)
    sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
    popStyle()
end

@JakAttak Nice demo. It can be condensed to this. Tap screen for flash.


-- Flash

function setup()
    tw={tm=0}
end

function draw()
    background(40, 40, 50)
    blendMode(NORMAL)
    sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
    
    pushStyle()
    blendMode(ADDITIVE)
    tint(color(255,255,255,tw.tm))
    sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
    popStyle()
end

function touched(t)
    if t.state==BEGAN then
        tw={tm=255}
        tween(.25,tw,{tm=0})
    end
end

Or the touched function can be changed to this for an increase in brightness and a decrease back to normal.


function touched(t)
    if t.state==BEGAN then
        t1=tween(.15,tw,{tm=255})
        t2=tween(.15,tw,{tm=0})
        tween.sequence(t1,t2)
        
    end
end

Wow thanks all, I shall try those out!! Thank you

@dave1707, nice, the reason the fill function was there along with tint was for if you need to do the same stuff with text, rects, or ellipses

I’ve spotted where my problem is. I’m not tinting the sprite as I’m using the animation engine @West created and shared - which is super awesome!

I suppose it’s knowing within that code where to place the tint

Here’s the code and you’ll see the if statement showing tint currently

Ship = class()

function Ship:init(x,y,w,h,image)
    -- you can accept and set parameters here
    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.units = {}
    self.frame = 0
    self.F=mesh()
    self.img=readImage(image)
    self.F.texture=self.img
    self.rows=3 --number of rows in the sprite sheet
    self.cols=3 -- number of columns in the sprite sheet
    self.state=self.straight
    --moaction=0
    --constants
    self.straight=0
    self.left=1
    self.right=2
    self.deady=3
    self.hit=false
    --arrays to hold the animation
    self.animdelay=0
    self.amimdelaymax=3
    self.animx={}
    self.animy={}
    --define the frames
    --straight
    self.animx[0]={1,1,1,1}
    self.animy[0]={2,1,2,1}
    --shipleft
    self.animx[1]={0,0,0,0}
    self.animy[1]={2,1,2,1}
    --shipright
    self.animx[2]={2,2,2,2}
    self.animy[2]={2,1,2,1}
    --dead
    self.animx[3]={0,1,2,2}
    self.animy[3]={0,0,0,0}
    self.curFrame=1
end

function Ship:draw()
    self.F:clear() --clear the mesh
    


    --cycle through the animation
    --add a delay to solw the animation down
    self.animdelay = self.animdelay + 1
    if self.animdelay>3 then
        --move to the next frame in the animation
        self.curFrame = self.curFrame + 1

        if self.curFrame>#self.animx[self.state] and self.state==self.right then
        self.state=self.right
        self.curFrame=1
        end
        if self.curFrame>#self.animx[self.state] and self.state==self.left then
        self.state=self.left
        self.curFrame=1
    end
    if self.curFrame>#self.animx[self.state] and self.state==self.straight then
        self.state=self.straight
        self.curFrame=1
        end
    if self.curFrame>#self.animx[self.state] and self.state==self.deady then
        self.state=self.deady
        self.curFrame=4
        end
    --reset the animation delay counter
    self.animdelay=0
    end
    

    
    local idx = self.F:addRect(self.x, self.y, self.w, self.h)
    self.F:setRectTex(idx,(self.animx[self.state][self.curFrame])/self.cols,(self.animy[self.state][self.curFrame])/
self.rows,1/self.cols, 1/self.rows)
    --draw the mesh
        if self.hit==true then
        tint(255,255,255,255)
        self.hit=false
    end
    self.F:draw()
end

Hmmmm still can’t tint the class or the mesh…

Tint only works for sprites, not meshes. Setting tint to white by itself does nothing, because white is effectively no colour (if you think of 0-255 actually being 0-1, and tint is a multiply operation, so tinting white is just multiplying by one). That’s why, in the solutions above, drawing over the sprite a number of times with blend mode set to additive is key. The tint just uses the alpha channel to make these extra layers appear and disappear. Here is a version of @dave1707 's code that works with meshes. I draw the ship 3 extra times to get it close to white. A more efficient way would be to use ? shader. Give me a few minutes and I’ll cook one up for you.

-- Bitmap Flash


-- Flash

function setup()
    tw={tm=0}
    ship=mesh()
    ship.texture=readImage("Space Art:Red Ship")
    ship:addRect(0,0,ship.texture.width,ship.texture.height)
    ship:setRectTex(1,0,0,1,1)
end

function draw()
    background(40, 40, 50)
    blendMode(NORMAL)
   -- pushMatrix()
    translate(WIDTH*0.5, HEIGHT*0.5)
  --  sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
    
    ship:draw()
    pushStyle()
    blendMode(ADDITIVE)
    --  tint(color(255,tw.tm))
    if flash then
        for i=1,3 do
            --  sprite("Space Art:Red Ship", WIDTH / 2, HEIGHT / 2)
            ship:draw()
        end
        flash = flash - 1
        if flash==0 then flash=nil end
    end
    popStyle()
end

function touched(t)
    if t.state==ENDED then
        flash=5
    end
end


Hey @yojimbo2000 thanks for this. So I’m clear is this a shader? Ive heard of them but what it looks like I’m new to. Thanks!

No, this is the shader version. I think this is the most efficient way of getting the effect you want: EDIT: removed the colour calculations for greater speed. EDIT 2: removed some unnecessary lines.


--# Main
-- Bitmap Flash

function setup()
    ship=mesh()
    ship.texture=readImage("Space Art:Red Ship")
    ship:addRect(0,0,ship.texture.width,ship.texture.height)
end

function draw()
    background(40, 40, 50)
    translate(WIDTH*0.5, HEIGHT*0.5)    
    ship:draw()
end

function touched(t)
    if t.state==ENDED then
        ship.shader=shader(BitmapFlash.vs,BitmapFlash.fs)
        tween.delay(0.05, function() ship.shader=nil end)
    end
end

--# BitmapFlash
BitmapFlash={
vs=[[//
// A basic vertex shader
//

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

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

//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 = color; //color info not needed
    vTexCoord = texCoord;
    
    //Multiply the vertex position by our combined transform
    gl_Position = modelViewProjection * position;
}
]],
fs=[[//
// 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 );
    col.rgb = col.aaa;  //this is the key change I made. sets the rgb values to the same as alpha
    //Set the output color to the texture color
    gl_FragColor = col;
}
]]
}

The shader just changes one line compared to the standard one. It’s the most efficient way to do this because you only have to draw the mesh once (with the blendMode(additive) version you have to draw it at least twice, and a few more times if you want to get it to white).

One drawback is that it operates on the entire mesh (as does the blendMode solution). So if you have a number of enemies all on one mesh, moving them around with setRect (by far the fastest method if you need to have thousands of moving objects), then all of them will flash white at once, which probably isn’t what you want. In those cases you’d need to create a white image outline (similar to the solution @SolderKing posted in the other thread) in a different area of the texture image (like creating a sprite sheet) and then adjust the texture coordinates for the rectangle that had been hit.

But for meshes that just contain a single object, such as a hero class, I think the shader is the way to go.

There might actually be a way to do this though without the shader, using the 4-variable version of blendMode. I don’t really understand how the full version of blendMode works though.

It’s interesting, as someone who used to programme in the 16-bit days (Blitz Basic, which is still being actively developed, 20+ years later), the effect you’re after was easy in the pre OpenGL system. Back then, a sprite needed to have a mask to cut out an area from the backdrop. So achieving the White flash effect was very easy, you just drew the mask without drawing the image (if I remember correctly… Was a while ago now!)

I played around with the four variable version of blendMode, but couldn’t get it to go completely white, rgb=a, like in my shader.

It’s interesting the approach you talk about with 16bit imagery and having to create a mask for the original object and using that to make a white box.

Sounds like this is what I’ll use for my hero ship to flash as it gets hit but not other aliens. That’s fine as the aliens I’ll bake into the bitmaps the first frame of the explosion to be white as hit.

Interesting how using tint on a sprite colours all sprites below what level too as they are drawn onto the stage.

For the shader I’m going to take the relevant code and place it in the set up and draw parts of the ship object. As the object collides not through touching the screen but hitting another object I’ll have to move them.

I see there are two void main() functions triggered with different code within them is that becaus void main us c++?

The shader uses a language called Open GL ES, which does look a bit like C. @Ignatz 's eBook on shaders is the best place to start with shaders.

The first string “vs” is the vertex shader, so that is executed once for each vertex in the mesh (of which there are 6, one rectangle consisting of two triangles). The above vertex shader is almost identical to the default one used for all drawing operations. It just passes the texCoord and vertex position to the fragment shader.

The second string, “fs”, is the fragment shader, this is executed for every pixel. Again this is almost the same as the default one. It takes the texCoords and looks up the corresponding pixel in the texture. But then, it sets the red, green, and blue values of that pixel to be the same value as the alpha channel with the line col.rgb = col.aaa (pretty much the only change I made). So the alpha channel of the image is converted into a greyscale mask.

Yes, tint affects all subsequent drawing operations until you use noTint (same as tint (255, 255)). It also is part of the style stack, so if you use pushStyle and popStyle either side of the tint call, other drawing operations own’t be affected.

You could use the shader to create the white bitmap for the enemy class. Use setContext and then draw into the image using the shader. It would save you having two lots of code for creating the white flashes.

I think the simplest approach, if you’re just using one image for all the ships, is to create a white version of it and sprite that…

I agree @Ignatz, I mentioned that in another thread(although I was doing it for a sprite, not a mesh). That would be simplest, unless of course you were to create images in whatever image editing program you use that were just white.

@majormorgan you could always do something like this:

-- Use this function to perform your initial setup
function setup()
    -- create a white image the same size of your sprite
    a = image(95, 52)
    setContext(a)
    fill(255, 255, 255, 255)
    rect(0,0,95,52)
    setContext()
    -- your sprite
    b = readImage("Tyrian Remastered:Chest Mecha")

    -- new empty image, same size as your sprite
    c = image(95, 52)

    -- set drawing to c
    setContext(c)
    -- draw white image
    sprite(a, 47.5, 26)
    blendMode(ZERO, SRC_ALPHA)
    -- draw sprite
    sprite(b, 47.5, 26)
    blendMode(NORMAL)
    setContext()
    popStyle()
end

-- This function gets called once every frame
function draw()
    sprite(c, WIDTH/2, HEIGHT/2)
    sprite(b, WIDTH/2 + 200, HEIGHT/2)
end

this blendMode will take image A (just a fully white image) and draw every pixel of image b that isn’t transparent, it acts funky with shadows etc tho

Also: I didn’t write this code completely by myself, I got this code from somewhere on the forum, tho idk who posted it exactly, I’ll edit this post if I find out who I saw this from