반응형
블로그 이미지
개발자로서 현장에서 일하면서 새로 접하는 기술들이나 알게된 정보 등을 정리하기 위한 블로그입니다. 운 좋게 미국에서 큰 회사들의 프로젝트에서 컬설턴트로 일하고 있어서 새로운 기술들을 접할 기회가 많이 있습니다. 미국의 IT 프로젝트에서 사용되는 툴들에 대해 많은 분들과 정보를 공유하고 싶습니다.
솔웅

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
자주 사용하는 기능을 모듈화 하면 나중에 재사용할 때 아주 편하고 개발 기간도 많이 save해 줍니다.
예를 들어 스크린 사방 벽에 wall을 만들고 객체들이 이 벽에 맞고 튀어야 할 때 사방 벽 만드는 부분을 모듈화 한다던지.
아니면 레벨별로 배경화면을 다르게 해야 되는 게임을 개발 할 때 배경화면 설정 부분을 모듈화 한다던지.
그 외 여러가지 자기만의 로직을 모듈화 해 두면 그게 자산이 되고 나중에 개발할 때도 많은 도움이 됩니다.

오늘은 이렇게 자기만의 코드를 모듈화 하는 방법에 대해 알아보겠습니다.


낯익은 사진이죠?
자기가 힘들여서 터득한 know how를 열심히 다른 개발자들고 Share하고 있는 peach입니다.
홈페이지는 http://peachpellen.com/ 입니다. 가셔서 고맙다고 코멘트 남기는 것도 좋을 것 같습니다.

이 앱에는 두가지가 모둘화 돼 있습니다.

첫번째는 background image 를 표시하고 이 배경이미지를 클릭하면 없어지는 함수입니다.
그리고 두번째는 x,y좌표 파라미터를 받아서 peach.png 이미지 파일을 해당 x,y좌표에 display 하는 겁니다.

이 모듈화 된 소스를 보겠습니다. ----- setup.lua -----
-- Makes sure the file can be "seen"
module(..., package.seeall)

-- Set up the background
function background (event)
    -- Display the background
    local background = display.newImage ("bg.png")
        -- The function to remove the background
        function killbackground (event)
            background:removeSelf()
        end
    -- Add the event listener to the background
    background:addEventListener("tap", killbackground)
end

-- Insert the traditional Peach pic, this time using params
function peach (params)
    -- My picture
    local peach = display.newImage ("peach.png")
    -- The X and Y of my pic, defined when the function is called
    peach.x = params.x
    peach.y = params.y  
end

첫번째 부분은 모듈화 하기 위해 지정해 줘야 하는 코로나 SDK의 규칙입니다.
두번째 event를 인수로 받는 background라는 함수입니다.
bg.png를 display하고 여기에 tap 이벤트 리스너를 달아서 tab하면 배경이미지를 없애는 함수 입니다.
세번째 함수는 peach라는 함수로 peach.png를 display하고 x,y좌표를 전달받은 x,y좌표로 설정해 이미지를 그 위치에 그려줍니다.

그러면 이 모듈화된 부분을 어떻게 사용하는지 알아보겠습니다.

-- Hides the status bar
display.setStatusBar (display.HiddenStatusBar)

-- Requires setup.lua
require "setup"

-- Performs our "background" event from setup.lua (Create background, create remove function, add listener)
setup.background()

-- Performs our "peach" event; note the x and y - these are our "params"
setup.peach ({ x = 160, y = 240 })

-- Uncomment the block below to see how useful this type of code is when spawning lots of the same image/object
--[[
setup.peach ({ x = 60, y = 100 })
setup.peach ({ x = 60, y = 400 })
setup.peach ({ x = 260, y = 100 })
setup.peach ({ x = 260, y = 400 })
--]]

--[[
*NOTES*
Like many of my tutorials this is as simple as it gets; it shows how to add an image, add a function to that image and
remove that image all in one function from a seperate Lua file.

There is near limitless potential for using multiple files to keep your code clean and organised however this mini
tutorial is only intented to highlight the basics.

By learning how to use the simple examples shown here you are well on your way to much tidier code; something that
will really come in handy as your projects grow larger and more ambitious.
--]]

실제 코드는 단 세 줄 입니다.
우리의 친구 peach가 아주 자세하게 주석을 달아 줬네요.

우선 setup.lua를 require하구요.
background()함수를 불러옵니다.
그리고 peach함수도 해당 파라미터를 넣어서 불러오구요.

그러면 끝입니다.

이렇게 되면 setup.lua에 있는 함수는 다른 앱을 개발 할 때도 그대로 모듈로 사용할 수 있습니다.

원본 소스코드는 아래 압축파일에 있습니다.


그럼 다음 시간에 뵐께요.
반응형


반응형
오늘은 벽돌 깨기 게임 만들기 마지막회가 될것 같은데요.
어제까지 일단 화면에 표시할 모든 객체들은 다 표시했습니다.
그리고 화면에서는 공만 여기저기 튀면서 움직이고 있습니다.

이제 남은 일은 밑에 있는 paddle을 유저가 좌우로 움직일 수 있도록 해야 하고,
공이 벽돌에 맞으면 벽돌이 없어지게 해야 하고,
공이 bottom Wall에 맞으면 공이 없어지고 새로 시작하도록 해야 합니다.

그럼 우선 첫번째 paddle을 움직이는 것 부터 해 볼까요?

지난 시간 까지 했던 paddle 생성 함수는 아래와 같습니다.
function createPaddle()
    local paddleWidth = 100
    local paddleHeight = 10
    local paddle = display.newRect( display.contentWidth / 2 - paddleWidth / 2, display.contentHeight - 50, paddleWidth, paddleHeight )
    physics.addBody(paddle, "static", {friction=0, bounce=1})
end

자 touch 이벤트 리스너를 달겠습니다.
Runtime:addEventListener("touch", movePaddle)

paddle에 달지 않고 Runtime에 달았습니다. 유저가 paddle에 닿았을 때만 움직일 수 있도록 하려면 Runtime을 paddle로 바꾸면 됩니다.
하지만 여기선 유저가 움직일 수 있는 객체가 paddle밖에 없고 화면 전체에서 이 이벤트 리스너를 받는게 더 효과적이기 때문에 Runtime에 이벤트 리스너를 달았습니다.

이 이벤트 리스너는 화면 아무곳이나 touch하면 movePaddle 함수가 실행 되도록 하라는 의미 입니다.
이 이벤트 리스너 앞에 movePaddle 함수를 만듭니다.
그리고 event.x를 paddle.x에 대입하면 이 paddle의 위치가 좌우로 움직입니다.

local movePaddle = function(event)
    paddle.x = event.x
end


자 이제 유저가 paddle을 움직일 수 있습니다.

그 다음은 공이 벽돌에 맞으면 그 벽돌이 없어지도록 할께요.
addBody를 할 때 공은 dynamic으로 설정하고 나머지 객체들은 모두 static으로 설정했습니다.
 이 dynamic으로 설정한 공에 충돌(collision)을 체크하는 로직을 적용하도록 하겠습니다.
function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
end

지난 시간까지 한 소스코드 입니다.
이제 ball에 collision 이벤트 리스너를 답니다.
ball:addEventListener("collision", ball)

공이 어딘가에 충돌했을 때 ball.collision 함수가 실행 됩니다.
여기서 어딘가는 physics.addBody 가 적용된 객체들을 말합니다.

그럼 ball.collision함수를 만들어 보겠습니다.
    ball.collision = function(self, event)
        if(event.phase == "ended") then
            if(event.other.type == "destructible") then
                event.other:removeSelf()
            end
        end
    end

벽돌을 만들 때 각각의 벽돌에 type을 destructible 이라고 주었었습니다.
여기서 event.other는 충돌의 상대편을 말합니다. event.target은 충돌 당사자를 말합니다. 그러므로 event.other는 벽돌이고 event.target은 ball 이겠죠.
event.other.type이 destructible이면 즉 벽돌이면 event.other:removeSelf() 합니다. 즉 그 벽돌을 없앱니다.
아주 간단하죠?

이제 벽돌 깨기의 주요 기능이 다 구현 됐습니다.

여기서 보너스로 한가지만 더 한다면 공이 bottom wall에 부딪혔을 때 공이 없어지고 새로 시작하도록 하겠습니다.

이것도 공이 collision 이벤트를 일으켰을 때 일어나는 현상이기 때문에 아까 만들었던 ball.collision 함수 안에 아래 코드를 넣습니다.
        if(event.other.type == "bottomWall") then
            self:removeSelf()
            local onTimerComplete = function(event)
                createBall()
            end
            timer.performWithDelay(500, onTimerComplete , 1)
        end

이걸 넣고 실행해 보시면 공이 바닥에 닿으면 없어지고 가운데에서 다시 생성 되는 것을 보실 수 있을 겁니다.

이 경우 collision 이벤트가 발생했을 때 event.other.type이 bottomWall 이면 self:removeSelf()를 해 줍니다. other가 아니라 자신이 없어지는 겁니다.
그리고 timer로 0.5초의 시간을 두고 onTimerComplete 함수를 한번 호출하도록 만듭니다.
이 함수에서는 createBall()을 호출합니다.
그러면 공이 바닥에 부딪혀서 없어지고 0.5초 후에 새 공이 가운데에서 만들어져서 움직이기 시작할 겁니다.

전체 소스는 아래와 같습니다.
require ("physics")

function main()
    setUpPhysics()
    createWalls()
    createBall()
    createPaddle()
    createBricks()
end

function setUpPhysics()
    physics.start()
    physics.setGravity(0,0)
end

function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
   
    ball.collision = function(self, event)
        if(event.phase == "ended") then
            if(event.other.type == "destructible") then
                event.other:removeSelf()
            end
        end
       
        if(event.other.type == "bottomWall") then
            self:removeSelf()
            local onTimerComplete = function(event)
                createBall()
            end
            timer.performWithDelay(500, onTimerComplete , 1)
        end
    end

    ball:addEventListener("collision", ball)
end

function createWalls()
    local wallThickness = 10
    -- Left wall
    local wall = display.newRect( 0, 0, wallThickness, display.contentHeight )
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Top wall
    wall = display.newRect(0,0, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Right wall
    wall = display.newRect(display.contentWidth - wallThickness, 0, wallThickness, display.contentHeight)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Bottom wall
    wall = display.newRect(0, display.contentHeight - wallThickness, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    wall.type = "bottomWall"
end

function createPaddle()
   
    local paddleWidth = 100
    local paddleHeight = 10
   
    local paddle = display.newRect( display.contentWidth / 2 - paddleWidth / 2, display.contentHeight - 50, paddleWidth, paddleHeight )
    physics.addBody(paddle, "static", {friction=0, bounce=1})

    local  movePaddle = function(event)
            paddle.x = event.x
    end

    Runtime:addEventListener("touch", movePaddle)
   
end
function createBricks()
    local brickWidth = 40
    local brickHeight = 20
    local numOfRows = 4
    local numOfCols = 6
    local topLeft = {x= display.contentWidth / 2 - (brickWidth * numOfCols ) / 2, y= 50}
    local row
    local col
    for row = 0, numOfRows - 1 do
        for col = 0, numOfCols - 1 do
            -- Create a brick
            local brick = display.newRect( topLeft.x + (col * brickWidth), topLeft.y + (row * brickHeight), brickWidth, brickHeight )
            brick:setFillColor(math.random(50, 255), math.random(50, 255), math.random(50, 255), 255)
            brick.type = "destructible"
            physics.addBody(brick, "static", {friction=0, bounce = 1})
        end
    end
end

main()

원본 소스는 ball:setLinearVelocity(75, 150) 를 startGame()에 넣고 사용했는데 저는 하다 보니까 그냥 createBall에 넣었네요. 두개 다 똑 같습니다.
다만 나중에 게임에 여러 기능을 추가하다 보면 startGame()을 따로 Call할 일이 많이 생길 수 있는데 그 때는 원본 소스 같이 따로 함수로 관리하는게 더 낫겠죠.

이 소스는 Corona SDK를 만든 회사인 Ansca Mobile에서 제공한 샘플 소스입니다.
아래 유튜브로 가시면 이 샘플 코드에 대한 강좌를 들으실 수 있습니다.

제가 설명했던 것과는 조금 다르게 설명하겠죠?

이것도 들어보시면 도움이 될 거예요.

그럼 다음시간에 뵐께요.
반응형


반응형
어제 했던 소스는 아래와 같습니다.

require ("physics")

function main()
    setUpPhysics()
    createBall()
end

function setUpPhysics()
    physics.start()
    physics.setGravity(0,0)
end

function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
end

main()


정리하자면 공을 만들고 그 공에 physics 엔진을 적용하고 움직이는 것 까지 했습니다.

오늘은 벽을 만들어서 그 벽에 공이 튀게 만들겠습니다.

일단 함수 이름은 createWalls()로 하고 사각형으로 그려보겠습니다.

function createWalls()
    local wallThickness = 10
    -- Left wall
    local wall = display.newRect( 0, 0, wallThickness, display.contentHeight )
    -- Top wall
    wall = display.newRect(0,0, display.contentWidth, wallThickness)
    -- Right wall
    wall = display.newRect(display.contentWidth - wallThickness, 0, wallThickness, display.contentHeight)
    -- Bottom wall
    wall = display.newRect(0, display.contentHeight - wallThickness, display.contentWidth, wallThickness)
end

벽 두께는 10픽셀로 합니다.

그리고 main()함수에서 이 createWall() 함수를 Call 하세요.


이제 사방에 벽이 생겼습니다.
실행하면 공이 벽을 지나쳐서 갑니다.
저 벽에 공이 튀게 만드려면 이 wall들에 addBody를 해 주면 됩니다.

지금까지의 소스는 아래와 같습니다.
require ("physics")

function main()
    setUpPhysics()
    createWalls()
    createBall()
end

function setUpPhysics()
    physics.start()
    physics.setGravity(0,0)
end

function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
end

function createWalls()
    local wallThickness = 10
    -- Left wall
    local wall = display.newRect( 0, 0, wallThickness, display.contentHeight )
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Top wall
    wall = display.newRect(0,0, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Right wall
    wall = display.newRect(display.contentWidth - wallThickness, 0, wallThickness, display.contentHeight)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Bottom wall
    wall = display.newRect(0, display.contentHeight - wallThickness, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    wall.type = "bottomWall"
end

main()

이제 실행하면 공이 계속 벽면에 튀게 됩니다.

여기서 유심히 볼 점은 벽면 모두 변수 이름이 wall입니다. 이 변수를 한번 만들어서 계속 재활용 한 겁니다.
그리고 맨 마지막에 bottom Wall을 만들었고 type을 bottomWall이라고 명명했습니다.
이는 다른 세 벽은 공이 맞고 튀기만 하면 되고 아래 벽엔 맞으면 다른 핸들링을 하기 위해서 이렇게 한 겁니다.

이 부분은 나중에 구현 해 보겠습니다.

이제 아래에 paddle을 한번 만들어 볼까요?
밑에 적당한 위치에 적당한 길이의 사각형을 만들겠습니다.

function createPaddle()
    local paddleWidth = 100
    local paddleHeight = 10
    local paddle = display.newRect( display.contentWidth / 2 - paddleWidth / 2, display.contentHeight - 50, paddleWidth, paddleHeight )
end

보시는 대로 왼쪽 오른쪽 길이(Width)는 100픽셀이고 높이는 10픽셀인 사각형을 그리고 이것을 가운데에 위치시킵니다.
실행 시키면 아직까지 paddle에 physics를 적용하지 않았기 때문에 공은 그냥 통과합니다.
여기에 physics를 아래와 같이 적용하세요.
physics.addBody(paddle, "static", {friction=0, bounce=1})

이러면 공이 지나가다가 이 paddle에 맞으면 튀게 됩니다.

여기에 유저가 paddle을 움직일 수 있도록 하는 기능을 다뤄야 하는데요.
이 기능은 다음 강좌에서 하게 될 겁니다.

이번엔 벽돌들을 만들어 볼까요?
createBricks() 함수를 만드세요.

그리고 사각형을 만들께요. Width는 40 Height는 20으로 하겠습니다.
    local brickWidth = 40
    local brickHeight = 20
local brick = display.newRect( 50, 50, brickWidth, brickHeight )
brick:setFillColor(50, 250, 100, 255)

전체 소스는 아래와 같습니다.
require ("physics")

function main()
    setUpPhysics()
    createWalls()
    createBall()
    createPaddle()
    createBricks()
end

function setUpPhysics()
    physics.start()
    physics.setGravity(0,0)
end

function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
end

function createWalls()
    local wallThickness = 10
    -- Left wall
    local wall = display.newRect( 0, 0, wallThickness, display.contentHeight )
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Top wall
    wall = display.newRect(0,0, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Right wall
    wall = display.newRect(display.contentWidth - wallThickness, 0, wallThickness, display.contentHeight)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Bottom wall
    wall = display.newRect(0, display.contentHeight - wallThickness, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    wall.type = "bottomWall"
end

function createPaddle()
    local paddleWidth = 100
    local paddleHeight = 10
    local paddle = display.newRect( display.contentWidth / 2 - paddleWidth / 2, display.contentHeight - 50, paddleWidth, paddleHeight )
    physics.addBody(paddle, "static", {friction=0, bounce=1})
end

function createBricks()
    local brickWidth = 40
    local brickHeight = 20
    local brick = display.newRect( 50, 50, brickWidth, brickHeight )
    brick:setFillColor(50, 250, 100, 255)
end

main()

이제 벽돌이 하나 생겼습니다.
저 벽돌에 공이 맞았을 때 튀게하려면 지금까지 다른 객체에 했던 식으로 addBody를 하면 됩니다.

그런데 여기서 생각해야 할 점은 벽돌은 1개가 아니라 수십개가 만들어 져야 합니다.
저런식으로 하나하나 각각 위치와 색을 지정해서 벽돌을 만들어도 됩니다.
그런데 그러면 프로그램 답지가 않습니다.

for문을 사용해서 간단하게 벽돌들을 만들겠습니다.

저런 벽돌을 좌우로 6개씩 4줄 총 24개를 만드는 로직을 구현하겠습니다.
아까 벽돌의 두께와 높이는 40,20으로 정했구요.
numOfRows=4, numOfCols = 6 을 추가하겠습니다.

그리고 첫 시작점은 아래와 같이 잡습니다.
local topLeft = {x= display.contentWidth / 2 - (brickWidth * numOfCols ) / 2, y= 50}

이 벽돌은 처음 1줄 6개를 그리고 그 다음 줄 6개 그리는 식으로 4번 작업을 할 겁니다.
그 로직은 아래와 같습니다.
    local row
    local col
    for row = 0, numOfRows - 1 do
        for col = 0, numOfCols - 1 do
            -- Create a brick
            local brick = display.newRect( topLeft.x + (col * brickWidth), topLeft.y + (row * brickHeight), brickWidth, brickHeight )
            brick:setFillColor(math.random(50, 255), math.random(50, 255), math.random(50, 255), 255)
        end
    end


벽돌색은 Random하게 지정했습니다.
여러분 취향대로 라인별로 색을 지정해도 되고 열 별로 지정해도 되고 마음대로 바꾸셔도 됩니다.

여기에 이 벽돌을 맞고도 공이 튀어나오게 하려면 각 벽돌에 addBody를 하면 됩니다.
그리고 type은 destructible로 명명합니다.
나중에 무딪히면 없애야 되거든요.

벽돌에 addBody를 한 오늘의 최종 소스코드는 아래와 같습니다.

require ("physics")

function main()
    setUpPhysics()
    createWalls()
    createBall()
    createPaddle()
    createBricks()
end

function setUpPhysics()
    physics.start()
    physics.setGravity(0,0)
end

function createBall()
    ball = display.newCircle( display.contentWidth / 2, display.contentHeight / 2, 10 )
    physics.addBody(ball, "dynamic", {friction=0, bounce = 1, radius=ballRadius})
    ball:setLinearVelocity(75, 150)
end

function createWalls()
    local wallThickness = 10
    -- Left wall
    local wall = display.newRect( 0, 0, wallThickness, display.contentHeight )
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Top wall
    wall = display.newRect(0,0, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Right wall
    wall = display.newRect(display.contentWidth - wallThickness, 0, wallThickness, display.contentHeight)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    -- Bottom wall
    wall = display.newRect(0, display.contentHeight - wallThickness, display.contentWidth, wallThickness)
    physics.addBody(wall, "static", {friction=0, bounce = 1})
    wall.type = "bottomWall"
end

function createPaddle()
    local paddleWidth = 100
    local paddleHeight = 10
    local paddle = display.newRect( display.contentWidth / 2 - paddleWidth / 2, display.contentHeight - 50, paddleWidth, paddleHeight )
    physics.addBody(paddle, "static", {friction=0, bounce=1})
end

function createBricks()
    local brickWidth = 40
    local brickHeight = 20
    local numOfRows = 4
    local numOfCols = 6
    local topLeft = {x= display.contentWidth / 2 - (brickWidth * numOfCols ) / 2, y= 50}
    local row
    local col
    for row = 0, numOfRows - 1 do
        for col = 0, numOfCols - 1 do
            -- Create a brick
            local brick = display.newRect( topLeft.x + (col * brickWidth), topLeft.y + (row * brickHeight), brickWidth, brickHeight )
            brick:setFillColor(math.random(50, 255), math.random(50, 255), math.random(50, 255), 255)
            brick.type = "destructible"
            physics.addBody(brick, "static", {friction=0, bounce = 1})
        end
    end
end

main()

이제 공이 벽돌에 부딪히면 벽돌이 없어지고 아래 벽에 부딪히면 게임이 새로 시작하고 paddle을 유저가 좌우로 움직이도록 하는 작업이 남았습니다.

내일은 이 작업에 대해 알아보겠습니다.

반응형