How to use local variables in games properly?

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
ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

How to use local variables in games properly?

Post by ilovelove2019 » Sun Dec 08, 2019 10:34 pm

Hello everyone, I just learned how to program games, when I encounter bugs, I often ask the community for help. Some people have read my code and they say there's a lot of instability, it's about me abusing the global variable. They advised me to use as many local variables as possible because it helps me limit the risk. But now I still can't fully understand it.
They said I should apply the local variable to my code. And I see, when using local variables, accessing it elsewhere becomes extremely difficult. So today, I want to ask you some questions:
  • * Firstly, in my particular case. They say listOfFruits and listOfBlock must be local. When I fixed it to local, I got an error with the Apple.lua file, saying that I cannot access listOfFruits. Take a look at my specific code, you will understand my case. I want to program with local variables the right way, using it as much as possible.
    Please take a look and adjust my code so listOfFruits and Apple.lua still work well, help me.

    * Secondly, I need a correct game structure. Some helpers, they also told me I had to restructure my game to make it easier to use local variables and manage objects. But I don't know what I should do. Please help me, read through my code and refactor so I can use local properly,
    But don't get too far from the current structure. If you give me specific examples, I would be very happy. For example, now I need a way to manage the bottles in the game, add it in main.lua, manage itself in bottle.lua, some functions of the bottle are to destroy it from the world. , update, ... In the example,
    You try to come up with a few cases which require access to variables from elsewhere. You just need to write a sample for me 2 files, then I will read and understand that code, later I can program the game properly. For me to imitate is the fastest way of learning (of course, I have to understand it too). I know I have a lot of demands,
    but please forgive me for this, I'm still incompetent and in need of your guidance and advice. Thank you for your interest
This is my code: main.lua

Code: Select all

--- require libs
Object = require "codes/libs/classic"
bump = require "codes/libs/bump"
anim8 = require "codes/libs/anim8"

 -- create new world 
world = bump.newWorld()
---------game variables
listOfBlocks = {}
listOfFruits = {}
isRequired = false
currentLevel = 1
local background = love.graphics.newImage("sources/arts/tiles/tiles.png")
--- require 
if isRequired == false then
    require "codes/entities/characters/knight"
    require "codes/entities/blocks/block"
    require "codes/entities/items/fruits/apple"
    isRequired = true
end

function love.load()
    initGame()
    loadMapLevel1()
end

function love.update(dt)
    --- update player object
    player:update(dt) 
end

function love.draw()
    --- draw background
    drawBackGround()
    --- draw blocks
    for i,v in ipairs(listOfBlocks) do
        v:draw()
    end
    --- draw fruits
    for i,v in ipairs(listOfFruits) do
        v:draw()
    end
    --- draw player object
    player:draw()
end
---- keyboard event
function love.keypressed(key)
    if key == "w" and player.onGround then
        player.yVelocity = player.jumpVelocity
    end
end
--- load map level 1
function loadMapLevel1()
    --- blocks 
    map = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {0, 0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
           {1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}
  buildMap()  
  --- fruits
  table.insert(listOfFruits, Apple(256, 64, 1))
  table.insert(listOfFruits, Apple(128, 16, 2))
  table.insert(listOfFruits, Apple(512, 400, 3))
  table.insert(listOfFruits, Apple(256, 128, 4))
end
--- build map for all level with the data in function loadMapLevel'i' 
function buildMap()
    --id : 1.grass 2.dirt
    for y = 1, 15 do
        for x = 1, 30 do
            if map[y][x] == 1 then
                table.insert(listOfBlocks, Block((x - 1) * 32, (y - 1) * 32, 1))
            elseif map[y][x] == 2 then
                table.insert(listOfBlocks, Block((x - 1) * 32, (y - 1) * 32, 2))
            end
        end
    end
end
--- init the game
function initGame()
    -- create player
    addPlayer("knight", 400, 300)
end
--- addPlayer function
function addPlayer(name, x, y)
    local who = name
    local xPos, yPos = x, y
    if who == "knight" then
        player = Knight(xPos, yPos)
    end
end
--- draw background
function drawBackGround()
    if currentLevel == 1 then
        local quads = {}
        ---winter quad----
        for i = 10, 16 do
            local quad = love.graphics.newQuad(0, (i-1) * 32, 32, 32, background:getDimensions())
            table.insert(quads, quad)
        end  
        --- draw background---
        for i = 1, 30 do
            love.graphics.draw(background, quads[1], (i-1)*32, 0)
            love.graphics.draw(background, quads[1], (i-1)*32, 32)
            love.graphics.draw(background, quads[2], (i-1)*32, 32*2)
            love.graphics.draw(background, quads[3], (i-1)*32, 32*3)
            love.graphics.draw(background, quads[3], (i-1)*32, 32*4)
            love.graphics.draw(background, quads[3], (i-1)*32, 32*5)
            love.graphics.draw(background, quads[3], (i-1)*32, 32*6)
            love.graphics.draw(background, quads[3], (i-1)*32, 32*7)
            love.graphics.draw(background, quads[4], (i-1)*32, 32*8)
            love.graphics.draw(background, quads[5], (i-1)*32, 32*9)
            love.graphics.draw(background, quads[5], (i-1)*32, 32*10)
            love.graphics.draw(background, quads[6], (i-1)*32, 32*11)
            love.graphics.draw(background, quads[7], (i-1)*32, 32*12)
            love.graphics.draw(background, quads[7], (i-1)*32, 32*13)
            love.graphics.draw(background, quads[7], (i-1)*32, 32*14)
    end
    end
end
knight.lua:

Code: Select all

Knight = Object:extend()

function Knight:new(x, y)
    self.x = x
    self.y = y
    self.width = 32
    self.height = 48
    self.scaleX = 1
    --- art
    self.sprite = love.graphics.newImage("sources/arts/characters/knight/knight.png")
    self.grid = anim8.newGrid(self.width, self.height, self.sprite:getWidth(), self.sprite:getHeight())
    self.animations = {}
    self.animations.idle = anim8.newAnimation(self.grid(1, 1, 2, 1, 3, 1, 2, 1), 0.1)
    self.animations.walk = anim8.newAnimation(self.grid(4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 4, 1), 0.05)
    self.anim = self.animations.idle
    ---- collision properties
    self.colWidth = 16
    self.colHeight =  24
    --- physics properties
    self.xVelocity = 0
    self.yVelocity = 0
    self.gravity = 1000
    self.terminalVelocity = 800
    self.onGround = false
    self.jumpVelocity = -400
    ----add to world
    world:add(self, self.x, self.y, self.colWidth, self.colHeight)
end

--- knight collision filter
local function knightFilter(item, other)
    if other.isBlock then return "slide"
    elseif other.isApple then return "cross" end
end

function Knight:update(dt)
    --- moveleft and right 
    if love.keyboard.isDown("a") then
        self.xVelocity = -100
        self.scaleX = -1
        self.anim = self.animations.walk
    elseif love.keyboard.isDown("d") then
        self.xVelocity = 100
        self.scaleX = 1
        self.anim = self.animations.walk
    else
        self.xVelocity = 0
        self.anim = self.animations.idle
    end
    --- apply gravity
    if self.yVelocity < self.terminalVelocity then
        self.yVelocity = self.yVelocity + self.gravity * dt
    else
        self.yVelocity = self.terminalVelocity
    end
    --- apply to move with bump
    local goalX, goalY = self.x + self.xVelocity * dt, self.y + self.yVelocity * dt
    local actualX, actualY, cols, len = world:move(self, goalX, goalY, knightFilter)
    --- check for collision
    self.onGround = false
    for i = 1, len do
        local col = cols[i]
        local other = cols[i].other
        if (col.normal.y == -1 or col.normal.y == 1) and col.other.isBlock then
            self.yVelocity = 0
        end
        if col.normal.y == -1 and col.other.isBlock then
            self.onGround = true
        end
        -- check collision with fruit
        if other.isApple then
            other:boom()
        end
    end
    --- store real x, y
    self.x, self.y = actualX, actualY
    --- update animations
    self.anim:update(dt)
end

function Knight:draw()
    love.graphics.setColor(1, 1, 1)
    self.anim:draw(self.sprite, self.x + 8, self.y + 4, nil, self.scaleX, 1, 16, 24)
    love.graphics.rectangle("line", self.x, self.y, self.colWidth, self.colHeight)
end
block.lua:

Code: Select all

Block = Object:extend()

function Block:new(x, y, id)
    self.x = x
    self.y = y
    self.width = 32
    self.height = 32
    self.sprite = love.graphics.newImage("sources/arts/tiles/tiles.png")
    self.id = id
    if self.id == 1 then
        self.quad = love.graphics.newQuad(0, 0, 32, 32, self.sprite:getDimensions())
    elseif self.id == 2 then
        self.quad = love.graphics.newQuad(0, 32, 32, 32, self.sprite:getDimensions())
    end
    self.isBlock = true
    world:add(self, self.x, self.y, self.width, self.height)
end

function Block:update(dt)
    
end

function Block:draw()
    love.graphics.setColor(1, 1, 1)
    love.graphics.draw(self.sprite, self.quad, self.x, self.y)
    love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
apple.lua:

Code: Select all

Apple = Object:extend()

function Apple:new(x, y, ripelevel)
    self.x = x
    self.y = y
    self.width = 16
    self.height = 16
    self.ripelevel = ripelevel
    self.sprite = love.graphics.newImage("sources/arts/fruits/fruit.png")
    self.quads = {}
    for i = 1, 4 do
        local quad = love.graphics.newQuad(0, (i-1)*32, 32, 32, self.sprite:getDimensions())
        table.insert(self.quads, quad)
    end  
    self.isApple = true
    world:add(self, self.x, self.y, self.width, self.height)
end

function Apple:update(dt)
    
end

function Apple:boom()
    for i = #listOfFruits, 1, -1 do
			  if listOfFruits[i] == self then
			    	world:remove(listOfFruits[i])
			    	table.remove(listOfFruits, i)
        end
		end
end

function Apple:draw()
    love.graphics.setColor(1, 1, 1)
    love.graphics.draw(self.sprite, self.quads[self.ripelevel], self.x - 10, self.y - 12)
    love.graphics.rectangle("line", self.x, self.y, self.width, self.height)
end
Beside: this project still works but many people have warned me. Many past mistakes made me quit other projects because of the inappropriate local usage. Hope you help
Edit : The link to my current problem on Github: https://github.com/bananaback/Help-me-in-this
If I provide incomplete information then you just ask, I will provide as soon as possible

User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: How to use local variables in games properly?

Post by raidho36 » Sun Dec 08, 2019 11:27 pm

You simply declare everything that doesn't needs to be globally accessible as local. That's it. Pass around values through function arguments if you need to. Making local duplicates of global variables helps with performance (but they don't track global variable changes). Don't go nuts with generating closures at runtime though, that's not compiled and is rather slow, doing it once is fine but doing things like creating in-place functions will really tank performance.

A pro tip on the house: if your code got completely borked and you have no idea what's wrong with it, rewrite it from scratch and make effort to keep it functioning as you go. You can try your luck from last known working version, but if your code is broken so badly that there's no way to fix it, it's probably because it has deeply embedded flaws in it, so it's a good idea to toss the entire thing into the trash.

Finally, the game code doesn't needs to be good, it just needs to function. As long as it works, it's fine. Famous example; Minecraft has some of the most godawful code you can find in a commercial project, it was so bad that Microsoft decided they'd rather spend additional ton of cash to rewrite it than maintain and upgrade existing codebase - and yet here we are.

ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

Re: How to use local variables in games properly?

Post by ilovelove2019 » Mon Dec 09, 2019 5:54 am

Ye, the game code doesn't needs to be good, it just needs to function.

ilovelove2019
Citizen
Posts: 78
Joined: Wed Sep 11, 2019 10:38 am

Re: How to use local variables in games properly?

Post by ilovelove2019 » Mon Dec 09, 2019 5:57 am

raidho36 wrote:
Sun Dec 08, 2019 11:27 pm
You simply declare everything that doesn't needs to be globally accessible as local. That's it. Pass around values through function arguments if you need to. Making local duplicates of global variables helps with performance (but they don't track global variable changes). Don't go nuts with generating closures at runtime though, that's not compiled and is rather slow, doing it once is fine but doing things like creating in-place functions will really tank performance.
Can you give me an example of: "Pass around values ​​through function arguments" and "making local duplicates of global variables"?

And... what it mean? "Don't go nuts with generating closures at runtime though"
So sorry, I'm so stupid

User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: How to use local variables in games properly?

Post by raidho36 » Mon Dec 09, 2019 6:10 am

Passing around values:

Code: Select all

local function do_many_things ( foo, bar )
  do_things ( foo, 1, 2, 3 )
  do_other_things ( bar, 'a', 'b', 'c' )
end
do_many_things ( table1, table2 )
Making local duplicates:

Code: Select all

local mmax, mmin, mabs = math.max, math.min, math.abs
local function do_math ( value )
   return mmax ( mmin ( mabs ( value ), 1 ), 0 )
end
Generating closures (OK):

Code: Select all

local foo = 0
local function do_stuff ( bar )
   foo = foo + bar
   return foo
end
print ( do_stuff ( 1 ) )
print ( do_stuff ( 1 ) )
Generating closures (bad, generates new closures every time you call master function):

Code: Select all

local function make_function_that_does_stuff ( )
  local foo = 0
  local function do_stuff ( bar ) 
    foo = foo + bar
    return foo
  end
  return do_stuff
end
local do_stuff1 = make_function_that_does_stuff ( )
print ( do_stuff1 ( 1 ) )
print ( do_stuff1 ( 1 ) )
local do_stuff2 = make_function_that_does_stuff ( )
print ( do_stuff2 ( 10 ) )
print ( do_stuff2 ( 10 ) )
This, and many other useful things are discussed in the "Programming in Lua" book, and Lua user manual.

The "duplicates" example is technically also closures.

Post Reply

Who is online

Users browsing this forum: actually_reb and 10 guests