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

최근에 받은 트랙백

글 보관함


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()를 사용해도 상관은 없습니다.






반응형

Comment


Posted on . Written by


수요일 FAQ 시간이죠. 이제 코로나에서도 안드로이드 기기의 Local Notification 을 지원하게 됐죠. 오늘은 이와 관련돼서 자주 문의 되는 질문을 다루겠습니다.


1. How do I detect that a local notification occurred.


Local notification 를 감지하는 방법에는 두가지가 있습니다. 예를 들어 앱이 running 혹은  suspended 이고 유저가 notification을 tap 했다면 여러분 앱의 notification listener 가 fire 될 겁니다. 만약 앱이 exited (applicationExit) 상태이고 notification이 fire 된다면 notification 이 앱을 open 하고 launchArgs.notification parameter 를 set 할 겁니다. 여러분은 코드 내에서 그 notification parameter 를 set 하시고 notification 리스너를 call 하셔야 합니다. (왜냐하면 앱이 시작할 때 이것을 자동으로 call 하지 않거든요.)


local launchArgs = ...      -- at the top of your program code

local function notificationListener( event )
    -- Handle Local Notification here
end


여러분 앱이 유저가 notification event를 touching 해서 앱이 시작했는지 여부를 알아낼 수 있는 코드가 아래에 있습니다.


 if launchArgs and launchArgs.notification then
     -- Need to call the notification listener since it won’t get called if the
     --the app was already closed.
     notificationListener( launchArgs.notification )
 end


notification listener 에서 event.applicationState는 유저에 의해 norification 이 involve 됐을 경우 앱의 상태를 나타냅니다. “active”는 앱이 running 될 때 notification 이 fire 된 것을 의미합니다. 그리고 “inactive”는 notification 이 앱이 suspended 혹은 closed 됐을 때 fire 됐음을 의미합니다.





2. How do I detect that a user ignored a local notification?


답은 그렇게 할 수 없다 입니다. 만약 유저가 notification을 delete (or ignores) 했다면 notification listener 는 call 이 되지 않을 겁니다. 그리고 여러분의 앱은 그게 fire 되지 않으니까 그게 ignored 됐는지도 알 수 없습니다. (여러분 앱은 계속 notification schedule 을 track 할 겁니다. 그리고 notification 이 fire 되면 그 때 알겁니다.)


3. What are the differences between local notifications in iOS and Android?


iOS와 Android 의 가장 큰 다른 점은 “badge.” 입니다. 그건 iOS device 에서 앱 아이콘에 표시되는 숫자인데요 notification 이 fire 되면 표시됩니다. 일지 않은 메세지의 갯수를 표시하게 되죠. 그 값과 의미는 앱에 의해 정의됩니다. 안드로이드는 이 badge number 를 이용하지 않습니다.


4. How do I update the badge number in iOS?


badge number 는 notification 이 fire 됐을 때 세팅됩니다. 혹은 아래 코드를 사용해서 update 할 수도 있는데요. 이 코드는 현재의 badge 값을 읽어서 숫자를 증가시키는 겁니다.


local badge = native.getProperty( "applicationIconBadgeNumber" )
badge = badge + 1
native.setProperty( "applicationIconBadgeNumber", badge )



이렇게 함으로서 notification이 fire 됐을 때 badge number를 무엇으로 할 건지 정할 수 있습니다. 일반적으로 각 notification 에 대해 badge number 를 increment 하죠. 이 badge number 를 clear 하시려면 native.setProperty 를 call 해서 badge 값을 0으로 하시면 됩니다.


5. Is there any examples using Local Notifications?


최근에 릴리즈된 CoronaSDK (build 971) 에 Local Notification 에 대한 sample 앱이 있습니다. 이 샘플은 이 local notification을 사용할 때 활용할 수 있는 많은 기능들이 있습니다.


오늘은 여기까지 입니다. 유익한 시간 되었길 바랍니다.



반응형

Comment



Posted on . Written by


오늘의 튜토리얼은 코로나 Ambassador 이면서 Mobile App Development with Corona: Getting Started.”의 저자인 Brian Burton 의 글입니다. 이글은  Burton’s Media Group에서 발표된 내용입니다.


제가 자주 하는 질문 중 하나가 remote server 에서 어떻게 데이터를 mobile app 으로 전달할 수가 있는가 입니다. (대개 그 데이터는 데이터베이스에 저장되 있겠죠.)

이와 관련해서 가장 좋은 방법을 소개해 드리려고 많이 연구했습니다. 아마 얼마나 많은 방법들을 제가 시도해 봤는지 알려드리려면 시리즈를 연재해야 될 겁니다. 데이터를 가지고 올 수 있는 방법은 아주 많이 있고 또 여러 possibilities 들도 많이 있습니다. 모두 찾아내려면 아마 끝이 안 날 겁니다. 그래서 이것들을 더 찾아내고나서 몇달 후에 얘기하는 것보다 리모트 서버에서 모바일 앱으로 데이터를 전달하는 한가지 방법이라도 우선 소개해 드리는게 좋을 것 같습니다.


The Process


간단하게 하기 위해 표준 3-tier architecture 를 사용할 겁니다.




Remote Database


이 튜토리얼에서는 리모트 서버에 MySQL을 만들어 둘 겁니다. 3개의 필드를 가지고 있는 1개의 테이블을 사용하겠습니다. (id, Movie (title), Year (영화가 릴리즈 된 해)




Logic or Middleware


MySQL 데이트베이스로부터 데이터를 pull 해 오기 위한 middleware 로 PHP를 사용하겠습니다.  그리고 그 데이터를 모바일 디바이스에 return 하기 전에 JSON format 으로 encoding 할 겁니다.


<?php
$host="localhost"; //replace with your hostname
$username="database_example"; //replace with your username
$password="Password"; //replace with your password
$db_name="MyMovies"; //replace with your database
$con=mysql_connect("$host", "$username", "$password")or die("cannot connect");
mysql_select_db("$db_name") or die ("cannot select DB");
$sql = "select * from BestMovies"; //replace with your table name
$result = mysql_query($sql);
$json = array();
if(mysql_num_rows($result)){while($row=mysql_fetch_row($result)){
$json[]=$row;
}
}
mysql_close($db_name);
echo json_encode($json);
?>

Client


아래는 remote server 로부터 정보를 가져오고 JSON 형식으로 된 그 정보를 decode 하는 코로나 소스코드 입니다. 이 파일을 local SQLite file로 저장하세요. 그리고 나서 SQLite 의 content 로 화면에 load 하세요. 데이터베이스는 persistent 입니다. 그러니까 이 코드를 여러번 실행해서 remote database를 call 하면 SQLite 데이터베이스에 new data 를 계속 추가할 겁니다.


local sqlite3 = require ("sqlite3")
local myNewData
local json = require ("json")
local decodedData

local SaveData = function ()

  --Save new data to a sqlite file
  --open SQLite database, if it doesn't exist, create database
  local path = system.pathForFile("movies.sqlite", system.DocumentsDirectory)
  db = sqlite3.open( path )
  print(path)
      
  --setup the table if it doesn't exist
  local tablesetup = "CREATE TABLE IF NOT EXISTS mymovies
(id INTEGER PRIMARY KEY, movie, year);"
  db:exec( tablesetup )
  print(tablesetup)
    
  --save data to database
  local counter = 1
  local index = "movie"..counter
  local movie = decodedData[index]
  print(movie)

  while ( movie ~= nil ) do
    local tablefill ="INSERT INTO mymovies
VALUES (NULL,'"
.. movie[2] .. "','" .. movie[3] .."');"
    print(tablefill)
    db:exec( tablefill )
    counter=counter+1
    index = "movie"..counter
    movie = decodedData[index]
  end
 
  --everything is saved to SQLite database; close database
  db:close()
      
  --Load database contents to screen
  --open database
  local path = system.pathForFile("movies.sqlite", system.DocumentsDirectory)
  db = sqlite3.open( path )
  print(path)
        
  --print all the table contents
  local sql = "SELECT * FROM mymovies"
  for row in db:nrows(sql) do
    local text = row.movie.." "..row.year
    local t = display.newText( text, 20, 30 * row.id, native.systemFont, 24 )
    t:setTextColor( 255,255,255 )
  end
  db:close()

end


local function networkListener( event )

  if ( event.isError ) then
    print( "Network error!")
  else
    myNewData = event.response
    print ("From server: "..myNewData)
    decodedData = (json.decode( myNewData))
    SaveData()
  end

end
network.request( "http://www.BurtonsMediaGroup.com/myMovies.php",
"GET", networkListener )


여기까지가 기본적인 튜토리얼 입니다. 유튜브에 좀 더 자세하게 설명한 비디오를 올렸습니다.

보시려면 유튜브를 참조하세요.




반응형

Comment


Posted on . Written by


오늘의 guest tutorial 은 Omid Ahoural 의 두번째 글 입니다. ArdentKid 라는 별명으로 불리는 inDie Game Developer 입니다. 지난 2년간 Omid는 코로나를 접해 왔습니다. 그리고 그가 참여한 첫번째 앱인 "Balloon Bazooka" 의 출시를 앞두고 있습니다. 그는 또한 Corona 강좌를 제공하고 있기도 하구요. 그의 블로그 www.ardentkid.com에 가시면 보실 수 있습니다.



Optimizing Transitions


이제 곧 나올 저의 게임 퍼포먼스를 올리기 위해서 game sprite들에 좀 더 효율적인 transition을 사용하기로 했습니다. TimeSpaceMagic 같은 library들을 찾을 수 있었는데요. pause 와 time-warping 관련한 겁니다. 이 라이브러리들은 모두 transition property들인 at Runtime을 calculate 하는데요 작업하는데 꽤 힘든 일 입니다. transition이 항상 같다면 이런 작업은 필요 없겠죠.


그래서 저는 AK-Tween 을 만들었습니다. predetermining transitions 과 그것들을 at Runtime 에 manually 적용하는 겁니다. 다른 것들을 벤치마크해서 다른 Runtime transition 접근법보다 (제가 찾은 가장 efficient 한 방법보다도) 20% 정도 빠르게 동작하도록 만들었습니다. 이 라리브러리의 alpha 버전은 AS3의 GTween을 바탕으로 해서 쉽게 만들 수 있었습니다.  그러니까 이 라이브러리는 그 결과가 코로나의 transition easing 과 비교해도 비슷할 만큼 쉽게 만들었습니다. (아래 iPhone4 테스트 결과를 보세요.)


The Core Idea


“AK-Tween”  의 계산(calculates) 는 table이나 배열에 저장된 값들에 의해 load time (Runtime이 아닌)에 일어납니다. 그리고 그 값들이 필요하면 custom Runtime function을 사용해서 iterate 합니다. 아래에 배열값을 return 하는 tween calculation code 의 일 부분이 있습니다. 한 눈에 보시고 이해하실 수 있도록 간략하게 만들었습니다.


--AKtween.lua
local function tweenCalc( config, anim )

  local time = config.time or 1000
  local ease = config.ease
  local totFrames = 0.06 * time --frames at 60FPS

  local step = 1/totFrames

  if ( ease == "outQuad" ) then

    for i=1,totFrames do
      pos = pos + step
      local index = i +startArr
      local ratio = -pos * (pos-2) arr[index] = from + (ratio * delta)
    end

  elseif ( tween == "inQuad" ) then
    ...

  end

  return arr
end



아래는 이 tween 을 실제로 set up 하는 부분입니다. (예제로 공이 튕기는 것을 구현하겠습니다.)


--CREATE TWEEN FOR BALL BOUNCE
local AKtween = require( "AKtween" )

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

local bounceTween = AKtween:newTween( {time=400, y=-120, ease="outQuad"} )
--bounceTween.yArr = [-4.79, -4.58, -4.37, -4.16, -3.95 ...
-0.83, -0.625, -0.416, -0.20, 0]
--#bounceTween.yArr = 24

bounceTween:append( {time=400, y=0, "inQuad"} )
--bounceTween.yArr = [-4.79, -4.58, -4.37, ...
-0.416, -0.20, 0, 0.20, 0.416 ... 4.37, 4.58, 4.79]
--#bounceTween.yArr = 48

ball.yBounceArr = bounceTween.yArr --JUST GET THE Y-VALUES,
--bounceTween:apply( ball, "bounce" )
--OR ALLOW AKTWEEN TO HANDLE THE WHOLE ANIMATION
 

이렇게 하면 y=0 to y=-120 까지 볼이 튀어 올라가고 다시 내려올 겁니다. 그 움직임은 quadratic ease를 사용해서 자연스러울 거구요.  시간은 400ms 가 걸릴겁니다. (24 frames at 60 fps). 우리가 한 일은 마지막 줄에 :apply() function을 call 한 것입니다. 그리고 이 tween을 AKtween 에 의해 제대로 컨트롤 되게 하기 위해 ball:playTween(“bounce”)를 사용했습니다. 이것과 똑 같은 일을 할건데요. 라이브러리에서 생성된 y-value array만 사용해 보겠습니다.

그러니까 ball.yBounceArrpredetermined y-values가 저장돼 있는 거죠. Runtime ball:bounce() script를 set up 하기만 하면 됩니다.


function ball:bounce()
local yVals = self.yBounceArr
  local totFrames = #yVals
  local curFrame = 1

  --RUNTIME SCRIPT, OCCURS ON EVERY FRAME
  local function frameCount()
    if ( curFrame <= totFrames ) then
      self:translate( 0, yVals[curFrame] )
      curFrame = curFrame+1
    else
      curFrame = 1
    end
  end

  --SAVE REFERENCE TO RUNTIME FUNCTION, AND BEGIN
  self.bounceFC = frameCount
  Runtime:addEventListener( "enterFrame", frameCount )

end


 


불이 튕기기를 원하면 언제든지 ball:bounce()를 call 합니다. 그러면 frameCount라고
하는 local Runtime script 를 setup 합니다. 이는 frameCount는 각 프레임마다 공에
새로운 y의 위치값을 세팅할 겁니다.

이 reference는 또한 ball.bounceFC에도 저장돼 있습니다. 이 ball.bounceFC
튕기는 것을 pause, resume, or stop할 수 있게 합니다.


function ball:pause()

  if ( self.bounceFC ) then
    Runtime:removeEventListener( "enterFrame", self.bounceFC )
  end
end

function ball:resume()
  if ( self.bounceFC ) then
    Runtime:addEventListener( "enterFrame", self.bounceFC )
  end
end

function ball:stop()
  if ( self.bounceFC ) then
    Runtime:removeEventListener( "enterFrame", self.bounceFC )
    self.bounceFC = nil
  end
end

manual way 는 regular transition 보다 implement 하는데 더 어렵습니다. 하지만
자신만의 animation property들을 사용해서 원하는 결과를 정확히 만들어 낼 수
있는 장점이 있죠.

performance 측면에서도 아주 효율적입니다. 또한 pause-enabled 하구요.
저의 dynamic sprites implementation과 같이 사용하면 게임이 아주 자연스럽게 움직일
겁니다.


Benchmark Results



아래가 이 메소드를 사용한 후의 benchmark result 입니다.
두 매뉴얼과 AKtween play function 들은 아래와 같이 주목할 만한 결과를 보여 줍니다.


요약하자면 우리는 우리의 object들을 transitioning 하기 위해
underlying alternate method를 정의했습니다.
이러한 benchmark는 이 메소드가 아주 효율적이라는 것을 증명했구요.
우리는 그냥 단순히 배열에서 값을 읽어서 이것을 해당 object property 에 적용하는
일만 합니다.

이러한 작업은 AKtween juggler에 object를 pass 함으로서 그 기능을 사용하게 됩니다.
혹은 local runtime script를 추가하거나 제거함으로서 애니메이션을 전체적으로 control
할 수 있습니다.

두 방법 모두 AKtween을 사용하시면 됩니다. 이것이 여러분 앱 개발에 도움이 될 수
있는지 잘 확인해 보세요.




반응형

Comment


오늘은 CoronaLabs 에서 facebook을 통해 알려준 Lua Performance 관련 한 정보를 정리하겠습니다.


있는 곳은 http://trac.caspring.org/wiki/LuaPerformance 입니다.


여기에 정리해 두고 나중에 Corona SDK project 진행할 때 미리 한번 훑어 보고 해야겠네요.



Things you should know about Lua's performance


This wiki is a result of some lua performance tests (the widget is included with ca sandbox).


TEST 1: localize

Code:

local min = math.min

Results:

normal way: 0.719 (158%)
localized: 0.453 (100%)

Conclusion:

  -> Yeah, we should localize all used funtions.


TEST 2: localized class-methods (with only 3 accesses!)

Code1:

  for i=1,1000000 do
    local x = class.test()
    local y = class.test()
    local z = class.test()
  end

Code2:

  for i=1,1000000 do
    local test = class.test
    local x = test()
    local y = test()
    local z = test()
  end

Results:

normal way: 1.203 (102%)
localized: 1.172 (100%)

Conclusion:

  -> No, it isn't faster to localize a class method IN the function call.


TEST 3: unpack a table

Code1:

  for i=1,1000000 do
    local x = min( a[1],a[2],a[3],a[4] )
  end

Code2:

  local unpack = unpack
  for i=1,1000000 do
    local x = min( unpack(a) )
  end

Code3:

  local function unpack4(a)
    return a[1],a[2],a[3],a[4]
  end
  for i=1,1000000 do
    local x = min( unpack4(a) )
  end

Results:

with [ ]: 0.485 (100%)
unpack(): 1.093 (225%)
custom unpack4: 0.641 (131%)

Conclusion:

  -> Don't use unpack() in time critical code!


TEST 4: determine maximum and set it ('>' vs. max)

Code1:

  local max = math.max
  for i=1,1000000 do
     x = max(random(cnt),x)
  end

Code2:

  for i=1,1000000 do
    local r = random(cnt)
    if (r>x) then x = r end
  end

Results:

math.max: 0.437 (156%)
'if > then': 0.282 (100%)

Conclusion:

  -> Don't use math.[max|min]() in time critical code!


TEST 5: nil checks ('if' vs. 'or')

Code1:

  for i=1,1000000 do
    local y,x
    if (random()>0.5) then y=1 end 
    if (y==nil) then x=1 else x=y end
  end

Code2:

  for i=1,1000000 do
    local y
    if (random()>0.5) then y=1 end 
    local x=y or 1
  end

Results:

nil-check: 0.297 (106%)
a=x or y: 0.281 (100%)

Conclusion:

  -> WOW! the or-operator is faster than a nil-check. Use it! :D


TEST 6: 'x2' vs. 'x*x'

Code1:

  for i=1,1000000 do
     local y = x^2
  end

Code2:

  for i=1,1000000 do
     local y = x*x
  end

Results:

x^2: 1.422 (110%)
x*x: 1.297 (100%)


TEST 7: modulus operators (math.mod vs. %)

Code1:

  local fmod = math.fmod
  for i=1,1000000 do
    if (fmod(i,30)<1) then
      local x = 1
    end
  end

Code2:

  for i=1,1000000 do
    if ((i%30)<1) then
      local x = 1
    end
  end

Results:

math.mod: 0.281 (355%)
%: 0.079 (100%)

Conclusion:

  -> Don't use math.fmod() for positive numbers (for negative ones % and fmod() have different results!)!


TEST 8: functions as param for other functions

Code1:

  local func1 = function(a,b,func) 
    return func(a+b) 
  end

  for i=1,1000000 do
    local x = func1(1,2,function(a) return a*2 end)
  end

Code2:

  local func1 = function(a,b,func) 
    return func(a+b) 
  end
  local func2 = function(a) 
    return a*2 
  end

  for i=1,1000000 do
    local x = func1(1,2,func2)
  end

Results:

defined in function param: 3.890 (1144%)
defined as local: 0.344 (100%)

Conclusion:

  -> REALLY, LOCALIZE YOUR FUNCTIONS ALWAYS BEFORE SENDING THEM INTO ANOTHER FUNCTION!!!
     i.e if you use gl.BeginEnd(), gl.CreateList(), ...!!!


TEST 9: for-loops

Code1:

  for i=1,1000000 do
    for j,v in pairs(a) do
      x=v
    end
  end

Code2:

  for i=1,1000000 do
    for j,v in ipairs(a) do
      x=v
    end
  end

Code3:

  for i=1,1000000 do
    for i=1,100 do
      x=a[i]
    end
  end

Code4:

  for i=1,1000000 do
    for i=1,#a do
      x=a[i]
    end
  end

Code5:

  for i=1,1000000 do
    local length = #a
    for i=1,length do
      x=a[i]
    end
  end

Results:

pairs: 3.078 (217%)
ipairs: 3.344 (236%)
for i=1,x do: 1.422 (100%)
for i=1,#atable do 1.422 (100%)
for i=1,atable_length do: 1.562 (110%)

Conclusion:

  -> Don't use pairs() or ipairs()!
     Try to save the table-size somewhere and use "for i=1,x do"!


TEST 10: array access (with [ ]) vs. object access (with .method)

Code1:

  for i=1,1000000 do
    x = a["foo"]
  end

Code2:

  for i=1,1000000 do
    x = a.foo
  end

Results:

atable["foo"]: 1.125 (100%)
atable.foo: 1.141 (101%)


TEST 11: buffered table item access

Code1:

  for i=1,1000000 do
    for n=1,100 do
      a[n].x=a[n].x+1
    end
  end

Code2:

  for i=1,1000000 do
    for n=1,100 do
      local y = a[n]
      y.x=y.x+1
    end
  end

Results:

'a[n].x=a[n].x+1': 1.453 (127%)
'local y=a[n]; y.x=y.x+1': 1.140 (100%)


TEST 12: adding table items (table.insert vs. [ ])

Code1:

  local tinsert = table.insert
  for i=1,1000000 do
    tinsert(a,i)
  end

Code2:

  for i=1,1000000 do
    a[i]=i
  end

Code3:

  for i=1,1000000 do
    a[#a+1]=i
  end

Code4:

  local count = 1
  for i=1,1000000 do
    d[count]=i
    count=count+1
  end

Results:

table.insert: 1.250 (727%)
a[i]: 0.172 (100%)
a[#a+1]=x: 0.453 (263%)
a[count++]=x: 0.203 (118%)

Conclusion:

  -> Don't use table.insert!!!
     Try to save the table-size somewhere and use "a[count+1]=x"!



반응형

Comment


Posted on . Written by


수요일 다시 FAQ 시간 입니다. 오늘은 Lua 와 관련해서 자주 들어오는 질문을 모아봤습니다. basic Lua language 프로그래밍에서 루아 언어만의 특징도 다뤘습니다.


1. Do I need to end Corona code lines with “;”?


아래와 같은 Corona(Lua) 코드를 보셨을 겁니다.


local myRect = display.newRect( 0, 0, 100, 50 );


또 아래와 같은 코드도 보셨을 겁니다.


local myRect = display.newRect( 0, 0, 100, 50 )


두 코드가 다른 것은 맨 마지막에 semicolon (“;”) character가 있느냐 없느냐죠. 많은 languages(JavaScript, C++, ObjectiveC, etc.) 라인의 끝을 가리키는 표시로 semicolon을 요구합니다. 하지만 Lua 는 굳이 semicolon을 넣지 않아도 스스로 알아서 라인의 끝을 인식합니다. 또한 맨 마지막에 semicolon 을 넣어도 상관은 없습니다. 단지 반드시 넣어야 되는 것은 아닙니다.


가끔 한 줄에 여러 statement를 넣을 때가 있죠.


a = 100; b = 200


Lua 에서는 아래 코드도 틀린 구문이 아닙니다.


a = 100 b = 200


2. Can Lua functions return multiple values?


이 기능은 Lua 만의 기능입니다. 바로 함수에서 여러 return 값을 반환할 수 있다는 거죠.

아래 예제를 보세요.


local function testConnection()
    if not noConnection then
        return false, "Connection Failed!"
    else
        return true
    end
end

local result, reason = testConnection()
print( result, reason )


connection이 fail 되면 false를 return 하고 동시에 string 도 return 할 수 있습니다. connection 이 제대로 작동하면 true 만 return 합니다. (reason 의 값은 nil 이 될 겁니다.)


3. Can you assign multiple values to multiple variables?


또한 Lua 에만 있는 편리한 기능이 한번에 여러 변수에 여러 값들을 대입하는 겁니다.


local red, green, blue = 100, 255, 128


이것은 아래 코드와 같습니다.


red = 100
green = 255
blue = 128


둘 중 어느 방법을 사용할 지는 여러분의 선택에 달렸습니다. 둘 다 맞는 거니까요.


4. Are parentheses needed in “if” statements?


몇 몇 language 들에서는 conditional statement 에 parentheses({}) 를 사용해야 합니다.


-- C code
if ( a > b) {
     ...
}


Lua 에서는 optional 입니다. 단지 then 과 end 를 사용해야합니다.


-- Lua code
if a > b then
    ...
end 


5. How do I add Block Comments to my code?


Block 주석은 코드에 어떤 설명을 넣거나 개발을 할 때 유용하게 사용할 수 있습니다.

아래 예제가 있습니다.


--[[
local function testConnection()
 if not noConnection then
 return false, "Connection Failed!"
 else
 return true
 end
end
--]]


이 주석은 “––[[“로 시작해서 ”––]]”로 끝났습니다. 루아에서는 이 주석을 해제하는 간단한 방법을 제공합니다. 주석 시작부분에 - 를 한번 더 넣으면 주석이 해제 되는 겁니다.


---[[
local function testConnection()
 if not noConnection then
 return false, "Connection Failed!"
 else
 return true
 end
end
--]]


위의 코드는 실행 될 겁니다. 왜냐하면 “–––[[“ (three dashes instead of two)를 사용했기 때문이죠.


여기까지가 오늘의 FAQ 였습니다.


감사합니다.


반응형

Comment

FAQ Wednesday: Display Objects

2012. 10. 25. 13:55 | Posted by 솔웅


Posted on . Written by


또 다시 FAQ 시간이 돌아왔습니다. 오늘은 display objects 와 관련해서 자주 질문 되는 내용을 다루겠습니다.


1. Why is my iOS App file larger with Daily Builds compared to building with the release build (894)?


build 900 부터  iOS “developer” builds 시 PNGCRUSH 가 더이상 발생하지 않습니다. build 시간을 절약하기 위해서 만들었는데 그렇다고 해서 PNG 이미지가 압축되지 않는건 아닙니다. AdHoc or Distribution build 시에는 아직 PNGCRUSH 부분이 변경되지 않았습니다.


2. How can I load images that are larger than the device’s screen size, without it scaling down the image?


display.newImag는 디바이스의 해상도에 맞춰서 이미지 크기를 표시할 겁니다. automatic scaling을 사용하지 않으시려면 “isFullResolution” flag을 true로 하시면 됩니다.


-- display.newImage( [parentGroup,] filename [,baseDirectory] [,left,top] [,isFullResolution])
local image = display.newImage( "myImage.png", 0, 0, true )

display.newImageRect로는 로드될 떄 자동으로 이미지를 scale down 하지는 않습니다. 이 API는 반드시 이미지의 width 와 height 을 지정해 줘야합니다. 이렇게 높이와 너비를 지정하는것도 디바이스의 스크린 크기보다 더 큰 이미지를 로드하는 방법중 하나입니다.


디바이스는 로드할 수 있는 maximum 해상도가 있습니다. 대개 2048 x 2048 입니다. 이점 유의하세요.





3. If I put code like display.newRect(0, 0, 80, 40), at coordinates 0,0, my rectangle will appear cropped at top-left of the mobile screen.


newRect 가 생성됐을 때 TopLeft reference로 생성되기 때문에 로드될 때 object 가 잘리지 않을 겁니다. 그러니까 0,0 이면 화면의 top left 에 사각형의 top left 코너가 맞게 나올 겁니다. 아마 status bar 를 보이도록 하셨으면 잘려보일 수도 있습니다. 이럴 땐 아래처럼 status bar를 없애시면 됩니다.


display.setStatusBar(display.HiddenStatusBar)


대부분의 display objects 들은 TopLeft reference point를 사용합니다. object 가 생성된 이후에 그 reference point를 Center reference 로 바꿉니다. 아래는 화면의 중앙에 사각형을 놓는 방법 중 하나입니다.


local rect = display.newRect( 0, 0, 80, 40 )
rect.x = display.contentCenterX
rect.y = display.contentCenterY


아래처럼 하면 정확하게 사각형이 중앙에 위치하지 않고 사각형의 TopLeft 가 화면 중앙에 위치할 겁니다.


local rect = display.newRect( display.contentCenterX, display.contentCenterY, 80, 40 )

display object를 생성할 때 TopLeft reference 를 사용하면 다음과 같은 object들을 제외하고는 모두 적용됩니다. display.newCircle and The display.newGroup. display.newCircle는 center reference로 생성됩니다. display.newGroup은 처음 생성되면 empty 인 상태이고 어떤 object가 그룹 내에 속하게 되면 TopLeft reference 를 사용하게 됩니다.


4. setReferencePoint is not working when I use display.topLeftCenterReferencePoint. What’s wrong?


setReferencePoint에 대한 파라미터의 spell 이 정확한지 보세요. 대소문자도 맞아야 합니다. 그렇지 않으면 제대로 작동되지 않을 겁니다. Top Left Center reference point로 세팅하기 위한 정확한 값은 display.TopLeftCenterReferecePoint (with a capital “T”) 입니다. 불행히도 spell 이 틀렸더라도 API는 warning 이나 에러메세지를 보여주지 않습니다. 이와 관련해서는 저희들 수정할 목록에 이 부분이 포함되 있다는 것을 알려 드립니다.


local image = display.newImage( "myImage.png", 0, 0 )
image:setReferencePoint( display.TopCenterReferencePoint )
image.x = 20
image.y = 100



That’s it for today’s questions. I hope you enjoyed it and even learned a few things.

여기까지가 오늘의 질문과 답변입니다. 감사합니다.




반응형

Comment

Sprite Sheet 활용하기 (Advanced)

2012. 10. 11. 13:54 | Posted by 솔웅


Posted on . Written by


오늘의 게스트 Tutorial은 Omid Abourai 입니다. 인디 게임 개발자 이며 ArdenKid 라는 별명을 가지고 있습니다. 그는 2년여간 코로나 SDK 를 가지고 개발을 했으며 곧 그가 주도한 첫 게임인 "Balloon Bazooka"를 릴리즈 할 예정입니다. 그의 블로그는 www.ardentkid.com 입니다.  



Basic Sprite Sheets


만약 지난주의 를 animation tutorial 놓치셨다면 그것 부터 보세요. 이 튜토리얼은 지난주 튜토리얼의 basic sprite methods 의 연장선에 있는 겁니다.


Sprite sheet는 CPU 의 부담을 덜어준다는 면에서 아주 훌륭한 기술입니다. 그런데 그냥 vector animation 과 비교해서 단점이 있는 것도 사실입니다. 예를 들어 메모리를 많이 잡아먹는다든지 앱 크기가 커진다든지 하는 것들이요. (애플의 경우 20메가가 넘으면 wifi로 다운로드 받으라고 하는데 저희는 한 앱이 20메가를 넘지 않도록 작업을 하고 있습니다.)

그리고 예를 들어 한 캐릭터가 옷을 갈아입고 악세사리를 바꾸고 할 경우는 어떨까요? 우리의 캐릭터를 각 옷별로 또 악세사리별로 중복해서 만든다면 sprite sheet 를 쓰는 잇점이 줄어들겠죠. 이럴 경우 처음부터 부분 부분 별로 만들어서 활용한다면 적은 이미지 용량으로 훨씬 많은 효과를 낼 수 있을 겁니다.



이 튜토리얼은 그 이슈와 관련해서 가능한 해결법을 다룰 겁니다. 비밀은 코딩과 애니메이션 간에 좀 더 긴밀한 관계를 만드는 겁니다.

조만간 발매될 Balloon Bazooka 앱에서 사용했던 kid 관련 스프라이트 쉬트를 샘플로 시작해 보겠습니다.






이 sheet 는 코로나의 basic sprite animation에 맞게 디자인 됐습니다. 이미지를 로드한 다음 sequence들을 셋업하고 원하는 sequence들을 play 하는 방식이죠. 좀 더 진보된 테크닉을 원하신다면 아래 내용을 말씀해 드리고 싶습니다. 그런데 일단 우선은 이 basic methods가 익숙해지셔야 합니다.


Splitting the Animation Elements


우리는 화면에서 동시에 같은 sprite 표현을 사용하는 여러 kids 를 만들겁니다. 우리는 그들에게 다양한 옷과 살 색 등등을 적용할 겁니다. 다행히도 프로그램적으로 비쥬얼 property들을 tweening 함으로서 비슷한 애니메이션들을 만들 수 있습니다. 프로그램으로 scale, rotation, translation 등을 다야하게 적용할 수 있습니다. (우선 편의상 이번에는 팔은 가만히 있고 발만 움직이는 걸로 한번 만들어 보겠습니다.)


먼저 이 kid를 토막을 내겠습니다. color 별로 따로 만들고 나중에 이것들을 합칠께요. 이 작업을 하기 위한 새로운 image sheet 는 아래와 같을 겁니다.





이 new sheet 의 file size 가 225KB 에서 109KB로 줄어들었습니다. 그러면서도 다양하게 의상과 피부색을 조합할 수 있게 됐죠. 50%가 넘게 파일 사이즈를 줄였고 우리고 표현하고 싶은 다양한 kid 들을 표현할 수 있게 됐습니다.


이제 이 각각의 element들을 코드안에 individual sprite 로 정의 하겠습니다.


-- DECLARE CHARACTER SEQUENCES (MOST ARE JUST STATIC CHERRY-PICKED FRAMES!)
local sequenceData = {
  { name="beachboy_hat_yellow", frames={1} },
  { name="beachboy_body_dark", frames={34} },
  { name="beachboy_shorts_red", frames={42} },
  { name="beachboy_arm_dark", frames={ 5,12,15,19,23,26 }, loopDirection="bounce" },
  { name="beachboy_foot_dark", frames={47} }
}

-- CREATE A DISPLAY GROUP FOR THE CHARACTER
local beachboy = display.newGroup()

-- CREATE BODY PARTS AS SPRITES
local hat = display.newSprite( sheet, sequenceData )
      hat:setSequence( "beachboy_hat_yellow" )
local body = display.newSprite( sheet, sequenceData )
      body:setSequence( "beachboy_body_dark" )
local shorts = display.newSprite( sheet, sequenceData )
      shorts:setSequence( "beachboy_shorts_red" )
local rightArm = display.newSprite( sheet, sequenceData )
      rightArm:setSequence( "beachboy_arm_dark" )
local leftArm = display.newSprite( sheet, sequenceData )
      leftArm:setSequence( "beachboy_arm_dark" )
local rightFoot = display.newSprite( sheet, sequenceData )
      rightFoot:setSequence( "beachboy_foot_dark" )
local leftFoot = display.newSprite( sheet, sequenceData )
      leftFoot:setSequence( "beachboy_foot_dark" )

-- POSITION PARTS & ORIENT L/R SIDES WITH SCALING
shorts.x, shorts.y = 0, 15
leftArm.x, leftArm.y = -20, -18
leftArm.xScale = -1 --flip 'leftArm' horizontally
-- etc...

-- INSERT ELEMENTS INTO THE DISPLAY GROUP, ORDERED BOTTOM TO TOP
beachboy:insert(leftFoot)
beachboy:insert(rightFoot)
beachboy:insert(shorts)
-- etc...

-- STORE REFERENCES TO EACH ELEMENT
beachboy["feet"] = {leftFoot, rightFoot}
beachboy["shorts"] = shorts
beachboy["body"] = body
-- etc...



Animating the Elements


이 모든 element들이 제자리를 잡고 여러분의 캐릭터가 show up 하면 이제 그 캐릭터의 발 움직임을 줘서 애니메이션을 만들 수가 있습니다.


--WALK SEQUENCE
function beachboy:walk()

  local feet = self.feet

  --CLEAR EXISTING TRANSITIONS IF RUNNING
  if not ( self.currentTransitions ) then self.currentTransitions = {} end --add container table if not present
  local currTrans = self.currentTransitions
  local tot = #currTrans
  for i = tot,1,-1 do --loop backwards through transitions and clear each one
    if (currTrans[i]) then transition.cancel(currTrans[i]) ; currTrans[i] = nil end
  end

  local t = 500 --WALK CYCLE TIMING
  local dist = 160 --FOOT MOVEMENT DISTANCE
  local ease = easing.inOutQuad --FOR A NATURAL SWINGING MOTION

  --RECURSIVE ANIMATION FUNCTION
  local function anim()
    for i = 1,#feet do
      currTrans[i] = transition.to(feet[i], {y=dist, time=t, transition=ease, onComplete=function()
        currTrans[i] = transition.to(feet[i], {y=0, time=t, transition=ease, onComplete=function()
          anim()
        end})
      end})
    end
  end

  --START THE ANIMATION
  anim()

end


 

이제 우리만의 Runtime animation code 를 call 하고 있죠? 우리는 이 튜토리얼에서 계속 반복되는 transition들을 사용하고 있습니다. 여기서 팔도 발하고 같이 어울리게 애니메이션을 주고 싶다면 여기서 조금 더 작업을 해야 합니다. (이 부분은 따로 다루지는 않겠습니다.)

이제 같은 메모리로 여러 color들을 표현할 수 있게 됐습니다.


“Tinting” the Elements



여기서 각 파트별로 programatic tinting을 사용해서 color 까지도 코딩으로 넣을 수가 있습니다. 이것은 코로나의 setFillColor() method를 사용하면 됩니다. 우선 모든 부분을 흰색으로 만듭니다. 아래 샘플 이미지가 있습니다.







아래에 코딩으로 어떻게 sprite object들에 tint를 하는지에 대한 예제가 있습니다.

-- ACCESS EACH ELEMENT FROM THE CONTAINER TABLE BY INDEX NAME
beachboy.hat:setFillColor(252, 206, 0)
beachboy.body:setFillColor(126, 79, 33)
beachboy.shorts:setFillColor(220, 0, 0)
beachboy.feet[1]:setFillColor(126, 79, 33) --access 'leftFoot' by position within 'feet' table
beachboy.feet[2]:setFillColor(126, 79, 33) --access 'rightFoot' by position within 'feet' table
-- etc...

  

이제 원하는 색을 넣을 수도 있고 랜덤하게 색을 넣을 수도 있습니다. 여러분 마음대로 하시면 됩니다. 단지 다른 animation sequence 될 때마다 색을 넣어야 하는 작업을 해야 되겠죠. 그리고 캐릭터가 움직이면서 좀 더 CPU 를 잡아 먹을 겁니다. 그리고 한 객체에 한가지 색만 넣을 수 있겠죠. 줄무늬 옷이나 뭐 그런거는 좀 힘듭니다. 흰색과 회색을 사용해서 조금 sprite 한 느낌은 줄 수 있겠죠.


In Summary



해 보니까 이 방법은 요즘 대부분의 모바일 디바이스에서 사용하는 예외적인 방법이더라구요. 이 방법을 사용하면 texture memory를 훨씬 줄일 수 있습니다. 우리는 이 beachboy 캐릭터의 용량을 225 KB에서 109 KB로 줄였었습니다. 그리고 마지막 방법까지 하면 12KB 로 95%나 줄일 수 있었습니다.


그리고 또 다른 효과는 각 파트별로 다른 애니메이션 효과를 줄 수 있게 된 겁니다. 팔 하나만 움직이게 할 수도 있고 두 팔을 다 움직이게 할 수도 있구요 또 더 나아가서는 펀치나 킥하는 장면도 넣을 수 있겠죠.


그리고 각 character element 별로 tint/color 를 할 수가 있어서요 아주 다양한 캐릭터를 만들어 낼 수 있게 됐습니다.

이렇게 dynamically-optimized sprite sheet 를 사용하면 아주 다양하고 훌륭한 효과를 낼 수가 있습니다. 그리고 여러분의 코딩에 따라서 한정된 이미지로 개성있는 캐릭터를 계속 만들어 낼 수 있구요.


반응형

Comment

FAQ Wednesday: iOS 6 버그들

2012. 10. 9. 05:08 | Posted by 솔웅


Posted on . Written by


It’s Wednesday and time for another FAQ session. Here are some frequently asked questions (FAQ) about current iOS 6 bugs.

수요일의 FAQ 시간인데요. 오늘은 요즘 많이 질문되는 iOS 6 bug들과 관련한 내용을 다루겠습니다.


1. Why does GameCenter crash my app on iOS 6?


이전 FAQ에서 말씀 드렸듯이 이미 알려진 iOS 6 Apple bug 가 있습니다. landscape 모드인 앱에서 일어나는데요. GameCenter 의 로그인 화면이나 photo picker 가 나올 때 충돌하는 현상이 있습니다.

아이폰 디바이스의 이 두가지 네이티브 object들은 portrait mode 만 있거든요. landscape 앱에서 이 두 네이티브 object 들이 display 될 떄 crash 가 일어납니다. iPad 에서는 문제되지 않아요. 이미 애플 버그로 등록된 내용입니다.


2. I see a number of GameCenter work-arounds on StackOverflow.com. Why can’t you include those fixes in Corona?


인터넷이나 애플 개발자 포럼등에 있는 work-around를 저희들도 보고 있습니다. 그리고 그 해결책들을 찾기 위해 노력하구 있구요. GameCenter와 관련한 이슈에 대한 해결책과 이것을 어떻게 portrait 모드인 이 화면을 landscape인 앱에서 작동되게 하느냐 같은 문제들이 있죠.  그리고 GameCenter 에 대해 Portrait mode로 display 하도록 하면 그와 더불어서 UIViewController objects 들 즉 TextField, TextBox, webViews같은 Native objects 들도 GameCenter object 와 같이 Portrait, Landscape 모드에 맞춰서 돌아가는 문제도 있습니다.

그러면 Landscape 에 맞춰서 앱을 만들었는데 그 layout 이 이상하게 돌아가게 되겠죠.



지난 며칠간 저희들은 GameCenter 객체가 portrait 모드로 돌아가도록 하면서 다른 native object들은 돌아가지 않도록 하는 방법들을 연구했습니다. 애플에서 제공하는 API로는 이를 해결할 방법을 찾지 못했습니다. 애플 private API 로는 그것을 해결하는 방법을 찾았습니다만 애플은 이 private API를 사용하는 것을 금지하고 있습니다. 아마 그 방법을 사용하면 앱스토어에서 심사할 때 reject 될 겁니다. 그러니까 그 방법은 해결책이 될 수 없게 됐습니다.





3. How can I configure my app to work around the GameCenter iOS 6 bug?


만약 여러분의 앱이 Landscape mode에서만 운영이 된다면 그리고 GameCenter 나 Photo Picker 를 사용해야 된다면 build.setting를 portrait mode가 가능하도록 수정해 주시면 됩니다. 그러면 GameCenter와 Photo Picker 시 충돌이 일어나지 않습니다. 단지 Corona content 는 landscape mode 만 되도록 해야 앱을 사용할 수 있겠죠. 이 방법의 단점은 1) 여러분 앱이 landscapeRight 만 가능하게됩니다. 2) textFields, textBoxes, webView 같은 native object 들이 portrait 일 경우 돌아가게 됩니다.  GameCenter 는 rotate 되도록 하면서 다른 native object 들은 rotate 되지 않도록 하는 방법은 없습니다.


settings = {
orientation =
{
default = "landscapeRight",
content = "landscapeRight",
  supported = { "landscapeRight", "portrait" },
},
}



content = “landscapeRight” line을 잘 보세요. landscapeLeft 는 허용이 되지 않습니다. 코로나는 이 부분에 오직 한개의 orientation 만 세팅하기 때문입니다. 그리고 이렇게 하면 Corona content  는 landscapeRight 로 모두 고정이 되지만 supported 에서 landscapeRight와 portrait을 허용했기 때문에 native objects 들은 portrait 모드에서 돌아가게 됩니다. 



build 926 에서는 GameCenter event.type을 추가했습니다. 아마 이것을 활용해서 rotation 이슈를 콘트롤 하실 수 있을 겁니다. GameCenter의 initCallback 에 대해 event.type이 showSignIn 으로 잡히게 됩니다. 이 시점은 GameCenter의 로그인 화면이 막 나타나기 직전 입니다. 이 때 native object 들을 업애거나 hide 혹은 delay showing을 시키실 수 있습니다. 유저가 사인하고 난 이후에는 이 event.type이 없어집니다. 이렇게 되면  initCallback 은 evnet.type == "init" 이 됩니다. 유저가 이미 GameCenter에 로그인 된 상태라면 showSignIn 이벤트가 일어나지 않습니다. 곧바로 init event로 갈겁니다. 


그리고 orientation listener를 추가하는 것이 필요할 겁니다. 유저가 디바이스를 rotate 시켰는데도 native 객체들이 그대로 있을 수 있으니까요. 디바이스가 로테이트 되면 이것을 catch 해서 해당 native objects 들을 hide 시키는 방법도 있겠죠.


local function initCallback( event )
    -- "showSignIn" is only available on iOS 6+
    if event.type == "showSignIn" then
        print( "showSignIn event found" )
-- This is an opportunity to pause your game or do other things you might need to do while the Game Center Sign-In controller is up.
-- For the iOS 6.0 landscape orientation bug, this is an opportunity to remove native objects so they won't rotate.
-- This is type "init" for all versions of Game Center.

     elseif event.data then
print( "init event found" )
loggedIntoGC = true
gameNetwork.request( "loadScores", { leaderboard={ category=leaderBoards[currentBoard], playerScope="Global", timeScope="AllTime", range={1,3} }, listener=requestCallback } )
    else
-- User canceled sign-in
end

end

 

이 방법들이 이상적인 혹은 근본적인 해결책은 아닙니다. 가장 근본적인 방법은 애플이 이 버그를 해결하는 것이죠. 저희들이 버그 리포트를 했지만 여러분들도 애플에 버그 리포트를 해 주시면 좀 더 빨리 해결 될 수 있을 겁니다.



4. My app doesn’t use GameCenter but does use PhotoPicker and it crashes too. What can be done about that?


위에서와 같이 build.settings을 수정하면 GameCenter와 PhotoPicker 모두에서 동작합니다. (몇가지 제한사항도 마찬가지구요) 물론 sign-in listener event.type은 GameCenter에만 해당이 되는겁니다. 이 문제를 근본적으로 해결하려면 애플에 버그리포트를 하고 그런 버그 리포트가 많이 쌓이게되면 애플에서 우선순위를 두어서 수정을 할 겁니다. 애플에 버그 리포트를 해 주세요.



5. Why does my iOS 6 app crash when I use network.request?


애플은 iOS6에서 network delegate에 대한 약간의 변화를 주었습니다. 그런데 아주 짧은 시간에 많은 network.request가 일어날 경우 iOS 6 앱이 충돌하는 현상이 발생하고 있습니다. 저희들은 그 해결 방법을 찾았구요 다음 Daily Build 에 반영할 예정입니다. (build 926 이후부터) 맥 시뮬레이터에서도 비슷한 이슈가 있었는데요. 이것도 build 926 부터 수정이 될 겁니다.


6. Why can’t I see my print message in the Xcode Console?


몇주 전에 이와 관련해서 알려드렸었는데요. iOS 6 애플에서 애플리케이션에 있는 메세지를 capture 하는 방법이 그 이전과 다르게 진행됩니다. 그래서 print 메세지가 Xcode Console 로 가는 부분이 제대로 작동이 되지 않고 있습니다. (Xcode 에 연결된 디바이스) 이와 관련된 좋은 소식은요 해결방법을 찾았다는 거구요 Daily Build 925 버전부터는 제대로 작동하게 됐습니다.



여기까지가 오늘의 질문과 답변들입니다.



반응형

Comment



Posted on . Written by


다시 수요일의 FAQ 시간이 왔습니다. 이번주는 iOS builds, API documentation 과 안드로이드에 대해 자주 질문 되는 것들을 추려봤습니다.



1. I’m using build 922 and I’m getting a code signing error after an iOS build. What’s wrong?


build 922 부터 코로나는 iOS 6 SDK 에 대해 빌드 할 수 있도록 했습니다. 여기에는 Xcode 4.5 가 필요합니다. Xcode 가 4.5 이전 버전일 경우 이 에러메세지를 보시게 됩니다.



2. I see new APIs added to Corona but the API docs are not being updated.


web 상의 API documentation은 현재 릴리즈 된 버전 (2012.894) 에 대한 documentation 입니다. 이 API에 대한 documentation은 Daily Builds page의 zip 파일로 압축돼 있는 Daily Build 안에 추가되거나 수정 된 내용이 들어갑니다.



3. I’m trying to use File I/O on Android to read files and it’s not working. It does work in the Simulator and on iOS. Is this a bug?


Android APK 는 zip 파일 입니다. 리소스 디렉토리에 있는 파일들은 APK 파일 안에 들어가게 됩니다. 즉 zip 파일로 압축된다는 얘기죠. io.open() 같은 Lua I/O API는 zip 파일 안에 있는 파일들을 압축을 풀어서 오픈할 수가 없습니다. 그러니까 APK 안에 있는 파일을 오픈하지 못하는 겁니다.





그래서 저희들은 안드로이드와 관련해서 특별하게 동작할 수 있는 기능을 구현했습니다. system.pathForFile() 이라는 건데요. 특정 파일 타입에서 이 메소드는 자동으로 그 파일의 압축을 풀어서 outside 디렉토리에 copy를 합니다. 일종의 해킹이랄 수 있는데요. 좋은 방법은 아닙니다. 왜냐하면 storage 공간을 낭비하는 일이거든요. 어쨌든 이 방법으로 Lua I/O API들이 특정 파일 타입들에 access 할 수 있습니다.


system.pathForFile()은 다음과 같은 파일 형식을 제외한 파일 타입들을 자동 압축 해제 합니다. : 
 *.html
 *.htm
 *.3gp
 *.m4v
 *.mp4
 *.png
 *.jpg
 *.ttf


무슨 의미냐 하면요 위의 파일들은 Lua I/O API로 access 할 수 없다는 의미입니다. 다른 모든 파일들은 system.pathForFile()을 call 하면 자동적으로 압축을 풉니다. 이런 이유로 여러분은 여러분의 HTML file에 access 할 수 없는거죠. HTML 파일이 auto-extracted 되지 않는 이유는 HTML 파일에는 이미지들도 링크가 돼 있기 때문에 Corona 가 이 웹 페이지를 보이도록 하려면 이미지들 까지 auto-extract  해야 되기 때문입니다. 만약 그렇게 되면 이미지들은 아주 많은 저장공간을 차지하게 될 겁니다. 그래서 이 기능을 넣지 않는 겁니다.


위의 파일들에 access 하는 한 방법은 (e.g. png, jpg) file.png.txt 같이 rename 하는 겁니다. 그러면 특정 파일을 Documents나 Temporary 디렉토리에 copy 할 수 있을 겁니다. 그리고 나서 다시 원래대로 이름을 되돌리면 되겠죠. 용량이 큰 파일이라면 메모리 공간을 아주 많이 소비할 거라는 것을 명심해 두세요.



4. I’m displaying an image and trying to update it with a new image and it’s not updating. What’s wrong?


코로나는 파일 이름을 근거로 해서 이미지 데이터를 캐싱합니다. 똑 같은 이름으로 이미지를 로드하려고 한다면 그리고 이전 이미지가 현재 active 상황이라면 코로나는 이것을 같은 이미지로 생각할 겁니다. 그러면 이전 이미지를 사용하겠죠. 이런 상황은 서버에서 이미지를 가져오는데 실제 이미지는 바뀌었는데 이름은 바뀌지 않은 경우에 발생합니다. 해결 방법은 로딩하기 전에 새 이미지의 이름을 바꾸거나 새로운 이미지를 로딩하기 이전에 이전의 이미지를 지우는 겁니다. 이전 이미지 객체에 removeSelf를 call 하세요. 그러면 이미지 데이터와 파일 이름 사이의 관계까지도 remove 될 겁니다.



5. How to get a cleaner ADB Logcat debug display


이건 질문은 아니고 그냥 제가 코로나 포럼에서 찾아내서 알려드리는 팁입니다. (그 글을 올린 분 성함이 없어서 밝히지를 못합니다. 죄송합니다.) 그 팁은 Android OS 가 보내는 다른 busy 메세지들은 보이지 않고 그냥 print 메세지만 보이게 하는 방법입니다. Android SDK를 인스톨 하신분들은 코드를 디버깅할 때 ADB 프로그램을 사용 하실 겁니다.


안드로이드 SDK 에서 아래와 같이 adb를 실행하세요.

adb logcat Corona:v *:s

만약 제대로 작동하지 않으면 파라미터들을 없애고 다시 시도해 보세요.



여기까지고 오늘의 FAQ 들이었습니다. 감사합니다.

반응형

Comment

이전 1 2 3 4 5 ··· 8 다음