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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
Posted on . Written by


Improved Android audio


우리 엔지니어들한테 물어보면 디바이스의 오디오 관련해서 아무런 문제 없이 다루는 것이 어렵다고 말합니다. 애플조차도 그렇게 하지는 못합니다. 다만 다른 것들 보다 문제가 덜 생기도록 만들죠.

daily build 942에서부터 우리의 오디오 backend로 Android 2.3+ 디바이스에 있는 OpenSL-ES 를 사용하기 시작했습니다. 이 back-end 는 뭐하는 걸까요?







처음 듣는 분들을 위해 말씀 드리자면 오디오가 작동되지 않는 디바이스(e.g. the Vizio, one of the Samsung SII models, etc)의 고질적인 문제 점들을 수정했다는 얘기입니다. 또한 아주 미묘한 performance 개선 사항들이 있습니다. 예를 들어 동시에 여러 sound를 play 할 경우 hisses and pops 같은 것들이 찌그러지는 경우가 있는데 이런 부분들이 살짝 개선됐습니다.


만약 안드로이드의 ‘EggBreaker’ sample 을 play 하면 객체가 튕기는 시점에 튕기는 소리가 나지 않는 것을 느끼실 수 있을 겁니다. 이것은 longstanding latency 문제 때문인데요. 안드로이드 초창기부터 있어 왔던 겁니다. 즉 안드로이드 OS와 관련한 이슈중 하나죠. 최근 이 부분이 아주 살짝 개선되기는 했습니다. 이번 개선사항 중 가장 중요한 것은 안드로이드의 Core OS change 로 부터 온 것 입니다. Android 4.1 인 JellyBean 부터 적용됐습니다.



New Widgets!


저희들은 widget framework 을 좀 더 개선하고 cleaning up 하기 위해 많은 노력을 기울였습니다. initial framework을 생성했을 때 오리지널 코드를 상하기 쉽도록 하는 많은 것들(brittle, fragile, and just unmaintainable)을 접합니다.

widget framework 에 대해 개선하고 싶은 부분들이 아주 많이 있지만 간단하지 않은 부분들 또한 많이 있습니다. 저희들이 하기로 한 부분은 각 widget들의 어떻게 해야 제대로 작동하는 것인지에 대해 rethink 해 보기로 했습니다.





기존 widget에 rewrite 하는 부분은 위험도가 있습니다. 저희들은 새로운 widget을 먼저 만들고 이 부분이 확실하게 동작하면 이 새로운 widget들을 이전의 widget들과 바꿀 겁니다.


Daily build 942 에는 새로운 몇개의 widget들이 선보입니다.


  • On/Off Switch. 3가지 형태로 나옵니다.: an actual on/off switch, a checkbox, and a single radio button.
  • Spinner. The canonical use-case is lots of photos being download asynchronously. Note if you make a blocking API call (e.g. a luasocket call), this will not animate. For that case, you should use native.setActivityIndicator().
  • Stepper. You tap ‘+’ or ‘-’ and the values go up and down within a predefined range.
  • Progress view. A bar that goes from 0% to 100% filled.


좀 더 많은 정보들은 API docs 의 daily build 942 snapshot 부분에서 보실 수 있게 될 겁니다. 또한 이번 빌드에서 업데이트된 WidgetDemo sample app 을 제공해 드릴 겁니다. 


다음에 저희들이 작업할 widget들은 segmented controls 와 search fields 입니다.


A Teaser


Finally, I’m going to talk about something that I’m not really going to talk about — yet. What I mean is, this topic deserves a post of its own, so we’re saving that for next week. What am I talking about?

마지막으로 지금 얘기할 것은 이번 글에서 다루지는 않을 겁니다. 무슨 얘기냐 하면 이 이슈는 따로 글로 작성해서 보여드릴 거라는 겁니다. 다음주에 이와 관련해서 말씀 드리겠습니다. 


단지 daily build 942 에서 이와 관련된 기능을 살짝 맛 보실 수 있습니다.


Android: Added local/scheduled notification support.


이와 관련해서 아주 많은 부분들의 작업이 있었습니다. 코로나 같은 cross-platform development framework을 사용하는 잇점을 제대로 느끼실 수 있을 겁니다.



notification은 platform 마다 완전히 다릅니다. 코로나는 이 완전히 다른 사용법을 한가지 방법만 취하면 구현 가능하도록 간단하게 여러분께 길을 제공해 드릴 겁니다.


저희들은 여러분들이 해야할 일들을 줄이기 위해서 여러 복잡하고 어려운 부분을 줄이는 여러 큰 일들을 꾸준히 진행하고 있습니다.


감사합니다.






반응형


반응형
Posted on

. Written by


할로윈 시즌 입니다. 오늘은 코로나의 blend modes  에 대해서 다루겠습니다. 그리고 이것이 어떻게 앱 내에서 창조적인 비쥬얼 효과를 내는데 사용될 수 있는지에 대해서도 알아보구요. 대부분의 Corona API들이 그렇지만 이런 효과도 간단한 코딩에 의해서 구현할 수 있습니다. Blend mode는 다양한 시각 효과에서 아주 유용하게 사용할 수 있습니다. 그리고 포토샵 같은 데서나 낼 수 있었던 그런 효과들을 내고 싶은 디자이너들도 활용하실 수 있습니다. blend mode가 무엇인지 모르시는 분들은 이 글을 끝까지 읽으세요. 이 글에서는 두개의 Halloween-theme image들을 사용할 겁니다. spooky swamp 와 wisps요. 이 글을 그대로 따라하면서 배우고 싶으신 분들은 여기에서 이미지를 다운 받아서 사용하세요. (제가 만든거니까요 다른데에 배포하지는 말아주세요.)




Layers and Pixels


다른 그래픽 어플리케이션과 개발 언어와 마찬가지로 코로나에도 layers 가 있어서 back에서 front로 z-index 순으로 비쥬얼 객체들을 arrange 합니다. 레이어들은 코로나에서는 display groups를 말하고 용법은 같습니다.





보시는 바대로 위 레이어에 있는 객체(wisps) 의 모든 픽셀들은 그 아래 있는 레이어의 객체 앞에 overlap 돼 있습니다. 그리고 그 픽셀들이 렌더링되서 뒤의것과 잘 조화 돼 있죠.  이것은 Painter's Algorithm 이라는 개념입니다. 실제 캔버스에 그림을 그린다고 보면 붓질을 하면 물감은 이전 물감의 위에 덧칠되게 됩니다. 이것이 Painter's Algorithm 입니다.


About Visual “Blending”


위의 예와 같은 기초적인 layering 도 유용하고 필요한 기술이긴 하지만 creative visual effect를 구현하기에는 한계가 있습니다. 그래서 대부분의 그래픽 애플리케이션은 (코로나도 마찬가지구요.) 레이어들에 blending 이라는 사용할 수 있도록 허용하고 있습니다.


core level에서 보면 blending 은 수학적인 알고리즘을 이용해서 여러 레이어들에 걸쳐 픽셀들이 overlapping 돼 서로 combine (blend) 하도록 하는 메소드 입니다. 단지 위에 오는 레이어의 픽셀들이 밑의 픽셀들에 overlapping 되서 밑의 것을 안 보이도록 하는  것이 아니라 blend를 사용하면 multiple pixels 들이 한꺼번에 보이게 됩니다.



현실세계를 상상해 보시죠. Painter’s Algorithm analogy를 사용하면 붓의 터치가 가해지면 밑의 색에 덧칠 됩니다. 또 그 색에 붓질을 하게 되면 마찬가지로 덧칠을 하게 되죠. blend 도 마찬가지로 digital painting canvas 에서 똑 같은 일을 하는 겁니다. 다만 실제 세계의 캔버스에서와는 다르게 표시되게 되죠.


디지털 환경에서 생각해 보면 blending은 3원색-red, green, and blue (RGB)- 모두에 적용 됩니다. 이 각각의 색을 channel 이라고 하는데요. 더 자세히는 다루지 못하지만 각 channel 마다 0 에서 255 까지의 숫자 범위가 있다는 것은 알아 두시기 바랍니다. 0 은 none 을 의미하고 255는 maximum을 의미합니다. 얼마나 이런 channel 들이 mix 되느냐에 따라서 최종 색이 결정 되는 겁니다. RGB 값 0,0,0 은 검은색이고 255,255,255 는 흰색입니다. 이 RGB에 대해 더 자세히 알고 싶으시면 여기를 참조하세요.


이 튜토리얼을 보시려면 각 color channel 마다 0-255 의 숫자 범위가 있다는 것만 아시면 됩니다. 이 글에서는 blend 가 어떻게 각 channel의 숫자들을 다루고 그 픽셀의 최종색을 만드는지를 설명 드릴 겁니다.



object.blendMode


코로나에서 객체에 blend mode를 apply 하려면 blendMode parameter에 add, multiply, screen, or normal 중 하나의 값을 대입시키면 됩니다.


예를 들어



wisps.blendMode = "add"



“normal” blend mode (default) 는 그냥 blend mode를 사용하지 않는다는 의미입니다. 그 외에 3개의 blend mode에 대해서는 아래에서 설명 하겠습니다.

한가지 기억하셔야 할 것은 반드시 blend mode는 per-object basis로 apply 해야 한다는 것입니다. object:setFillColor() 와 비슷합니다. blend mode를 전체 display group에 한번에 적용할 수는 없습니다. 하나의 display group 에 다 같은 blend mode를 적용하시려면 display group 의 child 만큼 loop를 돌려서 각각의 아이템마다 blend mode를 별도로 적용시켜야 합니다.


Blend Mode “add”


add blend mode 는 불이나 레이저, exaggerated 같은 glowing visual effects를 주는데 효과적입니다. 대부분의 그래픽 애플리케이션에서는 linear dodge로 알려져 있기도 합니다. 하는 일은 단순하게 모든 레이어에 걸쳐서 각 color channel 의 값을 adds 하는 겁니다.





이제 테크니컬하게 들어가 볼까요! 객체에 부여된 RGB 값들은 아래와 같습니다.


background:      ( 34, 34, 44 )
red circle:      ( 255, 51, 51 )
green circle:    ( 0, 153, 51 )
blue rectangle:  ( 102, 153, 204 )



With each object set to blendMode=“add”, Corona’s OpenGL engine simply adds the channel values for each pixel.

각 객체마다 blendMode=“add”로 세팅 돼 있습니다. 코로나의 OpenGL 엔진은 단순히 각 픽셀의 channel 값을 더할 겁니다.


산수를 한번 해 보죠. 빨간 원과 배경이 합쳐지는 부분에 대해 계산을 합니다.



R: 34 + 255 = 289
G: 34 + 51  = 85
B: 44 + 51  = 95



R (red) 값은 255를 넘었네요. (maximum 값이 255 잖아요). 이럴 경우는 그냥 255로 됩니다. 그러니까 최종 RGB 값은 (255,85,95) 가 되죠. 원래 빨간색의 RGB 값은 (255,51,51) 였죠. 두 이미지에서 빨간 원 부분을 보세요. blend 가 적용된 부분의 좀 더 밝아 졌죠.


다음은 좀 더 복잡한 부분을 볼까요. 빨간색과 녹색 원이 겹쳐지는 부분입니다. 그리고 파란 네모가 겹쳐진 부분은 제외하구요.



R: 34 + 255 + 0   = 289
G: 34 + 51  + 153 = 238
B: 44 + 51  + 51  = 146



이 경우 background 까지 합해서 각 channel 의 값들의 합을 구합니다. 255 가 넘는 겂이 있으면 그냥 255로 정해 지는 거구요. 그러면 최종값은 (255,238,146)이 됩니다.


add blend를 사용하면 우리의 Halloween scene 은 어떻게 될 까요? wisps 에 blendMode=“add”를 적용하면 좀 더 creative 한 효과를 볼 수 있습니다. 하얀 wisps 부분은 눈부시게 빛나는 것 같고 어두운 wisps 부분도 (원래 보라색이었던 부분) 약간 빛나면서 번져 보입니다.




여기에는 없지만 만약 black pixels 들이 있다면 그 픽셀들은 완전 투명하게 될 겁니다. 왜 그러냐구요? 검은색의 RGB 값은 (0,0,0) 이거든요. 그러니까 픽셀에 아무 값도 더해지지 않게 됩니다. 그러면 당연히 밑의 색이 그대로 보이겠죠. 그러니까 검은색은 투명한 효과가 나게 되는 겁니다.

요약하자면 add blend mode는 각 color channel 의 값을 더해 주는 겁니다. 더해진 값이 255가 넘으면 그냥 255로 정해지는 거구요. 그러니까 모든 색이 더 밝아 지겠죠. additive blending 은 그래서 레이저, 불, flash 들 같은 밝고 colorful 한 효과를 내는데 좋습니다.


IMPORTANT NOTE:  저 위에서 계산하는 거 보셨죠? 여러분이 프로그래밍할 때 저 계산을 할 필요는 없습니다. 코로나에서 해 주니까요. 여러분은 그냥 어떤 blend 효과를 사용할 지만 정하시면 됩니다.



Blend Mode “multiply”


이 이름만 보고는 딱 와 닿지 않지만 multiply blend mode는 사실 좀 더 어둡게 만드는 효과가 필요할 때 사용하시면 됩니다. 예를 들어 그림자 효과 같은 것을 넣을 때 좋죠.


color channel value들을 더하는 대신에 multiply blend mode는 (짐작하시겠지만) 그 값들을 multiply (곱하기) 합니다. 이렇게 하면 RGB channel 값이 65025 (255*255) 같이 아주 높아지겠죠. add blend mode 였다면 모두 255(하얀색)이 될 겁니다. 그런데 왜 multiply blend 는 좀 더 어둡게 될 까요? 왜냐하면 곱한 값을 다시 255로 나누거든요.

아래 그 공식을 보겠습니다.




resulting color = ( top color * bottom color ) / 255




다시 각 부분의 RGB 값을 볼까요.


background:      ( 34, 34, 44 )
red circle:      ( 255, 51, 51 )
green circle:    ( 0, 153, 51 )
blue rectangle:  ( 102, 153, 204 )



빨간 원의 왼쪽 부분을 계산해 봅시다. (잘 보세요. 좀 더 어두워 질 거예요.)



R: ( 34 * 255 ) / 255 = 34
G: ( 34 * 51 )  / 255 = 6.8
B: ( 44 * 51 )  / 255 = 8.8




먼저 곱한 다음에 그 값을 다시 255로 나누기 때문에 결과값이 낮은 겁니다. 그러면 더 어두운 색이 되겠죠. 검은색은 어떻게 되는지도 보셔야 할 겁니다. 검은색은 그 값이 0 이죠. 0에 무슨 값을 곱해도 0 이 되죠. 그래서 black 인 부분은 항상 black 이 됩니다.

아래 이미지는 Halloween 이미지들에 blendMode=“multiply” 값을 세팅한 결과 입니다.





보시다시피 wisps 가 있는 부분들은 더 어두워 졌죠? 이 효과는 이런 Halloween 에 맞는 효과는 아니네요. 그냥 multiply blend 가 어떻게 효과를 내는지 이해하는데만 사용하세요. 흰색(255) 부분은 어떻게 됐을 까요. 255에 무엇을 곱하더라도 다시 255로 나누면 그냥 그 색이 되겠죠? 그러니까 이 효과에서는 흰색은 투명한 효과가 납니다. 그냥 밑에 있는 색이 그대로 보이는 거죠.


Blend Mode “screen”


screen blend mode 도 add blend mode 와 마찬가지로 좀 더 밝게 만들어 주는데요. 다만 아주 어두운 부분은 오히려 lightening 하지 않습니다. 이 blend mode의 algorithm은 좀 복잡한데요. channel 값들은 마이너스를 하고 곱하기를 하고 그리고 다시 마이너스를 하고 뭐 그렇습니다.

아래 그 공식이 있습니다.



resulting color = 255 - ( ( ( 255 - top color ) * ( 255 - bottom color ) ) / 255 )



multiply blend 의 반대 효과를 주어서 밝게 만듭니다. 그러니까 add blend 와 비슷한 효과를 주기는 하지만 아주 똑 같지는 않구요.





이 screen blend 의 공식은 복잡합니다. 다행히 여러분은 이런 계산을 하지 않으셔도 됩니다. 단지 여러분은 Halloween 장면에 어떤게 더 효과적인지만 정하시면 됩니다.






이것도 효과가 괜찮네요. 이 효과의 특징은 additive blend 와 비슷하지만 그것보다 살짝 연하다는 겁니다. wisps 의 어두운 부분들은 아주 연하게 표현이 됐습니다. 이렇게 어두운 부분은 additive blend 가 좀 더 낫지 않나 싶네요.


In Summary


코로나에서의 Blend modes는 특정 상황에서는 아주 powerful 한 tool 이 될 수 있습니다. 이 기능을 적용하는 것도 아주 간단합니다. (단지 한 줄만 코딩해 넣으면 됩니다.)  코로나의 그래픽 엔진이 모든 계산을 다 해 줄 겁니다. 어떤 blend mode를 사용할 까는 여러분 앱의 scene 에 어떤 효과를 넣어야 할지 여러분이 결정하시면 됩니다.  질문이 있으신 분들은 여기 에 댓글로 질문해 주세요. Happy Halloween!



반응형

FAQ Wednesday: Display Objects

2012. 10. 26. 05: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.

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




반응형


반응형

Parallax Simplified

Posted on . Written by

I

이번주 화요일의 튜토리얼은 코로나 Ambassador 인 Brent Sorrentino 씨가 해 주셨습니다. parallax scrolling 과 여러분 앱에 customizable touch-and darg parallax view 를 구현하는 데모 프로젝트에 대해 다룹니다.


We Live in a Parallax World



Parallax 는 천문학, photography,광학 에서 다루는 개념인데요 좀 의미가 광범위 합니다.  모바일 게임 개발자의 경우 2-dimensional scrolling simulation 에 대해 작업할 경우 parallax는 카메라와 움직이는 객체 사이의 거리감 (sense of “distance”) 을 제공하게 되죠.  이 메소드는 16 비트 닌텐도 시절부터 사용되어져 왔습니다. 그리고 요즘 인기있는 Angry Birds 와 Squids 등에서도 사용되고 있죠.


코로나의 display groups를 활용하면 차례로 겹쳐진 layer 들로 parallax scrolling을 간단하게 구현할 수 있습니다. 이론적으로는 그런데 실제 많은 개발자들이 실제 이것을 코딩하려고 하면 많이 헛갈려 하시죠. 오늘의 튜토리얼과 데모 프로젝트는 이 부분에 촛점이 맞춰져 있습니다.




1. Getting Started


처음으로 우리가 할 작업은 우리의 parallax "layers" 들을 display groups 로 configure 하는 겁니다. main.lua 파일에 아래 코드를 복사해 넣으세요.


--First, declare a local reference variable for the stage.
local stage = display.currentStage

--Declare your parallax layers in back-to-front order and specify their distance ratio.
--This "distanceRatio" sets the layer's scrolling speed in relation to the foreground (1.0) layer.
local back00 = display.newGroup() ; back00.distanceRatio = 0.2
local back01 = display.newGroup() ; back01.distanceRatio = 0.4
local back02 = display.newGroup() ; back02.distanceRatio = 0.6
local back03 = display.newGroup() ; back03.distanceRatio = 0.8
local foreground = display.newGroup() ; foreground.distanceRatio = 1.0

--IMPORTANT NOTE 1: You MUST have one layer set to 1.0 ratio, even if it won't contain any objects.
--IMPORTANT NOTE 2: Attach a reference variable named "refGroup" to the stage table and set its
--value to the 1.0 layer from above. This group will be used as the core reference group in
--relation to screen touch-and-drag.
stage.refGroup = foreground


  

각 parallax layer 는 “distanceRatio” parameter를 정해 줍니다. 이것은 아주 중요한 부분입니다. 우리는 이것을 foreground 의 1:1 scrolling 과 연결해서 각 레이어별로 스크롤링 스피드를 주는부분에서 사용할 겁니다.  만약 특정 layer 가 parallax scrolling에서 제외되어야 한다면 간단하게 이 파라미터를 undeclare 하면 됩니다.  예를 들어 이 display group setup의 hierarchy 중에 한개 나 두개의 layer 를 scroll 하고 싶지 않다면 , 예를 들어 UI/button/widget layer 같은 경우가 될 수 있겠죠, 이럴 경우 이 distanceRatio 값을 넣지 않으면 됩니다. 이 “distanceRatio” parameter 가 있는 layer 만 parallax scrolling 이 될 테니까요.


Additional note: “distanceRatio” parameter 는 여러분이 원하신다면 1.0 보다 클 수도 있습니다.  예를 들어 1.4로 했다면 이 레이어는 foreground 보다 더 빨리 움직일 겁니다.  이건 아마 카메라와 foreground 사이에 연한 안개가 있도록 할 경우에 활용할 수 있겠네요.


2. Setting Scroll Limits


대부분의 시나리오에서 어떤 제한된 공간(세계) 가 있게 되죠. 유저가 어느쪽으로 어디까지 drag 할 수 있는지에 대한 제한 등이 그런 것들인데요. 이런것들은 다음에 나오는 4개의 변수들에 의해 해결할 수 있습니다. 다음에 이것들을 쉽게 사용하도록 하기 위해 stage 의 파라미터들로서 이 4개를 정의해 둡니다.

여러분의 프로젝트에 다음 한 줄을 추가해 주세요.


stage.xMin = -400 ; stage.xMax = 400 ; stage.yMin = -150 ; stage.yMax = 1000
 

이 4개의 파라미터가 간단하게 그 제한된 공간을 나타낼 겁니다. xMin는  대개 음수가 되겠죠. 왼쪽까지 얼마나 멀리 그 공간이 정해질지를 정합니다. xMax는 오른쪽까지의 공간이구요. yMinyMax는 위쪽과 아래쪽으로 스크롤할 수 있는 공간을 나타낼겁니다.

이 숫자들은 모두 픽셀단위를 사용합니다. % 가 아닙니다. 또한 foreground 1:1 레이어는 움직이지 않을 거구요. 이 foreground layer 이외의 layer 들은 이 제한사항들이 적용될 겁니다.


3. Add Layers to a Containing Table


이제 우리는 이 parallax layer 들을 paraGroups라는 이름의 테이블에 포함시킬 겁니다. 그리고 그 테이블은 stage 의 sub-table 이 될 거구요.

아래 코드를 여러분 프로젝트에 복사해 넣으세요.


stage.paraGroups = {}
for g=1,stage.numChildren do
  if ( stage[g].distanceRatio ) then stage.paraGroups[#stage.paraGroups+1] = stage[g] end
end

  

이 코드를 보면 첫번째로 테이블을 정의합니다. 그리고 display groups 에 대해 stage 의 각 child 숫자 만큼 루프를 돌립니다. 그리고 if 문을 사용해서 “distanceRatio” parameter가 있는 경우에만 테이블에 집어 넣습니다.


4. Populate the World


다음 코드에서는 gradient background를 세팅할 겁니다. 그리고 our world 에 몇개의 샘플 객체들을 추가할 거구요. 이 코드들에 대해서는 일일이 설명하지는 않을께요. 여러분들은 여러분들 나름대로의 parallax world 를 만들실 거니까요. 아래에서 하나의 객체를 각 parallax layer 에 add 하는 것을 보실텐데요. 여러분은 많은 객체들과 이미지들을 추가하셔야 한다는 것만 기억해 두세요.


local cX = display.contentWidth/2 ; local cY = display.contentHeight/2
local bkgrad = graphics.newGradient( {42,17,10}, {0,0,0}, "up" )
local bk = display.newRect( 0,0,cX*2,cY*2 ) ; bk:setFillColor( bkgrad ) ; bk:toBack()

local object00 = display.newRect( back00,cX-16,cY-16,32,32 ) ;
    object00:setFillColor(150,0,50,120)
local object01 = display.newRect( back01,cX-32,cY-32,64,64 ) ;
    object01
:setFillColor(150,20,50,140)
local object02 = display.newRect( back02,cX-48,cY-48,96,96 ) ;
    object02
:setFillColor(150,40,50,160)
local object03 = display.newRect( back03,cX-64,cY-64,128,128 ) ;
    object03
:setFillColor(150,60,50,180)
local foreObject = display.newRect( foreground,cX-80,cY-80,160,160 ) ;
    foreObject
:setFillColor(150,80,50,200)


5. Implementing Touch and Drag

여러분이 움직일 수 없다면 이 scrollable world 는 소용이 없겠죠. 아래 함수는 screen touch 의 "began" 과 "moved" phases 를 사용해서 paraGroups table 안에 있는 모든 그룹들을 스크린 내에서 여러분이 drag 할 수 있도록 만들겁니다.

이 함수와 touch listener 를 여러분 프로젝트에 복사해 넣으세요.


local function touchScreen( event )
  local stageID = event.target
  local refGroup = stageID.refGroup ; local paraGroups = stageID.paraGroups
  local eventX = event.x ; local eventY = event.y

  if ( #paraGroups < 1 ) then return end

  if ( event.phase == "began" ) then

    for i=1,#paraGroups do
      paraGroups[i].offsetX = eventX - refGroup.x
      paraGroups[i].offsetY = eventY - refGroup.y
    end

  elseif ( event.phase == "moved" ) then

    local xDif = eventX - refGroup.offsetX
    local yDif = eventY - refGroup.offsetY

--If you are NOT limiting the boundaries of your world, comment out these 2 lines.
    if ( xDif < stageID.xMin ) then xDif = stageID.xMin
    elseif
( xDif > stageID.xMax ) then xDif = stageID.xMax end
    if ( yDif < stageID.yMin ) then yDif = stageID.yMin
    elseif ( yDif > stageID.yMax ) then yDif = stageID.yMax end

    for i=1,#paraGroups do
      local group = paraGroups[i]
      group.x = xDif * group.distanceRatio
      group.y = yDif * group.distanceRatio
      group = nil
    end
    xDif, yDif = nil,nil

  end

  stageID, refGroup, paraGroups, eventX, eventY = nil,nil,nil,nil,nil
  return true

end

stage:addEventListener( "touch", touchScreen )



이 코드에 대해서도 일일이 설명을 드리지는 않겠습니다. 단지 이 behavior를 summarize 할께요.


began phase에서 우리는 paraGroups table에 대해 루프를 돌립니다. 그리고 the foreground reference group 에 대해 각각 특정 X/Y 값을 세팅합니다. 이것은 core 1:1 group 의 포지션에 근거해서 각 layer들의 위치를 정해주기 위해 필요한 부분 입니다.


moved phase 는 실제 action 이 일어나는 곳입니다. 우선 첫번째로 어디에 유저가 touch 했는가에 따라 관련 group 들을 움직이기 위해 xDifyDif ”delta” values를 정해야 합니다. 그리고 바로 직전의 phase check 으로부터 얼마나 멀리 움직였는지도 정해야 하구요.


그 다음으로는 위의 Step #2 에서 정해 놓은 world boundary 들의 바깥으로 X, Y 포지션이 나갔는지 여부를 체크합니다. 4개 중에 하나라도 밖으로 나가게 되면 xDifyDif value 를 pre-adjust  합니다. 그렇게 함으로서 그 world 밖으로는 절대 나가는 일이 없게 되는거죠. 코드 안에 적어놨듯이 scrolling 의 제한을 주고 싶지 않으면 이 부분을 주석을 달 수도 있습니다. 


마지막으로는 paraGroups table에 루프를 돌리고 각각의 parallax layer의 위치를 조정합니다. 이 때 group 에서 정해 줬던 distanceRatio value에 따라 위치를 조정하게 됩니다. distance ratio 가 1.0 인 foreground group은 정확히 유저의 touch 를 따라서 움직이게 됩니다. 그리고 그 이외에 layer 들은 여러분이 정해 준 distance ratio 에 따라서 약간씩 다르게 움직입니다. 한번 ratio들을 바꾸고 나서 다시 시도해 보세요. 움직임이 다를 겁니다. 여러번 테스트 해 보시고 가장 그럴듯한 ratio 들을 정하셔서 사용하세요.



아주 간단하죠? 이제 몇개의 layer 든지 상관없이 scrolling touch-and-drag parallax world 를 구현할 수 있는 flexible 한 메소드를 구현했습니다. 이제 실전에서는 각각의 ratio 들만 조정해서 사용하시면 됩니다. 아니면 database 나 text file을 이용해서 dynamically 움직임들을 조정 할 수도 있죠.


6. Cleaning Up


마지막으로 layer references들을 clean up 하는 것을 잊지 마세요. 특히 여러분이 Storyboard 나 Director 와 같은 scene manager 를 사용하고 있다면 더 잊지 마셔야 됩니다. 각 scene manager 에서 사용하는 standard cleanup practices 를 사용하는 것 외에 parallax display groups 에 대한 references 를 가지고 있는 paraGroups table 을 clean up 하는 것도 중요합니다. scene 들이 바뀔 때마다 이 작업들이 이루어 지도록 해야 합니다. touch listener도 remove 하는 것 잊지 마시구요. (전체 메소드가 전체 scene 에서 작동하는 global level 이면 필요가 없겠지만요.)



for i=#stage.paraGroups,1,-1 do
  stage.paraGroups[i] = nil
end
stage:removeEventListener( "touch", touchScreen )


Looking Forward!


다음 튜토리얼에서는 이 메소드를 build 하고 dynamic 하게 parallax layer 를 zoom in/out 하는 간편한 zoom feature에 대해 다루겠습니다. 그 때까지 이 데모 project 를 구현하고 수정하고 테스트 해 보시고 질문이나 제안이 있으면 댓글 달아 주세요.



반응형


반응형
Posted on . Written by


iOS6의 landscape/Game Center orientation issue 와 관련 코로나에서 많은 개선을 했다는 것을 말씀 드리게 되서 기쁩니다. 이 개선된 사항들은 Corona daily build 930에서 사용하실 수 있습니다.

처음 저희가 이 문제에 접근할 때 물리학자들이 자연의 forces들을 unify 하기 위한 접금 자세와 같은 자세였습니다. 최대한 일반적인 해결책을 찾으려고 애를 썼죠. 이 iOS6와 이슈와 관련한 해결책을 찾으면서 iOS5와 iOS 4.3 버전도 같이 동시에 테스트 해 가면서 일반적인 해결법을 찾으로겨 노력했습니다. 그래서 약간 stopgap 문제 같은게 생기기도 했었죠. ( #3 in last week’s FAQ)


개인적으로 그 stopgap 이 마음에 들지 않았습니다. 그래서 지난주에 팀에게 다른 방법을 찾아 보라고 부탁했고 좀 더 나은 방법을 찾아보자고 제안했습니다. 금요일 이전에 새롭게 전략을 만들었고 이대로 하면 훨씬 나아질 것이라고 생각했습니다.


이 새 전략은 거의 외과수술 수준의 작업을 해야 하는 작업입니다. 저희들이 무엇을 하는가 하면 이 workaround 가 오직 iOS 6 에서만 일어나도록 하는 겁니다. 이 현상은 iPhone 과 iPod Touch에서만 일어나거든요. -iPad에서는 이러한 현상이 일어나지 않습니다. -





저희들이 해야 할 일은 여러분의 info.plist에 special key 인 ‘CoronaUseIOS6LandscapeOnlyWorkaround’ 를 추가 하는 겁니다. 저희들이 이름을 조금 복잡하고 unfriendly하게  만들었는데요 애플이 다음버전에서는 이 버그를 해결하기를 바라는 마음에서 그렇게 지었습니다.


이 기능을 사용하시려면 build.settings 파일에  special ‘plist’ section을 추가하시면 됩니다.


settings =
{
orientation =
{
default = "landscapeRight",
supported =
{
"landscapeRight",
"landscapeLeft",
},
},
iphone =
{
plist =
{
CoronaUseIOS6LandscapeOnlyWorkaround = true,
},
}
}

 

(NOTE: 만약 이전의 workaround를 사용하신다면 여러분은 portrait 세팅을 remove 하셔야 합니다. 그리고 content key 도 remove 하셔야 됩니다. 그렇게 하지 않으시려면 그냥 위의 코드를 사용하시면 됩니다.)


이건 기본적으로 여러분 앱이 landscape 앱이라는 것을 코로나에 알려주는 겁니다. 그러면 이 special workaround를 activate 하고 싶다는 것을 얘기하는 것이죠. 이 workaround는 iOS 6 에서 돌아가는 iPhone 이나 iPod Touch 에서만 작동할 겁니다. iOS 5 에는 GameCenter bug 가 없습니다. 그러니까 거기서는 여러분의 landscape 앱이 제대로 잘 작동할 겁니다.


이제 마지막 문제가 있습니다. iOS 6 에서 돌아가는 iPad 의 photo picker 이슈 입니다. 만약에 여러분이 landscape 앱을 가지고 있다면 이 이슈가 발생할 겁니다. 이 부분은 다음 daily build 에서 해결책을 적용할 예정입니다.


반응형

Sprite Sheet 활용하기 (Advanced)

2012. 10. 12. 05: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 를 사용하면 아주 다양하고 훌륭한 효과를 낼 수가 있습니다. 그리고 여러분의 코딩에 따라서 한정된 이미지로 개성있는 캐릭터를 계속 만들어 낼 수 있구요.


반응형


반응형
Posted on . Written by


화요일의 Tutorial이 다시 돌아왔습니다. 오늘의 튜토리얼은

Brent Sorrentino 가 작성한 건데요. Northern Colorado 에서 Corona Ambassador 로 활동하고 있습니다. Brent는 2년여 코로나 커뮤니팅서 활발히 활동하고 있습니다. 그는 프리랜서 여행사진작가이고 코로나 개발자이며 그래픽 디자이너 입니다. 그만의 앱을 개발하기 위해 코로나를 사용하고 있습니다. 또한 정기적으로 포럼에서 여러사람에게 도움을 주고 있고 여러 이슈들을 해결해 주고 있습니다 그의 웹사이트를 보시려면 여기를 클릭하세요.



오늘의 튜토리얼은 어떻게 animated sprite를 implement 할 것인가와 그와 관련된 API 들에 관한 겁니다. 아마 이전 튜토리얼에서 다룬 내용도 있을 겁니다. 올해 초 현재의 스프라이트 시스템이 소개 된 이후 몇가지 주요 개선사항이 있었습니다. 그리고 많은 개발자들이 아직도 코로나에서 어떻게 sprite를 implement 하는지에 대해 혼동을 하고 있습니다. 이 sprite를 사용하는 방법에는 두가지가 있습니다.


  1. The old (and now depreciated) sprite.* library.
  2. The current sprite APIs that work cohesively with image sheets and the display.* library.


이렇게 코로나의 스프라이트 라이브러리가 변화를 보였는데요. 그 중에서 현재 버전을 사용할 것을 강력히 추천합니다. 현재 방법을 사용하지 않았거나 코로나에서 스프라이트를 한번도 사용해 보지 않았다면 이 튜토리얼이 많이 도움이 될 겁니다.


코로나의 basic animation에 이미 익숙하신 분이라면 이 튜토리얼이 sprite event listeners에 대한 모든 정보를 제공하고 어떻게 implement 할 것인지를 가이드 해 드릴 겁니다.


Tutorial Contents

  1. Configuring a Basic Image Sheet or “Sprite Sheet”
  2. Defining Animation Sequences
  3. Declaring a Sprite Object
  4. Sprite Control Methods — managing playback and sequences
  5. Sprite Properties — accessing and setting various sprite properties
  6. Sprite Event Listeners — detecting sequence events such as “ended”, “loop”, and “next”
  7. Frame Trimming Support


Configuring a Basic Image Sheet or “Sprite Sheet”



정확히 image sheet 이 뭘까요? 한번 상상해 보세요. 여러분들의 animated object 들을 위해 각각 의 프레임들을 그린 한장의 종이가 있다고요. 코로나에서 다른 전문적인 용어로는 texture atlas, image map, or sprite sheet 라고 합니다. 이걸 그냥 간단히 image sheet라고 부를께요. 이것은 static 이든 animated object 이든지 상관없이 활용할 수 있습니다.

graphics.newImageSheet() API에 대한 자세한 사용법과 샘플들은 여기에 있습니다. 이 튜토리얼에서는  그중에서 어떻게 animated sprite를 위해 image sheets를 사용할지에 대해 다루겠습니다.



아래 그림은 running cat 을 표현하는 샘플 image sheet 입니다. 이 튜토리얼을 공부하면서 이 이미지를 사용하시려면 여기에 hi-resolution version 이미지가 있습니다. 이 sheet 에는 8개의 프레임이 순서대로 있습니다. 디폴트로 애니메이션은 top-left frame 부터 시작해서 오른쪽으로 진행하죠. 오른쪽 끝까지 가면 그 다음 줄로 갑니다. 그리고 전체 프레임을 다 돌면 중지합니다.






코로나에서 이 image sheet 을 어떻게 다루는지 보겠습니다. 우선 uniform-sized frame 인 basic image sheet 를 위해 indexed table을 setup 합니다.

local sheetData = {
  width = 512, --the width of each frame
  height = 256, --the height of each frame
  numFrames = 8, --the total number of frames on the sheet
  sheetContentWidth = 1024, --the total width of the image sheet (see note below)

  sheetContentHeight = 1024 --the total height of the image sheet (see note below)

}


IMPORTANT:  sheetContentWidthsheetContentHeightoverall 1:1 dimensions of the image sheet (전체 가로 세로)를 나타냅니다.1:1 content scale 은 여러분이 앱의 config.lua file에 set up 한 내용을 기준으로 합니다.  이렇게 하면 다른 디바이스별로 다른 이미지를 사용할 수 있도록 합니다. 예를 들어 @1 sheet는 original iPad 에 맞는 이미지를 사용하고 high-resolution @2 sheet 는 Retina iPad 에 맞는 이미지를 사용할 수 있습니다. 그러니까 1:1 sheet이 1024×1024 pixels 이라면 2:1 sheet 는 2048×2048 인 이미지가 되는거죠. image sheet setup에는 항상 1:1 dimensions를 사용하세요. config.lua file 세팅이 정확하다면 그 다음부터는 코로나가 알아서 처리할 겁니다.



이제 실제 image sheet 를 정의하세요. 괄호 안에 image file 과 위에 정의한 data table을 넣어주시면 됩니다. 아래처럼 파일이름만 넣으면 그 이미지 파일을 여러분 프로젝트 디렉토리의 root 에서 찾을 겁니다. 


local mySheet = graphics.newImageSheet( "runningcat.png", sheetData )


Defining Animation Sequences


여러분은 두가지 방법으로 animated frame 순서를 정하실 수 있습니다.

  • consecutively using a starting frame index and frame count
  • non-consecutively using a specific order of frames


이 두 가지 방법으로 코로나에서 sprite system 을 아주 flexible 하게 사용하실 수 있습니다. 하나의 image sheet 으로 여러 animation sequences를 사용해서 활용할 수가 있죠. 두 경우 모두 sub-table에서 comma-separated array 로 sequences를 define 하시면 됩니다.


Consecutive frames


local sequenceData = {
  { name = "normalRun", --name of animation sequence

    start = 1, --starting frame index

    count = 8, --total number of frames to animate consecutively before stopping or looping

    time = 800, --optional, in milliseconds; if not supplied, the sprite is frame-based

    loopCount = 0, --optional. 0 (default) repeats forever; a positive integer specifies the number of loops

    loopDirection = "forward" --optional, either "forward" (default) or "bounce" which will play forward then backwards through the sequence of frames

  } --if defining more sequences, place a comma here and proceed to the next sequence sub-table


}



Non-consecutive frames


local
sequenceData = {
  { name = "fastRun",
    frames = { 1,2,4,5,6,7 }, --specific order of frame indexes from the image sheet

    time = 250,
    loopCount = 0
  } --if defining more sequences, place a comma here and proceed to the next sequence sub-table

}

Mixed sequences (both consecutive and non-consecutive sequences)


local
sequenceData = {
{ name="normalRun", start=1, count=8, time=800 },
  { name="fastRun", frames={ 1,2,4,5,6,7 }, time=250, loopCount=0 }

}

 

Declaring the Sprite Object


현재의 sprite method를 사용하려면 display.newSprite() API 로 sprite를 선언하셔야 합니다. 신택스는 간단합니다.


display.newSprite( [parent,] imageSheet, sequenceData )
  • parent = The parent display group in which to insert the sprite (optional).
  • imageSheet = The image sheet which the sprite should utilize.
  • sequenceData = The array of animation sequences which you set up. The sprite will default to the first sequence in the array unless you specify otherwise (see Sprite Control Methods below).


이 튜토리얼에 나온 예제로 한다면 sprite 선언은 아래와 같을 겁니다.

local animation = display.newSprite( mySheet, sequenceData )


이제 이 sprite 는 display object가 됐습니다. 그냥 일반 static image, vector objects 같은 것들과 같게 됐죠. 이제 이 sprite 는 움직일 수도 있고 manipulated 될 수도 있고 physics body 를 입힐 수도 있고... 등등을 할 수 있습니다. 이 sprite object 를 remove 시키려면object:removeSelf()display.remove( object ) 를 사용하시면 됩니다. remove 한 다음에 nil로 세팅하는 것을 잊지 마세요.


Sprite Control Methods


이 스프라이트 시스템은 4가지 주요 control methods를 제공합니다. 여러분들은 이것으로 스프라이트의 playback과 sequence 를 control 하실 수 있습니다.


  • animation:play()
    Start the animation playing. Animations do not begin playing when you create them — you must start each animation manually using this command.
  • animation:pause()
    Pauses the animation. There is no “stop” control method; instead, pause the animation using this method.
  • animation:setFrame( frame )


    Immediately set or skip to the indicated frame index. If you want to “stop and reset” an animation sometime after you have started playing it, use the :pause() and :setFrame( frame ) commands consecutively, setting the frame back to the beginning of the sequence.
  • animation:setSequence( sequence )


    Set the sprite to a specific sequence that you declared in your sequence array. For example, if you want to change your cat animation from “normalRun” to “fastRun”, you would use animation:setSequence( “fastRun” ) and use animation:play() to begin playing it, since the animation will not play automatically after you change the sequence.


Putting It Together


전체 애니메이션을 함께 넣어보죠. 달리는 고양이를 normalRun 과 fastRun 두가지로 셋업할 겁니다. 아래 예제가 있습니다.

local sheetData = { width=512, height=256, numFrames=8,

sheetContentWidth=1024, sheetContentHeight=1024 }

local mySheet = graphics.newImageSheet( "runningcat.png", sheetData )

local sequenceData = {
  { name = "normalRun", start=1, count=8, time=800 },
  { name = "fastRun", frames={ 1,2,4,5,6,7 }, time=250 }
}



local animation = display.newSprite( mySheet, sequenceData )
animation.x = display.contentWidth/2 --center the sprite horizontally
animation.y = display.contentHeight/2 --center the sprite vertically

animation:play()





코로나 시뮬레이터로 테스트 해 보세요. 저 고양이가 화면 중앙에 나올겁니다. 그리고 normalRun sequence로 animating 되겠죠. (sequence를 따로 선언하지 않으면 위에 얘기했던 디폴트 순수대로 진행합니다.)
테스트를 위해 animation:play()앞에 animation:setSequence( “fastRun” )를 넣어 보세요.


Sprite Properties


Corona provides several properties which can yield information about existing sprites. You can even modify the timeScale (relative speed) of a particular sprite. These properties are as follows:

코로나에는 현재의 스프라이트에 적용할 수 있는 몇가지 프로퍼티들을 제공합니다. 특정 스프라이트의 timeScale (relative speed)를 modify 할 수도 있습니다. 아래 프로퍼티들을


  • object.frame
    A read-only property that represents the currently shown frame index of the loaded sequence. This does not set the frame — use the :setFrame() command to explicitly set an animation frame.
  • object.isPlaying
    Returns true if the animation is currently playing; false if it is not.
  • object.numFrames
    A read-only property that represents the number of frames in currently loaded sequence.
  • object.sequence
    A read-only property that returns the name of the currently playing sequence.
  • object.timeScale
    Gets or sets the scale to be applied to the animation time. This is used to control a sprite’s animation speed dynamically. For example, a time scale of 1.0 (default) runs the animation at normal speed. A time scale of 2.0 runs the animation twice as fast. A time scale of 0.5 runs the animation at half speed. The maximum allowed value is 20.0 and the minimum allowed value is 0.05. The value supports up to 2 decimal places.


Sprite Event Listeners


이제 basic sprite 선언과 두개의 sequences (“normalRun” , “fastRun”) 가 생겼습니다. 

이제 sprite event listener를 살펴보죠. 그리고 그것을 어떻게 implement 하는지에 대해 알아보겠습니다. sprite event listener 의 정의는 'sprite의 activity를 "listens" 하고 그 정보를 listener function에 전달하는 것' 입니다.


예를 들어 여러분의 달리는 고양이를 "normalRun" sequence로 4번(4 cycles)를 돌게 한 다음에 "fastRun" sequence로 바꿀 수 있습니다. 이것은 standard timer 로 표현하기에는 불가능한 효과죠. 그래서  그 대신에 sprite event listener를 implement 하는 겁니다.



예제를 보기 전에 sprite에서 사용 가능한 5가지 를 event phases보겠습니다.

이 phases 들은 최근의 코로나 Public Release 인 (2012.894) 버전 이후부터 사용하실 수 있습니다.


  • began = The sprite has started playing.
  • ended = The sprite has finished playing.
  • bounce = The sprite has bounced from forward to backward while playing.
  • loop = The sprite has looped to the beginning of the sequence.
  • next = The sprite has played a subsequent frame that’s not one of the above phases.


이 phases 를 어떻게 listen 할지에 대해 다루겠습니다. 근데 우선 달리는 고양이에 loopCount = 4를 추가해서 normalRun 의 sequence를 바꾸고 시작하도록 하죠. 이렇게 하면 4번의 loop가 끝나면 ended phase를 받을 수 있도록 해 줍니다.


local sequenceData = {
  { name = "normalRun", start=1, count=8, time=800, loopCount=4 }, --add loopCount=4

  { name = "fastRun", frames={ 1,2,4,5,6,7 }, time=250 }
}

 

Now, let’s write the listener function and add the actual event listener to the running cat. You can place this at the end of your sample code, after the sequences are declared and the sprite object placed on the screen.

이제 listener function을 만들어 봅시다. 그리고 달리는 고양이에 실제 event listener를 달아보죠. 아래 내용을 위 샘플 코드의 마지막 부분에 sequence 가 선언된 다음에 추가하시면 됩니다.


local function mySpriteListener( event )
if ( event.phase == "ended" ) then
    local thisSprite = event.target --"event.target" references the sprite
    thisSprite:setSequence( "fastRun" ) --switch to "fastRun" sequence
    thisSprite:play() --play the new sequence; it won't play automatically!
  end

end

animation:addEventListener( "sprite", mySpriteListener ) --add a sprite listener to your sprite

 
sprite listener 가 이제 모든 phases를 listener 함수에 전달하게 됩니다. if-then 구문을 사용해서 이 phases를 사용하는 것은 개발자가 코딩해야 할 부분입니다. 특정 시점에 우리는 ended phase를 listen 하게 됩니다. sequence의 loopCount parameter에 의해 4번의 루프가 끝나면 이 ended phase가 발생하도록 했죠. 이 ended phase 를 받으면 이 cat animation을 fastRun sequence로 바꾸고 play 하게 됩니다.

하나의 sprite listener를 모든 sprite 에 대해 사용하실 수 있습니다. listener 함수에서 event.target을 사용해서 원하는 스프라이트를 catch 해서 사용하실 수 있는겁니다.


Frame Trimming Supported



최근의 Public Build (2012.894) 에서는 frame trimming 도 지원합니다. 3rd-party sprite utilities 인 SpriteLoqTexturePacker 같은 곳에서 이 기능을 제공하고 있습니다. 그리고 코로나에서도 이 어플리케이션과 호환성 있게 이 기능을 사용할 수 있도록 하고 있습니다.

frame trimming 예제는 current sprite methods 를 사용하고 있는데 이것은 여러분 시스템의 코로나 어플리케이션에 있는 “HorseAnimation” sample project에서 확인 하실 수 있습니다.


CoronaSDK → SampleCode → Sprites → HorseAnimation



이와 관련해서는 다음 튜토리얼에서 다뤄지게 될 것 같습니다. 그동안 여러분은 샘플 프로젝트를 보셔도 되고 여기에서 imageSheet documentation를 보시면 많은 도움이 되실 겁니다.


In Summary


이 튜토리얼은 current sprite methods 의 대부분을 다뤘습니다. 여기에 basic image sheets, animation sequences 정의하기, sprite playback 다루기, 다양한 sprite property들 다루기 그리고 sprite event listener 사용하기 등을 추가로 다루고 있습니다. 모쪼록 이 튜토리얼이 개발자 여러분에게 스프라이트를 이용해서 앱을 만드는데 도움을 드릴 수 있기를 바라겠습니다. 특히 이전 버전의 sprite library 를 사용하시던 분들에게 새로 바뀐 sprite library 를 사용하시는데 도움이 되기를 바라구요.


반응형


반응형
Posted on . Written by


존 레논이 말했죠, life는 네가 다른 plan을 만들 때 일어난다고요. 그런 비슷한 일들이 최근 저희들에게 일어났었습니다. Kindle HD/NOOK HD 에 대해 관심을 많이 가진 이후로요. 이런 디바이스들에 대한 Corona’s support 를 하기 위해 Kindle HD에 대한 지원을 진행했고 NOOK HD 에 대한 지원도 많이 진행 됐습니다.


한가지 우리가 찾아낸 것은 여러분의 ‘build.settings’ file 에 어떤 Lua syntax 에러가 있으면 여러분의 APK는 NOOK pre-flight submission tests에 통과하지 못할 거라는 겁니다. 여러분의 편으를 위해서 저희들이 약간의 변화를 주려고 합니다. 그래서 그 파일에 에러가 있더라도 그 APK 가 제대로 동작할 수 있도록요.


이런 critical 한 이슈들에 대해 정리하는 동안 daily build 929 버전부터 가능하도록 한 이슈들이 몇가지 있습니다.



그 중 하나가 맵뷰를 로딩할 때 배경색을 흰색에서 회색으로 바꾼건데요. 이렇게 하면 activity indicator를 보는것이 훨씬 더 쉽습니다. 또한 native activity indicator 가 전체 스크린에 나오는 버그도 수정했습니다. 이 두 이슈는 NOOK HD 가 다른 안드로이드 디바이스와는 다르게 그들의 default를 세팅하지 않았기 때문에 일어나는 것 같습니다. 또한 NOOK HD 에서 맵뷰가 redrawing 하지 않는 버그도 찾아냈는데요. 운 좋게도 이와 관련된 작업을 standard와 custom android 디바이스에서 하고 있었거든요. 그래서 쉽게 고칠 수 있었습니다.

그리고 여러분 중에 인터페이스에 관심 있는 분들은 다음 내용이 흥미로울 겁니다. new NOOK HD 에서 제공되는 alert box의 default theme 이 별로 좋지가 않습니다. 그래서 이 것을 Jellybean(4.1) 의 디폴트 theme 인 (“Holo Dark”) 을 사용하기로 했습니다. 디폴트 인 왼쪽 이미지와 Jellybean theme 인 오른쪽 이미지를 비교해 보세요.






daily build 929 에서는 안드로이드 디바이스에는 이 4.1 theme을 적용할 겁니다. 이것은 standard Android (Google Play) 이든 custom Android devices 이든 (Kindle HD, NOOK HD) 모두에 적용 됩니다. 이렇게 함으로서 전체 안드로이드 스펙트럼에 걸쳐 여러분 앱의 일관성이 유지되도록 할 겁니다.


위 내용은 Android 3.x 이상의 디바이스에 적용 됩니다. 만약에 여러분 앱이 더 오래된 디바이스에서 run 된다면 alerts의 older look을 제공할 겁니다. (older Kindles와 NOOKs 도 마찬가지 입니다.)



아래 왼쪽은 old one 이고 오른쪽은 new one 입니다. 보시면 OK, Cancel 버튼의 위치가 바뀐것을 보실 수 있을 겁니다.




반응형


반응형

Posted on . Written by




Corona daily build 928에 적용될 내용을 공지합니다. 저희들은 스토리보드와 widget library들의 여러 버그들을 반견하고 수정했습니다. 또한 차세대 widgets 를 위한 기반작업으로 widget library에 여러 작업들을 진행하고 있습니다.



이 라이브러리에 대한 어떤 기능들이 퇴보하거나 한 사항들은 없음을 말씀 드리고 싶습니다. 예를 들어 한 버그를 없애기 위해 그 기능을 제한한다든가 하는 것들요. 그래서 이렇게 heads up  정보를 알려 드립니다.



아래가 저희들이 수정한 버그들의 리스트들입니다.

Storyboard

  • 15040 – Storyboard overlayEnded stack overflow when calling gotoScene
  • 17590 – Storyboard immediate scene transitions cause simulator to hang when using Runtime::addEventListener in enterScene.
  • 15384 – modal overlays let touch events through if x < 0
  • 15264 – Storyboard: overlayEnded being called infinitely
  • 15781 – Storyboard transition when using letterbox and universal app
  • 15257 – Memory Leak with storyboard.printMemUsage()
  • 16540 – enterScene called multiple times
  • 14794 – effect time in storyboard.hideOverlay()

Widget

  • 16094 – tableView methods scrollToY() not taking topPadding property into account, and scrollToIndex() not working.
  • 15293 – Inconsistent size property of Button widget’s label) – By adding method setLabelSize( newSize ) method
  • 17029 – tableViews/scrollView widgets scrolling does not stop on tap
  • 16605 – listener not working in newTableView




반응형


반응형
Posted on . Written by


Corona에 몇가지 더 업데이트 된 사항이 적용됐습니다.


첫번째로 Tom 의 iOS 6 FAQ 를 권해드립니다. 애플의 iOS 6와 관련된 아주 많은 버그들이 소개돼 있습니다. 이러한 버그와 관련된 문제를 극복하기 위해 여러방면에서 노력하고 있고 여러분들이 활용할 수 있는 해결 방법들을 소개해 드리고 있습니다.


두번째로 NOOK HD 프로토타입으로 좀 더 많은 테스트를 할 수 있는 기회를 가졌습니다. NOOK team 으로부터 그 기회를 받았죠. NOOK 팀과 같이 해결해야할 많은 이슈들을 찾아냈습니다. 아마 이 문제들은 코로나 뿐만이 아니라 다른 앱에도 적용되는 문제들이 많이 있고 이를 NOOK team 이 해결하기 위해 계속 작업하고 있습니다.


  • The virtual keyboard causes a resize event, causing touches to be incorrectly offset.
  • Resuming an app sometimes shows a black screen if nothing is being animated. This happens rarely — only on specific situations (no animation occurring when app resumes from suspend) — and there’s a simple workaround that I mentioned at the bottom of Monday’s post.
  • The navigation bar half covers a fullscreen video player’s media controls.
  • The native activity indicator is not working


맵뷰에서 맵이 손가락을 따라 움직이지 않는 이슈를 찾아냈습니다. 이것이 NOOK의 문제인지 아니면 우리의 implementation 문제인지 아직 확실하지는 않습니다. (어쨋든 이 맵뷰는 다른 곳에서는 잘 작동합니다. 그리고 NOOK의 old 버전에서도 잘 작동하구요.)


그리고 WebView/WebPopup 이 JNI 와 충돌하는 것도 찾았구요. 이 이슈는 해결을 했구요. daily build 927 부터 적용이 될 겁니다.


우리는 이러한 버그들을 해결하기 위해 NOOK 팀과 계속 작업을 해 나가고 있습니다.


* * *


이 이슈들과 관련해서 이 블로그에 우리의 roadmap 과 어디까지 진행되고 있는지에 대해 공지해 드리겠습니다.


오늘 아침에 이러한 내용들과 또 CoronaGeek 관련된 내용들에 대해 얘기 했습니다. 아래 video를 보시면 그 내용을 보실 수 있습니다.



반응형