home..

Bytepath #1 게임 루프

목차

시작

시작하기 전에 LÖVE를 설치하고 어떻게 LÖVE 프로젝트를 실행하는지 아셔야합니다. 이 튜토리얼에서 LÖVE의 버전은 0.10.2이며 여기서 다운 받으실 수 있습니다. 스텝을 따라가면서 자세한 설명은 이 페이지를 참고하시길 바랍니다. 다되었다면 프로젝트 폴더 안에 main.lua 파일을 다음처럼 만듭니다.

function love.load()

end

function love.update(dt)

end

function love.draw()

end

실행시키면 윈도우창이 보이시고 검은 화면을 출력합니다. 위 코드에서 LÖVE 프로젝트의 프로그램 시작 시 love.load 함수가 한번 실행되고 love.updatelove.draw가 매 프레임마다 실행됩니다. 그래서, 만약 이미지를 불러와서 출력시키고 싶다면 다음처럼 작성하시면 됩니다.

function love.load()
    image = love.graphics.newImage('image.png')
end

function love.update(dt)

end

function love.draw()
    love.graphics.draw(image, 0, 0)
end

love.graphics.newImage는 이미지 텍스처를 불러와 image 변수에 할당한 뒤 매 프레임마다 (0, 0) 위치에 출력합니다. love.draw가 매 프레임마다 불려지는 지 확인하려면 다음을 시도해보세요.

love.graphics.draw(image, love.math.random(0, 800), love.math.random(0, 600))

윈도의 기본 사이즈는 800x600이기 때문에 스크린 전체에서 이미지가 매우 빠르고 랜덤하게 출력됩니다.

매 프레임 사이는 화면이 초기화되며 그렇지 않다면 그려지는 이미지가 화면 전체를 천천히 채우게 될겁니다. LÖVE는 매 프레임의 끝에 화면 초기화를 해주고 있습니다. 한번 이 게임 루프를 어떻게 바꿀 수 있는지 알아봅시다.

게임 루프

기본 LÖVE의 게임 루프는 love.run에서 볼 수 있으며 다음과 같습니다.

function love.run()
    if love.math then
	love.math.setRandomSeed(os.time())
    end

    if love.load then love.load(arg) end

    -- We don't want the first frame's dt to include time taken by love.load.
    if love.timer then love.timer.step() end

    local dt = 0

    -- Main loop time.
    while true do
        -- Process events.
        if love.event then
	    love.event.pump()
	    for name, a,b,c,d,e,f in love.event.poll() do
	        if name == "quit" then
		    if not love.quit or not love.quit() then
		        return a
		    end
	        end
		love.handlers[name](a,b,c,d,e,f)
	    end
        end

	-- Update dt, as we'll be passing it to update
	if love.timer then
	    love.timer.step()
	    dt = love.timer.getDelta()
	end

	-- Call update and draw
	if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled

	if love.graphics and love.graphics.isActive() then
	    love.graphics.clear(love.graphics.getBackgroundColor())
	    love.graphics.origin()
            if love.draw then love.draw() end
	    love.graphics.present()
	end

	if love.timer then love.timer.sleep(0.001) end
    end
end

프로그램이 love.run을 시작하면 거기서 모든 일이 일어납니다. 함수는 주석이 잘되어 있어 LÖVE 위키에서 각 함수가 무엇을 하는 지 알 수 있습니다만 하나 하나 살펴봅시다.

if love.math then
    love.math.setRandomSeed(os.time())
end

첫 라인에서 만약 love.math가 nil이 아니라면 이라는 조건문을 볼 수 있습니다. Lua에서는 falsenil을 제외한 모든 변수가 true입니다. 그래서 love.math가 뭐로 정의되었든 if love.math는 true가 됩니다. LÖVE의 경우 이러한 변수는 conf.lua 파일에서 활성화 여부를 설정합니다. 지금은 이 파일에 대해 몰라도 되지만 굳이 언급하는 이유는 이 파일은 love.math와 같은 개별 시스템의 활성화 여부도 설정할 수 있기 때문에 이 함수에서 확인하는 것입니다.

Lua에서는 일반적으로 변수를 정의하지 않거나 정의되지 않은 변수를 참조한다면 nil을 반환합니다. 그래서 random_variable = 1 처럼 변수를 정의하지 않는다면 if random_variable는 false가 될 겁니다.

만약, love.math 모듈이 활성화되어 있으면(기본값은 활성화되어 있음) seed를 현재 시간을 기준으로 설정합니다. love.math.setRandomSeedos.time을 참고하세요. 그 다음은 love.load 함수가 실행됩니다.

if love.load then love.load(arg) end

arg는 프로젝트 실행 시 LÖVE에 넘겨지는 명령어 인수 입니다. 보면 알 수 있듯이 love.load가 한번만 불리는 이유는 실제로 한번만 불리기 때문입니다. 반면, update와 draw 함수는 loop를 통해 여러번 불리게 됩니다. (한번의 loop는 한 프레임과 대응됩니다.)

-- lova.load를 포함한 첫 프레임의 dt를 포함시키지 않기 위해
if love.timer then love.timer.step() end

local dt = 0

love.load가 정상적으로 실행되었다면, love.timer가 정의되어 있는 지 확인 후 마지막 두 프레임 사이에 시간이 얼마나 걸렸는 지 측정하는 love.timer.step을 실행합니다. love.load는 처리하는 데 많은 시간(이미지, 사운드 등 모든 것을 로드하기 때문에)이 걸리기에 주석에서 설명했듯이 게임의 첫 프레임의 love.timer.getDelta는 사용하지 않습니다.

dt는 0으로 초기화합니다. Lua에서 변수는 기본적으로 전역(global)이기 때문에 local dt를 현재 블록의 지역 변수로 한정시킵니다. 이 경우 지역은 love.run 함수로 한정됩니다. 블록에 관해서는 여기를 보세요

-- Main loop time.
while true do
    -- Process events.
    if love.event then
        love.event.pump()
        for name, a,b,c,d,e,f in love.event.poll() do
            if name == "quit" then
                if not love.quit or not love.quit() then
                    return a
                end
            end
            love.handlers[name](a,b,c,d,e,f)
        end
    end
end

여기가 메인 루프의 시작입니다. 각 프레임에서 첫 번째는 이벤트 처리입니다. love.event.pump는 이벤트를 이벤트 큐에 푸시하고 해당 이벤트에 대한 설명에 따라 해당 이벤트는 사용자에 의해 생성됩니다. 예를 들면 키 누르기, 마우스 클릭, 창 크기 조정 등이 있습니다.

루프는 love.event.poll를 사용하여 이벤트 큐를 통해 각 이벤트를 처리합니다. love.handlers는 관련된 콜백(callback) 함수를 정의한 테이블입니다. 예를 들어, love.handlers.quitlove.quit 함수가 존재한다면 실행합니다.

LÖVE는 이벤트가 발생할 때 호출될 콜백을 main.lua 파일에서 정의할 수 있습니다. 콜백의 전체 리스트는 다음을 참고 하세요. 콜백은 다음에 자세히 다루도록 하겠지만 콜백은 이렇게 실행됩니다. love.handlers[name]으로 넘겨지는 a, b, c, d, e, f 인수는 관련 함수에서 사용할 수 있는 모든 가능한 인수입니다. 예를 들어, love.keypressed는 인수로 키 눌림, 키 코드, 키 눌림 반복 여부를 받으며 이 경우 love.keypresseda, b, c는 어떤 한 값으로 정의되지만 d, e, f는 nil이 됩니다.

-- Update dt, as we'll be passing it to update
if love.timer then
    love.timer.step()
    dt = love.timer.getDelta()
end

-- Call update and draw
if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled

love.timer.step은 마지막 두 프레임 사이의 시간을 측정하고 love.timer.getDelta로 반환되는 값을 변경합니다. 그래서 dt는 이전 프레임이 실행되는데 걸리는 시간을 포함합니다. 이 값은 프레임률의 변화에도 불구하고 love.update 함수에 전달되어 일정한 속도로 사물을 정의할 수 있기 때문에 유용합니다.

if love.graphics and love.graphics.isActive() then
    love.graphics.clear(love.graphics.getBackgroundColor())
    love.graphics.origin()
    if love.draw then love.draw() end
    love.graphics.present()
end

love.update를 부른 후 love.draw가 호출됩니다. 하지만 그전에 love.graphics 모듈이 유효한지 확인하고 화면에 그릴 수 있는 지 love.graphics.isActive로 확인합니다. 화면은 미리 정의된 배경색(초기값은 검은색)으로 love.graphics.clear로 초기화합니다.

© 2022 HookSSi   •  Powered by Soopr   •  Theme  Moonwalk