User:Hans Adler/Sandbox

From Wikibooks, open books for an open world
Jump to navigation Jump to search

Lua Game Programming with LÖVE[edit | edit source]

Current version is 0.7.2, which already includes a version of Lua 5.1. Older games written for 0.6.0 require a compatibility package. LÖVE is also known under the more search-friendly name love2d.

Prerequisites[edit | edit source]

To develop and run LÖVE programs, almost any computer running a recent version of one of the three major operating systems will do: Windows XP or higher, MacOS X or Linux. However, some features may not work with on-board graphics cards.

[Installation]

[Downloading and running games]

For programming, any standard text editor suitable for programming will do. Basically this excludes only Windows Notepad. Ideally the text editor should support Lua text highlighting and running Lua programs. Notepad++ is a popular choice under Windows, and an excellent cross-platform choice is SciTE. Both are open source.

Hello World! and the game loop[edit | edit source]

-- Content of file "main.lua":
function love.draw()
    love.graphics.print(50, 50, "Hello World!")
end

The main file of a LÖVE program must always be called main.lua and must be located directly in the game's directory. If it is named Main.lua instead, it will only work under Windows, and if it is located in a subdirectory of the game's directory it will not work at all. LÖVE uses a simple system of implicit invocation in which the following functions are called at various stages:

  1. love.load(args)
  2. love.update(dt)
  3. love.draw()
  4. event handlers:
    • love.mousepressed(x,y,button), love.mousereleased(x,y,button)
    • love.keypressed(key,unicode), love.keyreleased(key)
    • love.joystickpressed(joystick,button), love.joystickreleased(joystick,button)
    • love.focus(f)
  5. love.quit()

The load callback is run only once.

In each iteration of the main game loop, first the update callback is called, then the draw callback and finally any applicable event handlers. When the program terminates cleanly, the quit callback is run once. Note that in our first example we have written the text in the draw callback rather than the update callback. Otherwise it would not be visible because the draw callback always starts with a black screen! If we don't need to do anything in some of the callbacks and handlers, then we simply don't implement them.

The game loop will typically run a certain number of times per second (sometimes referred to as the frame rate). Its speed may not be constant and is not easily predictable. In particular, it depends on the speed of the computer and on the time spent in the various callbacks.

Some of the callbacks and event handlers have arguments which they use to provide you with some information. (As always in Lua, if you don't need the information, you can simply declare the functions without these arguments.)

If the program was started from the command line, then you will get any options (i.e. anything that was written after the name of the command) from the args in love.load. The dt argument of love.update tells you the number of (fractional) seconds since the last update. The f argument of love.focus is true or false depending on whether your game's window has gained or lost focus the focus, i.e. on whether it is currently the window on your desktop which accepts keyboard input.

Loading, saving and displaying images[edit | edit source]

A graphical version of the Hello World! program can be implemented as follows.

-- Content of file "main.lua":
function love.load()
    greeting = love.graphics.newImage("greeting.png")
end
function love.draw()
    love.graphics.draw(greeting, 0, 0, 0, 1, 1, 0, 0)
end

The program expects a file greeting.png in the game directory. Unfortunately, with low-end graphics cards such as Intel on-board graphics you will likely see a white rectangle instead of the image. The only way you can avoid this problem is by making both the width and the height of your image a power of 2. In other words, to avoid problems the width must be one of 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 etc., and the same restriction applies independently to the height. This so-called power of 2 syndrome is currently a key restriction of LÖVE on low-end graphics cards. According to developers, this problem will be solved with the next release, LÖVE 0.8.0.

Dealing with power of 2 syndrome[edit | edit source]

If you have already run into power of 2 syndrome and don't want to wait for the next version of LÖVE, you can use the following workaround, which is essentially that described on the LÖVE wiki.[1]

-- Content of file "main.lua":
function newPaddedImage(filename)
    -- First we just read the image file into memory.
    -- That's what ImageData objects are for.
    -- That way, the graphics card has nothing to do with this (yet).
    local source = love.image.newImageData(filename)
   
    -- w is the width of the loaded image.
    -- wp is a power of 2 that is at least as big as w.
    -- Of course we take the smallest w that does the job.
    local w = source:getWidth()
    local wp = 1
    while wp < w do
        wp = 2*wp
    end

    -- h is the height of the loaded image.
    -- hp is a power of 2 that is at least as big as h.
    -- Of course we take the smallest h that does the job.
    local h = source:getHeight()
    local hp = 1
    while hp < h do
        hp = 2*hp
    end
   
    -- Maybe w and h are already powers of 2?
    -- Then we should skip the extra work of resizing
    -- and just return an Image object based on the ImageData.
    -- We can recognise this case by the fact that w == wp and h == hp.
    if w == wp and h == hp then
        return love.graphics.newImage(source)
    end

    -- Get a new ImageData object that has the appropriate power of 2 dimensions.
    local padded = love.image.newImageData(wp, hp)

    -- Paste our original image into the new, bigger one (in the lop left corner)
    -- and return an Image object based on the resized ImageData.
    padded:paste(source, 0, 0)
    return love.graphics.newImage(padded)
end

function love.load()
    greeting = newPaddedImage("greeting.png")
end
function love.draw()
    love.graphics.draw(greeting, 0, 0, 0, 1, 1, 0, 0)
end

External links[edit | edit source]