Animation problem

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
Nemo72
Prole
Posts: 5
Joined: Mon Jan 17, 2022 10:42 am

Animation problem

Post by Nemo72 »

Hello i’m a brand new beginner with lua and Love2D stuff. Sorry for my bad english cause i’m french ;) .
My goal is to code a little shoot them up. I need help because one of my fonction doesn’t work as I expect.

When a shoot hit an alien ship, for each ship hit i want to play an explosion animation. The problem is, when i shoot others alien ships, the frames of all explosions animations reset and doesn't play independently.

Video of the problem : https://vimeo.com/manage/videos/666469977

Here is below a short version of my code with the problem. For testing i just generates explosion while pressing the « space key » randomly placed. The explosion animations doesn’t continu independently. As soon as a new explosion is generated all the animation frames played reseting !

Thanks by advance for your helps !

Code: Select all

width = love.graphics.getWidth()
height = love.graphics.getHeight()


cooldown = 0

----------- explosion
explosion = {}
explosion.tx = 64
explosion.ty = 64
explosion.sprite_sheet = love.graphics.newImage("assets/exp2_0.png")
explosion.xline =0
explosion.yline = 0
explosion.sprite = love.graphics.newQuad(explosion.xline, explosion.yline, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.anim_timer = 0.09
explosion.frame = 0      
explosion.max_frame = 3 

------------------

tableFrameXexplosion = {0,64,128,192,0,64,128,192,0,64,128,192,0,64,128,192}
tableFrameYexplosion = {0,0,0,0,64,64,64,64,128,128,128,128,192,192,192,192}

ListOfExplosion = {}  

function love.draw()

  ------- explosions
  for i,v in ipairs(ListOfExplosion) do

    love.graphics.draw(explosion.sprite_sheet, v.spriteExplo, v.x, v.y)

  end
end

function love.update(dt)
  
  Shoot(dt)
  Explose(dt)
  
end

function Explose(dt)

  for i,v in pairs(ListOfExplosion)do
    if v.Explo_frame > 15 then
      table.remove(ListOfExplosion, i)
    end

  local b = v.Explo_frame
  b = math.floor(b)  

    v.Explo_xline = tableFrameXexplosion[b]
    v.Explo_yline = tableFrameYexplosion[b]

    v.Explo_frame = v.Explo_frame + explosion.anim_timer

    
    if v.Explo_xline or v.Explo_yline ~= nil then
      v.spriteExplo : setViewport(v.Explo_xline, v.Explo_yline, 64, 64)
    end
  end
end

function Shoot(dt)

  if love.keyboard.isDown ("space") and cooldown == 0 then
  
  local ex = love.math.random (5, 1020)
  local ey = love.math.random (5, 760)
    
  table.insert(ListOfExplosion, createExplosion(explosion.sprite,ex, ey,0,0,0))
    
    cooldown = 25
  end

  if cooldown > 0 then
    cooldown =  math.floor (cooldown  - 1 * dt)
  end
end
  function createExplosion(spriteExplo, a, b, Exploxline,Exployline,Exploframe)
  explod = {}
  explod.spriteExplo = spriteExplo
  explod.x = a
  explod.y = b
  explod.Explo_xline = Exploxline
  explod.Explo_yline = Exployline
  explod.Explo_frame = Exploframe

  return explod
end
PS : here is the link for the explosion spritesheet :
https://opengameart.org/sites/default/files/exp2_0.png
User avatar
pgimeno
Party member
Posts: 3657
Joined: Sun Oct 18, 2015 2:58 pm

Re: Animation problem

Post by pgimeno »

You're overwriting the viewport of the same quad over and over. You need to create one quad per sprite in the spritesheet.

You also have a problem with deleting entries. Delete them in advance and iterating in reverse order with a numeric loop, like this:

Code: Select all

function Explose(dt)
  for i = #ListOfExplosion, 1, -1 do
    if math.floor(ListOfExplosion[i].Explo_frame) > 15 then
      table.remove(ListOfExplosion, i)
    end
  end
  for i,v in ipairs(ListOfExplosion)do
    -- rest of the code
Nemo72
Prole
Posts: 5
Joined: Mon Jan 17, 2022 10:42 am

Re: Animation problem

Post by Nemo72 »

@pgimeno : thanks for answering and your advices !

I don't understand what do you mean about the viewport ? I stock the data of each exlosion in the variable "spritExplo" in a table relatively to each generated explosion. Can you show me what's wrong in my code ? Something to change in the draw callback perhaps ?
User avatar
pgimeno
Party member
Posts: 3657
Joined: Sun Oct 18, 2015 2:58 pm

Re: Animation problem

Post by pgimeno »

When you do this:

Code: Select all

explosion.sprite = love.graphics.newQuad(explosion.xline, explosion.yline, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
you are creating a single quad object and storing a reference to it in explosion.sprite.

Now when you do this:

Code: Select all

 table.insert(ListOfExplosion, createExplosion(explosion.sprite,ex, ey,0,0,0))
you are passing that single object to createExplosion, and then inside createExplosion, you are doing this:

Code: Select all

  explod.spriteExplo = spriteExplo
which sets explod.spriteExplo to a reference to that same object. It does NOT create a copy of the object. Therefore, all explosions share the same quad.

This means that when you do this:

Code: Select all

      v.spriteExplo : setViewport(v.Explo_xline, v.Explo_yline, 64, 64)
you're altering all explosions at the same time, because all of them share the same copy of the quad.

I can think of three solutions. One is to create the quad in createExplosion, which will probably be the simplest with your current code layout.

The second one is to create the 16 quads in advance, one per sprite in the spritesheet, and then set spriteExplo to the quad corresponding to the current frame number, updating it as the frame changes.

And the last option is to keep just one quad like now, but change the viewport before drawing each explosion in order to show the correct frame.

I don't recommend the last one, but it's an option. The first one probably only involves moving the quad creation line to inside createExplosion, so it's simple; however my recommendation is the second option, because it doesn't need creation and destruction of objects during the game. Neither does the last one, but it's a bit more costly than the second and harder to understand.

Note that all three options involve removing the quad as an argument to createExplosion.
Nemo72
Prole
Posts: 5
Joined: Mon Jan 17, 2022 10:42 am

Re: Animation problem

Post by Nemo72 »

Tank you very much pgimeno !
With your advices i nearly manage to correct my animation code.
I choose the second methode.

Just one probleme remain : the explosion table doesn't remove herself when the animation is done. The explosions during forever.
Perhaps a problem with the remove.table instruction...but i don't see what's wrong :

Code: Select all

width = love.graphics.getWidth()
height = love.graphics.getHeight()


cooldown = 0

----------- explosion
explosion = {}
explosion.tx = 64
explosion.ty = 64
explosion.sprite_sheet = love.graphics.newImage("assets/exp2_0.png")
explosion.xline =0
explosion.yline = 0

explosion.sprite = love.graphics.newQuad(0, 0, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.sprite01 = love.graphics.newQuad(0, 0, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite02 = love.graphics.newQuad(64, 0, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite03 = love.graphics.newQuad(128, 0, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite04 = love.graphics.newQuad(192, 0, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.sprite05 = love.graphics.newQuad(0, 64, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite06 = love.graphics.newQuad(64, 64, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite07 = love.graphics.newQuad(128, 64, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite08 = love.graphics.newQuad(192, 64, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.sprite09 = love.graphics.newQuad(0, 128, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite10 = love.graphics.newQuad(64, 128, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite11 = love.graphics.newQuad(128, 128, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite12 = love.graphics.newQuad(192, 128, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.sprite13 = love.graphics.newQuad(0, 192, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite14 = love.graphics.newQuad(64, 192, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite15 = love.graphics.newQuad(128, 192, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())
explosion.sprite16 = love.graphics.newQuad(192, 192, explosion.tx, explosion.ty, explosion.sprite_sheet: getDimensions())

explosion.anim_timer = 0.09
explosion.frame = 0      
explosion.max_frame = 3 

explo_quad_table = {explosion.sprite01, explosion.sprite02, explosion.sprite03, explosion.sprite04, explosion.sprite05, explosion.sprite06, explosion.sprite07,explosion.sprite08,explosion.sprite09,explosion.sprite10,explosion.sprite12,explosion.sprite13, explosion.sprite14, 
  explosion.sprite15, explosion.sprite16}
------------------

ListOfExplosion = {}  

function love.draw()

  ------- explosions
  for i,v in ipairs(ListOfExplosion) do
  
  love.graphics.draw(explosion.sprite_sheet, explo_quad_table [math.floor(v.Explo_frame)], v.x, v.y)
  love.graphics.print (math.floor(v.Explo_frame))
end

 love.graphics.print("Nr of explosions "..#ListOfExplosion,0,60)
end

function love.update(dt)
  
  Shoot(dt)
  Explose(dt)
 end

function Explose(dt)

   for i = #ListOfExplosion, 1, -1 do
    if math.floor(ListOfExplosion[i].Explo_frame) > 15 then
      table.remove(ListOfExplosion, i)
        
    end
  end
  for i,v in ipairs(ListOfExplosion)do
   
  
  v.Explo_frame = v.Explo_frame + explosion.anim_timer
    if v.Explo_frame > 16 then v.Explo_frame = 1
    end
      end
end

function Shoot(dt)

  if love.keyboard.isDown ("space") and cooldown == 0 then
  
  local ex = love.math.random (5, 1020)
  local ey = love.math.random (5, 760)
    
  table.insert(ListOfExplosion, createExplosion(ex,ey,1))
    
    cooldown = 25
  end

  if cooldown > 0 then
    cooldown =  math.floor (cooldown  - 1 * dt)
  end
end
  function createExplosion(a, b, Exploframe)
  explod = {}
  explod.x = a
  explod.y = b
  explod.Explo_frame = Exploframe

  return explod
end
User avatar
pgimeno
Party member
Posts: 3657
Joined: Sun Oct 18, 2015 2:58 pm

Re: Animation problem

Post by pgimeno »

Good job fixing the previous issue!

Now with the changes, the removal loop does not work as expected, because this code from the Explose function:

Code: Select all

    if v.Explo_frame > 16 then v.Explo_frame = 1
    end
is causing it to never be > 15 as required in order to clean it up. You can fix this by changing the Explose function as follows:

Code: Select all

function Explose(dt)
  for i = #ListOfExplosion, 1, -1 do
    local v = ListOfExplosion[i]
    v.Explo_frame = v.Explo_frame + explosion.anim_timer
    if math.floor(v.Explo_frame) > 15 then
      table.remove(ListOfExplosion, i)
    end
  end
end
Now there's another problem. anim_timer is constant (0.09) instead of depending on the frame rate. You can solve that by making it bigger (like 5.4) and multiplying it by dt before adding it to v.Explo_frame. So replace this line:

Code: Select all

    v.Explo_frame = v.Explo_frame + explosion.anim_timer
with this line:

Code: Select all

    v.Explo_frame = v.Explo_frame + explosion.anim_timer * dt
and this line:

Code: Select all

explosion.anim_timer = 0.09
with this line:

Code: Select all

explosion.anim_timer = 5.4
Nemo72
Prole
Posts: 5
Joined: Mon Jan 17, 2022 10:42 am

Re: Animation problem

Post by Nemo72 »

Thanks again for your help pgimeno ! It's now exactly what i want.

In conclusion, the main problem was a lack of understanding with the setViewport and newQuad instructions.

Setviewport can only be used for the animations of the same sprite in loops.

Have a nice day !
User avatar
darkfrei
Party member
Posts: 1197
Joined: Sat Feb 08, 2020 11:09 pm

Re: Animation problem

Post by darkfrei »

Code: Select all

function love.load()
	explosions = {}
	nFrames = 17
	image = love.graphics.newImage ('explosion.png') -- image 1088x64, 17 frames 64x64 in one line
	frames = {}
	for i = 1, nFrames do -- https://sheepolution.com/learn/book/17
		local x = 64*(i-1)
		local quad = love.graphics.newQuad(x, 0, 64, 64, image:getDimensions())		
		table.insert (frames, quad)
	end
end

Code: Select all

local function newExplosion (x, y)
	local explosion = {}
	explosion.valid = true
	explosion.x = x
	explosion.y = y
	explosion.r = math.pi/2 * math.random (0, 3)
	explosion.image = image
	explosion.time = love.timer.getTime( )
	explosion.tFrame = 1/30
	return explosion
end

Code: Select all

local function fastRemove (explosions, i)
	explosions[i] = explosions[#explosions]
	explosions[#explosions] = nil
end

Code: Select all

function love.draw()
	for i, explosion in ipairs (explosions) do
		local image = explosion.image
		local x, y, r = explosion.x, explosion.y, explosion.r
		-- no dt is needed:
		local currentFrame = math.floor((love.timer.getTime( ) - explosion.time)/explosion.tFrame)+1 
		if frames[currentFrame] then
			love.graphics.draw(explosion.image, frames[currentFrame], x, y, r, 1, 1, 32, 32)
		else
			explosion.valid = false
		end
	end
	for i, explosion in ipairs (explosions) do
		if not explosion.valid then
			fastRemove (explosions, i)
		end
	end
end

Code: Select all

function love.mousereleased( x, y, button, istouch, presses )
	if button == 1 then -- left mouse button
		table.insert (explosions, newExplosion (x, y))
	elseif button == 2 then -- right mouse button
	end
end
explosion CC0 from https://opengameart.org/content/explosion
Attachments
explosion-animation-01.love
(18.62 KiB) Downloaded 103 times
:awesome: in Lua we Löve
:awesome: Platformer Guide
:awesome: freebies
Nemo72
Prole
Posts: 5
Joined: Mon Jan 17, 2022 10:42 am

Re: Animation problem

Post by Nemo72 »

Thanks darkfrei for this nice alternative !
Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests