newby wanting some guidence

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
User avatar
Rob.m
Prole
Posts: 23
Joined: Mon Mar 11, 2013 1:57 am

newby wanting some guidence

Post by Rob.m »

Hi, I just downloaded Love/Lua and I am not realy getting it. I have a lot of history with programming so I must be doing something stupid.

Here is my first test program -

function love.load()
grid = love.graphics.newImage("grid.bmp")
srgb = love.graphics.newImage("srgb.bmp")
x = 0
y = 0
xo = 1
yo = 1
-- love.graphics.draw(srgb, 0, 0)
end

function move()
current_time = love.timer.getMicroTime()
if current_time / 500 == math.floor(current_time / 500) then
x = x + xo
end
end

function love.draw()
local display_init = false
if display_init == false then
local xo = 128 -- 50 - 32 = 18; 18 / 2 = 9; 9 - 1 = 8; 8 * 16 = 128
local yo = 28 -- 37.5 - 32 = 5.5; 5.5 / 2 = 2.75; 2.75 - 1 = 1.75; 1.75 * 16 = 28
local x
local y
for y = 1, 32 do -- 0 to 36.5 ie 37.5 places at 16x16 or 600 px
for x = 1, 32 do -- 0 to 49 ie 50 places at 16x16 or 800px
love.graphics.draw(grid, xo + x * 16, yo + y * 16)
end
end
display_init = true
end
love.graphics.draw(srgb, x * 16, y * 16)
love.graphics.print("FPS: " .. tostring(love.timer.getFPS()), 730, 0)
end

function love.keypressed(key)
if key == "left" and x > 0 then x = x - 1 end
if key == "right" and x < 49 then x = x + 1 end
if key == "up" and y > 0 then y = y - 1 end
if key == "down" and y < 36 then y = y + 1 end
end

the load draw and keypressed are doing what I expect but the move() doesn't seem to be doing anything.

I don't understand the program flow. Do I just dump code into the global (root) scope and does it run concurrently with the love. functions ??

What happens with code that is not within a function definition (root or global scope) does it run once or does it loop??

The language looks very BASIC like, should I be using labels and goto's ??

Do functions declared in the root scope run cincurrently with the love. functions or do they explicitly need to be called ??

I know these are stupid questions to someone who knows the answers. I started this half an hour ago and I would appeciate someone giving me a bit of a boost as I have extensive coding experience and I should be further along than I am so far.

Another thing - the love.draw() seems tricky, what if you have a game with multiple levels and tween screens? Do you use something like switch/case (if/then) to select the code for the current screen or tween screen?
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: newby wanting some guidence

Post by Lafolie »

Please wrap your code in [ code ] tags.

You seem to have a good grasp of BASIC. I would advise you to take a look at http://lua-users.org/wiki/TutorialDirectory which contains the answers to most of your questions.

Labels and gotos should be avoided (in most cases, they do have their uses). Using other structures like functions and tables is a much cleaner way to write your code.

As for Löve, what happens is this:

The entry point is main.lua. This is loaded first (with the exception of conf.lua, though it's use is solely for configuration). It should contain any of the love module callbacks you wish to use.

love.load is called once your main.lua has been read. Once this is completed love.run will perpetually call love.update and love.draw alternately. You can override love.run, however the supplied function is often all that is required.

To answer the question about 'free floating code' (not in a function), it is ran once (when the file is loaded). For example, the following would produce 2:

Code: Select all

love.load = function()
    x = 1 + y
end
y = 1
I hope this helps.
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
User avatar
Rob.m
Prole
Posts: 23
Joined: Mon Mar 11, 2013 1:57 am

Re: newby wanting some guidence

Post by Rob.m »

Thanks Lafolie

I now have a basic idea of how it works.

With the code below I can see that I can still get a decent frame rate when animating a large number of actors.

I did notice that the lowest times between calling update was about 5 miliseconds. When I tried 10000 concurrent animations it rose to about 12 miliseconds.

What I want to do is to build a basic engine for tiled games. I don't want the realy old school tile restrictions that every actor must be on a grid position. I want them to move by one or two pixels at a time.

Is there a way to have it so that some code executes at a presice rate and have that (or other) code then execute a separate block of code on say 1 in 16 times of the original code. ie - pixel animation that then executes logic code on every sixteenth execution when an animated actor is in a grid position.

someting like -

Code: Select all

-- code that executes every 1000 microseconds or one milisecond or 0.001 seconds
function do_all_animation()
  do_pixel_level_animations()
  local pixel_block_sync = 0
  pixel_block_sync = pixel_block_sync + 1
  if pixel_block_sync == 16 then do
    pixel_block_sync = 0
    do_block_level_animations()
  end
end


also - how do I make .love files to upload here ?


Code: Select all


function love.load()
  grid = love.graphics.newImage("grid.bmp")
  srgb = love.graphics.newImage("srgb.bmp")
  timer = 0
  update = 0
  balls = {}
  local x = 0
  local y = 0
  local xo = 0
  local yo = 0
  local ball_count = 1000
  local ball index = 1
  local this_ball = {}
  for ball_index = 1, ball_count do
    x = math.random(5, 779)
    y = math.random(5, 579)
    xo = 0
    yo = 0
    while xo * yo == 0 do
      xo = math.random(-2, 2) * 2
      yo = math.random(-2, 2) * 2
    end
    this_ball = {x, y, xo, yo}
    balls[ball_index] = this_ball
  end
end

function love.update(dt)
  update = dt
  local time = math.floor(love.timer.getMicroTime() * 50)
  if time ~= timer then
    timer = time
    local ball_count = #balls
    local ball_index
    for ball_index = 1, ball_count do
      this_ball = balls[ball_index]
      local x = this_ball[1]
      local y = this_ball[2]
      local xo = this_ball[3]
      local yo = this_ball[4]
      x = x + xo
      y = y + yo
      if x < 1 then
        x = -x
        xo = -xo
      end
      if x > 783 then
        x = 783 - (x - 783)
        xo = -xo
      end
      if y < 1 then
        y = -y
        yo = -yo
      end
      if y > 583 then
        y = 583 - (y - 583)
        yo = -yo
      end
      this_ball[1] = x
      this_ball[2] = y
      this_ball[3] = xo
      this_ball[4] = yo
      balls[ball_index] = this_ball
    end
  end
end


function love.draw()
  local display_init = false
  if display_init == false then
    local xo = 128 -- 50 - 32 = 18; 18 / 2 = 9; 9 - 1 = 8; 8 * 16 = 128
    local yo = 28 -- 37.5 - 32 = 5.5; 5.5 / 2 = 2.75; 2.75 - 1 = 1.75; 1.75 * 16 = 28
    local x
    local y
    for y = 1, 32 do -- 0 to 36.5 ie 37.5 places at 16x16 or 600 px
      for x = 1, 32 do -- 0 to 49 ie 50 places at 16x16 or 800px
        love.graphics.draw(grid, xo + x * 16, yo + y * 16)
      end
    end
    display_init = true
  end
  local ball_count = #balls
  local ball_index
  for ball_index = 1, ball_count do
    local this_ball = balls[ball_index]
    love.graphics.draw(srgb, this_ball[1], this_ball[2])
  end
  love.graphics.print(tostring(update), 400, 0)
  love.graphics.print(tostring(love.timer.getMicroTime()), 600, 0)
  love.graphics.print("FPS: " .. tostring(love.timer.getFPS()), 730, 0)
end

User avatar
substitute541
Party member
Posts: 484
Joined: Fri Aug 24, 2012 9:04 am
Location: Southern Leyte, Visayas, Philippines
Contact:

Re: newby wanting some guidence

Post by substitute541 »

See "Upload Attachment" when editing/creating a post. Alternatively, upload your file on a downloading site (I prefer Dropbox), and post the download link in your post using the [url] [/url] tags.
Currently designing themes for WordPress.

Sometimes lurks around the forum.
User avatar
micha
Inner party member
Posts: 1083
Joined: Wed Sep 26, 2012 5:13 pm

Re: newby wanting some guidence

Post by micha »

Hi Rob.m,

you misunderstand the concept behind time measurement in a loop.
What you want (i believe) is to move things at a certain speed. The update() function however is called at slightly irregular times, depending on what your OS does in the background and depending on the code you execute each frame. So in advance you never know how much time passes between one function call of love.update and another one.

In order to move around things in a constant speed you have two choices:
1) Try to move things with constant offset. For that you need constant time steps. You can achieve that by measuring the time since the last movement and when it is larger than the desired time step, move the object by a constant distance.

2) Work with variable time steps and adapt the movement offset accordingly.

The second method is the one, you should use. It gives you simpler code and works better.

Code-wise it looks like that:
This is what you have at the moment

Code: Select all

function move()
  current_time = love.timer.getMicroTime()
  if current_time / 500 == math.floor(current_time / 500) then
    x = x + xo
  end
end
Instead you use dt (which tells you how much time passed since update was called the last time) and write

Code: Select all

function move(dt)
x = x + xo*dt
end
For the second question. You want some part of the code to be performed only at certain time steps, lets say once each second. Use a time counter (define in the love.load()

Code: Select all

timeCount = 0
and update each frame:

Code: Select all

function love.update(dt)
  timeCount = timeCount + dt
  if timeCount >= 1 then
    timeCount = timeCount-1
    performSomething()
  end
end
The performSomething() will be called once a second.
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: newby wanting some guidence

Post by Lafolie »

You should also consider the SpriteBatch object when using tiles. A properly implemented spritebatch can drastically increase performance.

As for benchmarking... it's kind of tough, but you can get a rough estimate by tracking delatime, frames-per-second and memory usage.

Code: Select all

love.update = function(dt)
    print(dt) --deltatime, time that love.run call took
    print(love.timer.getFPS()) --frames-per-second. Not always the best way to judge performance!
    print(gcinfo()) --amount of memory used in the llvm
    print(love.timer.getMicroTime()) --precision timer. Useful for monitoring/debugging intensive functions and codeblocks
end
One thing to bear in mind is that often tracking performance has a significant impact. For example lots of print and love.graphics.print statements can cause an increase in dt and thus a deterioration in framerate.

I find the best way to judge your programs performance is by simply distributing it, whether it be to friends or other computers you own, and see what values are reported (FPS is a good candidate since your friends are likely to understand it).

Generally speaking you should not have to worry about performance. If it becomes an issue you're most likely doing something very inefficiently or perhaps even completely "wrong". Oh, there's also the odd caveat, such as rendering lots of text, but I did say generally speaking.:3

Oh, and here's my personal solution to tracking per-entity time values:

Code: Select all

love.load = function()
    object = {text = "Bucket", time = love.timer.getTIme(), delay = 1}
end

love.update(dt)
    t = love.timer.getTime()
    ---print "Bucket" every second
    if t - object.time > object.delay then
        print(object.text)
        object.time = t
    end
end
Since it doesn't use dt it is bound by real-time, which may or may not be desirable for you. I use this method of timekeeping for several things and find it to be quite elegant, but the previous method works just as well.
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
User avatar
Rob.m
Prole
Posts: 23
Joined: Mon Mar 11, 2013 1:57 am

Re: newby wanting some guidence

Post by Rob.m »

Thanks all.

Your solution if perfect lafolie.

I got excited when I saw -

object = {text = "Bucket", time = love.timer.getTIme(), delay = 1}

as at first I thought you were attaching a function within an psuedo object. time = function()

That is my next step but I will start another thread.
User avatar
Lafolie
Inner party member
Posts: 809
Joined: Tue Apr 05, 2011 2:59 pm
Location: SR388
Contact:

Re: newby wanting some guidence

Post by Lafolie »

Also I just noticed in the your draw code you do this:

Code: Select all

local someFlag = false
if someFlag == false then
...
The code within the if block will always be executed since you explicitly state that the condition evaluates to true. Also you should know that Lua's philosophy allows for some sugar when doing these sort of expressions:

Code: Select all

if not someVar then end
--is equivalent to
if someVar == nil then end
if not someVar == true then end
--in certain contexts it could also replace
if someVar ~= true then end

--checking that variable is 'defined' (not nil)
if someVar then end
You also do quite a few declarations and calculations in your draw code. It is considered good practice to minimalise the draw operations by doing as few calculations as possible. Ideally your draw code would contain nothing but draw calls and perhaps a few loops. The drawing arguments should be defined and maintained elsewhere (such as in update and initialisation functions).
Do you recognise when the world won't stop for you? Or when the days don't care what you've got to do? When the weight's too tough to lift up, what do you? Don't let them choose for you, that's on you.
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests