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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
Posted on

. Written by


수요일 FAQ 시간입니다. 코로나에서 custom fonts 를 사용하는것과 관련해서 자주 질문되는 것들입니다.

1. How do I load custom fonts?

어떻게 사용하는 platform 에 따라서 폰트들을 load 할 수 있나요?


iOS (iPhone, iPad)


iOS (아이폰, 아이패드) 는 build.settings 파일에 font 파일 이름을 추가하면 됩니다.


    iphone =
    {
        plist =
        {
            UIAppFonts =
            {
                "PTF55F.ttf",
                "AvenirLTStd-Black.otf",
                "SourceCodePro-Black.ttf"
            },
            UIApplicationExitsOnSuspend = true
        },
    }


폰트파일들은 resource 디렉토리에 있어야 합니다. 그래야지 iOS app bundle 로 compile 될 수 있습니다.


Mac


맥은 이 폰트들을 여러분의 시스템 안에 인스톨 해야 합니다. 폰트파일을 더블클릭하시면 Font Book app 이 그 폰트를 인스톨 할 겁니다. Mac 에서는 build.settings 파일에 추가할 것은 없습니다.


Android


폰트 파일이 resource 디렉토리에 있어야 합니다. 그래야 안드로이드 앱 bundle 로 컴파일 될 수 있습니다. 안드로이드에서는 build.settings file에 추가될 것은 없습니다.


Windows


윈도우에 custom font를 인스톨하는 것은 맥에서 인스톨하는 거랑 비슷합니다. 그 폰트를 사용하려면 시스템에 폰트를 인스톨 해야 합니다. 윈도우에서는 build.settings file에 추가될 것은 없습니다.



2. How do I use the custom fonts in my app?


Mac and iOS


맥과 iOS 에서 폰트를 사용하려면 폰트 이름을 명시해야 합니다. (파일이름이 아닙니다.) 해당 폰트의 이름이 무엇인지 알려주는 툴이 있을 겁니다. 제가 찾은 가장 쉬운 방법은 폰트를 load 하고 코로나에게 폰트 이름을 표시하도록 하는 겁니다. 시스템에 해당 폰트를 인스톨 했다면 맥 시뮬레이터에서 아래 코드를 실행해 보세요.


-- Code to have Corona display the font names found
--
local fonts = native.getFontNames()

count = 0

-- Count the number of total fonts
for i,fontname in ipairs(fonts) do
    count = count+1
end

print( "\rFont count = " .. count )

local name = "pt"     -- part of the Font name we are looking for

name = string.lower( name )

-- Display each font in the terminal console
for i, fontname in ipairs(fonts) do
    j, k = string.find( string.lower( fontname ), name )

    if( j ~= nil ) then

        print( "fontname = " .. tostring( fontname ) )
    
    end
end
---------------------------------------------------------


예를 들어 PTF55F는 “PTSerif-Regular” 가 될 겁니다. 바로 이 이름이 해당 폰트를 display 하고 싶을 때 명시해야 할 이름입니다.


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PTSerif-Regular", 24 )

Android


안드로이드에서는 파일 이름이 폰트 이름입니다. (파일의 확장자를 제외한 이름이죠)


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PTF55F", 24 )


안드로이드와 iOS 그리고 맥에서 같은 폰트 이름을 사용할 수 있는 팁이 있는데요. 폰트 파일 이름을 폰트 이름과 같이 하는 겁니다. 그려면 iOS/Mac 그리고 안드로이드에서 같은 이름을 사용할 수 있습니다.


Windows


The font name may be slightly different from Mac/iOS. In come cases spaces are inserted between words in the font name. In the PT Serif example, the font name is “PT Serif”. You can run the code snippet listed under the Mac and iOS section to get the exact name.

폰트 이름은 MAC/iOS 와는 약간 다릅니다. PT Serif 를 예를 들면 폰트 이름은 "PT Serif" 입니다.


CustFont = display.newText( "PTSerif-Regular", 40, 20, "PT Serif", 24 )


아래 예제는 각 플랫폼에 대해 폰트를 사용하는 예제입니다.


if "Win" == system.getInfo( "platformName" ) then PTSERIF = "PT Serif" elseif "Android" == system.getInfo( "platformName" ) then PTSERIF = "PTF55F" else -- Mac and iOS PTSERIF = "PTSerif-Regular" end CustFont = display.newText( "PTSerif-Regular", 40, 20, PTSERIF, 24 )


3. I’ve loaded the fonts but the fonts didn’t load. What’s wrong?


안드로이드와 윈도우즈에서는 폰트를 로드하지 못할 때는 터미널에 경고 메세지가 뜰겁니다.“WARNING: Could not load font xyz. Using default.”. iOS 와 맥에서는 로드를 하지 못했을 때 아무런 메세지도 뜨지 않습니다.

폰트를 로드하지 못하는 두가지 흔한 이유는 시스템이 로드되지 못한경우라던가 (윈도우나 맥) display.newText api에 잘못된 폰트 이름을 사용했을 경우 입니다. 윈도우와 관련해서 추가적으로 알아야할 것이 있는데 그 내용은 아래 질문 4에서 확인하세요.


4. Are OTF (Open Type Font) fonts supported in Corona?


OTF fonts는 맥과 iOS 에서 지원됩니다. 안드로이드는 build 984 후부터 지원하기 시작했습니다. 윈도우에서는 지원하지 않습니다. True Type Fonts (TTF) 는 모든 플랫폼에서 지원합니다.


5. What are the advantages of using Custom Fonts?


일단 custom font를 사용하면 더 이쁘거나 아니면 더 눈에 띄거나 앱에 더 잘 어울리거나 하겠죠. 그리고 또 다른 장점은 모든 플랫폼에서 text placement 의 일관성을 유지할 수 있습니다. 디폴트 폰트를 사용하면 각 플랫폼들마다 폰트가 다를수 있습니다. 그러면 폰트들 크기가 달라서 폰트들의 위치가 약간씩 다를 수 있습니다. custom font 를 사용하면 좀 더 쉽게 font placement 의 일관성을 지킬수가 있죠. 


여기까지가 오늘의 questions 입니다. 좋은 시간 되셨길 바랍니다.



반응형

코로나에서 time, date 다루기

2013. 1. 18. 06:49 | Posted by 솔웅


반응형
Posted on . Written by

“Time keeps on slippin’, slippin’, slippin’, into the future.” —Steve Miller Band / Fly Like An Eagle


어떤 프로그래밍 언어로 time 관련해서 작업을 하는 것은 프로그래머에게 혼란을 주게 됩니다. 이번주의 튜토리얼은 time 과 date 에 대해 다룹니다. 그리고 date 계산, time zone 문제 그리고 date formatting 관련해서 예제를 보여드리겠습니다.


코드를 보기전에 먼저 이해해 둬야할 몇가지 중요한 개념들이 있습니다. 컴퓨터에게는 time 의 기본 유닛은 1초 입니다. 이 값으로 우리는 date 도 계산할 수 있고 fraction 들도 다루고 심지어 time 의 더 작은 순간들도 계산해 냅니다. 사람에게 1초는 짧은 시간이지만 컴퓨터에게는 아주 긴 시간입니다. 사람들도 긴 시간의 경우 세분화해서 사용하듯이 컴퓨터도 마찬가지 입니다. 어떤 함수는 1/1000 초(milliseconds ) 단위로 작업을 하고 이것을 uSec 나 uSeconds으로 쓰기도 합니다. 더 좋은 컴퓨터는 이것보다 더 짧은 시간을 다루기도 합니다. 코로나에서는 3가지 종류의 time measurement 를 사용합니다.




Microseconds and Milliseconds


Microseconds 는 아래와 같이 call 하시면 됩니다 :


system.getTimer()


이 call은 여러분의 app 이 시작한 이후의 시간을 return 해 줍니다. 그 값은 milliseconds (1/1000ths) 단위 입니다. 대부분의 device들에서는 아래와 같은 값을 return 할 겁니다.


1839.3949


이 time은 microsecond resolution 으로 측정되는데요 여러분에게 보일 때는 milliseconds 로 변환해서 보여드리는 겁니다. 그리고 소숫점 아래는 microseconds 를 나타내죠. 이 마이크로세컨드 단위가 필요한 경우가 있을지 궁금해 하실수도 있습니다. 아마 실제로는 필요가 없겠죠. 아마 GPS 데이터를 다루거나 Einstein’s Pedometer (앱 이름인데요. 여러분이 빠르게 움직이면 얼마나 젊어지는지를 보여주는 앱입니다.) 작업을 하게 되면 이 microsecond resolution 이 필요할 수도 있습니다. 하지만 우리의 앱에서는 1초당 30~60 프레임을 사용할 거니까 1/30 에서 1/60 초 단위로 작업을 하게 될 겁니다. 그러니까 1/1000 초 보다 더 정교한 시간계산은 이 앱에서는 그다지 중요하지는 않습니다.


milliseconds와 관련해서 사용하는 함수들은 아래와 같습니다.


이 함수들은 millisecond 단위를 accept 합니다. 그러니까 이 함수에 값을 5000을 주면 그 시간은 5초가 되는 겁니다. 1/2 초는 500이 되겠죠. 2와 1/2 초는 2500이 됩니다.


timer.performWithDelay(500, someFunction)  -- a half second delay


초를 원하는 값으로 바꾸시려면 1000을 곱하면 되겠죠.


Dealing With Longer Periods of Time


위에서 언급했듯이 컴퓨터에서 time 의 기본적인 유닛은 1초입니다. iOS와 안드로이드 (Mac OS X 도 마찬가지로) 모두 Unix 에서 파생된 운영체제에 근거해 있습니다. 그리고 그런 운영체제에서 표준 "time" 함수는 1970년 1월 1일 자정 이후부터 경과된 시간을 초단위로 return 해 줍니다.  마이크로소프트는 좀 다른데요, 하지만 이미 많은 앱들이 C언어를 사용해서 만들어졌고 이 언어는 Unix 에 기반해 있거든요. 그리고 그 언어의 라이브러리도 이 time reference pointEpoch를 사용하구요.


마이너스일 경우에는 1970년 이전이 되겠죠. positive times 일 경우에는 1970년 이후가 되겠구요. 코로나에서는 os.time() API call을 사용해서 1970년 1월 1일 이후 지금까지의 number of seconds 를 얻을 수 있습니다.


print( os.time() )  -- outputs something like: 1358015442


사실 저 숫자만 나온다면 알아볼수가 없으니까 별로 의미가 없겠죠. 이것을 date math로 계산해서 실제 생활에서 쓰이는 날짜와 시간으로 바꿔야 알아볼 수가 있을 겁니다. 그런데 게임을 한번 생각해 보세요. turn-based game 이 있는데 유저가 너무 오랫동안 게임을 하고 있어서 어떤 "nudge" 가 필요한 경우 아래와 같이 마지막 move의 시간을 저장할 필요가 있을 겁니다.


player[1].lastMove = os.time()

그리고 나서 지난 한시간 동안 어떤 move 가 있는지 체크하고 싶으면 아래와 같이 하시면 됩니다.


local now = os.time()
if ( now > player[1].lastMove + 3600 ) then
  -- nudge the player
end


3600은 어디서 왔을까요? 60초 곱하기 60분을 하면 그 값입니다. 즉 1시간이죠. 그러니까 아래와 같이 하셔도 됩니다.


local now = os.time()
if ( now > player[1].lastMove + (60 * 60) ) then
  -- nudge the player
end

Lua 의 pre-processor 는 (60*60) 를 3600으로 convert 할겁니다. 그러면 여러분이 따로 미리 계산할 필요는 없겠죠. 아래와 같이 미리 날짜, 시간, 분을 상수로 정의해서 사용하셔도 됩니다.


DAY = 24 * 60 * 60
HOUR = 60 * 60
MINUTE = 60

local now = os.time()
if ( now > player[1].lastMove + HOUR ) then
  -- nudge the player
end


이렇게 초단위로 다룬 값으로 date math 를 사용하는 것은 아주 쉽습니다.


  • Is your time older than a week? Use ( 7 * 24 * 60 * 60 ).
  • Set an alarm in 30 days? Use ( 30 * 24 * 60 * 60 ).


또한 한달에 몇일이 있는지, 윤년인지 아닌지 등등도 계산할 것을 걱정하지 않으셔도 욉니다.

Working With Dates


Dates(날짜)와 관련해서는 조금 신경을 쓰셔야 됩니다. 왜냐하면 날짜를 표시할 때는 strings 를 사용하니까요.


  • April 1, 2010 4:53pm
  • April 1, 2010 4:53 P.M.
  • 4/1/10 16:53 MT
  • Sun Jan 13 15:17:32 EST 2013
  • 13-JAN-13 15:17


개발자로서 날짜를 component values로 parse 할 필요가 있습니다. date/time string의 각 파트별로 get 하셔야 합니다. (month, day, year, hour, minute, seconds and time zone). 만약에 month를 string 으로 가지고 있다면 이 string 을 number 로 그리고 timestamp로 convert 할 lookup table 을 사용하실 수 있습니다.


local monthNumbers = {}
monthNumbers["January"] = 1
monthNumbers["February"] = 2
monthNumbers["March"] = 3
--etc...
monthAsInteger = monthNumbers[monthString]


가장 일반적인 date format 중의 하나는 ISO-8601 time format입니다. 그 string 값은 대충 아래와 같습니다.


2012-01-12T12:04:35.03-0400


이 date 를 parse 하시려면 이 여러 bit들을  패턴을 사용한 각각의 값으로 break down 하기 위해 string:match string function 을 사용할 겁니다.


local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin =
  dateString:match( pattern )

Lua 를 처음 접하시는 분들 에게는 위 코드가 많이 복잡하게 보일 겁니다. 신택스가 그렇게 간단하지는 않죠? 이해하기 쉽도록 break down 해 보죠.


2012-01-12   T   12:04:35.03   -0400


T 이전 부분은 date 입니다. 알아보기 쉽고 또 나누기 쉽죠.  연,월,일이 숫자로 있고 각각 hyphen 으로 나눠져 있습니다. T는 시간 부분이 시작한다는 의미입니다. 그 다음에는 시,분,초를 가리키는 숫자들이 나오고 각각 colon들로 나눠져 있죠. 이 중 초단위는 floating point number 라는 것을 기억해 두세요. 그 다음부분에는 옵션으로 time zone이 나옵니다. 이 timezone 과 관련해서는 조금 더 얘기를 나눌 겁니다.


string:match() function은 어떤 패턴을 보여주는데요. string 의 span 에 대해 “wildcard” type searching 을 사용하는 그런 패턴이죠. 위의 경우에는 %d+ pattern을 상요해서 숫자를 찾고 있습니다. 이렇게 함으로서 Lua 에게 0에서 9까지의 숫자열을 찾도록 만들죠. + 표시는 한개 이상의 숫자열을 찾는 다는 것을 알려 주는 겁니다. 예를 들어 2012 같은 숫자열을요.


이 값들을 알아볼 수 있는 변수에 저장하기 위해 괄호 () 안에 어떤 pattern set들을 넣게 됩니다. 그러면 Lua에게 우리가 retrieve 하기 원하는 date/time 의 각각의 단위들을 알려줄 수가 있습니다. date 부분은 hyphen 들이 있는데요. 이것은 각 컴포넌트를 분리하는 기호입니다.


(%d+)%-(%d+)%-(%d+) -- gets the year, month and day
%a -- handles the single letter T
(%d+)%:(%d+)%:([%d%.]+) -- gets the hour, minutes and seconds



마지막 부분을 보세요. 숫자하고 점(.) 이 있죠? 초가 floating point value 이기 때문에 이렇게 찾는 겁니다. string matching에서 점(.) 은 “magic character” 입니다. 이 점 앞에 % 를 넣으셔야 Lua 는 실제 점(.) 을 찾을 겁니다.


마지막으로 + 나 - 구분자 다음에 timezone 정보가 옵니다. 혹은 Z 나 UTC 를 사용하기도 하죠. string:match function 은 각각의 match 되는 패턴들에 대한 값들을 return 할 겁니다. 그리고 그 값들을 변수에 담게 되죠. ISO-8601 standard는 timezone time 을 +/-HHMM+/-HH:MM를 사용합니다. 만약 이 timezone 정보가 없으면 여러분이 속해 있는 local timezone 을 사용할 겁니다. 아래의 패턴은 좀 복잡해 보이긴 하는데요. timezone 정보를 처리하는 코드 입니다.


([Z%p])(%d%d)%:?(%d%d)


Z 나 punctuation character 인 + 나 - 다음에 %p가 옵니다. 그 다음에는 두자리 숫자가 와야 됩니다. 그 다음에 colon 은 옵션인데요. 두개의 숫자가 더 올 수도 있습니다. 이 패턴은 tzoffset, offsethour, offsetmin등을 return 합니다. 이것을 date math 에서 사용할 숫자로 convert 하기 위해 os.time() function (documentation) 을 사용합니다.


local timestamp = os.time(
  {year=year, month=month, day=day, hour=hour, min=minute, sec=seconds} )



이제 1970년 1월 1일 이후 몇초가 경과했는지에 대한 값을 갖게 됐습니다. 그리고 이것을 date math 로 이용할 수 있게 됐구요. 한가지 문제가 있는데요. 아직까지 timezone 부분에 대해 일을 마치지는 않았습니다. 다행히 그 작업은 어렵지 않은데요. 지금 이미 기본 시간을 seconds 로 갖고 있으니까 계산만 하면 되곘죠. Z는 time 이 Coordinated Universal Time (UTC)라는 것을 가리키는 겁니다. 만약 이것이 UTC 나 local time 이라면 따로 timezone 과 관련해서 작업할 것은 없습니다.


local offset = 0
if ( tzoffset ) then
  if ( tzoffset == "+" or tzoffset == "-" ) then  -- we have a timezone!
    offset = offsethour * 60 + offsetmin
    if ( tzoffset == "-" ) then
      offset = offset * -1
    end
    timestamp = timestamp + offset
  end
end


os.time()은 여러분이 있는 곳의 timezone 을 사용해서 값을 return 할 겁니다.


아래 코드는 여러분이 라이브러리로 사용할 수 있는 샘플 예제 입니다.


function makeTimeStamp(dateString)
  local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%p])(%d%d)%:?(%d%d)"
  local year, month, day, hour, minute, seconds, tzoffset, offsethour, offsetmin =
    dateString:match(pattern)
  local timestamp = os.time(
    {year=year, month=month, day=day, hour=hour, min=minute, sec=seconds} )
  local offset = 0
  if ( tzoffset ) then
    if ( tzoffset == "+" or tzoffset == "-" ) then  -- we have a timezone!
      offset = offsethour * 60 + offsetmin
      if ( tzoffset == "-" ) then
        offset = offset * -1
      end
      timestamp = timestamp + offset
    end
  end
  return timestamp
end


Final note:  여기서는 아직 Daylight Savings Time(써머타임)을 적용하지는 않은 상황입니다. Daylight Savings Time 은 이 튜토리얼에서 다루지 않습니다.


Converting a Timestamp to Something Readable


어떤 경우에는 이 time 을 유저가 알아볼 수 있는 date string으로 표현할 필요가 있을 겁니다. 이게 바로 os.date() function의 목적이죠. (documentation).


os.date() function 은 두개의 파라미터를 받습니다. 둘 다 옵션인데요. 이 옵션 없이 call 하면 현재 있는 지역의 timezone 에 맞는 date/time 을 return 할 겁니다. 아마 아래와 같은 형식일 겁니다.


Sat Jan 12 14:07:30 2013


아주 다양한 형식으로 표현할수 있는데요. 다양한 formatting parameter 들을 이용하시면 됩니다. 이 format parameter들은 Unix/C++ library function strftime에 근거합니다.


print( os.date("%A") )  -- prints out a string representing the weekday.
print( os.date("%A %l:%M%p") )  -- prints out "Saturday 2:30PM"

디폴트로 os.date()가 return 하는 값은 여러분의 지역이나 time zone 에 근거할 겁니다.  RSS feeds, Corona Cloud date 등에서 일반적으로 사용하는 위에서 사용한 날짜 형식을 생성하기 원하신다면 UTC 에 근거한 output 을 받아야 합니다. 이것은 format parameter 앞에 ! 를 붙이시면 됩니다.


print( os.date("%FT%X%z") )  -- outputs: 2013-01-12T15:30:09-0500

or if you wanted it in UTC:

print( os.date("!%FT%XZ") )  -- outputs: 2013-01-12T19:30:09Z



strftime에 있는 다양한 formatting parameter 들은 다양한 format 으로 month, day, year, weekday, am/pm를 얻을 수 있도록 합니다. 



Counting Down Time


흔히 하는 질문으로 "어떻게 countdown timer 를 만들수 있죠?" 가 있습니다.

가장 좋은 방법은 직접 한번 구현해 보는 걸겁니다. 만약에 60안에 한 레벨을 끝내기를 원한다면 60초 후에 뭔가 이벤트를 발생시키기 위해 timer.performWithDelay()를 사용하시면 됩니다. 보기 좋은 UI output 과 함께 좀 더 그럴듯한 방법을 원하신다면 enterFrame event 에 Runtime listener를 사용하실 수도 있습니다. 그렇게 하면 시간을 체크할 수 있습니다. 혹은 1초 timer 를 계속 반복할 수도 있겠죠. 그럼 필요할 때 쉽게 pause 시키고 또 resume 시킬 수 있을 겁니다.


-------- RUNTIME METHOD --------
local startTime = os.time()
local levelTime = 60
local displayTime = display.newText(levelTime, 0, 0, "Helvetica", 20)

local function checkTime(event)
  local now = os.time()
  displayTime.text = levelTime - (now - startTime)
  if ( now > startTime + levelTime ) then
    -- code to end the level
  end
end
Runtime:addEventListener("enterFrame", checkTime)
-------- REPEATING TIMER METHOD --------
local levelTime = 60
local displayTime = display.newText( levelTime, 0, 0, "Helvetica", 20 )

local function checkTime(event)
  levelTime = levelTime - 1
  displayTime.text = levelTime
  if ( levelTime <= 0 ) then
    -- code to end the level
  end
end
timer.performWithDelay( 1000, checkTime, levelTime )



In Summary


Corona에서 time 과 date를 다루는 것은 약간 복잡한 신택스를 다뤄야 합니다. 이 튜토리얼이 그것을 이해하는데 도움이 되셨으면 합니다. 위의 예제들을 활용해서 앱 개발시 이용하시면 훨씬 수월하실 겁니다.


반응형

2012년의 Corona App 스타들

2013. 1. 12. 21:05 | Posted by 솔웅


반응형
Posted on . Written by



지난해에는 코로나로 만든 앱과 게입들이 금맥을 이루는 시기였습니다. 매주 저희 팁들은 그 주에 나온 여러 코로나 앱들 중 기록해 둘만한 앱을 하나씩 선정했었습니다. 6월부터 커뮤니티의 투표를 통해서 App of the Week 중에 그 달의 우승자를 선정하기 시작했죠. 매달 열정인 게임 유저들과 앱 유저들 그리고 코로나 커뮤니티 멤버들이 이 winner 를 선정하기 위해 수백명이 투표에 참여해 주셨습니다.


App of the Week에 선정되려면 어떻게 해야 되는지 궁금하신 분들을 위해서 방법을 알려드리자면요. 아주 간단합니다. 여러분이 개발한 앱을 Corona Showcase에 올려 주세요. 그리고 여러분 앱의 웹사이트나 페이스북 페이지에 “Made with Corona” badge를 달아 주세요. 저희 팀은 좋은 디자인과 사운드 interactivity, 독창성 그리고 유용성등을 바탕으로 신중하게 우승자를 선정합니다. 좀 더 자세한 사항은 App of the Week Guidelines를 보시면 나와 있습니다.



이제 2013년이 시작됐습니다. 이 App of the Week 을 통해서 아주 훌륭한 앱들이 더 소개될 수 있기를 기대합니다.


Inna






반응형

Multi-Element Physics Body 다루기

2013. 1. 11. 05:51 | Posted by 솔웅


반응형
Posted on . Written by



이번주의 튜토리얼은 코로나 물리 엔진에 대한 겁니다. 특히 multi-element physics bodies 부분에 대해서 다룹니다.

첫번째로 multi-element body가 무엇인지 부터 알고 넘어가야 할 것 같습니다. multi-element body 는 두개 이상의 shapes 들이 모여서 구성된 physics body 입니다. raddoll 처럼 weld joint 같은 joint 로 연결된 physical 객체들의 집합으로 하나가 된 physical 객체를 말하는 것이 아닙니다. multi-element body 는 여러 shape 들로 구성되지만 전체가 하나로 통일되고 고정된 객체로 각각의 element들이 move 하거나 flex 하지 않습니다.



Why Multi-Element Bodies?


물리엔진을 많이 사용해 보신 분들은 다 아시는 얘기 일텐데요. 간략하게 더 설명하겠습니다. Box2D 에서는 모든 physical body 들은 반드시 오목한 angle들이 없는 최대 8개 면을 가진 다각형 모양으로 그려집니다.





볼록한 다각형은 괜찮습니다 하지만 볼록한 angle들로만 돼 있고 trace 될 수 없는 body 나 8개면 이하이면서 정확하게 trace 될 수 없는 body 라면 어떨까요? 이 룰을 따르지 않는다면 충돌시 일어나는 reaction 들은 어떻게 나올지 모르게 됩니다.


해결책은 multi-element body 입니다. 여러개의 볼록한 모양이 하나의 body 로 되서 trace 되는 모양이 기본적인 모양입니다. 여러분들은 간단한 shape들을 사용해서 multi-element bodies 들을 만들어야 합니다. 최소한의 shape 들을 사용해서요. 이 작업이 좀 어려울 것 같으면 3rd party tool인 PhysicsEditor가 이 작업을 쉽게 할 수 있도록 도와 드립니다.


Part I – Per-Element Collision Control


이미 이전에 multi-element bodies 작업을 해 보신적이 있으시면요, 아마 이 기능이 아주 유용하다는 것을 아실겁니다. 하지만 이 기능에도 약간의 제한사항들이 있는데요. 이 기능의 capabilities를 먼저 살펴보죠.


  1. 각 element 들은 유니크한 collision filters들을 가질 수 있습니다. 이 기능을 multi-element body의 일 부분이 다른 객체와 충돌하거나 할 때 어떤 reaction이 일어나도록 할 수 있습니다. 다른 부분은 충돌해도 아무 일이 일어나지 않구요. 
  2. 각각의 element들은 센서로 세팅할 수도 있습니다. 다른 객체들이 그 부분을 지나갈 때 어떤 일이 일어나도록 할 수 있죠. 충돌효과를 줄 수도 있지만 그냥 지나가도록 할 수도 있고 이 때 어떤 일이 일어나도록 할 수 있습니다.
  3. 충돌시 각 element는 사전에 physics addBody() function에서 지정된 고유의 숫자를 return 할 수 있습니다. 예를 들어 첫번째 element 는 1 두번째는 2 등으로 지정할 수 있죠.


이러한 unique capability들도 있지만 아래와 같은 제한사항들도 있습니다.


  1. 일단 collision가 filter element나 body에 정의되면 Runtime 동안은 바뀔수가 없습니다.
  2. 만약 element 가 센서로 정의돼었다면 Runtime 동안 각 element 별로 바꿀 수 없습니다. 다만 전체 body 가 sensor 나 non-sensor 로 바뀔 수 있을 뿐입니다.


Overcoming These Limitations


근데 너무 실망하지는 마세요. 오늘 이 튜토리얼에서 그 두가지 제한사항들을 어떻게 극복할지도 보여드릴테니까요. 우리는 아 작업을 physics contact를 사용해서 구현할 겁니다. 최근 있었던 튜토리얼에서 이 기능을 소개해 드린적이 있는데요. 여기로 가시면 그 글을 보실 수 있습니다.


physics contact를 사용해서 사전에 결정할 수 있습니다. pre-collision listener 를 사용해서 실제 충돌이 일어날 때 어떤 일이 일어날지를 결정할 수 있단 겁니다. 여러분 앱의 로직ㅇㅔ 근거해서 전체 collision을 void  하도록 할 수 있거든요. 이 튜토리얼에서는 이 기능을 multi-element bodies 에서 활용해 보겠습니다.



아래 “space nebula,” 를 사용할 건데요. 소설을 써 보면 star-fighter 는 우선 먼저 저 모서리에 있는 shield 를 부숴야 합니다. 그 다음에 가운에에 있는 대마왕을 없애야 해요. 이 시나리오를 구혀하기 위해서는 전통적인 방법으로는 아래와 같은 제한사항들이 있습니다. 



  1. 만약에 전통적인 방법인 joint를 사용해서 구현한다면 한쪽 모서리가 파괴 됐을 때 나머지 부분들의 joint 연결이 불안정해 지게 되는 문제점이 있습니다.
  2. object.isSensor를 사용하면 all or nothing 이 됩니다. 그러니까 파괴된 한 부분만 센서로 바꿀 수 없습니다.


이런 제약사항때문에 전통적인 joint 방법이 아닌 physics contact를 사용할 겁니다. per-element collision detection을 사용해서 이 destructible shield 이슈를 해결해 보겠습니다.




Assembling the Nebula


Let’s examine how to create a multi-element body in Corona. We’ll create a 9-element body to trace the nebula. In Corona, we simply do this:

일단 코로나에서 어떻게 multi-element body를 생성하는지 공부해 봅시다. 우리는 nebula 를 trace 하기 위해 9개의 element body 를 만들겁니다. 코로나에서는 이를 위해 아래와 같이 하면 됩니다.


  • nebula image 를 스크린에 display 시킨다.
  • nebula에 대한 shape 을 정의한다. top 을 시작점으로 해서 필요한 작업을 한다. outlying pod에 대해서는 8각형을 사용해야 합니다. 왜냐하면 multi-element body 에서는 radial shape 을 offset 할 수 없거든요. 정확하게 원이 아니니까요. 팔각형으로 충돌에 대한 자연스러운 반응을 이끌어 내기에도 문제가 없을 겁니다.
  • physical body 를 add 하고 element들의 순서에 따라 API 에 각 shape 들을 pass 합니다. 순서가 중요합니다. collision detection 에서 숫자를 return 할 거거든요.


local nebula = display.newImage( "nebula.png" )
nebula.x, nebula.y = display.contentWidth/2, display.contentHeight/2
local podT = {1,-89, 14,-83, 20,-70, 14,-57, 1,-51, -12,-57, -18,-70, -12,-83}
local beamTR = {19,-63, 63,-19, 59,-14, 14,-59}
local podR = {69,-20, 82,-14, 88,-1, 82,12, 69,18, 56,12, 50,-1, 56,-14}
local beamBR = {19,61, 14,56, 58,13, 62,17}
local podB = {1,49, 14,55, 20,68, 14,81, 1,87, -12,81, -18,68, -12,55}
local beamBL = {-18,63, -64,17, -59,13, -14,58}
local podL = {-70,-20, -57,-14, -51,-1, -57,12, -70,18, -83,12, -89,-1, -83,-14}
local beamTL = {-18,-65, -14,-61, -59,-15, -64,-20}

physics
.addBody( nebula, "dynamic",
{shape=podT},
{shape=beamTR},
{shape=podR},
{shape=beamBR},
{shape=podB},
{shape=beamBL},
{shape=podL},
{shape=beamTL},
{radius=24} --radial body used for the nucleus
)
local shieldStates = { true, true, true, true, true, true, true, true }


이제 맨 마지막에 이 8개의 shield 객체들을 관리하기 위해 간단한 shieldStates table을 셋업하셔야 합니다. 이 테이블은 나중에 어떤 특정 element 를 on/off 시킬때 사용할 겁니다. 그리고 shield element 가 inact인지 destroyed 돼 있는지를 track 할 때도 사용할 거구요. 이를 위해 8개의 boolean 값을 가진 간단한 non-indexed table 을 사용합니다.



The Basic Pre-Collision Listener



그 다음으로는 기본적인 pre-collision listener를 정의할 겁니다. 이전 튜토리얼에서 설명했듯이 물리적인 접촉을 utilize 하기 위해 pre-collision listener를 사용해야 합니다. 왜냐하면 코로나에게 충돌이 일어나기 직전에 그 충돌상황을 관리하도록 해야 하기 때문이죠.


local function nebulaCollide( self,event )
  print( event.selfElement )
end
nebula.preCollision = nebulaCollide ; nebula:addEventListener( "preCollision", nebula )


이 함수는 이주 기본적인 구조로 돼 있습니다. 이제 nebula 와 어떤 것이라도 충돌하게 되면 해당 element가 정의 돼 있는 순서에 따라 event.selfElement로 그 element의 corresponding integer가 return 될 겁니다. 위에 있는 pod를 첫번째 element로 정의했죠. 그러니까 그 element는 1을 return 할 겁니다. 오른쪽 beam 은 2를 반환하고 오른쪽 pod 는 3을 반환하고 뭐 이런식으로 진행될 겁니다.


Enhancing the Pre-Collision Listener


이제 nebula 의 어떤 element 가 충돌했는지 알 수 있게 됐습니다. 이제 shieldStates table 을 이용해서 충돌을 처리할 지 말아야 할지를 처리할 수 있도록 만들어야 합니다. 만약 그 shield element 가 게임을 하다가 destroyed 됐다면 코로나에게 해당 충돌은 void 처리하라고 지시할 수 있습니다. (event.contact)


local function nebulaCollide( self,event )

--query the position (and state) from "shieldStates" table
local isElementIntact = shieldStates[event.selfElement]

if ( isElementIntact == false ) then
event.contact.isEnabled = false --use physics contact to void collision!
end
end
 

shieldStates table를 관리하는 것은 아주 간단합니다. shield pod 을 destroy 처리하려면 간단하게 아래 처럼 하면 됩니다.



shieldStates[5] = false


Building on this concept, you can now manage your nebula shields and enact other creative methods, including:

이제 여러분은 nebula shield를 관리할 수 있게 됐습니다. 그리고 아래 내용들을 포함해서 여러 효과들을 구현하실 수 있습니다.

  1. shield pod 이 destroy 됐다면 옆에 있는 beam 도 destroy 시킴.
  2. 특정 시간이 지나면 shield pod 을 rebuild 하고 옆의 beam 들도 다시 rebuild 함
  3. shieldStates table setup 을 확장해서 각 pod 의 에너지 양을 관리함.


per-element detection 과 함께 physics contact를 사용하면 전통적인 방법이 갖는 한계를 해결할 수 있다는 걸 알게 되셨을 겁니다.




Part II – Multi-Element Bodies and Sensors



이제 multi-element bodies와 관련되서 많이들 잘 못 이해하고 계시는 sensor 부분에 대해 얘기해 보겠습니다.


Corona 의 physics 에 대해서 경험이 있으신 분들은 어떤 모양이나 타입(dynamic, kinematic, or static)에서든 physical body 가 sensor 로서 동작할 수 있다는 것을 알 수 있을 겁니다. sensor 는 부딪혀서 튀긴다던가 하는 physical sense 는 없습니다.


multi-element bodies와 관련해서 많이 오해하기 쉬운게 뭐냐하면 모든 element 들은 seosor로서 colision event를 return 합니다. body 가 전체 통으로 돼 있는 객체라면 그 전체가 충돌을 체크하는 포인트가 되죠. 여기서 헛갈릴 수 있는 것이 sensor 에서는 충돌시 여러개의 began phase event를 받을 수 있게 된다는 거죠. 혹은 센서 지역 밖에 있는데도 작은 부분이 걸쳐 있어서  ended phase를 받을 수도 있습니다.


사실 센서는 원래 그러기 위해서 만들어 졌습니다. 예를 들어서 트랙을 달리는 경주용 자동차의 앞 바퀴에만 센서를 달 필요가 있는 경우가 있을 겁니다. 조종석은 트랙이 있더라고 앞바퀴만 통과하면 어떤 결과를 일으켜야 되는 경우가 있죠. 또 다른 경우는 multi-element body 전체가 센서 지역의 안에 혹은 밖에 있는 것을 체크해야 될 때도 있습니다. “jumping fish” 를 보죠. 물고기는 완전히 물 안에 존재해야만 하죠? 이것도 센서로 정의할 수 있을 까요?



Counting the Collisions






이 jumping fish 시나리오는 fish의 body 에 있는 각각의 element들에 대해 어떤 충돌이 일어나는지를 counting 함으로서 해결할 수 있습니다. 이를 위해 값들이 들어갈 테이블을 만들고 그 이름을 elementStates라고 할 겁니다. began phase 에서 해당 count를 1씩 증가시키고 ended phase 에서는 1씩 감소 시킬 겁니다. 각 element들은 센서와 함께 collision event 를 return 할 겁니다. 그래서 만약 한 element에 4개의 센서들이 overlap 된다면 count는 4가 되겠죠. 만약 그 센서들 중 3개가 밖으로 나간다면 count 는 1로 줄겠죠. element count 가 0이 될 때는 전체가 센서 밖에 나가 있다는 것을 말합니다. 그러면 물고기가 완전히 물밖으로 나간 상태고 물고기가 점프했다고 볼 수 있겠죠.



또한 우리는 elementsIn를 만들어서 물고기의 전체 element들 중에 얼마나 많이  센서 범위 안이나 밖에 있는지를 count 할 겁니다. 이 값은 절대 5를 넘지 않을 겁니다. 왜냐하면 물고기는 5개의 element들로 구성될 거기 때문이죠.

마지막으로 inWater라는 boolean flag 를 정의할 겁니다. 이로 인해서 완전히 물 속에 있는지 물 밖에 있는지를 알 수 있게 되죠. 코딩을 편하게 하기 위해서 이 세개의 item들은 fish 의 properties 로 정의할 겁니다.

아래에 기본적인 예제 코드가 있습니다.



local
fish = display.newImage( "jumpfish.png" )

fish.x, fish.y = display.contentWidth/2, display.contentHeight/2-200
local tail = {-117,12, -123,-46, -68,-13}
local bodyBack = {-89,-26, -61,-39, -20,-46, 20,-49, 42,27, -12,28, -66,16, -94,0}
local bodyFront = {20,-49, 71,-43, 107,-32, 121,-20, 126,-10, 108,5, 78,19, 43,27}
local finBack = {-39,23, -11,29, -10,41, -32,50}
local finFront = {-9,51, -11,28, 41,27, 15,42}

physics.addBody( fish, "dynamic",
{shape=tail},
{shape=bodyBack},
{shape=bodyFront},
{shape=finBack},
{shape=finFront}
)
fish.elementStates = { 0,0,0,0,0 } --table of per-element collision counts
fish.elementsIn = 0
fish.inWater = false



보시다시피 물고기의 element들은 Part 1에서 다뤘던 nebula 와 비슷하게 정의했죠? 거기에다가 추가적으로 table elementStates와 properties elementsIn and inWater 를 생성했습니다.


Managing the Count


이 물고기는 standard collision listener 가 필요합니다. pre-collision listener 가 아니라요. 이번에는 physics contact 기능을 사용하지 않을 겁니다.


local function fishCollide( self,event )

if ( event.phase == "began" ) then
if ( self.elementStates[event.selfElement] == 0 ) then
self.elementsIn = self.elementsIn+1
end
self.elementStates[event.selfElement] = self.elementStates[event.selfElement]+1
elseif ( event.phase == "ended" ) then
self.elementStates[event.selfElement] = self.elementStates[event.selfElement]-1
if ( self.elementStates[event.selfElement] == 0 ) then
self.elementsIn = self.elementsIn-1
end
end
if ( self.elementsIn == 0 and self.inWater == true ) then
self.inWater = false
print("FISH ENTIRELY OUT OF THE WATER!")
elseif ( self.elementsIn == 5 and self.inWater == false ) then
self.inWater = true
print("FISH ENTIRELY IN THE WATER!")
end
end
fish.collision = fishCollide ; fish:addEventListener( "collision", fish )

이제 단계별로 볼까요

began phase:

  1.  첫번째로 총돌한 element의 count가 0인지 체크합니다. 만약 0이면 이 element는 처음으로 센서지역으로 들어가는 element라는 것을 알수 있죠. 그리고 이 물고기의 전체 elementsIn count를 1 증가시킵니다.
  2. 그 다음으로는 특정 element의 count 를 1 증가 시킵니다.


ended phase:

  1. 충돌한 element의 count 에서 1을 뺍니다.
  2. 그 다음에 element의 count가 0인지 체크를 하죠. 만약 0이면 이 물고기는 전체 센서지역에서 완전히 밖에 있는 상태라는 것을 알 수 있습니다. 그리고 물고기의 total elementsIn count 에서 1을 줄입니다.


The conditional check:

  1. 물고기의 total elementsIn count 가 0인지 여부를 체크합니다. 이전에 물속에 있었는지 여부를 체크합니다. 두 조건 모두 pass 하면 물고기는 물밖에 완전히 나온 상태라는 것을 알 수 있죠. 그러면 inWater flag를 false로 세팅합니다.
  2. elseif 조건문은 물고기의 elementsIn count가 5인지를 체크합니다. 이전에 물에 완전히 들어가 있지 않은지 체크하죠. 두가지 조건이 pass 라면 이 물고기는 이 물고기는 완전히 물 속에 있다는 것을 알 수 있죠. 그러면 inWater flag를 true로 세팅합니다.


물고기가 완전히 물 속에 있는지 아니면 완전히 물 밖에 있는지에 대해 이렇게 sensor collision을 가지고 handle 할 수 있습니다 이 방법은 센서들을 overlapping 하고 neighboring 과도 같이 사용할 수 있습니다. 예를 들어 core body 로 물 센서 지역을 만들었고 그 위에 파도가 있다면 이 코드는 물고기의 모든 elements들을 이 센서들에 모두 적용 시킬 수도 있습니다.


In Summary


여기까지가 오늘의 튜토리얼입니다. 배우신대로 mutli-element physics bodies 기능으로 joint-assembled bodies 가 할 수 없는 효과를 줄 수 있습니다. 하지만 사용하시려면 몇개의 장애물은 넘어야 겠죠. 이 튜토리얼이 여러분이 앱을 만드시면서 닥치는 그런 장애를 극복하는데 도움이 되기를 바랍니다.


반응형

Corona tip: Shuffle it!

2013. 1. 10. 19:26 | Posted by 솔웅


반응형

Corona tip: Shuffle it!


랜덤하게 어떤 것을 뽑아내고 싶으세요? 그러면 아래처럼 Shuffle 함수를 만들어서 사용하세요.


 

local function shuffle(t)    

local rand = math.random    

assert(t, "shuffle() expected a table, got nil")    

local iterations = #t     local j        

 

for i = iterations, 2, -1 do        

j = rand(i)    

  t[i], t[j] = t[j], t[i]    

end

end

 

numbers = {1,2,3,4,5,6,7,8,9,10,11,12} -- say up to 52 if your're doing a card game   

 

shuffle(numbers)

 


간단하게 1에서 #numbers 까지의 테이블을 루프 돌리고 그 entry들의 배열을 랜덤하게 정렬하세요. 그 값들이 숫자가 아니라 문자라도 상관없습니다. 그리고 이미지라도 상관 없구요. 노래가 담긴 오디오 파일들일 수도 있겠죠. 그러면 많은 노래들을 shuffle 해서 play 시킬 수 있겠죠.






반응형


반응형

Corona tip: Is display.remove() better than object:removeSelf()?


display.remove() 가 object:removeSelf()보다 더 낫나요? 

 

display.remove는 아래와 똑 같습니다.

 

if object and object.removeSelf then   

   object:removeSelf()

end

 

그러니까 display.remove를 사용하면 두 줄을 줄일 수 있는 거죠.

 

display.remove(object)

 

이렇게 하면 코딩을 많이 줄일 수 있겠죠. 프로젝트 전체로 치면 아주 많이 줄일 수 있을 겁니다. 그 객체를 remove 하기 전에 우선 그 객체의 존재 여불를 체크해야 하는데 display.remove 는 이 작업을 자동으로 해 줍니다. 하지만 여러분이 스스로 객체를 체크하고 싶으시면  object:removeSelf()를 사용해도 상관은 없습니다.






반응형


반응형
Posted on . Written by



지난 몇달간 여러분들 중 많은 분들이 Corona Geek show 들을 보셨을 겁니다. 이 비디오들은 코로나 커뮤니티에서 왕성하게 활동해 오신 코로나 개발자인Charles Mckeever 가 모아놓은 건데요, 코로나 ecosystem 에서 진행중인 흥미로운 것들에 대해 얘기하고 일반적인 모바일 컴퓨팅에 대해 논의한 것들입니다.






저희들은 그 쇼들을(the shows) 쭉 보아왔습니다. 그리고 그 중 몇개는 저희들도 참여(participated) 했었습니다. 저희들은 그 아이디어를 사랑했고 Charles 가 그 일을 계속 진행하는 것에 대해 깊은 인상을 받았습니다. 저희들도 예전에 이와 비슷한 것들을 하려고 계획하긴 했었습니다만 실제로 그 일을 할 시간을 갖지는 못했었습니다.



그래서 이 Corona Geek 을 Corona Labs folder 에 포함시키고 Charles 를 on board 시키기로 했습니다. 우리는 Corona Geek 을 더 발전시켜 나갈 계획입니다. 저희들의 비전은 개발자 커뮤니티를 좀 더 활발하게 발전시켜 나가는 겁니다. 개발자들이 가서 모바일 개발과 모바일 마케팅에 대해 (코로나에 대한 것 뿐만 아니라) 배울수 있는 그런 커뮤니티를요. Corona Labs team, Corona Ambassadors 그리고 Corona developers로서 여러분의 경험을 공유할 수 있는 그런 공간 입니다. 이 공간을 TV 채널의 일부, 개발자 포럼의 일부 그리고 educational resource의 일부로 생각해 주세요. 거기에다가 어떤 좋은 아이디어가 떠오르면 그런 아이디어도 나눌 수 있는 공간입니다. 이게 Google+ Hangouts and video 와 비슷합니다. 하지만 Corona Geek 은 이 뿐만 아니라 다른 미디어로 더 확장해 나갈 겁니다.





저희들은 코로나의 개발자 공간에 대해 믿음을 갖고 있고 또 자랑스럽게 생각하고 있습니다. 여러분들이 저희 플랫폼을 사용하기로 결정했고 또 앱을 만들고 계신것이 영광스럽습니다. 이 플랫폼을 더 강력하게 만드는 것은 여러분들에게 더 가치있는 resources 들을 제공하는 것이란 것을 알고 있습니다. Corona Geek 으로 우리의 커뮤니티에 대한 노력을 배가할 것이며 이와 더불어 코로나를 아름답고 성공적인 게임과 앱을 개발하는 최고의 플랫폼으로 만드는 도구로 이를 활용할 것입니다.

이러한 일들을 하기 위해 Corona Geek 을 선택했습니다. 이와 관련된 비전과 계획은 today’s Hangout (Monday, January 7)에 있습니다. 태평양 시간으로 낮 12시에 시작할 겁니다. 여기에 여러분들을 초청합니다.



저희들은 또한 Corona Geek forum도 만들었습니다. 이 Corona Geek 과 관련한 여러분의 생각과 아이디어, 제안 을 듣고 싶습니다. 주저하지 마시고 저희들에게 직접 이메일을 주세요. 저희들의 이메일은 charles[AT]coronalabs[dot]com 과 david[AT]coronalabs[dot]com 입니다.



감사합니다.


David



반응형

내 앱에 애플의 iAds 광고 달기

2013. 1. 3. 09:44 | Posted by 솔웅


반응형

Posted on . Written by

“Show me the money!” — Cuba Gooding Jr. as Rod Tidwell in “Jerry McGuire”



Corona Labs 연말연시 선물중 하나가 애플 iAds 를 제공하게 된 것입니다. Project Gluon로 알려진 Corona SDK 플러그 인 시스템으로 만들어진 플러그인 중에 iAds 가 첫번째로 선보이는 플러그인입니다. iAds 는 daily build 992 이 후의 버전에서 사용 가능합니다.


Setup Simplified!


만약 애플의 어떤 기능을 사용하려고 하신다면 그 과정이 쉽지만은 않을 것이라는 걸 아실 겁니다. 코로나에서 제공하는 iAds 를 세팅하는 것은 놀라울 정도로 간단합니다. download 하기 위해 certificate 도 필요없고 인스톨하기 위한 provisioning profile들도 필요 없습니다. 그리고 iTunes Connect 에서 셋업하기 위한 그 까다로운 일도 안 하셔도 됩니다.


첫번째로, 여러분이 하실일은 iTunes Connect 계정으로 가시는 겁니다. “Contracts, Tax and Banking” section에서 iAds 에서 벌어들인 돈을 여러분의 은행계정으로 보낼수 있도록 하기 위해 여러 다양한 contract 에 동의하셔야 합니다.



아마 여러분은 유료 앱에 대한 은행 정보를 이미 셋업 하셨을 겁니다. 만약 지금 만드는 것이 여러분의 첫번째 앱이라면 혹은 지금까지 무료앱만 개발하셨다면 여기서 서류작업을 좀 하셔야 됩니다. 이미 이 부분을 셋업하셨다면 마케팅, finance 같은 다양한 entries 에 대한 contact 정보가 필요할 겁니다. 


그 다음으로는 “Manage Your Applications” section 으로 가세요. 여기서 아셔야 될 것은 이미 만든 앱에는 광고를 삽입할 수 없다는 겁니다. 이미 만든 앱의 새로운 버전을 업로드 하셔야 합니다. 왜냐하면 이미 만들어서 앱 스토어에 있는 앱 안에는 광고 관련 코딩이 안 돼 있을 테니까요.


새 버전 정보를 셋업했으면 “Manage iAds“ 라는 버튼을 보실 수 있을 겁니다. 여러분 앱이 17세 이하에게도 공개하시는 앱이라면 Save를 클릭해 주세요.




애플쪽의 셋업은 이게 다입니다. 이제 여러분 앱쪽으로 옮겨 보죠.

main.lua에서 ads 모듈을 turn on 하셔야 합니다.


local ads = require("ads")
 
local function adListener(event)
local msg = event.response
if event.isError then
-- Failed to receive an ad, we print the error message returned from the library.
print(msg)
end
end
 
ads.init( "iads", "com.yourcompany.yourappid", adListener )


com.yourcompany.yourappid setting 은 여러분 앱에 대한 bundle ID 입니다. iOS provisioning Portal 에서 셋업한 내용이 되겠죠. 광고를 보이고 보이지 않고 하기 위해 ads.init()를 사용하셔야 합니다. listener 함수에 테스트할 수 있도록 코딩을 해 넣으세요. 


iAds Functions


iAds 는 Corona SDK 의 ad system을 사용합니다. 아래 두 함수를 이용하실 수 있습니다.

  • ads.show()
  • ads.hide()


ads.hide() function은 보시면 아시겠죠? 광고를 화면에서 안 보이도록 할 때 사용하는 겁니다.

ads.show() function은 광고를 보이도록 할 때 사용하는 것이죠. 이 함수는 몇개의 파라미터를 사용해야 되는데요. positioning 등의 기능을 하도록 하기 위해 ad type 와a table of parameters들이 필요합니다.


ad type parameter 에는 아래 두가지 중 하나를 사용하실 수 있습니다.

  • banner
  • interstitial


Banner ads는 앱의 top 이나 bottom 에 모일 일반적인 사각형 앱을 말합니다. iAds 는 모든 디바이스에서 사용하실 수 있습니다. 다른 ad type으로 interstitial가 있는데요. scene들이나 level 들 사이에 표시될 fullscreen 광고가 그것입니다. IMPORTANT NOTE:interstitial ads는 iPad 에서만 가능합니다. iPhone 이나 iPod 에서는 사용하실 수 없습니다.

디폴트로 정해지는 positioning 도 괜찮다면 간단하게 아래 코드를 넣으시기만 하면 광고가 보일겁니다.


ads.show( "banner" )


iPad 앱을 개발하시고 fullscreen interstitial ad를 넣으시려면 아래와 같이 하세요.

ads.show( "interstitial" )


InMobi 와 Inneractive 와는 다르게 iAds 에서는 refresh time 을 control 하실 수 없습니다. 그 부분은 애플에서 관리합니다. interstitial ads 는 fullscreen 이기 떄문에 더군다나 이 기능이 필요 없구요. 여러분이 콘트롤 할 수 있는 것은 광고의 top-left 위치에 대한 x,y 값입니다. Ads 는 pixel 이 아니라 point 로 관리 됩니다. 만약 여러분 앱이 320X480 이라면 Retina device 에서는 두배크기로 보일 겁니다. 그리고 위치도 320X480 에 relative 된 위치가 사용될 겁니다.

배너 광고의 위치를 정하시려면 options table 에 x,y 파라미터에 값을 넣어 주시면 됩니다.

ads.show( "banner", { x=0, y=0 } )


이렇게 하면 디바이스의 top left 에 배너가 걸릴 겁니다. 이 값은 디폴트 값입니다. 애플은 iPhone/iPod 에 두가지 종류의 배너를 제공하고 iPad 에도 두가지 종류의 광고를 제공합니다.

이 광고들의 사이즈는 아래와 같습니다.

  • iPhone Portrait = 320×50
  • iPhone Landscape = 480×32
  • iPad Portrait = 768×66
  • iPad Landscape = 1024×66


값들이 달라서 광고를 위치지울 떄 좀 헛갈리실 수도 있습니다. 어떤 경우라도 width 는 full 이기 때문에 x 값은 0이외에 다른 값을 넣을 필요가 없을 겁니다. 만약 화면 아래쪽에 광고를 넣으시려면 코딩할 떄 조금 생각하셔야 할 겁니다. “Device Detection on Steroids” 에서 소개해 드린 device detection module 을 사용하신다면 아래처럼 하시면 될 겁니다.


local adY = display.contentHeight - 66
 
if not device.is_iPad then
if display.contentHeight > than display.contentWidth then
-- portrait
adY = display.contentHeight - 50
else
adY = display.contentHeight - 32
end
end
 
ads.show( "banner", { x=0, y=adY } )



오늘의 튜토리얼은 여기까지 입니다.

질문이 있으시면 댓글에 남겨 주세요.

새해 복 많이 받으세요.


반응형


반응형
Posted on . Written by


코로나 SDK 개발자들에게는 크리스마스가 일찍 왔습니다. 지난주 저희 팀들은 daily builds 에 여러 기능을 추가하느라고 하주 바쁜 시가늘 보냈습니다. 여러분들에게 소개해 드릴 새로운 기능들이 아주 많이 있습니다. 아래는 그 중 일부분입니다.


  • Build 987 — Inneractive ads SDK updated to 1.1.5
  • Build 990 — iOS 5/6 native Twitter access via native.showPopup()
  • Build 990 — new native.canShowPopup() API call to test for email, sms and twitter capability
  • Build 991 — Facebook SDK updated to 3.1.1
  • Build 992 — iAds support added
  • Build 993 — (drum roll please)… Android Push Notifications!


몇몇 개발자분들은 이런 새로운 기능들을 deploy 하고 계실 겁니다. 그리고 코로나 SDK의 new Plugin system인  Project Gluon 가 이런 작업들을 빨리 할 수 있도록 도와준 것에 대해 감사를 전합니다.


오늘은 Android Push Notifications에 대해 얘기를 해 보죠. 만약 애플에 push notification 을 deploy 해 보셨다면 아마 그 일은 certificates, keys, sandboxing, provisioning profiles, 3rd-party vendor setup 등등의 복잡한 것들을 처리하느라고 거의 로켓 과학하고 MBA 학위를 따는 것 만큼이나 힘들었을 겁니다. iOS 개발자 계정에 이러한 셋업을 다하고 나면 이제 여러분은 모바일 개발자로서 가장 어려운 부분을 해결한 겁니다.다행히도 여러분이 일단 애플쪽에 이런 setup 을 해 놓으셨다면 코로나의 push notification을 다루는 것은 아주 간단합니다.


이제 안드로이드 쪽을 살펴 보면 셋업 하는데 그렇게 많이 나쁘지는 않습니다. push notification 과 관련해 가장 큰 장애물은 Android OS 가 push notification을 관리하지 않는다는 겁니다. push 하는 것은 애플리케이션이 책임지는 겁니다. Corona Labs 는 이 안드로이드도 애플처럼 in-app implementation 을 만들기 위해 아주 많은 노력을 기울였습니다.


Four Basic Steps


안드로이드에서 push notification 을 얻는 방법은 다음 4 개의 기본 과정을 거칩니다.


  1. Setup for Google
  2. Setup for 3rd-party services (optional)
  3. Respond to registration events from your app
  4. Respond to push notifications as they arrive

1. Setup for Google



STEP 1:  브라우저에서  https://code.google.com/apis/console 로 가세요. 그리고 Services를 클릭하세요. Google Cloud Messaging for Android entry를 찾고 그것을 on 상태로 설정하세요. 아마 "Terms of Services" 에 동의 하셔야 할 겁니다.  거기서 제시하는 대로 다 동의 하세요.



STEP 2:   API Access를 클릭하세요. 그리고 Create new Server key를 클릭하세요. 아마 긴 화면이 나올 겁니다. IP address 쪽은 작성하실 필요가 없습니다. push notification을 send 할 때 사용하셔야 하니까 API Key 를 copy 하세요. (Corona Cloud service에서 했던 대로죠.)

NOTE:  Corona SDK sample app에는 push notification을 send 하는 기능이 있습니다. 여러분도 샘플로 테스트를 하시려면 이 API Key 를 사용하셔도 됩니다.



STEP 3:  API 콘솔의 Overview tab으로 가세요. 그리고 Project Number를 copy 하세요. Project ID는 무시하세요. 여러분의 Project Number는 또한 URL 이기도 합니다. 이 번호는 나중에 push notification 을 받을 때 필요하실 겁니다.



STEP 4: 이 부분은 그렇게 필요한 부분은 아닌데요. 일단 Google Play 개발자 콘솔에 방문하고 여러분이 업로드한 앱을 찾고 메타 데이터를 수정하고 Enable Google Cloud Messaging Stats entry를 찾으실 수 있습니다. 여기서 Project IDSender ID로 추가하세요. 이렇게 하면 여러분 앱의 GCM usage에 대한 stats 를 볼 수 있도록 해 줍니다.


2. Setup for 3rd-Party services (optional)


이 부분에서는 그다지 언급할 부분은 많이 없을 것 같습니다. Corona Cloud Services (Game Minion)에서부터 Urban Airship까지 많은 서비스 중 하나를 이용하실 수도 있고 또는  standard REST API call 을 사용해서 메세지를 보내는 여러분들만의 서비스를 사용하고 싶을 수도 있을 테니까요. 어느 경우에든 API Server Key는 있어야 됩니다.  GCM Server Key같은 걸 요구할 겁니다. 여러분이 제공해야 될 것은 이게 전부 입니다.


3. Respond to registration events from your app


iOS에 대한 push notification에 대한 코로나 Labs의  블로그 글을 올린것이 한 일년 전 쯤의 일입니다. 애플의 push notification을 셋업하는 일이 어려운 일의 대부분이었습니다. 대부분의 step 들이 다 거기에 대한 방법이었죠. step 4,5 번이 코로나와 관련된 부분이었습니다. 이 부분은 안드로이드에 대해서도 똑 같이 적용 됩니다.

push notification을 받기 위해서는 3개의 파일을 수정하셔야 합니다. 첫번째는 config.lua입니다.

아래 부분을 추가해 넣으시면 됩니다.


notification =
{
google =
{
projectNumber = "yourprojectnumberhere",
},
},

프로젝트 번호는 전부 다 숫자라도 따옴표 안에 있어야 합니다. 최근에 블로그에 올린 Ultimate config.lua 를 사용하실거라면  이 블록을 두번 복사해 넣으시면 됩니다. 하나는 tall 안드로이드 디바이스 부분 하나 하고 다른 디바이스 부분하고요.      


elseif ( display.pixelHeight / display.pixelWidth > 1.72 ) then
 
application =
{
content =
{
width = 320,
height = 570,
scale = "letterBox",
xAlign = "center",
yAlign = "center",
imageSuffix =
{
["@2x"] = 1.5,
["@4x"] = 3.0,
},
},
notification =
{
google = { projectNumber = "yourprojectnumberhere", },
},
}
else
application =
{
content =
{
width = 320,
height = 512,
scale = "letterBox",
xAlign = "center",
yAlign = "center",
imageSuffix =
{
["@2x"] = 1.5,
["@4x"] = 3.0,
},
},
notification =
{
google = { projectNumber = "yourprojectnumberhere", },
},
}
end

iOS device 부분에는 이 블럭을 넣으실 필요는 없습니다. 하지만 iOS 에 대한 push notification code를 이 블럭에 넣으셔야 된다는 점 기억해 두세요.

이제 두번째 수정하셔야 될 파일은 build.settings file입니다.

settings = {} table 안에 넣으셔야 될 겁니다. 그 안에 iPhone과 orientation 혹은 다른 블럭들이 있을 텐데요. 아래와 같이 안드로이드 부분을 세팅하시면 됩니다.


android =
{
permissions =
{
{ name = ".permission.C2D_MESSAGE", protectionLevel = "signature" },
},
usesPermissions =
{
"android.permission.INTERNET",
"android.permission.GET_ACCOUNTS",
"android.permission.RECEIVE_BOOT_COMPLETED",
"com.google.android.c2dm.permission.RECEIVE",
".permission.C2D_MESSAGE",
},
},



이제 마지막으로 main.lua 파일 입니다. 추가해야 될 코드가 있는데요. 이미 iOS push notification을 위해 이 코드를 넣으셨다면 이미 좋은 참고 자료를 확보한 셈입니다. 이 부분이 처음이라면 여러분이 추가하셔야 될 부분은 세부분으로 나눌 수 있습니다.   


첫번째로는 notification events 에 대해 respond 하기 위한 handler function을 셋업하셔야 합니다. 핸들링해야 할 이벤트가 두가지가 있는데요. (Registration and the Push Event itself) 둘 다 같은 handler function으로 관리하시면 됩니다. 이 함수를 사용하시기 위해 Runtime Listener 도 셋업 하셔야 됩니다.

suspended 나 stopped 상태에서 push notification이 여러분의 앱을 launches 시킬 어떤 특정 화면으로 가도록 프로그래밍을 하셔야 되는 걸 잊지 마세요. 


“remoteRegistration” event를 get 하면 애플이나 구글에 완전히 register 된 겁니다. 그리고 event.token variable에 여러분의 token 이 들어있는 상태가 됩니다. 이 값을 다른 서비스들과 함께 여러분의 디바이스에 register 시킬때 사용하실 수 있습니다. 이 remoteRegistration event 는 다른 서비스와 함께 register 시킬때나 나중에 token을 저장할 때 아주 유용합니다. (만약 여러분이 push sending service와 함께 로그인 과정이 끝나고 난 후 register 해야 할 떄 말이죠.)


local function onNotification(event)
 
if event.type == "remoteRegistration" then
-- This device has just been registered for push notifications.
-- Store the Registration ID that was assigned to this application by Google or Apple.
myRegistrationId = event.token
 
-- Display a message indicating that registration was successful.
local message = "This app has successfully registered for push notifications."
native.showAlert("Information", message, { "OK" })
-- Print the registration event to the log.
print("### --- Registration Event ---")
--
-- Here is where you put code to register with your push notification service
--
else
-- A push notification has just been received. Print it to the log.
print("### --- Notification Event ---")
-- here is where you respond to a push notification that comes in while the
-- app is running.
end
 
end


Now, to make this function do something useful, you need to hook it up to Corona’s event system:


Runtime:addEventListener( "notification", onNotification )


마지막으로 여러분의 앱이 running 상태가 아니라면 (i.e. it’s stopped/suspended, a push notification comes in for the app, and you tap on it) OS가 여러분의 앱을 launch 시킬 겁니다. 그 다음에 아무런 행동을 하지 않으면 유저가 앱 아이콘을 두드렸을 때 실행 되듯이 앱이 실행 될 겁니다. 하지만 그럴 경우 push notification의 content 내용에 따라 다른 특정 화면으로 앱을 시작하고 싶을 때는 어떻게 해야 할 까요? 예를 들어서 뉴스 관련 앱을 만들었고 새로운 속보가 push 됐다면 바로 그 story 로 화면이 시작되길 원하실 겁니다.


이럴 경우 launchArgs를 사용합니다. push notification은 custom data를 포함하고 있습니다.

앱이 시작될 때 이 launchArgs를 체크하세요. 그리고 필요하면 앱 실행부분을 코딩하셔서 원하는 기능을 구현하시면 됩니다. 이 코드는 대개 main.lua의 윗 부분에 있게 됩니다.


local launchArgs = ...
if launchArgs then
-- do something with the data in the launchArgs table
end


In Summary


이 튜토리얼의 내용은 여기까지 입니다. 이 push notification은 오직 iOS 디바이스와 Google Play 에 인스톨 된 앱과  안드로이드 디바이스 에서만 사용할 수 있다는 것을 유의하세요. Kindle Fire나 Barnes & Noble Nook 디바이스들에서는 이 기능을 제공하지 않습니다.

앞으로도 계속 튜토리얼들이 추가 될 겁니다. 관심있게 지켜봐 주시기 바랍니다.

Corona Labs 는 여러분들이 모두 연말 연시를 잘 보내시고 이 선물이 도움이 많이 되셨기를 바랍니다.

반응형


반응형

Posted on . Written by



2012년 모바일의 세계는 완전 날아다녔습니다. 코로나도 거기에 올라 탔었구요. 하드웨어 쪽에서는 정말 매끄럽고 날렵한 태블릿들과 수를 헤아일수 없는 전화기를 포함해서 많은 device 들이 release 됐었죠. 또한 iOS6 와 안드로이드 4.x 같은 메이저 OS 들의 업그레이드가 다양한 기능들과 함께 소개 됐었죠. 지난 1년 동안 Corona Labs는 이렇게 빠르게 변화하는 환경에 맞춰 계속 새로운 개발을 해 왔습니다.



우리는 2012년을 더 큰 그리고 더 강력한 팀으로 마감하게 됐습니다. 거기다가 저희들은 계속 성장해 나가고 있습니다. 저희가 개발자분들의 community 에 전달하고 싶은 말씀은 저희들은 아주 strong 하고 Corona 커뮤니티가 세계적으로 성장할 수 있도록 환상적인 명예 대사들과 함께 꾸준히 노력을 하고 있다는 겁니다. 기술적인 부분에서는 주요 milestone들을 기록할 수 있었습니다. 8월에 Corona Enterprise를 선보여 코로나 개발자 분들이 native code도 같이 사용해서 개발할 수 있도록 했습니다. 그리고 Project Gluon과 함께 코로나의 새로운 plugin system을 소개해 드리기 위해 열심히 개발하고 있습니다. 거기에다가 12월에는 end-to-end development platform인 Corona Cloud 를 2013년도 초에 실시하겠다고 발표 했었습니다.



또한 코로나 커뮤니티도 지속적으로 성장해 나가고 있습니다 2012년도에 코로나 개발자 수는 11만명에서 20만명으로 거의 두배가 뛰었습니다. 이 사실을 접하고 저희들은 놀라기도 했고 또 겸손해 지기도 했습니다. 여러분들의 도움으로 저희들은 같이 활기차고 자랑스러운 커뮤니티를 만들어갈 수 있었기 때문입니다. 인디 개발자에서부터 큰 규모의 스튜디오까지 코로나는 그분들의 열정을 받고 성장할 수 있었습니다. 2013년에는 훨씬 신나는 일이 생길거라고 약속 드립니다. 여러분들이 코로나로 만들어 낼 훌륭한 제품들이 정말 기대 되네요.



위대한 기회의 해인 2013년을 cheer 합니다.





반응형
이전 1 2 3 4 5 6 7 8 ··· 27 다음