Page 1 of 1

Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 6:20 am
by shatterblast
I do not consider myself a professional coder, but I pass it by as a hobby. To my understanding, it is standard etiquette to avoid global declarations. To my limited knowledge, I have made some globals into locals.

I am also aware that my software needs work, but I'm not yet ready to focus on animation speeds and sound clips. I prefer to work on game states next.

Anyhow, I could use some input into how to convert some of the globals in love.load() into local declarations instead. Any suggestions will be highly appreciated. Thanks!

Code: Select all

local back_ground = love.graphics.newImage("background01.png")

--______________________________________________________________________

local spritesheet1 = love.graphics.newImage("sparkle_set01.png")
local spritesheet2 = love.graphics.newImage("sparkle_set02.png")
local spritesheet3 = love.graphics.newImage("sparkle_set03.png")

--______________________________________________________________________

local spritesheet4 = love.graphics.newImage("safir_test01.png")
local spritesheet5 = love.graphics.newImage("shell_burst_03.png")

--______________________________________________________________________
  
local animationState_Effects = "Effect_0"
local animationState_Saphir = "Effect_Saphir_1"
local soundState = "Sound_1"

local sound03 = love.audio.newSource("explosion03.ogg", "static")
local sound04 = love.audio.newSource("explosion04.ogg", "static")


function love.load()
  require("anal")
  
  spritesheet1:setFilter("nearest", "nearest")
  animation1 = newAnimation(spritesheet1, 192, 192, 0.1, 110)
  --______________________________________________________________________
  
  spritesheet2:setFilter("nearest", "nearest")
  animation2 = newAnimation(spritesheet2, 200, 200, 0.1, 80)
  --______________________________________________________________________
    
  spritesheet3:setFilter("nearest", "nearest")
  animation3 = newAnimation(spritesheet3, 200, 200, 0.1, 80)
  --______________________________________________________________________  
    
  spritesheet4:setFilter("nearest", "nearest")
  --MAKE SURE THE LAST NUMBER IS THE NUMBER OF FRAMES!!!
  animation4 = newAnimation(spritesheet4, 120, 203, 0.1, 60)
  
  --______________________________________________________________________
  
    spritesheet5:setFilter("nearest", "nearest")
  --MAKE SURE THE LAST NUMBER IS THE NUMBER OF FRAMES!!!
  animation5 = newAnimation(spritesheet5, 159, 203, 0.1, 31)
  
  --______________________________________________________________________
  
  --TO DO: Add more sound here.
  --______________________________________________________________________
  
  --Background music for battles.  
  local battle_music_shuffle = table.shuffle{'battle_music_Derek_Oldman_-_Notch.ogg', 'battle_music_Melodie_en_sous-sol_-_azul_ul_inu.ogg', 'battle_music_mindthings_-_Eve_.ogg', 'battle_music_Outsider_-_Dangerous_Groups.ogg', 'battle_music_Piter_-_Reveranger__amp__Piter_-_HYMNOMYNA__chillout_.ogg', 'battle_music_Tripple_T_-_The_Chill_Area.ogg', 'battle_music_Wade_-_Pozitivli_Tuned_Up.ogg', 'battle_music_WoNd3RBoY_-_WoNd3RBoY_-_MilkyWay__original_mix_.ogg'}
  
  
  background_Music01 = love.audio.newSource(battle_music_shuffle[1])
  
  background_Music01:setVolume(0.25)  
  
  --______________________________________________________________________
end

function table.shuffle(array)
  local arrayCount = #array
    for i = arrayCount, 2, -1 do
      local j = love.math.random(1, i)
        array[i], array[j] = array[j], array[i]
    end
    return array
end

function love.update(dt)
  animation1:update(dt)         
  animation2:update(dt)
  animation3:update(dt)
  animation4:update(dt)
  animation5:update(dt)

end

function love.draw()
  love.graphics.draw(back_ground, 0, 0)
  
  love.graphics.print("Press 1, 2, 3, or 4 to alternate between animations.", 50, 50)
  love.graphics.print(animationState_Effects, 50, 110)
  
  if animationState_Saphir == "Effect_Saphir_1" then
    animation4:setMode("loop")
    animation4:draw(600, 150)    
    
  elseif animationState_Saphir == "Effect_Saphir_2" then
    animation5:setMode("once")
    animation5:draw(580, 150)
    
    if animation5:getCurrentFrame() == 31 then
      animationState_Saphir = "Effect_Saphir_1"
      animation4:reset()
      animation4:play()
      
    end
  end

  --______________________________________________________________________

  if animationState_Effects == "Effect_1" then
    animation1:setMode("once")
    animation1:draw(250, 250)
    
    if animation1:getCurrentFrame() == 110 then
      animationState_Effects = "Effect_0"
      
    end
       
  elseif animationState_Effects == "Effect_2" then
    animation2:setMode("once")
    animation2:draw(250, 250)
    
    if animation2:getCurrentFrame() == 80 then
      animationState_Effects = "Effect_0"
      
    end
    
  elseif animationState_Effects == "Effect_3" then
    animation3:setMode("once")
    animation3:draw(250, 250)
    
    if animation3:getCurrentFrame() == 80 then
      animationState_Effects = "Effect_0"
      
    end
          
  elseif animationState_Effects == "Effect_4" then
    animationState_Saphir = "Effect_Saphir_2"         
          
  end
  
  if not background_Music01:isPlaying() then
    stop_songs()
    
    --The reason to call this stuff a second time is to re-shuffle the music.
    battle_music_shuffle = table.shuffle{'battle_music_Derek_Oldman_-_Notch.ogg', 'battle_music_Melodie_en_sous-sol_-_azul_ul_inu.ogg', 'battle_music_mindthings_-_Eve_.ogg', 'battle_music_Outsider_-_Dangerous_Groups.ogg', 'battle_music_Piter_-_Reveranger__amp__Piter_-_HYMNOMYNA__chillout_.ogg', 'battle_music_Tripple_T_-_The_Chill_Area.ogg', 'battle_music_Wade_-_Pozitivli_Tuned_Up.ogg', 'battle_music_WoNd3RBoY_-_WoNd3RBoY_-_MilkyWay__original_mix_.ogg'}
    background_Music01 = love.audio.newSource(battle_music_shuffle[1])
    
    background_Music01:setVolume(0.25)    
    background_Music01:play()
  end
  
end

function love.keypressed(key)
  if key == "1" then
    animationState_Effects = "Effect_1"
    animation1:reset()
    animation1:play()
    
  elseif key == "2" then
    animationState_Effects = "Effect_2"
    animation2:reset()
    animation2:play()
    love.audio.play(sound03)
    
  elseif key == "3" then
    animationState_Effects = "Effect_3"
    animation3:reset()
    animation3:play()
    love.audio.play(sound04)
  
  elseif key == "4" then
    animationState_Saphir = "Effect_Saphir_2"
    animation5:reset()
    animation5:play()
       
  end
end

--Place more songs here as the list grows so that all background music ceases before starting a new one.
function stop_songs()
  love.audio.stop(background_Music01)
end
Love file:
https://www.dropbox.com/s/cy5s7erwij76k ... de_21.love

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 6:30 am
by micha
shatterblast wrote:To my understanding, it is standard etiquette to avoid global declarations.
While it is a good idea to reduce the number of global variable, don't forget that some data is used globally and then making it local will introduce a lot of extra work to access it from everywhere. So personally I try to follow this rule: Make those variables local, that act locally.

In your case you have the assets stored in global variables. In my opinion that makes sense, because you load them in love.load and access them in love.draw. How would you transfer this data, if not via globals?

Besides that, you can still tidy up the variable space a bit by putting all assets into a table. Then you have only one global variable for all the assets. That minimizes the chance of accidentally overwriting an existing variable.

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 6:37 am
by T-Bone
Just want to add that you can totally have local variables that are accessed from both love.load and love.draw, like this:

Code: Select all

local mpgisquote

function love.load()
    mpgisquote= "Oui, my parents say I'm related to a horse"
end

function love.draw()
    love.graphics.print(mpgisquote, 20, 20)
end

In that case the variable is local to the main.lua file and can't be accessed from other .lua files.

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 6:39 am
by shatterblast
micha wrote: How would you transfer this data, if not via globals?
Well, my approach so far has been that if it doesn't make the software crash, make it "local".
T-Bone wrote:Just want to add that you can totally have local variables that are accessed from both love.load and love.draw, like this:

Code: Select all

local mpgisquote

function love.load()
    mpgisquote= "Oui, my parents say I'm related to a horse"
end

function love.draw()
    love.graphics.print(mpgisquote, 20, 20)
end

In that case the variable is local to the main.lua file and can't be accessed from other .lua files.
I gave thought to this before, and does not putting the locals into love.draw() add to the game loop instead of outside it? From my understanding, that would reduce performance overall.

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 6:59 am
by miko
shatterblast wrote:I do not consider myself a professional coder, but I pass it by as a hobby. To my understanding, it is standard etiquette to avoid global declarations. To my limited knowledge, I have made some globals into locals.
You need in terms of "scope" - usually, globals mean accessible in every place in the program, while locals mean accessible within current scope (usually function body). The rule of thumb is to make variables occupy as small scope as possible, but not smaller.
So, if you place

Code: Select all

local back_ground = love.graphics.newImage("background01.png")
as your first line of main.lua file, that makes it accessible only within this file. But as this is your only file (no require() used), in practice this variable is "global" (accessible from every place in your program).

The other rule of thumb is to create as small number of globals as possible. So I would do:

Code: Select all

local Assets={}
local Sounds={}
local States={}
local Animations={}

function love.load()
 -- Images
  Assets.back_ground = love.graphics.newImage("background01.png")
  Assets.spritesheet1 = love.graphics.newImage("sparkle_set01.png")
...
  -- States
  States.animationState_Effects = "Effect_0"
  States.animationState_Saphir = "Effect_Saphir_1"
  States.soundState = "Sound_1"

  -- Sounds
  Sounds.sound03 = love.audio.newSource("explosion03.ogg", "static")
  Sounds.sound04 = love.audio.newSource("explosion04.ogg", "static")

  Assets.spritesheet1:setFilter("nearest", "nearest")
  Animations.animation1 = newAnimation(Assets.spritesheet1, 192, 192, 0.1, 110)
end

Or better yet, assuming you need only references to animations, not images, do like this:

Code: Select all

local Assets={}
local Sounds={}
local States={}
local Animations={}

function love.load()
  local spritesheet1 = love.graphics.newImage("sparkle_set01.png")
  spritesheet1:setFilter("nearest", "nearest")
  Animations.animation1 = newAnimation(spritesheet1, 192, 192, 0.1, 110)
end

function love.update(dt)
  for k, animation in pairs(Animations) do
    animation:update(dt)
  end 
end

function love.draw()
  Animations.animation1:draw(100, 100)
end
That makes variable spritesheet1 truly local to the love.load() function.

Also, see http://lua-users.org/wiki/ScopeTutorial

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 7:06 am
by shatterblast
miko wrote:The rule of thumb is to make variables occupy as small scope as possible, but not smaller.
So, if you place

Code: Select all

local back_ground = love.graphics.newImage("background01.png")
as your first line of main.lua file, that makes it accessible only within this file. But as this is your only file (no require() used), in practice this variable is "global" (accessible from every place in your program).

The other rule of thumb is to create as small number of globals as possible.

...

That makes variable spritesheet1 truly local to the love.load() function.
Thanks for the information! I will check to see how I can integrate this into my project.

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 9:15 am
by kikito
I am on my mobile and can't type much. Give a look at the bump 2.0 demo source code.

There's no global variables, just things required with `require` or passed by parameter.

For resource loading, there's a module called media.lua. It uses no global vars (only requires).

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 10:06 am
by davisdude
I'm not sure if I'm the greatest role model here, but I guess you could take a look at how I do it.
I keep all variables local, except in the file called 'Globals.lua'. In this file I have all my globals, as well as any variables needed from other files that I might need. Then, I simply put 'require( Globals )()' and it calls the function that registers all of the global utilities (like entities and such).

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 12:33 pm
by undef
Yep, like miko said.
Another performance boost can be gained by cashing lua functions to the scope of the file...

for example:

Code: Select all

local sin, pi, rand = math.sin, math.pi, math.random

for i=1, 1e100 do
    local x = rand( i )
    sin( pi*x )
end
This only really makes a difference if you use those functions very often, but I always do it, because:

1. it's shorter code
2. if I'm used to doing this I don't have to think about it when I really need it

Re: Trying to Get Rid of Globals

Posted: Thu Jun 05, 2014 1:11 pm
by Xgoff
kikito wrote:There's no global variables, just things required with `require` or passed by parameter.
this

i avoid globals completely (unless i need to override a built-in for some reason). putting all of your "global" state into modules and requiring them on-demand (into locals!) makes it much easier to find that information in the future. not to mention you'll avoid accidentally creating two or more globals with the same name but different contents, which is easy to do when you don't have a declaration in your files anywhere!

about the only places where using "globals" are all that useful are with your own function environments, mainly for dsl-y things, since *usually* their existence will be limited to certain code only