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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
오늘은 간단한 팁을 하나 살펴 보겠습니다.

소스와 이미지는 위 압축파일 안에 있습니다.


오늘의 팁은 배경화면이 계속 위에서 아래로 내려가는 효과 입니다.
이 소스에서는 두개의 이미지를 사용했습니다.
갤러그같은 게임에 응용할 수있을 것 같습니다.

소스를 분석 해 볼까요?

-------------------------------------------------------------------------
-- SCROLLING GAME BACKGROUND --
--------------------------------------------------------------------------

local localGroup = display.newGroup()
--First half of scrolling background
local bg1 = display.newImage("background1.png")
bg1:setReferencePoint(display.CenterLeftReferencePoint)
bg1.x = 0
bg1.y = 0
localGroup:insert(bg1)

-- Second half of scrolling background
local bg2 = display.newImage("hutbg.png")
bg2:setReferencePoint(display.CenterLeftReferencePoint)
bg2.x = 0
bg2.y = 480
localGroup:insert(bg2)

local tPrevious = system.getTimer()
local function move(event)
local tDelta = event.time - tPrevious
tPrevious = event.time

-- Change this to adjust the speed of the background
local yOffset = ( 0.15 * tDelta )

bg1.y = bg1.y + yOffset
bg2.y = bg2.y + yOffset

if bg1.y > 720 then
bg1:translate( 0, -480*2)
end
if bg2.y > 720 then
bg2:translate(0, -480*2)
end
end

-- Gets the background moving
Runtime:addEventListener( "enterFrame", move )

우선 localGroup을 만들구요.
배경 이미지 두개를 설정합니다.
하나는 x=0,y=0으로 위치를 지정하구요. 다른 하나는 x=0, y=480으로 지정했습니다.
(y는 이미지 높이를 생각해서 지정하셔야 할 겁니다.)

그리고 이 두 이미지를 localGroup에 insert하구요.

다음엔 system.getTimer()를 해서 tPrevious라는 변수에 넣습니다.
그 다음 move(event) 함수를 만듭니다.
이 함수는 맨 아래에 있는 리스너에서 호출 될 건데요. 맨 아래에 있는 리스너는 Runtime리스너이고 enterFrame으로 파라미터가 돼 있네요.
앱이 시작하면서 이 함수가 호출 될 겁니다.

이 함수 내용을 보면요.
tDelta 변수에 event.time - tPrevious 를 담구요. tPrevious에는 새롭게 이벤트 시간을 담습니다.
그리고 yOffset변수에는 대강의 이미지 스피드를 계산해 넣습니다.
이 부분은 여러분이 원하는 숫자를 맘대로 넣어보세요.
1을 넣으면 아주 천천히 흘러갈 테고 큰 숫자를 넣을 수록 아주 빨리 흘러갈 겁니다.

그리고 첫번째 백그라운드 이미지의 y 좌표를 이 속도 만큼 + 해 줍니다.
bg1.y 가 720 보다 크면 첫번째 백그라운드 이미지를 translate 합니다.
여기에 나오는 y좌표의 숫자 480, 720 등은 배경이미지의 높이에 따라 적당히 바꿔 주시면 됩니다.

첫번째 백그라운드 이미지 처럼 두번째 백그라운드 이미지도 속도만큼 y좌표를 옮겨주고 720보다 크면 translate 시킵니다.

이렇게 하면 두개의 배경 이미지가 계속 번갈아가며 아래로 흐릅니다.

오늘은 배경 이미지 스크롤 다운에 대한 간단한 팁을 알아봤습니다.

반응형


반응형
In App Purchasing 부분은 저도 잘 모릅니다.
우선 들어가기에 앞서 제가 잘 하는 방식인 샘플 코드 분석하기 부터 할까요?

샘플코드 주석

차근 차근 모범생 모드로 샘플코드에 있는 주석부터 보겠습니다.

--
-- Abstract: InApp sample project
--
-- This project demonstrates Corona In App Purchase support.
-- IMPORTANT:  Parts of this code can be exercised in Corona Simulator for
-- testing, but Corona Simulator cannot connect to the iTunes store.
-- To test with the iTunes store, you must
--   1. set up your own In App Purchase products in iTunes Connect
--   2. modify the code below to suit your products
--   3. set up a test user account and provisioning for your iOS test device
--   3. build and deploy on device
-- The code attempts to connect to the Apple iTunes store,
-- retrieve valid product information, and handle transactions.
--
-- Requires Corona build #261 or later.
--
-- Version: 1.0 (January 7, 2010)
--
-- Sample code is MIT licensed, see http://developer.anscamobile.com/code/license
-- Copyright (C) 2010 ANSCA Inc. All Rights Reserved.

일단 코로나 시뮬레이터로는 iTunes store에 접속 할 수 없다고 하니까 시뮬레이터로 하는 테스트에는 한계가 있겠네요.
테스트 하기 위해서는
1. iTunes Connect 에 본인 소유의 In App Purchase Products 를 셋업해야 한답니다.
2. 제공되는 샘플을 여러분의 product에 맞게 수정해야하구요.
3. 이 샘플코드를 빌드하고 핸드폰에 인스톨 해야 한답니다.
이 샘플코드는 iTunes store에 접속하기 위해 시도해서 product정보를 받고 일을 진행할 거랍니다.

주석을 보니까 코로나의 In App Purchase는 아이폰용 앱에만 적용 되나 봅니다.
(확실한 건 아닙니다. 저도 지금 처음 보면서 공부 하는 중이니까요. Stable version이 아닌 build 버전에서는 안드로이드에도 적용 될 수 있습니다.)

자 이제 샘플 코드를 직접 보겠습니다. (일단시작 부분부터 합니다.)

local ui = require("ui")
local store = require("store")   -- Available in Corona build #261 or later

local isSimulator = "simulator" == system.getInfo("environment")

display.setStatusBar( display.HiddenStatusBar )

-- Unbuffer console output for debugging
io.output():setvbuf('no')  -- Remove me for production code

local titlecard
function showTitle()
    if isSimulator then
        local myAlert = native.showAlert(  "iTunes Store not available in Corona Simulator",
                "Offline testing: see console for output.",
                { "OK" } )
        end
    titlecard = display.newImage( "titlecard.png", true )
    titlecard.x = display.contentCenterX
    titlecard.y = display.contentCenterY
end

local bg
local validProducts, invalidProducts = {}, {}
local descriptionArea

ui와 store를 require했습니다. store는 코로나 빌드 버전 261 이상에서만 가능하답니다.
테스트하기 전에 여러분 코로나 SDK 버전부터 확인하세요.


현재 제 맥에 깔려 있는 버전은 591 이니까 제 SDK에서도 안 되겠네요.

테스트를 위해서 버전 업 해야겠습니다.
제 SDK는 며칠전에 받은 stable한 최신 버전이었었는데...
아직 In App Purchase 기능 (store)은 stable한 버전에 속해 있지는 않습니다.2011년 10월 현재까지는요.

그 다음은 isSimulator 에 현재 디바이스가 시뮬레이터이면 true를 아니면 false를 받도록 했습니다.
그리고나서 아이폰의 status bar를 없앴구요.
io.output():setvbuf('no')는 디버깅을 위해서 콘솔의 아웃풋을 unbuffer한다는 내용인데요. 실제 빌드할 때는 필요 없는 부분입니다.
그리고 titlecard라는 local 변수를 하나 선언만 해 놨구요.
그리고 showTitle() 함수가 나옵니다.
이 함수가 하는 일을 보면요.
시뮬레이터이면 iTunes Store는 시뮬에서 안된다는 메세지를 Alert 화면으로 뿌려 줍니다.
그리고 아까 함수 밖에서 선언했던 titlecard 변수에 이미지를 디스플레이 하도록 넣고 x,y좌표를 설정해 줍니다.
이게 showTitle() 함수가 하는 일입니다.

그 다음엔 로컬로 bg를 선언하고 validProducts,invalidProducts 테이블(배열) 변수를 선언합니다. 그리고 descriptionArea라는 변수도 선언해 둡니다.

첫번째 파트는 자세히 살펴 봤구요. 뭐 별로 이해 안 가는 부분은 없었습니다.
이제 두번째 파트를 볼까요?
-------------------------------------------------------------------------------
--  Product IDs should match the In App Purchase products set up in iTunes Connect.
--  We cannot get them from the iTunes store so here they are hard coded;
--  your app could obtain them dynamically from your server.
-------------------------------------------------------------------------------
local listOfProducts =
{
    -- These Product IDs must already be set up in your store
    -- We'll use this list to retrieve prices etc. for each item
    -- Note, this simple test only has room for about 4 items, please adjust accordingly
   
    -- The iTunes store will not validate bad Product IDs
    "com.anscamobile.NewExampleInAppPurchase.ConsumableTier1",
    "com.anscamobile.NewExampleInAppPurchase.NonConsumableTier1",
    "com.anscamobile.NewExampleInAppPurchase.SubscriptionTier1",
}

주석부분을 보면요.
Products IDs 가 iTunes 에 셋업 돼 있는 프로덕트들과 일치해야 한다고 하네요.
iTunes에서 이 정보를 가져올 수 없어서 하드코딩을 해야 합니다.
자신의 서버를 가지고 있으면 다이나믹하게 이 정보들을 가져올 수 있도록 코딩 가능합니다.
그리고 실제 코딩부분을 보면 listOfProducts라는 배열을 로컬로 선언하고 그 안에 product IDs 정보를 넣습니다.
이 부분은 각자 iTunes에 먼저 셋업 하고 난 다음에 자신에게 맞는 정보를 넣어야 할 것 같습니다.

다음 세번째 파트를 보겠습니다.
이 부분이 이 샘플 코드 중에서는 제일 긴 부분이네요.
-------------------------------------------------------------------------------
-- Process and display product information obtained from store.
-- Constructs a button for each item
-------------------------------------------------------------------------------
function unpackValidProducts()

    -- Utility to build a buy button
    function newBuyButton (index)
        --    Handler for buy button
        local buttonDefault = "buttonBuy.png"
        local buttonOver = "buttonBuyDown.png"
        local buyThis = function ( product )
            print ("Ka-ching! Purchasing " ..product)
            -- Purchase the item
            if store.canMakePurchases then
                store.purchase( {product} )
            else
                native.showAlert("Store purchases are not available, please try again later",
                            { "OK" } )
            end
        end
        function buyThis_closure ( index )           
            -- Closure wrapper for buyThis() to remember which button
            return function ( event )
                    buyThis (validProducts[index].productIdentifier)         
                return true
            end       
        end
        local hideDescription = function ( )
            descriptionArea.text = "Select a product..."
        end
        local describeThis = function ( description )
            -- Display product description for testing
            print ("About this product:  " ..description)
            -- TODO wrap if necessary
            descriptionArea.text = description
            timer.performWithDelay( 2000, hideDescription) 
        end
        function describeThis_closure ( index )           
            -- Closure wrapper for describeThis() to remember which button
            return function ( event )
                    describeThis (validProducts[index].description)         
                return true
            end       
        end
        local formatted = string.format("%.2f", validProducts[index].price)
        local label = validProducts[index].title .. "  " ..formatted
        local myButton = ui.newButton{
                 default = buttonDefault, over = buttonOver,
                 onPress = describeThis_closure (index), onRelease = buyThis_closure (index),
                 text = "", textColor = {2, 0, 127, 255}, font="Marker Felt", size = 14, emboss = false
        }
        myButton:setReferencePoint(display.CenterLeftReferencePoint)
        myButton:setText(label)
        return myButton
    end

    -- Utility to build a restore button
    function newRestoreButton ()
        local buttonDefault = "buttonRestore.png"
        local buttonOver = "buttonRestoreDown.png"
        local restore = function ( product )
            -- Ask the iTunes Store to initiate restore transaction sequence
            print ("Restoring " )
            store.restore()
        end
        local hideDescription = function ( )
            descriptionArea.text = "Select a product..."
        end
        local describeThis = function ()
            -- Display info in description area
            print ("Test restore feature")
            descriptionArea.text = "Test restore feature"
            timer.performWithDelay( 2000, hideDescription) 
        end
            local label = "Test restore"
            local myButton = ui.newButton{
                     default = buttonDefault, over = buttonOver,
                     onPress = describeThis, onRelease = restore,
                     text = "", textColor = {255, 255, 1, 255}, font="Marker Felt", size = 14, emboss = false
            }
            myButton:setReferencePoint(display.CenterLeftReferencePoint)
            myButton:setText(label)
            return myButton
    end

    print ("Loading product list")
    if not validProducts then
        native.showAlert( "In App features not available", "initStore() failed", { "OK" } )       
    else
        print ("Product list loaded")
        print( "Country: " .. system.getPreference( "locale", "country" ) ) 
        -- Show store UI
        titlecard:removeSelf()
        bg = display.newImage( "storebg.png", true )
        bg.x = display.contentCenterX
        bg.y = display.contentCenterY       
        descriptionArea = native.newTextBox (10, 0.7*display.contentHeight, display.contentCenterX - 20, display.contentCenterY - 10)
        descriptionArea.text = "Select a product..."
        descriptionArea:setTextColor (2, 0, 127)
        descriptionArea.size = 18
        descriptionArea.hasBackground = false
        local buttonSpacing = 5
        print( "Found " .. #validProducts .. " valid items ")
        -- display the valid products in buttons
        for i=1, #validProducts do           
            -- Debug:  print out product info
            print ("Item " .. i .. ": " .. validProducts[i].productIdentifier
                            .. " (" .. validProducts[i].price .. ")")
            print (validProducts[i].title .. ",  ".. validProducts[i].description)

            -- create and position product button
            local myButton = newBuyButton(i)
            myButton.x = display.contentWidth - myButton.width - buttonSpacing
            myButton.y = i * buttonSpacing + (2 * i - 1) * myButton.height / 2
        end
        -- create and position Restore button
        local myButton = newRestoreButton()
        myButton.x = display.contentWidth - myButton.width - buttonSpacing
        myButton.y = display.contentHeight - myButton.height / 2 - buttonSpacing
       
        for i=1, #invalidProducts do
            -- Debug:  display the product info
            native.showAlert( "Item " .. invalidProducts[i] .. " is invalid.",
                            { "OK" } )
            print("Item " .. invalidProducts[i] .. " is invalid.")
        end

    end
end

우선 주석부터 살펴보면요. iTunes store로부터 얻은 상품 정보를 처리하고 보여주는 부분 입니다. 각 아이템 별로 버튼을 만듭니다.

처음에 unpackValidProdects() 함수가 나옵니다. 이 함수가 하는 일을 살펴 보겠습니다.
이 함수 안에는 두개의 함수가 있습니다.
첫번째는 newBuyButton(index) 함수입니다.
이 함수에는 bottonBuy 버튼 이미지와 이것이 눌렸을 때 나올 버튼 이미지 두개가 선언 돼 있습니다.
다음에 newBuyButton(index)의 로컬 함수인 buyThis함수가 있습니다.
butThis 함수는 product를 파라미터로 받습니다.
store.canMakePurchase가 true이면 store.purchase({product}) 로 아이템을 구매합니다. false이면 alert화면으로 구매가 불가능하다는 메세지를 뿌려줍니다.
여기까지가 newBuyButton(index)함수입니다.

그 다음 butThis_closure(index)를 보겠습니다.
이 함수는 buyThis()함수에서 validProducts 테이블(배열) 안의 상품 정보가 올바른 것인지를 체크합니다.

여기까지가 함수 unpackValidProducts()의 내용인데요. 상품을 확인하고 이것을 구매할 수 있도록 하는 기능이 있습니다.

다음으로는 hideDescription이라는 로컬함수가 있구요. 이 함수에는 descriptionArea의 text를 할당하는 역할을 합니다.

이어서 나오는 describeThis 로컬 함수는 파라미터로 description을 받습니다.
여기서도 descriptionArea 의 텍스트를 지정해 주는데 받은 파라미터인 description을 할당합니다. 그리고 timer.performWithDelay 함수를 써서 2000밀리 세컨드 단위로 hideDescription 함수를 실행합니다.

다음은 describeThis_closure(index)함수가 나옵니다.
이 함수는 describeThis 함수에 validProducts[index].description을 바라미터로 전달하면서 콜합니다.

이 함수들은 유저가 누른 아이템이 어떤것인지 알아내서 그 아이템을 표시해 주는 역할을 하네요.

다음엔 validProducts[index].price를 포맷에 맞게 string.format함수를 이용해서 바꾸구요.
lable에 validProducts[index].title 과 위에서 포맷한 formatted를 넣습니다.
다음 myButton에서는 처음에 require한 ui를 사용해서 새로운 버튼을 만들어 넣습니다.

지금까지는 newBuyButton에 대한 내용이구요.
그 다음엔 newRestoreButton에 대한 내용이 나옵니다.
내용은 위의 내용이랑 비슷할 겁니다.

마찬가지로 버튼 이미지 두개를 선언합니다. (나중에 눌렸을 때와 그렇지 않았을 때 사용할 버튼 이미지 입니다.)
restore 로컬함수에서 store.restore()로 iTunes Store에 restore 트랜잭션을 진행하도록 합니다.
hideDescription 로컬 함수는 위와 같구요.
descibeThis 로컬 함수도 위에 나온 것하고 같습니다.
그리고 Test restore 문자를 label에 넣고 myButton을 만듭니다.

여기까지가 newBuy버튼과 newRestore버튼을 만들고 그것을 눌렀을 때 아이템을 구매하거나 restore하는 기능을 합니다.

다음을 살펴 봅니다.

validProducts가 아니면 Alert 메세지를 띄워서 경고문구를 보여주구요.
validProdects이면 else 이하 부분을 실행합니다.

그 내용은 titlecard를 remove하고
백그라운드 이미지를 display합니다.
그리고 상품을 고르라는 텍스트 박스를 보여줍니다.
버튼 스페이스는 5로 지정하구요.
validProducts숫자에 맞게 버튼을 표시합니다. 이때는 newBuyButton함수를 이용합니다.

그 다음에 newRestoreButton()함수를 불러와서 버튼 세팅을 하구요.
invalidProducts 숫자 만큼 alert을 띄웁니다. 이 부분은 아마 디버깅을 위한 부분 같습니다.

여기까지가 세번째 파트로 가장 긴 부분이었습니다. 물건을 사고, Restore하는 내용을 담고 있었습니다.

다음 네번째 파트를 보겠습니다.

-------------------------------------------------------------------------------
-- Handler to receive product information
-- This callback is set up by store.loadProducts()
-------------------------------------------------------------------------------
function loadProductsCallback( event )
    -- Debug info for testing
    print("In loadProductsCallback()")
    print("event, event.name", event, event.name)
    print(event.products)
    print("#event.products", #event.products)
    io.flush()  -- remove for production

    -- save for later use
    validProducts = event.products
    invalidProducts = event.invalidProducts   
    unpackValidProducts ()
   
end

우선 주석을 보면 상품 정보를 받는 부분이라고 돼 있습니다. 이 콜백은 store.loadProducts() 함수에 의해 셋업 됩니다.

loadProductsCallback(event) 함수가 있습니다. 어떠한 리스너에서 이벤트를 받아서 이 함수를 콜 할 겁니다.
콜을 받으면 (event를 파라미터로 받으면) 이 함수는 디버그를 위해서 터미널에 이벤트 관련 된 정보를 뿌려주구요. io.flush()를 이용해서 일단 깨끗하게 정리합니다. (이전에 혹시 필요없는 정보가 남아 있을 경우 다 정리 하기 위해서 입니다.)
그리고 원래 구현하고자 하는 부분이 있는데요.
validProducts에 event.prodects를 담고 invalidProducts에 event.invalidProducts를 담습니다. 그리고 unpackValidProducts ()를 실행합니다.
unpackValidProducts () 는 첫 부분에서 만든 함수죠? 상품을 확인하고 이를 구매하도록 하는 함수였었습니다.

다음은 store.init()을 구현한 부분을 보겠습니다.
-------------------------------------------------------------------------------
-- Handler for all store transactions
-- This callback is set up by store.init()
-------------------------------------------------------------------------------
function transactionCallback( event )
    local infoString 
    print("transactionCallback: Received event ", event.name)
--[[
    -- Also available for your app to use:
    print("transaction", event.transaction)
    print("state", event.transaction.state)
    print("errorType", event.transaction.errorType)
    print("errorString", event.transaction.errorString)
--]]
--    print("testing", store.transactionStatePurchased, store.transactionErrorPaymentCancelled, store.transactionStateFailed )

    if event.transaction.state == "purchased" then
        infoString = "Transaction successful!"
        print (infoString)
        descriptionArea.text = infoString
    elseif  event.transaction.state == "restored" then
        -- Reminder: your app must store this information somewhere
        -- Here we just display some of it
        infoString = "Restoring transaction:" ..
                            "\n   Original ID: " ..event.transaction.originalTransactionIdentifier ..
                            "\n   Original date: "..event.transaction.originalDate
        print (infoString)
        descriptionArea.text = infoString
        print("productIdentifier", event.transaction.productIdentifier)
        print("receipt", event.transaction.receipt)
        print("transactionIdentifier", event.transaction.transactionIdentifier)
        print("date", event.transaction.date)
        print("originalReceipt", event.transaction.originalReceipt)

    elseif event.transaction.state == "cancelled" then
        infoString = "Transaction cancelled by user."
        print (infoString)
        descriptionArea.text = infoString

    elseif event.transaction.state == "failed" then       
        infoString = "Transaction failed, type: ",
            event.transaction.errorType, event.transaction.errorString
        print (infoString)
        descriptionArea.text = infoString
    else
        infoString = "Unknown event"
        print (infoString)
        descriptionArea.text = infoString
    end

    -- Tell the store we are done with the transaction.
    -- If you are providing downloadable content, do not call this until
    -- the download has completed.
    store.finishTransaction( event.transaction )
end

주석을 보면 모든 store 트랜잭션의 핸들러 입니다. 이 콜백은 store.init()에 의해 셋업 됩니다.
이 부분은 transactionCallback(event) 함수 하나가 있습니다.
어떠한 리스너에서 콜 되는 함수이고 event를 파라미터로 받습니다.

처음에 infoString변수를 선언하고 터미널에 event.name을 뿌려줍니다.

그 다음 이 이벤트 상태가 purchased일 경우 infoString에 Transaction successful!이라는 메세지를 할당하구 이를 descriptionArea의 텍스트에 넣습니다.
만약 이벤트가 restored일 경우에는 infoString에 event.transaction.originalTransactionIdentifier 와 event.transaction.originalDate 정보를 담습니다.
그리고 descriptionArea의 텍스트에 이를 할당합니다.

만약 이벤트가 cancelled일 경우 infoString에 Transaction cancelled by user 라는 텍스트를 할당하고 descriptionArea의 텍스트에 이를 할당합니다.

만약 이벤트 트랜잭션 상태가 failed일 경우는 에러 정보를 infoString에 담고 이 내용을 descriptionArea의 텍스트에 넣습니다.

그리고 이 이벤트 트랜잭션 상태가 아무것에도 해당되지 않을 경우에는 else부분을 실행합니다.
여기에는 infoString에 Unknown event라는 텍스트를 넣어서 이를 destriptionArea 의 텍스트에 넣습니다.

종합해 보면 이 파트는 event를 받아서 이 이벤트의 각 상태별로 다른 내용을 infoString에 넣고 이를 descriptionArea의 텍스트에 넣는 기능을 하네요.
그 다음에 마지막으로 store에 transaction을 finish하도록 합니다.

그 다음 파트를 보면 마찬가지로 어떤 리스너에서 콜 하는 함수인가 봅니다.
이는 파라미터로 event를 받는 걸 보고 짐작할 수 있거든요.
setupMyStore(event) 함수인데요.
-------------------------------------------------------------------------------
-- Setter upper
-------------------------------------------------------------------------------
function setupMyStore (event)
    store.loadProducts( listOfProducts, loadProductsCallback )
    print ("After store.loadProducts, waiting for callback")
   
    if isSimulator then
        -- No Store, so no callbacks, so exercise our GUI "manually" 
        validProducts[1] = {}
        validProducts[1].title = "Lemonade refill"
        validProducts[1].description = "A wonderful dummy product for testing"
        validProducts[1].price = 0.99
        validProducts[1].productIdentifier = "com.lemonadestand.consumable.001"
        validProducts[2] = {}
        validProducts[2].title = "Drinking glass"
        validProducts[2].description = "Makes lemonade easier to drink."
        validProducts[2].price = 1.99
        validProducts[2].productIdentifier = "com.lemonadestand.nonconsumable.001"
        validProducts[3] = {}
        validProducts[3].title = "Daily dose"
        validProducts[3].description = "Made fresh daily!"
        validProducts[3].price = 19.99
        validProducts[3].productIdentifier = "com.lemonadestand.subscription.001"
        unpackValidProducts()  
    end
end
처음에 store.loadProducts()를 실행합니다. iTunes store에 있는 상품들을 load하나 봅니다.
그게 끝이예요.
만약 기기가 시뮬레이터이면 그냥 이 샘플파일에 미리 지정한 세가지 상품이 뜨게끔 합니다.
이 샘플코드를 바탕으로 테스트를 하려면 처음 주석에 있는 것처럼 여러분의 상품 셋업이 iTunes Store에 돼 있어야 합니다. 저도 아직 하나도 없는데.. 저도 마찬가지구요.

-------------------------------------------------------------------------------
-- Main
-------------------------------------------------------------------------------

-- Show title card
showTitle ()

-- Connect to store at startup
store.init (transactionCallback )
print ("After init")

-- Hide title card, run store
timer.performWithDelay (1000, setupMyStore)

collectgarbage()

마지막 파트는 main부분으로 showTitle()을 시작하고 store.init(transactionCallback)을 실행합니다.
그리고 1초 간격으로 setUpMyStore를 진행합니다.
마지막엔 collectgarbage()를 해서 garbage data를 없애구요.

여기까지 코로나에서 제공한 in App Purchase 샘플 코드를 분석해 봤습니다.

일단 제대로 테스트 하기 위해서는 iTunes Store에 저의 products를 세팅하고
코로나 SDK도 더 최신 버전으로 깐 다음에 테스트를 진행 할 수 있겠네요.

혹시 iTunes Store에 in App Purchase 를 위해서 products 세팅하는 법 아시는 분 정보 부탁드릴께요...

지금까지 배운 코로나 코딩 중에 제일 복잡한 것 같은데요.

그럼 저도 이 선행 되어야 할 사항들 다 마무리 하고 테스트 한 다음에 다시 글을 올리겠습니다.

그럼....
반응형

코로나 네트워킹 과 웹 서비스 2

2011. 10. 24. 21:17 | Posted by 솔웅


반응형
Asynchronous HTTP (비동기 HTTP)

이 기능은 HTTP 메소드 (GET,POST 등)를 사용하거나 개발자가 header와 body를 사용해서 비동기식 HTTP와 HTTPS/SSL 콜을 만들도록 합니다.
코로나는 이 때 서버로부터 어떤 응답을 받는 동안 멈출 필요가 없도록 하는 기능을 제공합니다.
여러분은 네트워크를 통해서 파일을 다운로드 할 수 있습니다. 이 파일을 메모리에 로딩 할 필요가 없습니다. 이것은 해당 파일이나 이미지가 아주 큰 경우에 아주 유용합니다.
코로나는 이와 관련 두개의 샘플 프로젝트를 제공합니다.

AsynchHTTP

local myText = display.newText("(Waiting for response)", 0, 0, native.systemFont, 16)
myText.x = display.contentCenterX
myText.y = 120

local function networkListener( event )
    if ( event.isError ) then
        myText.text = "Network error!"
    else
        myText.text = "See Corona Terminal for response"
        print ( "RESPONSE: " .. event.response )
    end
end

-- Access Google over SSL:
network.request( "https://encrypted.google.com", "GET", networkListener )

편의를 위해 주석부분은 제거했습니다.
간단히 살펴보면 첫 세줄은 텍스트를 뿌려주는 겁니다.
networkListener 함수를 보면 event를 받아서 에러일 경우 에러메세지를 뿌려주고 에러가 아닐 경우는 그 결과값을 터미널에 뿌려줍니다.
결과값은 맨 마지막 줄 network.request를 통해서 받아온 겁니다.


뭐 request 하는 것도 없고 그냥 network.request() 에서 get 방식으로 가져오는 것 밖에는 없네요.

두 번째 예제도 보면요.

AsynchImageDownload

local function networkListener( event )
    if ( event.isError ) then
        print ( "Network error - download failed" )
    else
        myImage = display.newImage( "helloCopy.png", system.TemporaryDirectory, 60, 40 )
        myImage.alpha = 0
        transition.to( myImage, { alpha = 1.0 } )
    end
   
    print ( "RESPONSE: " .. event.response )
end

network.download(
    "http://developer.anscamobile.com/demo/hello.png",
    "GET",
    networkListener,
    "helloCopy.png",
    system.TemporaryDirectory )

-- NOTE: files saved to system.TemporaryDirectory are not guaranteed to persist across launches.
-- Use system.DocumentsDirectory if you want files to persist.

----------------------------------------------------------------------------------------------------
-- Method 2: use display.loadRemoteImage() to get the file and create a display object in one step

local function networkListener2( event )
    if ( event.isError ) then
        print ( "Network error - download failed" )
    else
        event.target.alpha = 0
        transition.to( event.target, { alpha = 1.0 } )
    end
   
    print ( "RESPONSE: " .. event.response )
end

myImage2 = display.loadRemoteImage(
    "http://developer.anscamobile.com/demo/hello.png",
    "GET",
    networkListener2,
    "helloCopy2.png",
    system.TemporaryDirectory,
    60, 280 )


이 샘플은 HTTP로 이미지를 download할 수 있도록 한 건데요.
network.download()부터 보시면 경로가 있고 GET방식으로 불러오고 파일 이름이 있고 이 파일을 system.TemporaryDirectory 에 저장하네요.
중간에 보면 networkListener함수를 불러오는데요.
이 함수는 event를 파라미터로 받습니다.

함수 안을 보면 에러일 경우 에러 메세지 뿌려주고 에러가 아닐경우는 system.TemporaryDirectory에 있는 이미지를 뿌려줍니다.

두 번째 myImage2 변수선언 부분을 보면요.
display.loadRemoteImage()함수를 썼는데 이것도 그 안의 내용은 network.download()와 똑 같습니다.
불러오는 함수가 networkListener2를 불러오고 맨 마지막에 60,280 이란 숫자가 있는게 다르네요.
networkListener2 함수를 살펴보면 마찬가지로 에러일 경우 에러메세지 뿌려주고 에러가 아닐경우는 event.target.alpha =0으로 투명하게 하고 이것을 transition.to함수로 투명도를 없앴습니다.

대충 보니까 network.download()는 다운로드만 받는거고 display.loadRemoteImage() 는 다운로드 받은 다음에 이것을 화면에 뿌려주기까지 하는 거네요.

먼저 공부도 하기 전에 샘플코드를 분석했는데요. 제대로 공부를 함 해 보겠습니다.

Network Requests

network.request( url, method, listener [, params] )
여기서 디폴트 method 는 GET 입니다.
결과 event는 아래와 같은 프로퍼티들을 가집니다.
    •    event.response -- 서버로부트 받은 결과 값 (String)
    •    event.isError -- 네트워크 에러와 관련한 True, False 값
이 두 파라미터는 첫 번째 예제에서 사용했었죠?

예제에는 없지만 여기에 헤더나 바디를 추가할 수 있습니다.
params.headers - 테이블로 선언 함
params.body - 스트링을 담은 HTTP 바디

headers = {}
 
headers["Content-Type"] = "application/json"
headers["Accept-Language"] = "en-US"
 
headers.body = "This is an example request body."

위 샘플을 보면 헤더에는 json 사용한다는 것과 언어는 미국식 영어를 사용한다는게 있고요. body에는 예제 바디라는 문장이 있습니다.
HTML 많이 사용해 보신 분들은 이게 뭔지 다 아시겠죠?

Network Downloads

위의 Network Requests와 거의 유사하구요 + 다운로드 기능까지 있는 겁니다.
메모리에 캐싱하지 않고 제공된 디렉토리에 제공된 이름의 파일을 HTTP로 다운로드 하는 겁니다. 큰 파일일 경우 유용하게 사용 하실 수 있습니다.

network.download( url, method, listener [, params], destFilename [, baseDir] )

디폴트 method는 GET입니다. baseDir는 system.DocumentsDirectory나 system.TemporaryDirectory가 될 수 있습니다.

결과 event로는 Network Requests와 같이 event.response와 event.isError가 있습니다.

예제는 위의 두 번째 예제를 봐 주세요.

Displaying Remote Images

이건 예제에서도 봤듯이 다운로드 하면서 곧바로 화면에 이미지를 출력해 주는 기능입니다.

display.loadRemoteImage( url, method, listener [, params], destFilename [, baseDir] [, x, y] )

다 똑 같구요. 마지막에 이미지의 x,y 값이 있는게 다릅니다.

이 함수에는 세가지 프로퍼티가 있습니다. event.response와 event.isError는 위에것들이랑 똑 같구요. 이 외에 event.target이 있습니다.
이 프로퍼티는 이미지가 다운로드 된 후의 새로 만들어진 display object입니다.

예제는 위 두 번째 예제파일 두 번째 부분을 봐 주세요.

오늘 살펴 본 것은 비동기식 HTTP 통신입니다.

이외에 코로나 DOC에 있는 내용은 OpenFeint, Credit, In-App Purchase 입니다.
Open Feint가 무엇인지 아시려면 아래 링크를 보시면 도움이 될 거예요 .
http://novathin.kr/146
그리고 일반 안드로이드 앱 프로그래밍에서 활용하려면 아래 링크를 참조하시구요.
http://blog.daum.net/gkrttod/1091
코로나에서 이 Open Feint를 사용하는 방법은 제가 공부하고 테스트 하고 난 다음에 올리겠습니다.

Credit은 코로나SDK를 만든 회사인 Ansca Mobile에서 제공하는 서비스 인 것 같습니다.
원격으로 User의 Point를 적립하고 이것을 나중에 in-App Purchase 등 현금 구매 할 때 사용할 수 있도록 하는 서비스 같은데요.
저도 이 부분은 공부를 좀 해 봐야겠어요.

개인적으로 In-App Purchase 에 관심이 있어서요.
나머지 네트워킹 주제들 중에 이 In-App Purchase를 먼저 공부하고 글을 올릴 예정입니다.

그럼...



반응형