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

최근에 받은 트랙백

글 보관함


오늘은 config.lua에서 세팅하는 imageSuffix에 대해 알아보겠습니다.

스마트폰은 종류가 아주 다양하고 해상도도 여러가지 입니다. 여기에 iPad나 Galaxy Tab 과 같은 태블릿도 종류가 많아서 어느 한 해상도에 맞추다보면 다른 디바이스에서는 이미지가 깔끔하게 안 나오는데요.

해상도에 맞는 이미지가 출력되도록 하는 방법중의 하나인 imageSuffix 사용하는 방법에 대해 알아보겠습니다.

이 글의 원본은 여기 가시면 보실 수 있습니다.
지난번 물방울 올라가는 효과도 이 친구가 개발해서 공유한 소스입니다.
Emanuele Feronato 인데요.
이 웹사이트에 가면 어도비(Adobe)의 플래시(Flash)나 포토샵 (PhotoShop), 워드프레스, PHP 같은 툴에 대한 좋은 정보도 있으니까 참조하시면 좋을 거예요.
그리고 고맙게 자신의 노력을 공유하는 우리 Emanuele에게 고맙다는 댓글도 달아 주시면 좋겠죠?


이번 앱은 위 두 이미지 가지고 작업을 할 건데요.
왼쪽 이미지는 iPhone에 맞는 480*320 크기이구요 오른쪽 이미지는 iPhone4에 맞는 960*640 입니다. 해상도 크기가 가로 세로 딱 두배 입니다.

여러분은 여러분들 나름대로 이미지를 사용하셔도 됩니다. 이미지 사이즈만 두개로 맞게 해 놓으면 되겠죠?
이 예제에서 왼쪽 이미지는 splash.png 입니다. 그리고 오른쪽 이미지는 splash_hd.png 입니다.
여기서 중요한 것은 큰 이미지는 뒤에 _hd가 붙었다는 겁니다.
여러분도 이미지 이름은 마음대로 하셔도 되지만 뒤에 _hd를 붙이셔야 합니다.
(아니면 다른 글자를 붙이셔야 하는데 나중에 config.lua 파일에서 이 부분을 수정하셔야 합니다.)

이제 build.settings, config.lua, main.lua를 만들어 볼까요?

settings =
{
    orientation =
    {
        default = "landscapeRight",
    },
}
위 파일은 build.settings 파일입니다.
앱 방향을 가로로 설정했네요.

다음은 config.lua인데요. 이 부분이 핵심입니다.
application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = "letterbox",
        imageSuffix =
        {
            ["_hd"] = 2,
        },
    },
}

해상도의 기본 설정은 iPhone  해상도인 320*480으로 설정했습니다.
그리고 scale은 letterbox로 했구요.

예전에도 다룬바 있는데 scale은 dynamic 하게 이미지를 해상도에 맞도록 바꿔주는 기능입니다.
none,letterbox,zoomEven, zoomStretch 네가지 종류가 있습니다.

letterbox는 이미지를 해상도에 맞게 늘려줍니다. 이미지 가로 세로 비율이 깨지지 않도록요. 만약 가로 세로 해상도 비율이 다르면 까만 배경이 나오는 부분이 있습니다. TV에서 영화를 영화 화면 비율로 상영할 때 처럼 말이죠.

zoomEven 은 이미지를 그냥 확대합니다. 때에 따라서 어떤 이미지는 화면 밖으로 나갈 수도 있습니다.

zoomStretch는 화면안에 모든 이미지가 들어오게 만들면서 화면을 꽉 채웁니다. 이렇게 되면 이미지의 가로 세로 비율이 달라져서 약간 이그러져 보이겠죠?

이 scale을 한 후 오늘 하려고 하는 imageSuffix를 코딩합니다.
위 코드의 의미는 해상도가 2배이면 이미지 이름 끝에 _hd가 붙은 이미지를 사용하라는 뜻입니다.

아래는 main.lua 입니다.

display.setStatusBar(display.HiddenStatusBar)
local bg = display.newImageRect("splash.png",480,320)
bg.x = bg.contentWidth/2
bg.y = bg.contentHeight/2

첫줄은 아이폰의 status bar를 없애고 배경이미지를 display 하고 위치를 가운데로 잡습니다.

main.lua 에서는 따로 코딩할 부분은 없습니다.

그러면 아래와 같이 iPhone과 iPhone 4에서 각각 다른 이미지를 display 하게 됩니다.


imageSuffix 에 대해 좀 더 자세히 알고 싶으시면 여기를 누르세요.
~~~~~~~~~~
반응형

Comment


얼마전에 눈발 흩날리는 효과를 주는 앱에 대해 다뤄봤습니다.
꽤 그럴듯 했죠?

오늘은 보니까 물 속에서 물방울 올라가는 효과 비슷한 코드가 소개됐더라구요.
이것도 꽤 그럴듯하고 나중에 앱 만들 때 유용하게 쓰일 수 있을 것 같습니다.

코드부터 보자면요. config.lua는 아래와 같이 만드세요.
application =
{
	content =
	{
		width = 320,
		height = 480,
		scale = "letterbox"
	},
}
해상도는 320*480 에 맞췄구요. scale은 letterbox로 했습니다.

build.settings는 아래와 같이 작성하세요.
settings = { orientation = { default = "landscapeRight", }, }

이러면 앱이 눕혀서 실행 되겠죠?

다음은 main.lua입니다.

display.setStatusBar(display.HiddenStatusBar) local background = display.newImage("background.png") local mobs = display.newGroup() math.randomseed(os.time()) for i = 1, 10 do local greenCircle = display.newImage("greencircle.png") greenCircle.x = math.random(0,479) greenCircle.y = math.random(0,319) local randomDirection = math.rad(math.random(0,359)) greenCircle.xSpeed = 2*math.cos(randomDirection) greenCircle.ySpeed = 2*math.sin(randomDirection) mobs.insert(mobs,greenCircle) end   local function update(e) for i = 1, mobs.numChildren do mobs[i].x = mobs[i].x + mobs[i].xSpeed mobs[i].y = mobs[i].y + mobs[i].ySpeed if(mobs[i].x<0) then mobs[i].x = mobs[i].x + 480 end if(mobs[i].x>480) then mobs[i].x = mobs[i].x - 480 end if(mobs[i].y<0) then mobs[i].y = mobs[i].y + 320 end if(mobs[i].y>320) then mobs[i].y = mobs[i].y - 320 end end end   Runtime:addEventListener("enterFrame",update)

것도 눈발 흩날리는 느낌을 주는 앱처럼 enterFrame을 사용하고 있습니다.
지난 글에서 다뤘던 것처럼 이 enterFrame을 사용할 때는 메모리 관리를
확실하게 해 줘야 합니다.

위 소스를 보면요.
처음엔 아이폰의 status bar를 없애는 거구요.
다음에 배경 이미지를 넣었고 mobs라는 group을 만들었습니다.
그리고 random값을 이용할 건데 그 seed를 os.time으로 설정했습니다.

다음에는 for 문이 있는데요.
물방울 모양이 될 이미지를 하나 display 합니다.
그리고 이 이미지를 특정한 위치에 놓습니다.
이 때 random 하게 x,y좌표를 만들어서 화면 여러곳에 퍼지게 만듭니다.

다음으로는 이 원이 갈 방향을 random 하게 만듭니다. 이것은 각도를 radian
값으로 바꿔서 값을 갖고 있게 됩니다.

이 각도와 라디안 그리고 sin,cos 계산공식은 게임을 만들 때 아주 자주
사용하게 됩니다.
저도 Spin the bottle 등을 만들 때 리얼한 느낌을 주기 위해서 이런 공식을
사용 했었는데요.
tool이 아니라 game 앱을 만들려면 이런 기초 수학을 알아 두시면 아주
좋습니다.

다음 10~11번째 줄은 삼각함수를 이용해서 x,y 좌표로 이 원이 움직이는 범위(속도)를 계산해 넣습니다.
이 값은 update 문이 계속 반복 되면서 계산해야 하기 때문에 원의 프로퍼티 값으로 가지고 있어야 합니다.
그래야 일정한 방향으로 원을 움직일 수 있게 됩니다.

그 다음은 아까 생성한 mobs라는 group에 원을 insert 합니다.

그 다음 update 함수인데요. 먼저 34번째 줄 리스너를 볼까요?
enterFrame을 사용해서 각 프레임마다 update 함수를 실행 하도록 합니다.
그럼 update 문을 보시면요.
mobs에 있는 children들을 scan 하고요 이 children의 갯수만큼 for 문을
돌립니다.
그러니까 각 원마다 어떤 행동을 하도록 컨트롤 할 수 있게 되는거죠.

거기서 하는 일은 아까 처음 for문에서 정했던 x,y의 스피드를 기반으로 원의
위치를 바꾸게 됍니다.
여기까지 하면 원이 자연스럽게 움직여서 물방울 효과를 주게 됩니다.

그런데 여기까지만 하면 원이 화면 밖으로 나가면 없어져 버리게 되서요.
다음 if 문들은 원이 화면 밖으로 나가게 되면 다시 화면 안으로 들어오도록
해 줍니다.

이 정도만 해도 아주 그럴싸한 물방울 효과가 나옵니다.
진짜 할리우드 영화처럼 real한 느낌을 주시려면 원의 좌표를 바꾸는 부분을
좀 더 정교하게 control 하면 되겠죠?

아래 유튜브 영상이 이 앱을 실행한 화면입니다.



좋은 효과 잘 활용하시구요.. 추천 추천 부탁드려요... ~~~~~~~
반응형

Comment


안녕하세요?

이번주에는 코로나에서 enterFrame에 대한 블로그 강좌를 했네요.
이 강좌 내용을 아래에 정리했습니다.

원본은 여기를 클릭하시면 보실 수 있습니다.

Understanding Corona’s enterFrame Event

오늘은 코로나의 enterFrame Event에 대해 알아보겠습니다.
Event 전반에 대해 알고 싶으시면 여기를 클릭하세요.

****** What is an enterFrame event?

앱이 시작하면서 여러분의 앱은 아주 빠른 속도로 화면을 새로 갱신하면서 보여주게 됩니다.
이 속도를 나타내는 단위는 frames-per-second (FPS)입니다. 코로나에서는 두가지 세팅을 지원하는데요. 30과 60 FPS입니다.
관련된 정보는 여기에 있습니다.

이 속도를 다른 말로는 framerate이라고도 합니다.
여러분이 config.lua파일에 60FPS로 설정을 했으면 이것은 1초에 스크린을 60번 다시 그린다는 겁니다.

enterFrame아라는 말은 새로운 프레임에 entering하는 순간을 말합니다. 즉 framerate이 30이라면 이 enterFrame이벤트는 1초에 30번 일어나게 됩니다.
그러니까 이 enterFrame 이벤트를 사용하면 여러분은 매 프레임마다 어떤 컨트롤을 할 수 있게 되는 겁니다.

****** enterFrame Listeners
이 enterFrame이벤트와 다른 많은 코로나 이벤트와 다른점은 리스너를 Runtime으로만 설정할 수 있다는 겁니다.
각 object마다 이 enterFrame을 따로 적용할 수는 없습니다.

아래 이 enterFrame 사용 예제를 보세요.

-- listener function
local function onEveryFrame( event )
    print( "This function will be called on every frame." )
end

-- assign the above function as an "enterFrame" listener
Runtime:addEventListener( "enterFrame", onEveryFrame )

우선 함수를 만들고 그 다음에 Runtime에 enterFrame 이벤트를 add합니다.
이 예제는 아주 기본적인 거구요. 다음 예제에서 조금 응용한 걸 보여드리겠습니다.

***** Translating objects
아래 예제는 각 프레임마다 객체가 천천히 아래로 내려갔다가 한번에 다시 올라가고 또 내려오는 동작을 반복하는 움직임을 보일 겁니다.

local ball = display.newImage( "redball.png" )
ball.x = math.random( display.contentWidth )
ball.y = -ball.contentHeight

-- listener function for enterFrame event
local function onEveryFrame( event )
    ball:translate( 0, 1 ) -- move ball 1pt down on every frame

    -- move ball above top of screen when it goes below the screen
    if ball.y > display.contentHeight then
        ball.y = -ball.contentHeight
    end
end

-- assign the above function as an "enterFrame" listener
Runtime:addEventListener( "enterFrame", onEveryFrame )

***** Tracking Time

enterFrame 리스너의 이벤트 테이블 안에는 여러분의 앱이 시작한 이후부터 지금까지의 시간에 대한 정보가 들어있습니다.
event.time이 시작한 시간인데요. system.getTimer()메소드를 통해서 값을 얻을 수 있습니다.
아래 예제는 유저가 버튼을 누르고 3초 이상 지났을 때를 체크하는 코드 입니다.
local timeThresh = 3

local button = display.newImage( "button.png" )
button.markTime = system.getTimer()

-- enterFrame listener function
local function trackTime( event )
    local secondsPassed = (event.time - button.markTime) / 1000 -- divide by 1k to get seconds
   
    if secondsPassed >= timeThresh then
        print( "You held down the button for at least " .. timeThresh .. " seconds." )
       
        -- stop tracking time
        Runtime:removeEventListener( "enterFrame", trackTime )
    end
end

-- touch event for the button object
function button:touch( event )
    if event.phase == "began" then
        print("Touched");
       
        -- assign touch focus to this object
        display.getCurrentStage():setFocus( self )
        self.isFocus = true
       
        -- mark time and start tracking time with enterFrame listener
        self.markTime = system.getTimer()
        Runtime:addEventListener( "enterFrame", trackTime )
   
    elseif self.isFocus then
        if event.phase == "ended" or event.phase == "cancelled" then
           
            -- stop tracking time
            Runtime:removeEventListener( "enterFrame", trackTime )
           
            -- remove touch focus from button
            display.getCurrentStage():setFocus( nil )
            self.isFocus = false
        end
    end
   
    return true
end

button:addEventListener( "touch", button )

이 코드 아주 유용하게 쓰여질 수도 있겠네요.

****** Special Considerations

이 enterFrame은 각 프레임별로 체크를 하기 때문에 아주 강력합니다. 대신에 메모리를 많이 소비하므로 꼭 필요할 때만
사용하고 그렇지 않을 때는 없애 줘야 합니다.
한 scene에서 enterFrame을 사용했는데 다른 scene으로 넘어갈 때 이 enterFrame을 제거하지 않는다면 유저가 다시 첫번째
scene으로 돌아 왔을 때는 각 프레임마다 이 enterFrame이 두번 실행 될 겁니다.
다시 다른데 갔다가 돌아오면 세번 실행되겠죠? 각 프레임마다.... 그럼 performance가 아주 형편 없어질 겁니다.

이 enterFrame사용시 절대 잊어서는 안되는 것이 이런 상황을 만들지 않는 겁니다.


~~~~~ ~~~~~
반응형

Comment


아래 싸이트에 Corona SDK로 cross platform 앱 개발시 지켜야할 10계명이 올라왔더라구요.

http://fullycroisened.com/10-strategic-tips-for-cross-platform-development-using-corona-sdk/

읽어보니까 많이 공감이 되네요.

그 10가지를 아래 소개합니다.



Corona SDK로 cross platform 앱 개발시 주의해야 할 10가지

1. 실제 기기에서 초기부터 그리고 자주 테스트 하라. 에뮬레이터를 너무 믿지 마라. 실제 기기에서 테스트 하라.
2. 시작하기 전에 해상도에 맞는 이미지 사이즈들을 어떻게 다이나믹하게 다룰지를 먼저 계획하라.
   (각 해상도에 맞춘 이미지들을 미리 계획한다면 많은 시간을 절약할 수 있다.)
3. build.settings 파일에 어떤 퍼미션들을 넣어야 하는지 알고 있어야 한다.
예)
    androidPermissions =
    {
      "android.permission.INTERNET",
      "android.permission.VIBRATE",
      "android.permission.READ_PHONE_STATE",
      "android.permission.CALL_PHONE",
     },
4. 좀 더 테스트 하라.
5. 각 플랫폼에 대한 API들의 제한 사항들을 알고 있어라.
   어떤 기능들은 플랫폼에 따라 기능이 제한 돼 있다.
6. 코딩을 하기 전에 모든것을 Sketch 하라.
   (이렇게 하면 분명 많은 시간을 절약 할 수 있다.)
7. 메모리 사용량과 texture map size 를 항상 체크하라.  
   이렇게 함으로서 쓸데없이 메모리를 차지하는 경우 (이를 일찍 발견하게 돼) 메모리 컨트롤을 더 쉽게 할 수 있다.
8. 지금 하고 있는 것을 다른 곳에서는 어떻게 처리 했는지 research를 해 봐라. 그리고 나서 진행하면 도움이 된다.
   그리고 이것을 공유하라 그리고 당신이 어떻게 좀 더 낫게 구현했는지 살펴 봐라.
9. 커뮤니티를 활용하라. 거기에는 도움을 줄 아주 많은 소스들이 있다.
10. 즐겨라. 코딩이 제일 잘 되는 시간은 밤 11:34부터 새벽 3:07분까지이다.

==> 대부분 공감을 하는데 10번은 공감을 못하겠네요.
일은 일하는 시간에 해야지... 그래야 능률도 더 잘 오르고......
이러면 완전 폐인 되잖아....
초창기에는 나도 밤 새 코딩을 하기도 했는데...
이제는 근무시간에만 딱 하는게 제일 좋더라구요.
밤에 할거면 차라리 일찍 자고 새벽에 일어나서 하던지.....
그리고 6번은 좀 더 강조 하고 싶어요. 단순히 스케치를 하라가 아니고 제대로 된  스토리 보드 만들고 다이어 그램 만들고 클래스/객체 설계도 만들고 진행하라고요. 제대로 기획을 해야 코딩도 제대로 하죠. 앱 만들 때 중요도 기획 80% 코딩 20%.  (내 의견)


  가시기 전 여기 손가락 꾹 ~~~~ ^^        추천 추천...
반응형

Comment

  1. the_evo 2012.02.09 02:37

    언제나 좋은 정보 감사합니다~~~이제 정말 자주 들어오게 되네요^^ 미리 스케치는 코로나 뿐 아니라 어디서든 중요한 사안인 것 같아요.

    프로그래머만이 아닌 기획자들.
    정확하고 명확한 스토리보드를 짜주는 것이 중요하다 생각합니다.
    우리나라는 안 그런 기획자들이 많죠.

    • 솔웅 2012.02.09 04:30 신고

      아주 정말 진짜로 공감합니다.
      기획이 뭔지도 모르면서 기획을 하면 프로그래머가 정말 고달픕니다. 기획을 잘 모르면 열심히 공부하던가 해야 되는데...
      자주 들러 주셔서 감사합니다.

  2. 아통 2012.02.09 17:29

    3번..작년 초까지만 해도 안드로이드의 진동 퍼미션은 기본 옵션이라 안쓰고 썼었는데 바뀐지 모르고 썼다가 어디서 죽는건지 모르고 한참 헤맸던 기억이 있네여 ;

    • 솔웅 2012.02.12 05:48 신고

      예 아주 동감합니다. 특히 진동같은 경우에는 시뮬레이터에서는 테스트할 수 없고 디바이스에서만 테스트가 가능한데 그러면 Log를 볼수가 없어서 원인을 알아내기 힘들죠...
      어쨌던 해결하고 나면 기분 좋구요..
      항상 즐코딩 되세요.

      감사합니다.

iOS - armv6 (-19033) error

2012. 1. 24. 07:56 | Posted by 솔웅


최신 버전으로 Corona SDK를 업데이트 하고 새 앱인 Fireman을 아이폰용으로 빌드하려고 했더니 아래 메세지가 나오더라구요.

Your application built but failed to pass Apple's validation tests.
Your application cannot be uploaded to the App Store until it passes Apple's validation tests, though you may install it directly to provisioned devices.

warning: iPhone/iPod Touch: application executable is missing a required architecture.  At least one of the following architecture(s) must be present: armv6 (-19033)


이 에러는 찾아 봤더니 Corona SDK 710 버전 이후 부터는 Xcode 버전 4.2 이상이 되어야 한 답니다.

제 Corona SDK 버전은 726 이고 Xcode 버전은 3,2.5 라서 안 됐나 봅니다.

이와 관련된 질문과 대답은 아래 코로나 SDK 포럼에 있습니다.
http://developer.anscamobile.com/forum/2011/12/20/warning-armv6-19033

그리고 Xcode 다운로드는 아래 사이트에서 하시면 됩니다.
http://developer.apple.com/xcode/



반응형

Comment

앱 화면 Screen Capture하기

2011. 12. 28. 11:26 | Posted by 솔웅


오랜만에 Corona SDK Tip을 공부합니다.

예전 텍스트, 이미지, 모양 표시하기... 2 에서 이런게 있다는 설명만 하고 넘어갔는데요.
오늘 예제와 함께 자세히 다뤄 볼께요.

display.setStatusBar( display.HiddenStatusBar )
 
-- Fill the screen with a green rectangle
local rect = display.newRect(0, 0, display.contentWidth, display.contentHeight)
rect:setFillColor(0, 255, 0)
 
-- Draw a circle on the screen
local circle = display.newCircle(155, 100, 36)
circle:setFillColor(255, 0, 0)
 
-- Capture the screen
local screenCap = display.captureScreen( true )
 
-- Remove the objects from the screen
rect:removeSelf()
circle:removeSelf()
 
-- Scale the screen capture, now on the screen, to half it's size
screenCap:scale(.5,.5)
 
-- Alert the user to look in the library (device)
-- or on the desktop (simulator) for the screen capture
local alert = native.showAlert( "Success", "Screen Capture Saved to Library", { "OK" } )
이 앱을 실행하면 위 화면이 실행되고 아래 화면이 캡쳐 됩니다.

왜 그런지 소스를 보실까요?
첫째줄은 아이폰에서 status 바를 없앱니다.
그 다음엔 커다란 녹색 사각형을 그리고 그 다음에 빨간 원을 그립니다.
녹색 사각형은 화면에 꽉 차게 그려집니다.
그 다음 display.captureScreen(true)를 해서 스크린을 캡쳐합니다.
(true)를 했으니까 이 파일은 디바이스 안에 이미지 파일로 저장됩니다.
그리고 사각형과 원을 screen에서 없앱니다.
그리고 아까 캡쳐한 것을 반 크기로 줄여서 화면에 표시합니다.
맨 마지막엔 제대로 스크린 캡쳐가 됐다는 Alert 메세지를 뿌립니다.

즉 이 앱은 꽉찬 녹색 사각형과 큰 원을 캡쳐한 후 두 사각형과 원을 지우고 절반크기로 다시 화면에 보여주는 앱입니다.

여기서 사각형과 원을 removeSelf()한 부분을 주석처리한 다음에 실행해 보세요.

그럼 이렇게 나올 겁니다.
보시다시피 캡쳐 전 도형들이 사라지지 않고 나와서 전체가 녹색 사각형이고 빨간 원도 좀 큽니다. 그리고 그 위에 캡쳐한 이미지를 절반크기로 올려 놓은 화면입니다.

간단하지만 때에 따라서는 아주 유용하겠죠?
display.captureScreen( false ) 하면 디바이스에는 저장되지 않습니다.
이것을 true로 할 경우
아이폰 같은 경우는 Photo Albums에 자동으로 저장이 되구요.
안드로이드는 build.settings에 아래와 같이 퍼미션을 주어야 합니다.
settings =
{
   androidPermissions =
   {
       "android.permission.WRITE_EXTERNAL_STORAGE"
   },
}
그리고 시뮬레이터는 컴퓨터 바탕화면에 캡쳐된 화면을 저장합니다.
(윈도우에서는 MyPictures\Corona Simulator" 디렉토리에 저장이 될 겁니다.)
저장된 파일은 png형식이고 이름은 Picture#.png이렇게 나갑니다.
10000개까지 가능합니다.


이 화면처럼 버튼을 누르면 화면을 캡쳐하도록 만들어 볼까요?
display.setStatusBar(display.HiddenStatusBar)
local background = display.newImage('background.png')
local captureButton = display.newImage('captureButton.png')

captureButton:setReferencePoint(display.CenterReferencePoint)
captureButton.x = display.contentWidth * 0.5
captureButton.y = display.contentHeight * 0.5

function captureButton:tap(e)
    local screenshot = display.captureScreen(true)
    -- Show Image in Photo Library
    media.show(media.PhotoLibrary)
end
captureButton:addEventListener("tap", captureButton)

이 소스처럼 button이미지에 리스너를 달아서 tap할 경우 display.captureScreen(true)를 하도록 하면 됩니다.
간단하죠?

샘플 파일은 아래에 있습니다.





반응형

Comment


오늘은 외부 모듈을 불러와 클래스를 실행하는 기본 개념에 대해 알아보겠습니다.
얼마전 모듈에 대해 한번 다룬적이 있었는데요.
이 모듈과 클래스 개념은 아주 중요한 거라서 처음 프로그래밍을 접하시는 분들이나 아직 이 부분에 대해 개념이 모호한 분들은 보실만 할 겁니다.

모든일이 그렇듯 반복과 숙달이 필요한 부분인 것 같습니다.

What is a Class?
이 글을 마치면 클래스가 무엇인지 감이 잡히실 겁니다. 여기서 알아야 될 것은 그 클래스들을 이용하는데 어떤 잇점이 있으며 어떻게 이용하는가 입니다. 실제로 편리하게 이용하기 위해서 만들어 진 것이니 무엇이 클래스인가 아는것도 다 사용하려고 하는 것 아니겠습니까?

그러니 일단 시작해 보겠습니다.

객체지향 프로그래밍에서 객체(Object)는 그냥 우리가 일상생활에서 보는 사물이나 동물, 사람 등입니다. (때로는 개념이 될 수도 있겠죠)
그리고 그 객체들은 일정한 행동 양식이 있습니다.
만약에 개라는 객체를 여러분 앱에서 표현하고자 한다면...
개는 뛰고, 짖고, 부비고, 자고, 돌고 그러죠?
만약 고전 오락 갤러그에서 외계인이라는 객체를 보면
옆으로 움직이고, 아래로 내려오고, 미사일을 쏘고, 아군 미사일에 맞으면 폭발하고 그러죠?

이렇게 객체들의 행위를 구현하는 부분을 메소드 또는 함수 라고 합니다.
(자바에서는 메소드라고 하고 루아-코로나-에서는 함수라고 합니다.)

그리고 이 객체와 메소드가 있는 곳이 클래스 입니다.

직접 보시죠.

오늘은 우리랑 친근한 개로 클래스를 만들겠습니다. (개 클래스가 되겠네요.)

-------------------------------------------------
-- dog.lua
-- Example "dog" class for Corona SDK tutorial.
-------------------------------------------------
local dog = {}
local dog_mt = { __index = dog } -- metatable
-------------------------------------------------
-- PRIVATE FUNCTIONS
-------------------------------------------------
local function getDogYears( realYears ) -- local; only visible in this module
return realYears * 7
end
-------------------------------------------------
-- PUBLIC FUNCTIONS
-------------------------------------------------
function dog.new( name, ageInYears ) -- constructor

local newDog = {
name = name or "Unnamed",
age = ageInYears or 2
}

return setmetatable( newDog, dog_mt )
end
-------------------------------------------------
function dog:rollOver()
print( self.name .. " rolled over." )
end
-------------------------------------------------
function dog:sit()
print( self.name .. " sits down in place." )
end
-------------------------------------------------
function dog:bark()
print( self.name .. " says \"woof!\"" )
end
-------------------------------------------------
function dog:printAge()
print( self.name .. " is " .. getDogYears( self.age ) .. " in dog years." )
end
-------------------------------------------------

return dog

처음에 dog이라는 변수가 테이블로 만들어 졌습니다.
테이블로 만든 이유는 개는 한마리가 아니라 여러마리가 있을 수 있잖아요?
도꾸, 메리,  쫑 이렇게...
그렇게 여러마리를 표현하기 위해서 테이블로 만들었습니다.
그리고 메타테이블로 dog_mt 변수를 만들었습니다.

그 다음엔 getDogYears라는 로컬 함수(메소드) 가 있고 realYear 라는 인수를 받습니다.
아까 함수는 어떤 동작을 표현한다고 그랬죠?
이 함수는 인수로 받은 값에 7을 곱하는 동작을 하네요.
개가 직접 하는 동작이 아니라 어떤 로직과 관련된 동작이군요.
이렇게 개념이나 로직과 관련된 행위도 메소드(함수)로 표현됩니다.
로컬로 선언한것은 자바에서 private로 선언한 것과 비슷한데요.
의미는 이 dog.lua라는 클래스 안에서만 이 함수를 call할 수 있다는 겁니다.
외부파일에서는 이 함수를 콜할수 없습니다.

다음은 글로벌 함수인 dog.new 함수가 있습니다. 이 함수는 name과 ageInYears를 인수로 받습니다.
하는 일은 이름과 나이를 세팅하고 이를 메타테이블에 세팅한 값을 반환해 줍니다.
글로벌 함수는 자바에서의 public 과 같은데요. 이건 외부에서도 이 함수를 콜 할 수 있다는 의미입니다.
dog.new함수는 자바에서의 생성자 역할을 하네요. dog란 객체를 생성하는 겁니다.
외부에서 개를 만들 때 (그게 도꾸든 메리든 쫑이든....) 이 함수를 제일 먼저 불러서 객체를 만들어야 한다는 얘기입니다.

다음은 모두 글로벌 함수네요. dog:rollOver,dog:sit,dog:back, dog:printAge 이렇게 네개의 함수가 있습니다.
구르고 앉고 짖는 등 개가 하는 행위를 구현하는 3개의 메소드와 나이를 표시하는 함수 이렇게 4개의 함수(메소드)가 있습니다.

여기서는 터미널에 개가 구른다, 앉는다, 짖는다 같이 문자로 뿌려지는데요.
나중에 게임같은걸 개발 하게 되면 이 부분에 개 이미지를 넣고 애니메이션 기능을 이용해서 구르거나 앉거나 짖는 것을 구현하면 됩니다.

그럼 이 dog 클래스를 어떻게 사용해야 할까요?

main.lua를 보겠습니다.

local dog = require( "dog" )
local dog1 = dog.new( "Yai-Ya", 4 )
local dog2 = dog.new( "Toki", 1 )
    dog1:printAge()
    dog2:printAge()
   
    dog1:rollOver()
    dog2:sit()

보시면 처음에 dog.lua를 require합니다. 이때 .lua는 쓰지않고 파일 이름만 씁니다.

그리고나서 dog1,dog2 이렇게 두마리 강아지를 만듭니다. (두개의 객체를 만들었습니다.)
dog.new로 만들었는데요. 첫번째 개는 이름이 Yai-Ya 고 나이는 4살이네요.
두번째 개는 이름이 Toki이고 나이는 1살이구요.

다음으로 두 개의 나이를 표시해 줍니다.
그리고 첫번째 개 Yai-Ya에게는 구르라고 하고 두번째 개 Toki에게는 앉으라고 합니다.

이것을 실행하면 아래와 같이 됩니다.


보시면
Yai-Ya 28살이고 Toki는 7살이라고 처음에 나오네요.
4살, 1살이라 그랬는데 28살 7살이라고 나온 이유는 dog class에서 printAge()를 보면 나이를 받아서 곱하기 7을 하는 getDogYears( self.age ) 함수를 콜하고 있죠?
그래서 개 나이에 7을 곱해서 나온 겁니다.
아마 개 나이 곱하기 7하면 얼추 사람 나이가 되나 봅니다.

그리고 다음엔 Yai-Ya는 구르고 Toki는 그 자리에 앉는다고 돼 있죠?

모듈과 클래스는 이렇게 이용하시는 겁니다.

처음에 개를 만들고 (이름, 나이) 그리고 그 개에게 짖고,앉고, 구르고 하는 것을 시키는 겁니다.

처음에 메타 테이블을 만들고 생성자 dog.new()에서 이를 return(반환) 했죠? 이것은 새로운 인스턴스를 만드는데 아주 효과적이기 때문입니다. 새로운 인스턴스라 함은 도꾸,메리,쫑 이 각각의 개들의 존재를 구분할 수 있게 해 준다는 겁니다. 이 메타테이블을 이용하지 않으면 개를 한마리 밖에 만들지 못할겁니다. 그것이 도꾸든 메리든 쫑이든....

이제 어느정도 클래스에 대해 감이 잡히셨나요?
처음에 얘기 드렸지만 반복과 숙달이 필요한 부분입니다.

이 글 원본은 아래에 있습니다.
원본을 바탕으로 설명은 그냥 제 방식으로 했습니다.
http://blog.anscamobile.com/2011/09/tutorial-modular-classes-in-corona/

혹시 이 코드가 실행되지 않으시는 분은 댓글 달아 주세요.
정식 등록자가 받을 수 있는 최신 업데이트 된 버전에서만 될 수도 있거든요.
그러니 혹시 실행이 안 되시면 저한테 알려 주세요.



그럼...
반응형

Comment


모바일 애플리케이션으로 E-Book을 구현하거나 하다 보면 책장 넘기는 효과를 주면 훨씬 더 실감나는 E-Book 앱을 만들 수 있습니다.

오늘은 이 책장 넘기는 효과 주는 방법에 대해 공부하겠습니다.
오늘 소개하는 코드는 아주 기본적인 기능만 있습니다.

이 코드에다 여러분이 여러 효과를 주시면 더욱 더 그럴싸한 e-Book을 만들 수 있을 겁니다.

페이지 넘김 효과를 주기 위해서는 두가지 방법으로 이 효과를 구현할 수 있습니다.
첫번째는 frame-based animation을 구현하는 겁니다.. 그러기 위해서 스크린 만한 이미지들이 필요합니다.  즉 아이폰, 아이패드에 맞는 이미지들이 따로따로 있어야 합니다. 그것도 폰을 세웠을 때와 눕혔을 때 모두 다 따로따로 있어야 합니다.

두번째는 optical illusion을 통해 하나의 이미지, bitmap mask 그리고 transitions를 통해 구현하는 방법입니다.

두번째 방법이 훨씬 좋겠죠? 오늘 제공될 소스코드에는 이 두번째 방법을 소개해 드릴겁니다.



Step 1. Preliminary Work

페이지 넘김 효과를 사용하기 위해서는 몇개의 이미지들이 필요합니다.

이 두 이미지는 각각 page1.jpg,page2.jpa입니다.
이것 말고 책장 넘기는 효과를 줄 이미지들도 필요합니다.


이제 이미지가 마련 됐으면 코딩을 해 보겠습니다.

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

local page1 = display.newImageRect( "page1.jpg", display.contentWidth, display.contentHeight )
page1:setReferencePoint( display.TopleftReferencePoint )
page1.x, page1.y = display.contentWidth*0.5, display.contentHeight*0.5

local page2 = display.newImageRect( "page2.jpg", display.contentWidth, display.contentHeight )
page2.x, page2.y = display.contentWidth*0.5, display.contentHeight*0.5
page2:toBack() -- make sure 2nd page is underneath the first one

local curlPage = display.newImageRect( "curlPage.png", display.contentWidth, display.contentHeight )
curlPage.x, curlPage.y = display.contentWidth*0.5, display.contentHeight*0.5
curlPage.isVisible = false

-- group to hold the page that will be turned (as well as the "curl" page)
local turnGroup = display.newGroup()

이 4개의 이미지를 불러오는 코드 입니다.
두번째 페이지는 첫번째 페이지 밑으로 가게 했습니다. 그리고 curlPage는 inVisible을 false로 했구요.
이 코드만 실행하면 책 겉장만 보일겁니다.

Step 2. Page Curl Functions

다음은 페이지 넘김 효과를 줄 함수들입니다.

-- The following function will turn the page "back"
local function gotoPrevious( curlPage, time )
local time = time or 500

curlPage.isVisible = true
local hideCurl = function()
curlPage.isVisible = false
turnGroup:setMask( nil )
end
transition.to( turnGroup, {maskX=display.contentWidth*0.5+100, time=time } )
transition.to( curlPage, { rotation=45, x=display.contentWidth+(display.contentWidth*0.10), y=display.contentHeight + (display.contentHeight*0.25), time=time, onComplete=hideCurl })
end

-- The following function will turn the page "forward"
local function gotoNext( currentPage, curlPage, time )
-- add "pages" to page turning group
turnGroup:insert( currentPage )
turnGroup:insert( curlPage )

-- mask should match dimensions of content (e.g. content width/height)
local curlMask = graphics.newMask( "mask_320x480.png" )
turnGroup:setMask( curlMask )

-- set initial mask position
turnGroup.maskX = display.contentWidth * 0.5+100
turnGroup.maskY = display.contentHeight * 0.5

-- prepare the page-to-be-turned and the curl image
currentPage:setReferencePoint( display.BottomLeftReferencePoint )
curlPage:setReferencePoint( display.BottomRightReferencePoint )
curlPage.rotation = 45
curlPage.x = display.contentWidth+(display.contentWidth*0.10)
curlPage.y = display.contentHeight + (display.contentHeight*0.25)
curlPage.isVisible = true

-- show pagecurl animation and transition away (next page should already be in position)
local time = time or 500
transition.to( turnGroup, { maskX=-display.contentWidth*0.75, time=time } )
transition.to( curlPage, { rotation=0, x=0, y=display.contentHeight+20, time=time} )
curlPage.yScale = curlPage.y * 0.2
end

소스코드를 좀 살펴 볼까요?

처음에 나오는 함수는 gotoPrevious 함수 입니다.
인수로 curlPage와 time을 받습니다.
그리고 로컬 변수 time에 값을 넣고 curlPage를 보이도록 바꿉니다.
이 함수의 로컬 함수 hideCurl에서 curlPage를 안 보이도록 하고 setMask를 해제하는 로직을 넣어 둡니다.
transition.to로 turnGroup에 움직이는 효과를 주고 또 curlPage도 움직이는 효과를 주고 이 효과가 끝나면 로컬 함수인 hideCurl을 불러와 curlPage를 안 보이도록 하고 setMask를 해제 하도록 합니다.

그 다음은 gotoNext 함수인데요 currentPage,curlPage,time을 인수로 받습니다.
이 함수 내용은 좀 기네요.
turnGroup에 인수로 받은 currentPage와 curlPage를 insert 합니다.

그리고 curlMask 로컬 변수에 graphcs.newMask로 mask_320X480.png를 세팅해 넣습니다.
graphics.newMask 는 http://developer.anscamobile.com/node/5248 API를 참조하세요.
그리고 이 curlMask를 turnGrooup에 insert 합니다.
maskX와 maskY 값을 할당해 주고요 이미지들의 레퍼런스 포인트를 bottomLeft BottomRight 로 주고 curlPage를 45도 회전시키고 이 curlPage의 위치를 설정해 줍니다.
그리고 이 curlPage.isVisible을 true로 세팅해 줍니다.
그 다음 time값을 세팅하고 transition.to로 움직임 효과들을 줍니다.

이렇게 하면 앞장으로 넘기고 뒷장으로 넘기는 효과를 주는 함수까지 완료 했습니다.

다시 간단히 정리하자면요.
gotoNext()
1. page-to-turn으로 insert하고 curl 이미지를 turnGroup에 넣는다.
2. 비트맵 마스크를 turnGroup에 적용한다.
3. 페이지와 curl  이미지의 레퍼런스 포인트를 bottom right으로 한다.
4. curl 이미지를 회전시키고 시작 위치를 잡아준다.
5. transition을 시작한다.

gotoPrevious()
1. curl 이미지를 visible하게 한다.
2. onComplete 리스너를 달아서 transotion이 끝나면 hiding 페이지를 하도록 하고 turnGroup에서 비트맵 마스크를 없앤다.
3. transition 애니메이션을 시작한다.

Step3. Initiate the Page Curl

timer.performWithDelay( 2000, function() gotoNext( page1, curlPage, 500 ); end )
timer.performWithDelay( 5000, function() gotoPrevious( curlPage, 500 ); end )

이 함수들을 실행하기 위해 2초후에 gotoNext함수를 부르고 5초후에 gotoPrevious 함수를 부릅니다.

그러니까 이 소스코드로는 손가락으로 책장을 넘기는 효과가 아니라 2초후에 책장이 넘어가고 5초후에 책장이 다시 넘어오게 될 겁니다.

제대로 구현하려면 여러분들이 touch나 tap 리스너를 달아서 이 책장 넘김 효과를 컨트롤 하셔야 합니다.

이 소스코드에 대한 원문 강좌는 아래 페이지에 있습니다.
http://blog.anscamobile.com/2011/12/how-to-create-a-page-curl-in-corona/

그리고 소스파일은 아래에 있습니다.

그럼 오늘도 즐코딩.....


반응형

Comment


자주 사용하는 기능을 모듈화 하면 나중에 재사용할 때 아주 편하고 개발 기간도 많이 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에 있는 함수는 다른 앱을 개발 할 때도 그대로 모듈로 사용할 수 있습니다.

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


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

Comment

드래그 하기 기초

2011. 11. 24. 08:23 | Posted by 솔웅



오늘은 Thanks Giving day라서 휴일입니다.

사무실에 안 가고 집에서 글을 쓰게 됐네요.
근데 여유있게 글을 쓸 상황은 아니군요.

점심 초대를 받아서 좀 있다 나가야 하거든요.

오늘은 간단하면서도 아주 자주 쓰이는 테크닉을 정리하겠습니다.

앱을 만들 때 특히 게임 같은 앱을 만들 때 드래깅 기술을 많이 사용하게 됩니다.
가장 기본적으로 알아 둬야 할것은 아래와 같습니다.
touch 이벤트 리스너를 사용하기
began에서 현재 위치 저장하기
moved에서 현재 위치 바꿔주기

이 정도만 확실하게 알아 두시면 됩니다.


스크린에 사각형을 그리고 저 사각형을 드래그 하는 앱입니다.
아래 소스코드 참조하세요.
-- create object
local myObject = display.newRect( 0, 0, 100, 100 )
myObject:setFillColor( 255 )

-- touch listener function
function myObject:touch( event )
    if event.phase == "began" then

        self.markX = self.x -- store x location of object
        self.markY = self.y -- store y location of object

    elseif event.phase == "moved" then

        local x = (event.x - event.xStart) + self.markX
        local y = (event.y - event.yStart) + self.markY
       
        self.x, self.y = x, y -- move object based on calculations above
    end
   
    return true
end

-- make 'myObject' listen for touch events
myObject:addEventListener( "touch", myObject )

우선 흰색 사각형을 그리구요.
그 다음 touch 이벤트 리스너에서 호출할 함수를 만듭니다.
맨 아래 부터 부시면 이 myObject라는 객체에 touch 이벤트 리스너를 달았다는 것을 잘 보세요.

이제 myObject라는 함수를 보겠습니다.

began 일 때 해당 객체(myObject)의 위치를 정합니다.
moved일 때 began일 때의 위치 event.xStart 를 현재 위치에서 빼고 그러니까 이동 거리를 구하고 객체의 위치 self.markX를 더해 줍니다.
그리고 그 위치값을  myObject 객체의 위치값으로 대입합니다.
그러면 이동하게 됩니다.

아주 간단합니다. 하지만 반드시 알고 있어야 하는 겁니다.

반응형

Comment

이전 1 2 3 4 5 6 7 8 다음