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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
앱을 사용하다 일정 시간 동안 사용하지 않으면 핸드폰 화면이 black이 됩니다.

오랫동안 사용하지 않아도 화면이 계속 켜져 있는 상태로 둘 수 있는 방법이 있습니다.
system.setIdleTimer( enabled )

디폴트는 true입니다.
system.setIdleTimer( true )

true일 경우 이 idle timer가 작동을 하고 오랫동안 사용이 없으면 스크린이 흐려졌다가 sleep상태로 바뀝니다.

system.setIdleTimer( false )
위와 같이 false로 하면 오랫동안 사용하지 않아도 해당 앱이 계속 켜져있게 됩니다.
(맥이나 윈도우 시뮬레이터에서는 작동하지 않습니다.)

아마 앱을 tilt로 동작시키고 touch는 일어날 필요가 없는 앱이라면 이 기능이 반드시 필요할 수도 있겟네요.

많이 필요한 기능은 아니지만 알아두면 쓸모가 있겠죠?
반응형


반응형
요즘 만드는 앱에 다양한 애니메이션 효과를 사용하고 있습니다.
스피드 콘트롤 할 일이 많아서 찾아봤는데요.

mivieclip은 frame base라 근본적으로 frame 속도를 바꾸지 않는한 speed control 이 힘듭니다. (Sprites는 time base랍니다.)

http://blog.anscamobile.com/2010/06/improved-movieclip-library/ 에 있는 speed control 관련 내용을 볼께요.

하찮은 내용일 수도 있겠지만 코로나로 앱을 개발하면서 꼭 알아두어야 할, 그러지 않으면 반드시 한번은 에너지 낭비를 해야 될 그런 내용이더라구요.
한번쯤 정리해 두는것도 좋을 것 같습니다.

Richard HakinJune 8th, 2010 at 2:59 pm

We desperately need a speed control as the animation just play crazy

앱을 쌈박하게 만들기 위해서는 애니메이션 스피드 조절 기능이 완전 필요합니다.

PaulAugust 3rd, 2010 at 4:41 am

What is the best way to control the speed of the animation?

Would it be a case of using a timer and calling something like

myAnim:nextFrame()

애니메이션의 스피드 조절하는 가장 좋은 방법이 뭐지요?

myAnim:nextFrame() 함수를 타이머를 이용해서 부르는 것이 방법이 될까요?

EvanAugust 3rd, 2010 at 11:21 am

@Paul – your suggestion would probably work well in some cases, although if you had more than a few clips onscreen, you’d want to test it on target devices to see the performance. I don’t think timers are necessarily that expensive, but they might add up if you had a lot of functions constantly polling the system time.

The movieclip library is primarily designed for a Flash-like model, in which there’s a global framerate for everything. For greater timing control, or for complex animation cases, we’d recommend using Game Edition and the sprite-sheet feature.

In addition to using texture memory much more efficiently (one big image rather than lots of little ones), that feature includes an animated-sprite API that lets you set different animation speeds for different sprites, or even for different sequences within the same sprite. Also, it’s a time-based API rather than frame-based, so the total animation time will remain the same even on slower devices that need to drop frames — the engine automatically handles all this under the hood.

@Paul - 당신의 방법이 잘 적용되는 케이스도 있을 거예요. 아마 무비크립이 적거나 특정한 디바이스에서만 테스트를 원한다면 더 그럴거예요. 하지만 내 생각엔 타이머는 앱을 좀 헤비하게 만들것 같네요.

무비클립은 플래쉬를 모델로 디자인 된 겁니다. 기본적으로 전체 frame rate가 있고 이것이 모든 무비클립에 동일하게 적용됩니다. 좀 더 타이밍 콘트롤을 하고 싶거나 복잡한 애니메이션을 원한다면 Game Edition과 Sprite sheet를 사용하기를 권장합니다.

더군다나 sprite API는 메모리도 효율적으로 운용할 수 있어요. 그리고 각 애니메이션 마다 다른 스피드를 줄 수도 있구요. 스프라이트 애니메이션은 time base 입니다. movieclip은 프레임 베이스이구요. 그래서 사양이 낮은 기계에서도 각 애니메이션별 스피드 차이는 동일하게 나타날 겁니다.

TomAugust 16th, 2011 at 8:27 am

@Chan, You should use the Sprite library instead of the Movieclip library because it gives you better control over the animations. You do have a onComplete listener with Sprites.

@Chan, 무비클립 라이브러리 보다는 스프라이트 라이브러리를 이용해야 애니메이션의 속도 컨트롤이 더 수월합니다. 그리고 스프라이트에는 onComplete리스너도 있습니다.

=======  o ===== o ===== o ====== o ====== o ====== o ======

자 이렇게 애니메이션 기능의 스피드 조절을 위해서는 MovieClip보다는 스프라이트를 사용하라는 것이 코로나SDK의 기본 입장입니다.

그런데 Michael Hartlef 라는 개발자가 movieclip.lua를 수정해서 animation speed를 control 할 수 있는 방법을 개발해서 공유하고 있습니다.

 http://developer.anscamobile.com/code/modified-moviecliplua

위 싸이트에 가면 그 소스 코드가 있습니다.

따로 lua파일을 올리니까 이걸 다운 받으셔도 됩니다.

이 파일의 91번째 줄 에 있는 animSpeed =1.0 의 값을 바꾸시면 애니메이션의 속도를 조절 할 수 있습니다.

이것도 movieClip이기 때문에 framebase이고 이 스피드는 모든 애니메이션에 동일하게 적용됩니다.

애니메이션마다 각각 다른 스피드 컨트롤을 해야 된다면 여전히 movieClip보다는 Sprite를 사용하는 것이 좋습니다.

하지만 Michael Hartlef 이 발견한 방법도 아주 유용하네요.
저도 지금 개발하고 있는 앱에 Michael Hartlef이 개발한 방법을 사용하려고 합니다.

위 소스파일 중에 -- added by M.Hartlef June 12th, 2010 이렇게 주석이 달린 부분이 Hartlef가 추가한 부분입니다.

필요하신분은 이 코드 사용하면서 무료로 자기 노력의 결과물을 공유한 Hartlef에게 감사하는 마음을 가지셔야겠죠?

In everything, Give thanks.

그럼
반응형


반응형

오늘 다룰 예제는 작은 원을 드래그해서 큰 원에 닿으면 작은 원의 색을 바뀌게 하는 기능 입니다.

기본적으로 Physics와 Collision 이벤트 리스너를 이용합니다.
그리고 addBody에서 isSensor 기능을 이용하게 됩니다.


이 isSensor 기능이 오늘 보실 새로운 기능입니다. 그리고 두번째 예제에서 preCollision 이벤트도 덤으로 보시겠습니다.


결과 화면을 먼저 보시고 소스코드를 보겠습니다.



display.setStatusBar(display.HiddenStatusBar)

require ( "physics" )
physics.start()
physics.setGravity( 0, 0 )
local hero = display.newCircle( 40, 40, 30 )
hero.name = "hero"
physics.addBody(hero, "dynamic")

local function moveHero (event)
hero.x = event.x
hero.y = event.y
end
hero:addEventListener("touch", moveHero)

local colorSensor = display.newCircle( 160, 240, 80 )
physics.addBody(colorSensor, {isSensor = true})
colorSensor.alpha = 0.2

colorSensor:addEventListener("collision", colorSensor)
function colorSensor:collision (event)
if event.other.name == "hero" and event.phase == "began" then
red = math.random(0,255)
green = math.random(0,255)
blue = math.random(0,255)
hero:setFillColor(red, green, blue)
end
end

소스를 보시면 우선 status바를 없애고 physics를 require하고 중력을 0으로 합니다.
나중에 원 두개 다 dynamic으로 addBody를 할 거니까 중력을 0으로 하지 않으면 밑으로 떨어지겠죠?

그리고 hero라는 이름으로 작은 원을 만들고 name을 정해주고 dynamic으로 addBody를 해 줍니다.

이 작은 원에 touch 리스너 이벤트를 달아서 touch 한 지점에 위치하게 됩니다.
그러면 손가락으로 잡고 끌면 따라 오겠네요.

다음엔 큰 원을 만들고 addBody에서 isSensor = true로 선언합니다.

이 큰 원에 collision이벤트를 줍니다.
이 이벤트에서 불러오는 함수는 (colorSensor) event.other.name이 hero이고 이벤트가 시작 됐을 때 hero.setFillColor로 작은 원의 색을 바꿔 줍니다.

간단하죠?

여기서 isSensor를 false 로 하면 어떻게 될까요?
그러면 둘 다 dynamic으로 addBody를 했기 때문에 둘이 부딪히면 큰 원이 밀려 날 겁니다.

isSensor를 true로 한다는 얘기는 physics 객체가 와서 부딪혀도 밀려나지않고 제자리에서 센서의 역할만 한다는 의미입니다.

한가지만 더 볼까요?

이번 예제는 노란 사각형이 위에서 떨어지는데 첫번째 흰 줄은 그냥 통과하고 두번째 흰 줄에서 걸리는 프로그램 입니다.

local physics = require("physics")
physics.start()
display.setStatusBar( display.HiddenStatusBar )
 
local platform1 = display.newRect( 20, 200, 280, 15 )
platform1.myName = "platform1"
physics.addBody(platform1, "static")
 
local platform2 = display.newRect( 20, 320, 280, 15 )
platform2.myName = "platform2"
physics.addBody(platform2, "static")
 
local box = display.newRect( 20, 20, 50, 50 )
box:setFillColor(255,255,0,255)
box.myName = "box"
physics.addBody(box)
 
local function onLocalPreCollision( self, event )
        -- Let box pass through platform 1
        local platform = event.other
        if platform.myName == "platform1" then
                platform.isSensor = true
        end
end
 
box.preCollision = onLocalPreCollision
box:addEventListener( "preCollision", box )

첫번째 세줄은 그냥 넘어가겠습니다.
다음은 두개의 긴 사각형을 만들고 모두 static으로 선언합니다.
그리고 각각 myName에 이름을 할당합니다.

그리고 노란 정사각형을 만들로 addBody를 합니다. 디폴트로 dynamic이 선언됩니다.

여기에 노란 정사각형에 preCollision 이벤트를 달로 이 preCollision은 onLocalPreCollision 함수를 콜 합니다.

preCollision은 충돌하기 직전에 호출 되는 이벤트 입니다.
그러니까 onLocalPreCollision은 충돌되기 직전에 이것이 platform1 이면 isSensor를 true로 바꿔서 그냥 통과 시켜 버리게 됩니다.

오늘 배운 기능도 꽤 유용한 기능이네요.

잘 알아두면 게임 만들 때 아주 요긴하게 쓰일 것 같습니다.

그럼 다음 시간에 또 뵐께요.

반응형


반응형
간단하지만 유용한 팁들

1. 애니메이션 일시 정지 시키고 다시 시작 시키기

local logo = display.newImage( "endinggreen.png", 100, 140 )
logo.isPaused = true;
function logo:enterFrame(event)
        -- do something like make the logo bounce around the edges of the screen
        logo:rotate(-5)
end
 
Runtime:addEventListener( "enterFrame", logo );
 
function logo:tap( event )
        if (logo.isPaused) then -- initially nil which is false anyways
                Runtime:removeEventListener( "enterFrame", self )
                logo.isPaused=false;
        else
                Runtime:addEventListener( "enterFrame", self )
                logo.isPaused = true;
        end
        return true -- we handled the event so don't propagate
end
 
logo:addEventListener( "tap", logo )

위 코드는 앱을 실행하면 이미지가 회전하도록 만들었습니다.
1. 이미지 logo를 만들고 isPaused=true를 해 준 상태에서 Runtime 이벤트 리스너로 logo:enterFrame 함수를 실행시키비다.
2. logo:enterFrame 함수에서는 이미지를 rotate 시켜 줍니다.

그 다음은 이미지를 한번 tap하면 회전이 정지하고 다시 tap하면 회전하도록 합니다.
1. logo 이미지에 이벤트 리스너 tap을 달고 logo 함수를 호출함
2. logo:tap 함수에서는 isPaused 가 true이면 회전을 멈추고 isPaused를 false로 만들고
   만약 isPaused가 false이면 Runtime enterFrame 리스너를 다시 시작합니다.

이 소스 코드는 http://developer.anscamobile.com/content/application-programming-guide-common-design-tasks 에 있습니다.
그런데 이 코드로는 내가 기대한 대로 안 되서 약간 수정했으니 참고하세요.

2. Managing Screen

인트로 화면 - 메인 화면 - 게임 화면 등 각 스크린 별 이동이 있어야 할 때 Group object를 사용하면 좋습니다.
각 화면별로 그룹을 만들어서 다음 화면으로 넘어갈 때 transitions를 사용하면 화면 이동의 효과가 있습니다.
저의 경우는 이런 화면 이동을 쉽게 하도록 도와주는 director.lua를 require해서 사용하고 있습니다.
저는 단지 director.lua의 함수를 불러와 사용하기 때문에 transition 기능은 따로 코딩하지 않습니다.
director.lua 사용법은 나중에 따로 다루도록 하겠습니다.

3. 앱 시작 종료 시 데이터 저장하기

local path = system.pathForFile( "data.txt", system.DocumentsDirectory )
 
-- io.open opens a file at path. returns nil if no file found
local file = io.open( path, "r" )
if file then
   -- read all contents of file into a string
   local contents = file:read( "*a" )
   print( "Contents of " .. path .. "\n" .. contents )
   io.close( file )
else
   -- create file b/c it doesn't exist yet
   file = io.open( path, "w" )
   local numbers = {1,2,3,4,5,6,7,8,9}
   file:write( "Feed me data!\n", numbers[1], numbers[2], "\n" )
   for _,v in ipairs( numbers ) do file:write( v, " " ) end
   file:write( "\nNo more data\n" )
   io.close( file )
end



위 코드는 게임 등을 만들 때 점수나 기타 저장 할 사항들을 저장하는 방법을 보여줍니다.
적당한 시점에 데이터들을 파일로 저장하고 다음 앱을 실행 할 때 불러와서 이어서 할 수 있도록 할 수 있습니다.
Corona DOC 에 소개 돼 있는 테크닉이긴 한데 그리 특별한 테크닉은 아니네요.
데이터가 많을 경우는 SQLite를 사용해도 되겠죠?

위 코드를 활용해서 Runtime system 이벤트 리스너로 앱 시작할 때 혹은 앱이 종료할 때 데이터를 저장할 수 있습니다.

local function shouldResume()
        -- return true or false depending on whether we need to resume
end
 
local function onSystemEvent( event )
        if event.type == "applicationExit" then
                -- save stuff to disk
        elseif event.type == "applicationStart" then
                if shouldResume() then
                        -- load stuff off disk
                else
                        -- start app up normally
                end
        end
end
 
Runtime:addEventListener( "system", onSystemEvent );


system 이벤트에서 event.type 이 applicationExit일 때나 applicationStart 일 때 어떠한 행위를 하도록 핸들링 하시면 됩니다.


========= o ========= o ========= o ========= o =========

이상으로 총 36강에 걸쳐 Ansca Mobile의 Corona SDK 웹 사이트에 있는 매뉴얼인 Corona DOC 를 다뤘습니다.
원본은 http://developer.anscamobile.com/resources/docs 에 있습니다.

이제 기본 교과서를 뗀 셈입니다.
다음엔 참고서를 가지고 좀 더 실용적인 응용방법을 터득해야겠죠?

지금 제가 개발 하면서 얻은 TIP은 따로 카테고리를 만들어서 소개해 드리고 있습니다.
그리고 개발을 편리하게 해 주는 3rd Party 제품들 소개 및 사용법 알아보는 코너도 괜찮을 것 같구요.
이것 외에 Corona SDK 에서 제공하는 기본 샘플 예제 분석 이나 다른 공개된 예제 분석 카테고리를 별도로 만들까 합니다.
남들이 짜 놓은 코드를 분석하는 것도 많은 도움이 됩니다.


혹시 여러분 중에서도 분석을 원하시는 다른 샘플 코드가 있으면 보내주세요.
같이 분석해 보죠.

제 이메일은 solkit2011@yahoo.com 입니다.

그리고 코드 분석 외에 다른 카테고리를 만들어서 공부하면 좋겠다는 의견도 있으면 댓글에 달아 주세요.
그러면 제 공부하고 또 그 경험을 다른 많은 분들과 공유하는데 많은 도움이 될 것 같습니다.

아무쪼록 제 글이 여러분들에게 조금이나마 도움이 되기를 바랍니다.

다음에 다른 카테고리에서 만나뵙겠습니다.

감사합니다.

P.S. 이 Corona DOC 카테고리에 있는 강좌 중에도 질문이 있으시면 댓글에 달아주세요.
성실히 답변해 드릴꼐요...

반응형

Corona SDK 프로그래밍 테크닉 3

2011. 11. 10. 18:02 | Posted by 솔웅


반응형
코로나는 외부 라이브러리를 만들거나 로딩할 때 루아 모듈 기능을 사용 합니다. 코로나 SDK와 함께 제공되는 여러 라이브러리들이 있습니다. (예:ui.lua, sprite.lua)
그리고 별도로 파일로 제공되는 라이브러리들도 있습니다. (예:Button, Movieclip, direction 등등)
개발자가 별도로 자신만의 모듈을 만들어 낼 수도 있습니다.

외부 라이브러리 만들기
가장 쉬운 예제는 아래 샘플 코드처럼 하는 겁니다. 이렇게 module() 부분을 넣으면 main.lua에서 불러와서 사용할 수 있습니다. 이 파일의 이름은 확장자가 .lua이어야 합니다.  이 파일을 main.lua와 같은 폴더에 넣습니다. (하위나 상위 폴더에 넣으면 안 되더라구요.)

module(..., package.seeall)
 
-- Declare the functions you want in your module
function hello()
        print ("Hello, module")
end

파일의 확장자는 .lua라야 합니다.

외부 라이브러리 로딩 하기
외부 라이브러리를 로드하려면 require(module name)을 사용해야 합니다.
그러면 외부 모듈 파일 안에 있는 함수를 사용 할 수 있습니다.

-- Load external library (should be in the same folder as main.lua)
local testlib = require("testlib")
 
-- Now the functions in "testlib.lua" are available here:
 
-- call the testlib library function hello(). The "slow" way
testlib.hello()
 
-- cache same function, if you call more than once
local hello = testlib.hello
 
-- now all future invocations are "fast"
hello()

위 예제 코드는 testlib.lua 파일을 require하는 겁니다. main.lua 윗부분에 require 라인을 넣습니다. 그러면 testlib.lua파일 안에 있는 함수(클래스)를 사용할 수 있습니다.
사용하는 방법은 require한 변수.함수 이름 (testlib.hello) 형식으로 사용할 수 있습니다.

Syntax
module(name[,…])
모듈을 생성합니다. 모듈 안에 있는 함수를 불러올 때 그 함수는 글로벌 함수 이거나 글로벌 변수에 대입되거나 해야 합니다. 즉 외부에서 곧바로 로컬 변수나 함수를 불러올 수 없습니다. 다면 외부에서 부른 글로벌 변수에 로컬 함수나 변수를 대입된 경우 사용할 수 있습니다. (좀 헛갈릴 수도 있는데요. 샘플 예제를 보면 쉽게 이해 되실거예요. 샘플 예제는 아래 '외부 모듈 접근 방법 예' 를 보시면 나옵니다.)

require(modname)
해당 모듈을 로딩 합니다.

외부 모듈 접근 방법 예

한 파일(main.lua)에 모든 코드를 다 넣으면 너무 길어서 프로그램을 짠 프로그래머도 이해하기 어려워 질 겁니다. 이 경우 모듈을 이용해서 외부 파일을 만들고 단지 이를 불러오게 되면 코드의 가독성이 높아 질 겁니다.
또한 그 코드가 여러곳에서 여러번 불릴때는 더 많은 코딩의 절약을 할 수 있습니다.

모듈 사용의 한 예를 보여드리겠습니다.
level1.lua
module(..., package.seeall)
 
function loadLevel()

-- create a group for this level
local screenGroup = display.newGroup()

-- create some content for this level
local levelText = display.newText( "Level One", 20, 20, "Helvetica-Bold", 24 )

-- insert the text into this level's group
screenGroup:insert( levelText )

-- calling loadlevel will return the display group
return screenGroup
end
위 코드를 level1.lua로 저장하세요.

그리고 main.lua에 아래 코드를 넣으세요.

local levelGroup = require( "level1" ).loadLevel()
이러면 main.lua를 실행시키면 level1.lua의 loadLevel() 함수가 실행 됩니다.
main.lua안에 있는 코드는 아래와 내용이 같습니다.

local levelGroup = require("level1")
levelGroup.loadLevel()

아래 하나를 더 볼까요?

module(..., package.seeall)

function testFunction1()
    print( "Test 1" )
end

function testFunction2()
    print( "Test 2" )
end

function testFunction3()
    print( "Test 3" )
end

위 코드를 gamefunctions.lua로 저장을 하구요.

local examplemod = require "examplemodule"

examplemod.testFunction1() -- prints "Test 1" to terminal
examplemod.testFunction2() -- prints "Test 2" to terminal
examplemod.testFunction3() -- prints "Test 3" to terminal

이 코드를 main.lua에 저장하세요.

그러면 터미널에 gamefunctions.lua에 있는 세개의 함수 실행결과 Test 1 Test 2 Test 3 가 찍힐 겁니다.

그러면 gamefunctions.lua를 아래와 같이 바꿔보세요.

-- define a local table to store all references to functions/variables
local M = {}

-- functions are now local:
local testFunction1 = function()
    print( "Test 1" )
end
-- assign a reference to the above local function
M.testFunction1 = testFunction1

local testFunction2 = function()
    print( "Test 2" )
end
M.testFunction2 = testFunction2

local testFunction3 = function()
    print( "Test 3" )
end
M.testFunction3 = testFunction3

-- Finally, return the table to be used locally elsewhere
return M

이 코드를 실행 해 보면 결과값이 같을 겁니다.
그런데 이 코드에는 module(..., package.seeall) 가 없습니다.
이것 없이 모든 변수와 함수를 local로 선언해서 사용했습니다.

이렇게 모듈을 로컬로 선언해서 사용할 수 있으면 메모리 관리에 더 좋은 잇점이 있을 겁니다.

루아에서 module() 기능은 점점 중요도가 떨어질 거라는 얘기도 있습니다.

기본적인 모듈 사용법을 숙지 하신 후 실제 프로젝트에서는 나중에 제시한 방법을 사용하시면 좋을 것 같습니다. 혹시 이런 방법에 한계가 있다면 기본 모듈 사용법을 사용해야 할 수도 있으니까요.

Corona API TuneUP

Display library

Properties:
display.contentCenterX -- (equals 0.5*display.contentWidth)
화면의 x좌표 중앙입니다.
display.contentCenterY -- (equals 0.5*display.contentHeight)
화면의 y좌표 중앙입니다.
display.contentScaleX -- (the ratio between content pixel and screen pixel width)
전체 화면 대비 x좌표의 픽셀 비율입니다.
display.contentScaleY -- (the ratio between content pixel and screen pixel height)
전체 화면 대비 y좌표의 픽셀 비율입니다.

Functions:
display.setDefault( key, ... )
Sets default color values for fill, stroke, text, line. See Display Object Color Defaults (below) for details.
오브젝트를 채우는 디폴트 칼라 값을 지정합니다. 자세한 사항은 아래에 설명 됩니다.
display.newGroup( [child1 [, child2 [, child3 ... ]]] )
With no arguments, this will create an empty group and set parent to root (original behavior).
newGroup을 만들 때 사용합니다. 이 그룹은 여러 객체들을 하나의 객체처럼 관리하기 위해 사용 됩니다. 괄호 안에 값들이 없으면 empty group을 생성하게 됩니다. 이렇게 빈 그룹을 먼저 생성 한 후에 insert 할 수 있습니다.
Display Objects
Properties:
object.contentBounds -- (equivalent to object.stageBounds which is deprecated)
객체의 Bound 입니다. 이전 버전에서는 object.stageBounds였었습니다.
object.contentWidth -- (equivalent to object.stageWidth which is deprecated)
객체의 너비입니다. 이전 버전에서는 object.stageWidth 였었습니다.
object.contentHeight -- (equivalent to object.stageHeight which is deprecated)
객체의 높이 입니다. 이전 버전에서는 object.stageHeight였었습니다.

Object methods:
object:toFront() -- moves object to visual front of its parent group (object.parent)
객체를 소속된 그룹의 맨 앞으로 가져 옵니다.
object:toBack() -- moves object to visual back of its parent group (object.parent)
객체를 소속 된 그룹의 맨 뒤로 가져 갑니다.
object:localToContent( x, y ) -- maps x,y in object's local coordinates to content coordinates
로컬 좌표를 content 좌표로 바꿉니다.
object:contentToLocal( x, y ) -- maps x,y in content coordinates to object's local coordinates
content좌표를 local 좌표로 바꿉니다.

Display Object Color Defaults
조 앞에서 잠시 선보였던 겁니다. 여기서 좀 더 자세히 살펴 보겠습니다.
display.setDefault( key, ... )
괄호 안에 들어올 수 있는 키들은 아래와 같습니다.
    •    "fillColor" corresponds to the default fill color for vector objects. The default is initially white.
    •    벡터 객체를 위한 디폴트 fill color. 디폴트 값은 흰색입니다.
    •    "strokeColor" corresponds to the default stroke color for vector objects. The default is initially white.
    •    벡터 객체를 위한 stroke color. 디폴트 값은 흰색입니다.
    •    "lineColor" corresponds to the default line color for line objects. The default is initially white.
    •    라인 객체를 위한 디폴트 라인 칼라. 디폴트 값은 흰색 입니다.
    •    "textColor" corresponds to the default text color for text objects. The default is initially white.
    •    텍스트 객체를 위한 디폴트 텍스트 칼라. 디폴트 값은 흰색입니다.

위 값들은 굳이 바꿀 필요가 있나 하는 생각이 들지만 혹시 모르죠 개발 하다 보면 바꿔야 할 때가 있을지도요…

아래는 fillColor에 대한 예제입니다.
display.setDefault( "fillColor", gray )
display.setDefault( "fillColor", gray, alpha )
display.setDefault( "fillColor", red, green, blue )
display.setDefault( "fillColor", red, green, blue, alpha )
반응형

Corona SDK 프로그래밍 테크닉 2

2011. 11. 9. 00:01 | Posted by 솔웅


반응형
루아(Lua)에서 로컬 변수의 중요성

루아에서 가장 일반적으로 추천되는 것이 로컬화를 하라 입니다. 각각의 코드 블럭에 있는 변수를 로컬로 선언하는 것입니다. 퍼포먼스를 향상시키기 위해 아주 중요한 사항 입니다.
    myFirstName = "John"        -- global variable
    local myLastName = "Doe"    -- local variable

위 보기와 같이 변수 선언 할 때 앞에 local을 쓰면 로컬 변수이고 아무것도 쓰지 않으면 글로벌 변수 입니다.

아래 코드를 보세요.

  local myTable = { 1, 2, 3, 4, 5 }

    local printThird = function()
        print( myTable[1], myTable[2], myTable[3] )
    end

위 코드에서 myTable은 로컬입니다.
아래 코드는 이 코드와 무엇이 다를까요?

  local myTable = { 1, 2, 3, 4, 5 }

    local printThird = function()
        local t = myTable

        print( t[1], t[2], t[3] )
    end

함수 printThird 한에 t라는 로컬 변수를 선언하고 이 함수 밖의 로컬 변수인 myTable을 이 함수 안의 로컬 변수에 대입했습니다.
이것만 봐서는 크게 잇점은 없어 보입니다만 실제 앱 전체적으로는 긍정적인 효과를 줄 수 있습니다.

아래 코드를 보세요.

-- localize math function
    local mRandom = math.random

    local newObject = function()

        local obj = display.newImage( "endinggreen.png" )

        -- listener function
        function obj:enterFrame()

            -- speed up access to (already local) mRandom function
            local mRandom = mRandom

            -- do stuff...
            local rand1 = mRandom( 1, 10 )
            local rand2 = mRandom( 1, 10 )
            local rand3 = mRandom( 1, 10 )

            print( rand1, rand2, rand3 )
        end

        -- start the listener
        Runtime:addEventListener( "enterFrame", obj )

        return obj
    end

newObject();

이 코드는 rand1,rand2,rand3 의 세 랜덤 값들을 프레임마다 계속 터미널에 찍어 줍니다.



함수 밖에 local mRandom을 선언해 놓고 함수 안에서(두 번째 depth의 함수) local mRandom을 새로 만들고 이 변수에 함수 밖의 mRandom을 대입했죠? 사실 함수 안의 local mRandom = mRandom 부분을 없애도 프로그램은 제대로 돌아갈 겁니다.
하지만 이렇게 함수 안에서 다시 로컬로 변수를 선언하게 되면 퍼포먼스에 긍정적인 효과를 줍니다.
이런식으로 로컬 변수를 활용하는 습관을 들이면 프로그램 하실 때 많은 도움을 줄 겁니다.


Forward Referencing

항상 모든 것을 로컬 변수로 사용하는 것은 때로는 어려울 수 있습니다. 이럴 때 forward referencing 방법을 쓰시면 도움이 될 겁니다.

아래 코드를 보세요.

예제 1 에러코드
  local myFunction = function()
        callback()  -- ERROR: function 'callback' does not exist yet
    end

    local callback()
        print( "Hello World" )
    end

    myFunction()    -- will produce error (see above)

위 코드에서 myFunction안에서 callback()을 부를 때는 이 callback()함수가 존재하지 않습니다.
그렇기 때문에 에러가 날 겁니다.

예제 2 forward Referencing 사용한 코드

    local callback    -- forward reference (for later use)

    local myFunction = function()
        callback()    -- output: "Hello World"
    end

    -- function below is still local, because of forward reference
    callback = function()
        print( "Hello World" )
    end

    myFunction()    -- no error; happy code :-)

위 코드에서는 callback변수를 myFunction 이전에 로컬로 선언합니다.
그러면 myFunction에서 callback()함수를 호출 했을 때 이 변수를 따라가서 아래에 있는 callback()함수를 사용합니다.
이렇게 미리 변수만 로컬로 선언해 놓는 것을 forward reference라고 합니다.


Copies vs. Direct References

그럼 이 로컬 변수는 실제 선언된 값의 카피를 가지고 있는지 point를 해서 direct Reference를 하고 있는지 의문이 값니다.
정답은 루아에서는 경우에 따라 다릅니다.
테이블을 로컬화 시켰다면 이것은 실제 테이블을 direct reference 할 것입니다. 그 외에 단순히 숫자나 문자, 함수 등이라면 이것은 copy개념으로 이해하셔야 합니다.

    local myTable = { key1 = "original" }

    local testFunc = function()
        local t = myTable

        t.key1 = "modified"
    end

    testFunc()

    print( myTable.key1 )   -- output: modified

위의 코드에서 myTable은 실제 테이블의 reference를 사용합니다. 그러므로 testFunc()함수 안의 로컬 변수 t를 이용해서 값을 수정하면 실제 table의 값도 수정됩니다.



반대로 copy개념인 다른 숫자나 문자나 함수등은 이 경우 원래 함수의 값이 수정 되지는 않겠죠?

    local myNumber = 1
    local myString = 1
    local myFunction = function() print( "Hello World!" ); end

    local testFunc = function()
        local n = myNumber
        local s = myString
        local f = myFunction

        n = 2
        s = "2"
        f = nil
    end

    testFunc()

    print( myNumber, myString, tostring(myFunction) )

    -- OUTPUT:  1    1    function: 0x24a2e60

위 샘플 코드를 보세요.
결과 값을 보면 testFunc() 함수 안에서 선언한 n,s,f라는 로컬 변수에 다른 값을 대입시켰지만 이 함수 밖에서는 이 값이 바뀌지 않고 원래 값인 1,1이 출력 됩니다.



이 두 개의 차이를 잘 아셔야 실제 코딩에서 헛갈리지 않습니다. table만 direct reference를 합니다. 그러니까 Corona Display Objects 의 경우 테이블을 사용하니까 이것도 direct reference를 하겠죠? 고로 함수 안에서 로컬변수를 만들어서 Corona Display Objects를 바꾼 다면 함수 밖에서 최초에 선언했던 값도 바뀌겠죠?

Clearing the Reference

로컬 변수를 더 이상 사용하지 않을 때가 있을 겁니다. 또는 상위의 글로벌 값을 더 이상 사용하고 싶지 않을 때도 있구요. 이럴 경우 로컬 변수에 nil을 대입하면 됩니다.

    local myVar = myGlobal
    myVar = nil

이 경우 테이블의 경우는 어떻게 될 까요? 함수 안의 로컬 변수를 nil시키면 상위의 글로벌 테이블 변수의 값도 nil이 될까요?
정답은 그렇지 않다 입니다. 로컬을 nil시키면 이것은 로컬과 글로벌 변수와의 관계만 끊게 되기 때문에 상위의 글로벌 테이블 변수의 값까지 nil이 되지는 않습니다.

좀 헛갈리시나요?
이해가 되시면 잘 이해하세요. 프로그래밍할 때 아주 도움이 되실 겁니다.
잘 이해가 안 되신다면 일단 버릇을 들이세요. 나중에 이해할 때가 올겁니다.



오늘 까지는 프로그래밍 테크닉 중에서 퍼포먼스와 관련된 사항들을 알아 봤습니다.

다음 시간엔 외부 모듈과 패키지 사용법에 대해 알아 보겠습니다.
반응형

Corona SDK 프로그래밍 테크닉 1

2011. 11. 8. 20:54 | Posted by 솔웅


반응형

이번 시간하고 다음 시간엔 애플리케이션 퍼포먼스 및 메모리 관리에 대해 다룹니다.

아직까지 모바일 기계는 작기 때문에 CPU나 Memory 에 한계가 많습니다. 이로인한 속도 저하나 에러방지를 위해 신경써야할 퍼포먼스 및 메모리 관리에 대해 알아봅니다.


Performance and Optimization


앱 개발 할 때 항상 앱의 퍼포먼스에 대해 신경을 써야 합니다. 특히 아직까지 모바일은 메모리나 배터리 성능에 많은 한계가 있습니다. 

코로나에서는 garbage collection을 통해서 많은 부분을 자동으로 관리해 주고 있습니다.


코로나에서의 메모리 관리는 아래 다섯가지에 대해 고민 하면 됩니다.

1. Display objects

2. Global variables

3. Runtime Listeners

4. Timers

5. Transitions


메모리 낭비와 관련해서 체크할 방법이 있습니다.

아래 코드를 main.lua 파일 맨 아래에 넣으세요.

(main.lua말고 다른 파일에 넣어도 됩니다.)


local monitorMem = function()


    collectgarbage()

    print( "MemUsage: " .. collectgarbage("count") )


    local textMem = system.getInfo( "textureMemoryUsed" ) / 1000000

    print( "TexMem:   " .. textMem )

end


Runtime:addEventListener( "enterFrame", monitorMem )


MemUsage는 Lua 메모리를 나타냅니다. 그리고 TexMem은 그래픽 등이 사용하는 메모리를 나타냅니다. 여기서 중요한 것은 숫자가 얼마나 크냐가 아니고 앱이 진행 되면서 이 숫자가 어떻게 변하느냐를 보시는 겁니다.

스크린 1에서 2로 갔을 때 메모리 변화와 다시 1로 왔을 때의 메모리 변화를 보실 필요가 있습니다. 계속 늘어나기만 하면 어디에선가 메모리 누수가 있다는 얘기입니다.


Display Objects


디스플레이 오브젝트와 관련된 메모리 누수를 막는 방법은 객체를 정리할 때 항상 nil을 대입하는 겁니다.

display.remove( redBall )

redBall = nil



혹시 루핑을 돌면서 계속 객체를 새로 만들 경우 객체 remove를 하지 못하는 경우가 있는데요. 개발자는 항상 객체의 탄생과 소멸까지 책임 져야 합니다.


Global Variables


객체 생성할 때 local 을 붙이지 않으면 그것은 글로벌 변수입니다. 가능한 글로벌 변수보다 로컬 변수를 사용하는 것이 메모리 누수를 막는 방법입니다. 만약 사용하더라도 더이상 그 변수가 필요하지 않으면 반드시 nil 처리를 합니다. 그러면 루아의 garbage collector가 이것을 정리할 수 있습니다.


Runtime Listeners


display object를 remove할 때 이 객체에 붙어 있는 listener도 메모리에서 함께 해제 됩니다. Runtime에 리스너를 달게 되면 개발자가 직접 그 리스너를 remove할 때까지 메모리 해제 되지 않습니다. Runtime/enterFrame 리스너에서 일반적으로 일어나는 메모리 누수는 어느 한 screen에 Runtime 리스너가 달렸는데 그 스크린을 떠날때 이 리스너를 없애지 않는 경우 입니다. 이 경우 다음에 다시 이 스크린으로 돌아오게 되면 Runtime 리스너가 두개가 되기 때문입니다. 


Timers and Transitions


Timer와 Transition이 일반적으로 가장 crash를 많이 유발할 개연성이 있습니다. 예를 들어 time 하고 timer/transition start 사이에 end가 와 버리면 충돌 하게 됩니다. 또한 이것을 nil 처리 하지 않으며 이것은 메모리에 계속 남아 있으면서 좀 더 많은 메모리 누수를 유발합니다.


이것을 관리하는 효율적인 방법은 모든 timer와 transition들을 하나의 테이블(배열)로 관리하는 겁니다. 그러면 더이상 아무런 타이머와 트랜지션이 필요 하지 않을 때 한번에 nil처리 할 수 있습니다.


timerStash = {}

transitionStash = {}


function cancelAllTimers()

    local k, v


    for k,v in pairs(timerStash) do

        timer.cancel( v )

        v = nil; k = nil

    end


    timerStash = nil

    timerStash = {}

end


--


function cancelAllTransitions()

    local k, v


    for k,v in pairs(transitionStash) do

        transition.cancel( v )

        v = nil; k = nil

    end


    transitionStash = nil

    transitionStash = {}

end


아래는 timer와 transition 을 생성할 때의 예 입니다.

timerStash.newTimer = timer.performWithDelay( ...


transitionStash.newTransition = transition.to( myObject { ...


만약 cancel하지 말아야 될 타이머나 트랜지션은 이 테이블에 포함 시키지 않을 수 있습니다.


효과적으로 메모리 사용하기 


- 메모리 누수 방지하기 

- 가능한 리소스 파일 크기를 작게 하기

- 필요한 시점에 리소스 로드하기

- display object가 필요하지 않으면 display hierarchy 에서 remove 하기

나쁜 예)

-- rect is a global variable

rect = display.newRect(0,0,10,10)

rect:setFillColor(255,255,255)

 

local function removeOnTap( event )

   local t = event.target

   local parent = t.parent

 

   -- remove from display hierarchy

   -- but var "rect" still exists

   -- so memory is never freed

   parent:remove( t )

 

   return true

end

 

rect:addEventListener(   "tap", removeOnTap ) 


좋은 예)

-- rect is a local variable

local rect = display.newRect(0,0,10,10)

rect:setFillColor(255,255,255)

 

local function removeOnTap( event )

   local t = event.target

   local parent = t.parent

 

   -- remove from display hierarchy

   parent:remove( t )

 

   return true

end

 

rect:addEventListener(   "tap", removeOnTap ) 


위의 나쁜 예는 rect가 글로벌 변수로 선언이 돼 있기 때문에 parent:remove(t)를 해도 rect는 메모리에서 해제되지 않습니다. 좋은 예처럼 변수를 local로 처리하면 메모리가 깨끗하게 정리 됩니다.


Reducing Power Consumption (파워 소비 줄이기)

모바일 디바이스는 배터리 생명이 짧습니다. 개발자는 배터리 소비를 줄이기 위해 아래와 같은 사항들을 되도록 안 쓰도록 신경을 쓸 수 있습니다.

- Network traffic (Wi-Fi radios and baseband cell radios)

- GPS

- Accelerometers

- Disk accesses (reading/writing to files)


Network

위 요소들 중 네트워크 access가 가장 파워를 많이 사용합니다. 될 수 있는 대로 네트워크 트래픽을 줄여야 합니다.

아래 사항들을 참고 하세요.

- 필요할 때만 외부 네트워크 연결을 한다.

- 가능한 전달할 데이터의 사이즈를 최소화 한다.

- Transmit in bursts. More power is consumed the longer the radio is actively transmitting data. Therefore, transmit the data in bursts instead of spreading out the same data into smaller transmission packets over time.

- 위치 데이터는 GPS,cell, Wi-Fi 네트워크를 통해 얻어 집니다. 이 위치 데이터도 필요한 경우만 사용 합니다.



Graphics


Group objects



Turn off animations for non-visible objects

애니메이션이 non-visible이 될 경우 또는 그 객체가 스크린 밖에 위치할 때에는 가능한한 그 애니메이션을 멈춥니다.


Optimize image size

큰 이미지는 로드 할 때 더 많은 시간과 메모리를 소비합니다. 

이 큰 이미지가 더 이상 필요하지 않으면 parent group에서 이 객체를 remove합니다.

local image = display.newImage( "image.png" )

 

-- do stuff with image

 

image:getParent():remove( image )



Lua: Best Practice


컬 변수를 사용하라

글로벌 변수

for i = 1, 1000000 do 

   local x = math.sin(i) 

end  

컬 변수

local sin = math.sin 

for i = 1, 1000000 do 

   local x = sin(i) 

end 


특히 위와 같이 긴 프를 사용 할 경우 컬로 변수를 선하면 훨씬 가다. 위 드를 예로 들면 글로벌 변수 제는 컬 변수 다 30% 더 실행 시간이 느립다.


함수를 사용할 경우에 함수 밖에서 컬로 변수를 선언 하면 글로벌 변수도가 빠릅다.


글로벌

 function foo (x) 

   for i = 1, 1000000 do 

      x = x + math.sin(i) 

   end 

   return x 

end  


External local

local sin = math.sin 

function foo (x) 

   for i = 1, 1000000 do

      x = x + sin(i) 

   end 

   return x 

end 


이 경우도 글로벌 변수를 사용한 드가 30% 더 속도가 느립다.



x*0.5 x/2 다 빠릅다.

x*x x^2다 빠릅다.


배열에 객체 집어 넣기

t[#t+1] = itemtable.insert(t,item)다 빠릅다.


Constant Folding

i=111+111i=222 와 속도가 같습다. 하면 컴파일러는 그 값을 미리 계하기 때문입다.

1+2+x 는 (1+2) + x 로 주 되서 3+x와 같은 시간이 걸립다.

지만 x+1+2는  (x+1)+2로 주 되서 미리 계산하지 않아서 시간이 더 걸리게 됩다.


Cache properties in a local variable

어떤 값이 계속 사용된다면 Cashe에 올리는 것이 속도가 빠릅다.

Uncached

function foo (o)

   local n = 0

   for i = 1, 1000000 do

      -- lookup o.x each time 

      n = i + n * o.x

   end

   return n

end 


Cached

function foo (o)

   -- cache o.x before the loop

   local x = o.x

   local n = 0

   for i = 1, 1000000 do 

      n = i + n * x

   end 

   return n

end 



다음 시간에도 계속해서 여러 프로그래밍 테크닉에 대해서 알아 보겠습니다.

반응형

Corona SDK Native UI

2011. 11. 7. 23:45 | Posted by 솔웅


반응형

코로나 SDK의 native library는 다양한 native user interface를 제공합니다.

Activity Indicator

native.setActivityIndicator()

native.setActivityIndicator( true );
timer.performWithDelay( 10000,  function()
     native.setActivityIndicator( false )
end)
local bgImg = display.newImage( "background1.png" )

위 소스코드와 같이 true를 하면 아래 화면에서 보이는 것 처럼 디바이스 내의 Activity Indicator가 나옵니다. true인 동안에는 touch 같은 동작을 할 수 없게 됩니다. 이 소스코드는 10초동안 indicator가 true이고 그 이후는 평상시 처럼 동작하는 코드 입니다.



Alert
native.showAlert(title,message[,buttonLabels[,listener]])
popup alert 화면이 뜨고 거 화면에 버튼도 표시 됩니다. 애니메이션 같은 프로그램의 액티비티는 백그라운드에서 그대로 실행이 될 겁니다. 그러니까 게임 같은데서 replay를 위해 모두 멈춰야 되면 이 alert화면을 띄우기 전에 코딩으로 다 처리 해 줘야 되더라구요. 다만 touch 같은 유저와의 interactivity는 alert을 없애기 전 까지는 block 됩니다. button만 빼 놓으면 setActivityIndicator랑 조건이 비슷합니다.

title은 제목이고 message는 텍스트로 표시 될 내용입니다. 그 다음에 버튼을 표시하고 그 버튼을 누르면 실행할 리스너 함수를 넣을 수 있습니다.



아래 간단한 소스코드 보시겠습니다.

native.setActivityIndicator( true );
timer.performWithDelay( 3000,  function()
     native.setActivityIndicator( false )
end)
local bgImg = display.newImage( "background1.png" )

-- Handler that gets notified when the alert closes
local function onComplete( event )
        if "clicked" == event.action then
                local i = event.index
                if 1 == i then
                        -- Do nothing; dialog will simply dismiss
                elseif 2 == i then
                        -- Open URL if "Learn More" (the 2nd button) was clicked
                        system.openURL( "http://coronasdk.tistory.com" )
                end
        end
end
 
-- Show alert with five buttons
local alert = native.showAlert( "Corona", "Dream. Build. Ship.",
                                        { "OK", "Learn More" }, onComplete )

이 소스코드는 OK 와 Learn More 두개의 버튼이 뜨고 OK를 누르면 아무것도 안하고 Learn More를 누르면 위에 있는 웹 사이트로 가도록 한 코드 입니다.

이 showAlert에 넣을 수 있는 버튼의 갯수는 6개 까지 입니다. 이 버튼은 순서에 따라 event.index에 1~6번까지가 할당 됩니다.

event.action 에는 cancelled와 clicked 가 있습니다.

native.cancelAlert()
cancel버튼을 누르면 alert화면이 사라지지만 프로그래밍으로없애려면 이 구문을 씁니다. 예를 들어 alert화면이 뜨고 일정 시간 후에 자동으로 없어지게 만드는 기능 등에 사용 할 수 있겠죠?

-- Dismisses "alert" from previous code sample after 10 seconds
local function cancelMyAlert()
        native.cancelAlert( alert )
end
 
timer.performWithDelay( 10000, cancelMyAlert )


Fonts

native.newFont(name[,size])
원하는 폰트가 있으면 이 함수를 이용해서 폰트를 사용할 수 있습니다.

기본적으로는 아래 두 폰트를 사용할 수 있습니다.
native.systemFont , native.systemFontBold

native.getFontNames()를 통해서 사용 가능한 폰트들을 리턴 받을 수 있습니다.

Text Input

native.newTextField(left,top,width,height[,listener])
single-line textfield를 표시합니다.
앱 스토어에 올린 Spin the Bottle 1 개발 할 때 사용해 봤는데요. 이게 시뮬레이터에서는 지원이 안 됐습니다. 그래서 테스트 할 때마다 디바이스에 인스톨 하면서 테스트 해야 되서 불편하더라구요.
얼마전에 시뮬레이터에서도 지원이 된다고 해서 새 build 버전을 받았는데 아직 안정적이지 않아서 인스톨이 잘 안 되더라구요. 코로나 측에서 테스트 완료하고 곧 공개 할 것 같습니다.

TextField 의 property들은 아래와 같습니다.
object.align : left,center,right 가 있음
object.font : native.newFont()를 리턴합니다.
object.isSecure : 패스워드 같이 문자가 찍히지 않도록 합니다. 디폴트는 false로 문자가 찍히도록 돼 있습니다.
object.size : 텍스트의 크기
object.text : textfiend에 지정된 텍스트를 뿌려줍니다.

아래와 같은 메소드 들이 있습니다.
object:setTextColor(r,g,b[,a]) : 텍스트 칼라를 지정해 줍니다.



Listening for Keyboard Events
스마트폰에서 텍스트를 입력하려면 키보드가 나타납니다.
이 때 그 움직임을 catch 할 수 있는데요. 이것은 event.phase로 구분 합니다.
event.phase = "began" : 키보드가 나타났을 때
event.phase = "ended" : 텍스트 필드가 아니라 다른 필드를 누를 때 같이 사용자가 해당 텍스트 필드의 작업을 멈췄을 떄
event.phase = "submitted" : return 을 눌렀을 때

native.setKeyboardFocus(textField) : 키보드 포커스를 해당 텍스트 필드에 함. nil을 넣으면 포커스를 없애고 키보드를 사라지게합니다.


Supported Keyboard Types
input textfield에는 5가지 타입의 키보드가 지원 됩니다. 프로퍼티 이름은 "inputType"입니다.
numericField = native.newTextField( 50, 150, 220, 36, handlerFunction )
numericField.inputType = "number"

위 소스에서 number는 숫자만 입력할 수 있는 키보드가 나옵니다.
default는 일반적인 키보드 입니다.
phone 은 전화번호 입력을 위한 키보드 입니다.
url 은 웹 주소를 입력할 수 있는 키보드 입니다.
email 은 이메일 주소를 입력 할 수 있는 키보드 입니다.

Multiline Textfields
여러 줄을 입력할 수 있도록 하려면 텍스트 박스를 사용합니다.
native.newTextBox( left, top, width, height )
높이를 넘어가면 스크롤 바가 생깁니다.

프로퍼티들은 아래와 같습니다.
object.align : left,center,right 가 있습니다.
object.font : native.newFont()에 의해 리턴된 폰트 객체 입니다.
object.hasBackground 배경화면이 있으면 true이고 투명하면 false입니다.
object.size 텍스트 크기 입니다.
object.text 텍스트 박스 내의 문자 입니다.

메소드에는 칼라를 지정하는 메소드가 있습니다.
object:setTextColor(r,g,b[,a])


Web PopUps

이 웹 팝업을 사용하려면 아래와 같이 하면 됩니다.
native.showWebPopup( "http://coronasdk.tistory.com" )
특정 크기를 지정해 줄 수도 있습니다.
native.showWebPopup( 10, 10, 300, 300, "http://coronasdk.tistory.com" )

Web popup scaling
웹 팝업의 크기를 설정 하려면 아래와 같이 HTML 파일에 메타태그를 코딩해 넣으시면 됩니다.
<meta name="viewport" content="width=320; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"/>

웹 폰트 사이즈를 유지하려면 아래와 같이 CSS 코드를 추가합니다.
<style type="text/css">
html {
        -webkit-text-size-adjust: none;
}
</style>

Web Popup Options
웹 팝업 화면을 조정하려면 아래와 같은 6개의 파라미터로 조절 하실 수 있습니다.
native.showWebPopup( 10, 10, 300, 300, "http://coronasdk.tistory.com", {urlRequest=listener} )

옵션테이블의 아래 프로퍼티들을 사용하실 수 있습니다.

options.baseUrl : 베이스 url을 규정합니다.
options.hasBackground : 팝업에 백그라운드가 있는지 없는지에 대해 컨트롤 합니다.
options.urlRequest : 웹 페이지의 상대 경로를 사용할 수 있습니다. 예) interface/WebOverlay
<form action="corona:close">
        <input type="submit"/>
</form>
사용자가 웹 팝업의  Submit을 누르면 설정된 리스너 함수는 urlRequest 이벤트 객체를 보내집니다.

Removing the Web Popup
native.cancelWebPoppu()으로 팝업을 없앨 수 있습니다.

Handling Web Popup Events
옵션 테이블에 이벤트 핸들러를 urlRequest 프로퍼티에 제공하면 웹 팝업은 유저가 웹 페이지의 링크를 클릭할 때 이 이벤트를 핸들러에 전달됩니다. 아래와 같은 이벤트들이 전달 됩니다.
event.errorCode
event.errorMessage
event.name
event.url

이 이벤트 핸들러는 한번 클릭할 때마다 한번씩 실행됩니다.
function listener( event )
  --if no errors, then execute my code
  if event.errorMessage == nil then
          myOnClickHandler( event.url )
  end
  return true
end
 
local options = {
  baseUrl = system.ResourceDirectory,
  hasBackground = false,
  urlRequest = listener
}
 
native.showWebPopup( "test.html", options )

지난번 코로나에서 광고 다는 법 할 때 말한건데요.
코로나 SDK에서는 현재 공식적으로 inMobi 만 지원합니다.

그런데 이 웹 팝업을 이용해서 admob이나 iAd 같은 앱 광고도 넣을 수 있습니다.
공식적인 방법은 아니고 개발자들 끼리 방법을 개발해서 그 코드를 공유하고 있습니다.

인터넷을 찾아보시면 나올겁니다.
저도 이 방법을 배우게 되면 이곳에 글로 정리해 두겠습니다.

그럼...

반응형

SlideView 구현하기

2011. 11. 4. 22:36 | Posted by 솔웅


반응형
어떤 분이 질문을 하나 올려 주셨습니다.
비밀 댓글로 올려 주신걸로 봐서 구체적인 내용이 알려지길 원하시지는 않는 것 같네요.
그 분의 질문을 보면 SlideView 구현에 대해 알면 해결 될 수 있을 것 같습니다.
그래서 오늘은 SlideView에 대해서 알아 보도록 하겠습니다.


이 이미지는 Corona SDK를 깔면 기본으로 제공되는 샘플 코드 입니다.
사진을 좌우로 움직일수 있고 Terminal에 보면 현재 사진이 몇번 째 인지 그리고 dragDistance는 얼마인지 그리고 총 몇개의 이미지 중 몇번째인지 등의 정보가 나옵니다.

코로나 SDK에서 제공되는 샘플을 보면 원하는 기능을 생각보다 쉽게 구현하는 방법을 알 수 있습니다. 적극적으로 활용하기를 바랍니다.

일단 SlideView를 쉽게 구현하도록 코로나에서는 따로 클래스 파일을 제공합니다.

이 소스 코드도 열어서 분석 해 보시면 그렇게 어렵지는 않습니다.
그건 각자 해 보시고 오늘 글에서는 이 기능을 이용하는 방법을 살펴 보겠습니다.
일단 소스를 보겠습니다.

display.setStatusBar( display.HiddenStatusBar )

local slideView = require("slideView")
   
local myImages = {
    "myPhotos1.png",
    "myPhotos2.png",
    "myPhotos3.png",
    "myPhotos4.png"
}       

--slideView.new( myImages )
--slideView.new( myImages, "bg.png" )
slideView.new( myImages, nil, 40, 60 )

별거 없습니다. 이렇게 단 몇줄로 slideView 기능이 구현 됐습니다.
첫줄은 statusBar를 없애는 것이고
그 다음 줄이 slideView.lua를 이용하기 위해서 require 한 겁니다.
그리고 slideView에 넣을 이미지를 테이블(배열) 에 넣구요.
slideView.new()를 해 주시면 됩니다.
slideView.lua의 36번째 줄을 보시면
function new(imageSet,slideBackground,top,bottom) 으로 함수가 시작합니다.
전달하는 파라미터를 이미지세트와 백그라운드 이미지 그리고 top과 bottom값을 전달 할 수 있습니다.
아까 terminal에 뿌려줬던 텍스트들은 main.lua에 있지 않고 slideView.lua 파일 안에 있네요.
전체 소스 파일은 아래 파일을 다운 받으세요.


다음 예제를 소개해 드리겠습니다.


코로나 샘플 소스코드와 똑 같습니다. 다만 다른 점이 있다면 이미지세트를 불러오는게 아니라 다른 별도의 파일들을 불러오는 겁니다.
display.setStatusBar( display.HiddenStatusBar )

local slideView = require("slideView")
   
local mySlides = {
    "screen1",
    "screen2",
    "screen3",
}       

slideView.new( mySlides )

이것도 소스는 간단합니다. 단지 mySlides라는 테이블(배열)에 파일 이름이 들어가 있습니다.
대표로 screen1을 보시겠습니다.

module(..., package.seeall)

function new()
    local g = display.newGroup()
   
    local background = display.newRect(0,display.screenOriginY, display.contentWidth, display.contentHeight-display.screenOriginY)
    background:setFillColor(222, 222, 222)
    g:insert(background)
   
    local message = display.newText("Screen 1", 0, 0, native.systemFontBold, 16)
    message:setTextColor(0, 0, 0)
    message.x = display.contentWidth*0.5
    message.y = display.contentHeight*0.5
    g:insert(message)
   
    function g:cleanUp()
        g:removeSelf()
    end
   
    return g
end

첫번째는 다른 file(main.lua) 에서 require하려면 설정해 줘야 되는 모듈 부분입니다.
그리고 function new() 함수가 있습니다.
g라는 그룹이 그 다음줄에서 정위 되었구요. 배경 화면으로 사용될 사각형이 그려졌습니다. 이 사각형의 변수 이름은 background이고 color가 정해지고 그룹에 insert됐습니다.
그 다음에는 Screen이라는 텍스트를 message라는 변수에 담아서 display합니다.

이 샘플 예제는 아래 파일을 참조하세요.


Slide View 관련해서 알아 봤는데요.

혹시 코로나로 개발하시거나 공부하시면서 질문 사항 있으시면 주저마시고 댓글로 올려 주세요.

저도 찾아 보면서 공부가 많이 되네요.

혹시 질문 올리신 분께 충분히 답변이 됐는지 모르겠습니다.
더 질문이 있으시면 댓글 달아 주세요.

그럼....
반응형

Location and Maps

2011. 11. 4. 18:37 | Posted by 솔웅


반응형
Location and Maps

MapView는 구글 맵을 앱에서 사용할 수 있게 해 줍니다.
코로나에서의 MapView는 현재까지 iOS만 지원 됩니다. 안드로이드에서 안될 뿐만 아니라 코로나 시뮬레이터에서도 안 됩니다.
테스트 하려면 맥에 XCode 시뮬레이터 깔아 놓고 하던가 애플 디바이스를 구입하셔야 합니다.

Constructor
myMap =  native.newMapView(left,top,width,height)

특정 영역에 MapView를 표시하고 코로나 display object를 반환합니다. 이 object는 다른 display object처럼 움직이거나 회전할 수 있습니다.

Map attributes
myMap.mapType = "standard"
myMap.mapType = "satellite"
myMap.mapType = "hybrid"

mapType은 일반 타입의 지도, 위성에서 보는것 같은 타입의지도, hybrid지도를 선택할 수 있습니다.
안드로이드나 애플 앱을 이용해서 구글 맵 표시를 해 보셨다면 쉽게 이해 하실 겁니다.

myMap.isZoomEnabled = true
확대 축소를 가능하게 해 줍니다. 디폴트는 true입니다.

myMap.isScrollEnabled = true
스크롤을 가능하게 해 줍니다. 디폴트는 true 입니다.

myMap.isLocationUpdating = true
현재 위치를 표시할 수 있도록 합니다. 사용자가 이동하면 계속 업데이트 될 겁니다. 디폴트는 false입니다.

isVisible= myMap.isLocationVisible
표시되고 있는 지역 내에 현재 위치를 표시할 수 있는지 없는지를 리턴합니다.
이 값은 read only 입니다.


Map functions

latitude, longitude = myMap:getAddressLocation( "gough and post, sf" )
latitude, longitude = myMap:getAddressLocation( "120 university, palo alto, ca" )
latitude, longitude = myMap:getAddressLocation( "eiffel tower" )

주어진 스트링으로 검색해서 그 위도와 경도 값을 리턴합니다. 이것은 구글 맵 HTTP 메소드를 이용하게 됩니다. 지도에 마커를 표시할 수도 있고 해당 지점이 중앙으로 오도록 할 수도 있습니다.

myLocation = myMap:getUserLocation
사용자의 현재 위치를 반환합니다. 그 내부의 값들은 아래와 같습니다.

myLocation.longitude
myLocation.latitude
myLocation.altitude
myLocation.accuracy
myLocation.time
myLocation.speed
myLocation.direction
myLocation.isUpdating -- a Boolean that flags whether the location is currently updating

myMap:setRegion(latitude,longitude,latitudeSpan,longitudeSpan,isAnimated)

화면에 출력하는 지역을 새로운 지역으로 바꿉니다.

myMap:setCenter(latitude,longitude,isAnimated)

화면에 출력하는 지역을 해당 위도 경도를 중앙으로 하는 지역으로 바꿉니다.

myMap:addMarker(latitude,longitude,{title="Displayed Title", subtitle="subtitle text"})

지도위 특정 지점에 핀을 표시합니다. 그리고 그 핀을 터치하면 위 텍스트가 표시됩니다.

myMap:addMarker(latitude,longitude)
특정 지점에 핀을 표시합니다.

myMap:removeAllMarkers()
핀을 모두 제거합니다.

Address Lookup from Position (Reverse Geocoding)
위도와 경도는 대략적인 주소값으로 바꿀 수 있습니다. 이것을 하려면 구글의 reverse geocoding server에 해당 값을 주고 원하는 값을 받는 작업을 해야 합니다. 그러므로 이 결과 값은 구글이 가지고 있는 자료의 정확성에 따라서 오차가 날 수 있습니다.

이 기능을 사용하기 위해서 mapAddress 이벤트 리스너를 activate 시켜야 합니다. 이 리스너가 액티브 상태이면 nearestAddress() 함수로 근처의 주소 데이터를 가지고 올 수 있습니다.

local function mapAddressHandler( event )
    -- handle mapAddress event here
    print( "The specified location is in: " .. event.city .. ", " .. event.country )
end
 
myMap:nearestAddress( latitude, longitude )
 
Runtime:addEventListener( "mapAddress", mapAddressHandler )

이 이벤트 객체는 다음과 같은 attributes를 가진 핸들러 함수를 리턴합니다.
event.street -- the street name
event.streetDetail -- the street number (or other specifier for location on the street)
event.city -- the city or town
event.cityDetail -- additional city information, such as neighborhood
event.region -- the state, province, or similar region within the country
event.regionDetail -- the region below the state level (e.g., the county, in the U.S.)
event.postalCode -- the postal code
event.country -- the country name
event.countryCode -- the standard country abbreviation

만약에 에러가 있으면 event.isError 에 true값이 할당 됩니다.


위 지도 이미지들은 각각 mapType을 normal, satellite, hybrid로 한 것입니다.
소스를 볼까요?

display.setStatusBar( display.HiddenStatusBar )
_W = display.contentWidth;
_H = display.contentHeight;

-- Create a native MapView (requires XCode Simulator build or device build)
-- You can create multiple maps, if you like...
--
myMap = native.newMapView( 0, 0, _W, _H )
--myMap.mapType = "normal" -- other mapType options are "satellite" or "hybrid"
--myMap.mapType = "satellite"
myMap.mapType = "hybrid"

 
-- The MapView is just another Corona display object, and can be moved or rotated, etc.
myMap.x = display.contentWidth / 2
myMap.y = 120
 
-- Initialize map to a real location, since default location (0,0) is not very interesting
myMap:setCenter( 37.331692, -122.030456 )
 
local function callMap()
        -- Fetch the user's current location
        -- Note: in XCode Simulator, the current location defaults to Apple headquarters in Cupertino, CA
        local currentLocation = myMap:getUserLocation()
        local currentLatitude = currentLocation.latitude
        local currentLongitude = currentLocation.longitude
       
        -- Move map so that current location is at the center
        myMap:setCenter( currentLatitude, currentLongitude, true )
       
        -- Look up nearest address to this location (this is returned as a "mapAddress" event, handled above)
        myMap:nearestAddress( currentLatitude, currentLongitude )
end
 
-- A function to handle the "mapAddress" event (also known as "reverse geocoding")
--
local mapAddressHandler = function( event )
        local locationText =
                "Latitude: " .. currentLatitude ..
                ", Longitude: " .. currentLongitude ..
                ", Address: " .. event.streetDetail .. " " .. event.street ..
                ", " .. event.city ..
                ", " .. event.region ..
                ", " .. event.country ..
                ", " .. event.postalCode
               
        local alert = native.showAlert( "You Are Here", locationText, { "OK" } )
end
 
-- A listener for the address lookup result
-- (This could also be a table listener on the map itself, in case you have more than one simultaneous map.)
Runtime:addEventListener( "mapAddress", mapAddressHandler )
 
timer.performWithDelay( 1000, callMap )         -- get current location after 1 second

첫 세줄은 status바를 없애고 화면 너비와 높이를 구한 부분입니다.
첫번째로 myMap이라는 변수에 mapView를 대입합니다. native.newMapview를 이용하는데요 이때 x,y,너비,높이 를 지정해서 표시되는 지도영역의 크기를 설정할 수 있습니다.
이 영역은 코로나의 display object로서 그 위치와 크기를 재설정 할 수 있고 여러개를 만들수도 있습니다. 경우에 따라서는 움직이게 할 수도 있겠구요. 만약에 필요하다면 말이죠.

그 다음은 setCenter 로 위도와 경도를 맞춰 줍니다. 그러면 그 위도와 경도가 화면 중앙에 표시 될 겁니다.

그 다음 callMap() 함수에는 현재 유저가 있는 위치와 현재 위치의 위도 경도를 구해서 이것으로 setCenter를 다시 해 줍니다.

그리고 가장 가까운 곳의 주소지를 얻어 옵니다.

그 다음 mapddressHandler 함수에서는 위도, 경도, 거리, 도시, 지역, 국가, 우편번호 등의 정보를 표시할 수 있도록 해 줍니다.

마지막에는 이 두 함수를 불러올 리스너들이 있습니다.

코로나 SDK의 mapView도 아주 간편하게 사용할 수 있네요.

이 소스를 기본으로 이것 저것 많이 시도해 보시면 좋을 것 같네요.

맥이 있어야 되고  xCode가 세팅 돼 있어야 되든가 아이폰 같은 애플 디바이스가 있어야 되는 한계가 있지만요.

그럼...
반응형