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

최근에 올라온 글

최근에 달린 댓글

최근에 받은 트랙백

글 보관함

카테고리


반응형
어제에 이어서 계속 하겠습니다.

어제까지 한 소스는

--import the table view library
local tableView = require("tableView")
display.setStatusBar( display.HiddenStatusBar )

--local background = display.newImage("hutbg.png")

function listButtonRelease( event )
    self = event.target
    local id = self.id
    print(self.id)
end

local data = {
"감자떡",
"생선구이",
"된장찌개",
"막걸리",
"소주",
"동동주"
}

local myList = tableView.newList{
    data = data,
    default="listItemBg.png",
    onRelease=listButtonRelease,
}

여기까지 였습니다.

오늘은 여기에 이미지를 넣어 보겠습니다.
아래 이미지들을 받으세요.

제가 먹고 싶은 것 들인가 봐요.


이 그림들을 다 받으셨으면 이제 코딩을 시작하겠습니다.



우선 data 부터 바꾸겠습니다.
local data = {감자떡,생선구이,된장찌개,막걸리,소주,동동주}

이것을
local data = {}

--setup each row as a new table, then add title, subtitle, and image
data[1] = {}
data[1].title = "감자떡"
data[1].subtitle = "100% 감자로 만든 강원도 특산품"
data[1].image = "potato.png"

data[2] = {}
data[2].title = "생선구이"
data[2].subtitle = "맛있는 고갈비"
data[2].image = "grillfish.png"

data[3] = {}
data[3].title = "된장찌개"
data[3].subtitle = "구수한 된장찌개"
data[3].image = "soy.png"

data[4] = {}
data[4].title = "막걸리"
data[4].subtitle = "몸에 좋은 막걸리"
data[4].image = "mak.png"

data[5] = {}
data[5].title = "소주"
data[5].subtitle = "짜르르 몸에 퍼지는 소주"
data[5].image = "soju.png"

data[6] = {}
data[6].title = "동동주"
data[6].subtitle = "고향의 맛 동동주"
data[6].image = "dong.png"

이렇게 바꿔주세요.

이렇게 하고 실행 하면 아래와 같이 에러가 납니다.

잘 보시면 아시겠지만 첫번째 줄 디폴트 이미지를 뿌려주고나서 데이터를 뿌릴때 에러가 납니다.
데이터가 여러개라서 그런데요.
이렇게 한 row에 여러 텍스트라든지 혹은 텍스트 + 이미지를 표시하시려면 callback을 사용합니다.
그리고 이 callback에 있는 object들은 하나의 group으로 관리하게 됩니다.
그럼 myList를 아래와 같이 고쳐 보세요.

local myList = tableView.newList{
    data = data,
    default="listItemBg.png",
    onRelease=listButtonRelease,
    callback = function( row )
        local g = display.newGroup()
        return g  
    end
}

이제 에러는 안 나죠?
대신 callback 안에 어떤걸 표시할지 알려주지 않아서 그냥 row 배경이미지만 6개 나옵니다.

callback을 아래와 같이 채워주세요.
    callback = function( row )
        local g = display.newGroup()
            local img = display.newImage(row.image)
            g:insert(img)
            img.x = math.floor(img.width*0.5 + 6)
            img.y = math.floor(img.height*0.5)

            local title =  display.newText( row.title, 0, 0, native.systemFontBold, 14 )
            title:setTextColor(255, 255, 255)
            g:insert(title)
            title.x = title.width*0.5 + img.width + 6
            title.y = 30

            local subtitle =  display.newText( row.subtitle, 0, 0, native.systemFont, 12 )
            subtitle:setTextColor(255,255,255)
            g:insert(subtitle)
            subtitle.x = subtitle.width*0.5 + img.width + 6
            subtitle.y = title.y + title.height + 6
        return g  
    end

우선 이미지를 표시하고 group에 insert시키고 x,y 좌표를 설정해 줍니다.
title과 subtitle도 마찬가지로 합니다.

자 이제 제대로 ListView(TableView) 가 나오죠?

여기다가 배경 화면을 은은하게 깔겠습니다.

local background = display.newImage("hutbg.png")
background.alpha = 0.4

이렇게 간단하게 이미지 하나 불러오고 투명도를 약간 줬습니다.



이번 TableView 모든 소스와 이미지 파일 입니다.

받으셔서 참고 하세요.

그럼 더 신나는 코딩을 위해 다음 시간에도 새로운 걸 배울께요....
반응형


반응형
원래 오늘은 SQLite를 할 까 했는데요.
이건 나중에 하고 우선 List View (Table View)를 할께요.
제가 했던 안드로이드에서는 이걸 List View라고 했는데 아이폰쪽에서는 Table View라고 하나봐요.

우선 List View에서는 아래와 같은 파라미터들이 사용 됩니다.
data. 값들을 가지고 있는 테이블. 각 row별로 표시될 값들
default.  row의 배경 화면이 될 이미지. touch 영역 설정 가능
backgroundColor. R,G,B 값으로 배경 색을 정할 수 있음
callback. 각 row에 데이터를 어떻게 display할까를 정의하는 함수. 테이블의 각 값들은 callback함수의 할당 된 'item' 인자와 관련되서 사용됨.
over. 터치 시 보일 이미지
onRelease(optional). row가 tap된이후에 일어날 action을 정의한 함수의 이름
top. list의 위쪽 영역. 리스트가 시작하고 다시 돌아오는 포지션
bottom. 리스트의 아래쪽 영역. 스크롤이 초과해서 진행 되면 이쪽으로 돌아온다.
cat. 각 아이템의 카테고리 값을 저장하기 위해 사용되는 테이블 키 이름.
order. cat의 option 항목. 임의의 정렬을 지정할 수 있다.

아래와 같은 메소드들이 있습니다.

* myList:addScrollBar() and myList:removeScrollBar()
스크린에 스크롤바를 넣거나 없앤다.

* myList:addOnTop(object, xVal, yVal)
리스트에 처음에 object를 추가한다. (search bar나 text 같은)

* myList:addOnBottom(object, xVal, yVal)
리스트의 마지막에 object를 추가한다.

* myList:scrollTo(yVal, timeVal)
리스트를 다이나믹하게 움직이도록 한다.

* myList:cleanUp()
리스트를 없애고 메모리를 풀어준다. 그리고 모든 이벤트 리스너들을 정지시킨다.

그럼 이제 실제로 table view (List View)를 사용해 보겠습니다.
우선 tableView.lua 파일이 있어야 됩니다.
아래 파일을 다운 받으세요.

그리고 이 파일이 있는 폴더에 main.lua 파일을 만드세요.
우선 아래와 같이 tableView.lua 파일을 사용하겠다고 선언합니다.

--import the table view library
local tableView = require("tableView")

일단 여기서 편의상 아이폰의 statusbar를 없앨께요.
display.setStatusBar( display.HiddenStatusBar )

아주 간단한 테이블을 만들어 보겠습니다.
local data = {
"감자떡",
"생선구이",
"된장찌개",
"막걸리",
"소주",
"동동주"
}

이 데이타들을 아래와 같이 해 보세요.
그리고 tableView.lua에 있는 List View 메소드를 선언합니다.
local myList = tableView.newList{data=data}

실행해 보면 이렇게 나옵니다.
글자를 누르고 드래그 하듯이 움직이면 터미널처럼 touch: began moved, ended 가 나옵니다.
이걸 보면 touch 리스너가 달려있고 began,moved,ended 이벤트시 어떤 동작을 지정할 수 있다는 걸 알 수 있겠죠?
그리고 드래그를 하지 않고 그냥 tap 만 하면 아래와 같이 나옵니다.

터미널에 보면 onRelease 필드가 nil 이라고 하면서 에러메세지가 나오죠?

그럼 이 newList안에 아래와 같이 추가 하겠습니다.
onRelease=listButtonRelease,
그리고 그 위쪽에 listButtonRelease 함수를 아래와 같이 만들어 주세요.
function listButtonRelease( event )
    self = event.target
    local id = self.id
    print(self.id)
end

이렇게 하면 각 row를 클릭하면 에러가 나지 않고 그 id가 터미널에 출력 됩니다.
여기까지 하면 가장 간단한 Table View(List View)가 완성 된 겁니다.

좀 더 그럴듯 하게 리스트 뷰를 만들어 볼까요?
아래 이미지를 다운 받으세요.

그리고 myList 에 default="listItemBg.png", 를 추가해 보세요.
local myList = tableView.newList{
    data = data,
    default="listItemBg.png",
    onRelease=listButtonRelease,
}

그럼 이와 같이 그럴듯한 ListView (TableView) 가 됩니다.

자 오늘은 코로나 SDK에서 ListView(TableView) 표시하기 가장 기초적인 부분을 다뤄봤습니다.

다음시간에는 이 글 첫번째에서 소개했던 여러 파라미터들하고 메소드들을 사용해서 좀 더 다이나믹한 효과를 내는 방법을 알아보겠습니다.


반응형


반응형
오늘 공부할 Tables and Arrays 는 양이 매우 짧습니다.
그리고 제가 이 글을 회사에 1시간 일찍 출근해서 쓰는데요.
테이블 관련한 예제는 금방 만들어내기도 힘들고 해서 따로 예제를 올려드리지 못 할것 같구요.
그래서 오늘은 그냥 원문을 번역해 볼까 합니다.

아래 주소에서 복사해 왔습니다.
http://developer.anscamobile.com/content/tables-arrays

Tables and Arrays

Tables in Lua implement associative arrays. That is, they can be indexed not just with numbers, but also with strings or any other value of the language, except nil.

루아의 테이블은 배열 역할을 한다. 숫자값으로만이 아니라 스트링이나 다른 문자로도 인덱스 할 수 있다는 의미다. 단 nill은 제외한다.

This library provides generic functions for table manipulation. It provides all its functions inside the table table.

이 라이브러리는 테이블을 다루는 기능을 제공한다. 이 기능들은 table 이라는 테이블을 통해서 제공한다.

Most functions in the table library assume that the table represents a numerically-indexed array or list. For these functions, when we talk about the "length" of a table we mean the result of the length operator.

테이블 라이브러리의 대부분의 기능들은 숫자로 인덱스된 배열이나 리스트의 기능을 수행한다. 이 기능과 관련 우리가 테이블의 length라고 말하는 것은 length 오퍼레이터에 의해 받아온 결과를 말하는 것이다.

table.concat (table [, sep [, i [, j]]])

Given an array where all elements are strings or numbers, returns table[i]..sep..table[i+1] ... sep..table[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is the length of the table. If i is greater than j, returns the empty string.

테이블 내에 있는 문자나 숫자들을 table[i]..sep..table[i+1] ... sep..table[j] 식으로 리턴한다.  sep의 디폴트 값은 empty string이고 i는 1, j 는 테이블의 length이다. i가 j 보다 크면 empty string을 반환한다.

table.insert (table, [pos,] value)

Inserts element value at position pos in table, shifting up other elements to open space, if necessary. The default value for pos is n+1, where n is the length of the table, so that a call table.insert(t,x) inserts x at the end of table t; however, if you are inserting at the end of the table, it is faster to use the length operator: t[#t + 1] = x.

테이블의 pos 위치에 해당 value를 삽입한다. 필요하면 다른 값들은 위치를 이동하게 된다. pos의 디폴트는 n+1이다. (n은 테이블의 length). 예를 들어 table.insert(t,x)라고하면 x를 테이블 t의 맨 마지막 요소로 삽입하게 된다.

table.maxn (table)

Returns the largest positive numerical index of the given table, or zero if the table has no positive numerical indices. (To do its job this function does a linear traversal of the whole table.)

해당 테이블에 있는 값들의 갯수를 리턴하게 된다. 값이 없으면 0를 리턴한다.

table.remove (table [, pos])

Removes from table the element at position pos, shifting down other elements to close the space, if necessary. Returns the value of the removed element. The default value for pos is n, where n is the length of the table, so that a call table.remove(t) removes the last element of table t.

pos위치에 있는 값 remove한다. 필요하면 다른 값들의 위치를 이동한다. 그리고 remove된 값을 리턴한다. pos의 디폴터 값은 n이다. table.remove(t) 하면 테이블 t의 마지막 값이 remove된다.

table.sort (table [, comp])

Sorts table elements in a given order, in-place, from table[1] to table[n], where n is the length of the table. If comp is given, then it must be a function that receives two table elements, and returns false when the first is less than the second (so that not comp(a[i+1],a[i]) will be false after the sort). If comp is not given, then the standard Lua operator < is used instead.

주어진 조건으로 테이블 내의 값들을 sorting한다. comp 값이 주어지면 두개의 테이블 값들을 받게 된다. 만약 첫번째가 두번째보다 적으면 false를 리턴한다. comp가 주어지지 않으면 Lua의 standard operator 인 < 가 사용된다.

The sort algorithm is not stable; that is, elements considered equal by the given order may have their relative positions changed by the sort.

소팅은 주어진 조건에 맞게 이루어지므로 항상 조건이 고정돼 있는 것은 아니다.

==================

코로나 SDK에서의 테이블(배열)에 대해 훑어 봤습니다.

다음 주제는 Math인데요. 이것도 Math관련 함수를 죽 정리해 놓은것 뿐이니까 이것도 간단히 정리하겠습니다.

원문은 아래 주소에 있습니다.

http://developer.anscamobile.com/content/math

This library is an interface to the standard C math library. It provides all its functions inside the table math.

이 라이브러리는 standard C 의 math 라이브러리를 사용한다. 이 기능들은 math라는 테이블을 통해 제공된다.

math.abs (x)

Returns the absolute value of x.

x의 절대값을 리턴한다.

math.acos (x)

Returns the arc cosine of x (in radians).

x의 원호 코사인을 리턴한다.

math.asin (x)

Returns the arc sine of x (in radians).

x의 원호 사인을 리턴한다.

math.atan (x)

Returns the arc tangent of x (in radians).

x의 원호 탄젠트를 리턴한다.

math.atan2 (y, x)

Returns the arc tangent of y/x (in radians), but uses the signs of both parameters to find the quadrant of the result. (It also handles correctly the case of x being zero.)

y/x의 원호 탄젠트를 리턴한다.

math.ceil (x)

Returns the smallest integer larger than or equal to x.

x보다 크거나 같은 최소값의 인티저를 리턴한다.

즉 소수값을 자연수로 고쳐준다.

math.cos (x)

Returns the cosine of x (assumed to be in radians).

x의 코사인을 리턴한다.

math.cosh (x)

Returns the hyperbolic cosine of x.

x의 쌍곡선 코사인을 리턴한다.

math.deg (x)

Returns the angle x (given in radians) in degrees.

x의 각도를 리턴한다.

math.exp (x)

Returns the value ex.

ex의 값을 리턴한다.

math.floor (x)

Returns the largest integer smaller than or equal to x.

x보다 작거나 같은 수 중 가장 큰 자연수를 리턴한다.

math.fmod (x, y)

Returns the remainder of the division of x by y that rounds the quotient towards zero.

x를 y로 나눈 값의 나머지를 리턴한다.

math.frexp (x)

Returns m and e such that x = m2e, e is an integer and the absolute value of m is in the range [0.5, 1) (or zero when x is zero).

x=m2e 같은 m,e 를 리턴한다. e는 m의 자연수이고 절대값이다. m은 [0.5,1] 사이에 있다.

math.huge

The value HUGE_VAL, a value larger than or equal to any other numerical value.

다른 수들보다 큰 수.

math.ldexp (m, e)

Returns m2e (e should be an integer).

m2e를 리턴한다. (e는 자연수라야 한다.)

math.log (x)

Returns the natural logarithm of x.

x의 로그를 리턴한다.

math.log10 (x)

Returns the base-10 logarithm of x.

x의 -10로그를 리턴한다.

math.max (x, ...)

Returns the maximum value among its arguments.

괄호 안의 값 들 중 가장 큰 수를 리턴한다.

math.min (x, ...)

Returns the minimum value among its arguments.

괄호 안의 값들 중 가장 작은 수를 리턴한다.

math.modf (x)

Returns two numbers, the integral part of x and the fractional part of x.

integral x와 팩토리얼 x 두개 값을 리턴한다.

math.pi

The value of pi.

파이 값

math.pow (x, y)

Returns xy. (You can also use the expression x^y to compute this value.)

xy를 리턴한다.

math.rad (x)

Returns the angle x (given in degrees) in radians.

각도 x의 라디안 값을 리턴한다.

math.random ([m [, n]])

This function is an interface to the simple pseudo-random generator function rand provided by ANSI C. (No guarantees can be given for its statistical properties.)

랜덤 값을 구한다.

When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, math.random returns a uniform pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, math.random returns a uniform pseudo-random integer in the range [m, n].

m,n이 주어지지 않으면 디폴트로 (0,1)의 값으로 계산한다. m이 주어지면 [1,m]으로 계산한다. m,n모두 주어지면 범위는 [m,n]이 된다.

math.randomseed (x)

Sets x as the "seed" for the pseudo-random generator: equal seeds produce equal sequences of numbers.

math.sin (x)

Returns the sine of x (assumed to be in radians).

x의 사인 값을 리턴한다.

math.sinh (x)

Returns the hyperbolic sine of x.

x의 쌍곡선 사인 값을 리턴한다.

math.sqrt (x)

Returns the square root of x. (You can also use the expression x^0.5 to compute this value.)

x의 square root 값을 리턴한다.

math.tan (x)

Returns the tangent of x (assumed to be in radians).

x의 탄젠트 값을 리턴한다.

math.tanh (x)

Returns the hyperbolic tangent of x.

x의 쌍곡선 탄젠트 값을 리턴한다.

===========================

조금 골치 아픈 공식들이죠?
저는 spin the bottle  게임 만들 때 테이블 관련해서는 table.maxn(table)을 사용했었구요. 병돌리기 위해 두 점 사이의 각도를 구하기 위해 radian구하는 공식과 각도 구하는 공식 등을 사용했는데요.
그건 위에 있는 함수를 사용하지는 않고 그냥 공식이 있는 함수를 만들어서 사용했었습니다.
위에 있는 수학 함수 중에는 math.ceil 이라던가 math.random 정도만 자주 쓰는 거 같애요.
아무래도 제 수학 실력과 프로그래밍 실력이 딸려서 그러겠죠?
게임 만들 때 이런 수학 함수 잘 사용하면 좋다고 그러던데...

오늘은 Corona SDK 에서의 배열인 table과 standard C 언어에서 가져온 코로나 SDK 의 수학함수에 대해서 알아봤습니다.

다음시간엔 Corona에서의 SQLite 데이타 베이스 사용법에 대해 다루겠습니다.

감사합니다.

반응형

Data and Files 첫번째 시간 - String -

2011. 10. 11. 22:47 | Posted by 솔웅


반응형

코로나에서 String을 다루는 방법이 첫번째 살펴볼 주제있니다.
Data and Files 에는 String, Table (Array), Math, Database (SQLite), Files, Crypto 등의 이슈가 있습니다.

첫번째 시간으로 String 인데요.
오늘 배울 부분들을 제가 연습하느라고 샘플 코드를 작성해서 공부했었는데요.
이것을 올려드릴께요.

오늘 글을 보면서 이 샘플코드에 직접 이것 저것 바꿔보면서 출력되는 것을 보면 좀 더 이해가 쉬울것 같습니다.


이 소스를 돌려보면 터미널에 결과값들이 찍힙니다.

String Manipulation

String Library 는 string 을 다루는 함수들을 제공해 줍니다.
루아에서 스트링을 인덱스 할 때 첫번째 문자는 포지션 1 입니다. (C 나 자바와 같이 0이 아닙니다.)
이 숫자는 마이너스도 허용합니다. 이 경우 스티링 맨 마지막에서부터 뒷쪽으로 검색하게 됩니다.
그러니까 마지막 문자가 -1이 됩니다.
String library는 테이블 스트링으로 이러한 함수기능을 제공합니다. 또한 스트링 테이블의 _index 필드에 스트링에 대한 메타테이블도 세팅 되어 있습니다. 이것을 개발자들은 객체 지향 개념으로 이용할 수 있습니다. 예를 들어 string.byte(s,i)는 s:byte(i)로 바꿔도 결과 값은 같습니다..

string.byte(s[,i[,j]])
문자 s[i],s[i+1] 문자의 내부 numerical code가 리턴 됩니다. 디폴트는 1 입니다. j의 디폴트 값은 i값입니다. 이 numerical code는 서로 다른 플랫폼간의 이동 및 공유가 되지 않습니다.

string.char(...)
0 이상의 integer를 Receive 받습니다. argument 숫자 길이 만큼 스트링이 리턴 됩니다. 근데 이건 제가 이것 저것 넣어 봤는데.. 결과값이 안 나오더라구요. 혹시 여러분 중에 무엇인지 아시는 분 댓글 부탁드립니다.

string.find(s,pattern[,init[,plain]])
스트링 s내에서 패턴에 맞는 첫번째를 찾습니다. 매치 되는 것을 찾으면 s의 인텍스를 리턴합니다. 없으면 nil을 리턴합니다. 세번째 옵션으로 있는 init 검색을 시작할 위치를 나타냅니다. 디폴트는 1이고 마이너스 값도 올 수 있습니다. 네번째 plain이 false일 경우 pattern matching 이 turn off 됩니다. plain이 있으면 init은 반드시 있어야 됩니다.
패턴이 검색 되면 이 검색된 패턴이 리턴됩니다.

string.format(formatstring,...)
매치 되면 미리 formatted 된 버전이 리턴됩니다. format string은 일반 C 함수의 printf 와 유사한 기능이 있습니다. 다른 점은 옵션들 입니다. l,L,n,p,h 는 support 되지 않습니다. 그리고 q 라는 옵션이 제공 됩니다. q는 루아 번역기가 읽기 쉬운 형태로 스트링을 포맷합니다.

예를 들어
  string.format('%q', 'a string with "quotes" and \n new line')

      "a string with \"quotes\" and \
      new line"
로 포맷 됩니다.

c, d, E, e, f, g, G, i, o, u, X, and x 옵션은 argument로 숫자가 들어가게 됩니다. 그리고 q와 s 는 문자가 들어가게 됩니다.

샘플코드에 이런 패턴들을 다 넣어 봤습니다. 결과값들이 다 다르게 나오는데 어떤건지 잘 와 닿지가 않네요. 실제 상황에서는 어떻게 써 먹는 건지도 모르겠구요.
제 프로그래밍 경력이 많지 않고 또 깊지 않아서 잘 모르는 것 같습니다. 혹시 이걸 어느 경우에 써 먹을 수 있는지 아시는 분들 조언 부탁 드립니다.

string.gmatch(s.pattern)
iterator 함수가 리턴 됩니다. 검색된 곳의 그 다음 capture 가 리턴 됩니다. 패턴이 없으면 전체가 매치된 것으로 나옵니다.
예를 들어
     s = "hello world from Lua"
     for w in string.gmatch(s, "%a+") do
       print(w)
     end
이것은 스트링 s의 모든 것을 iterate 합니다. 그리고 한줄당 하나씩 프린트 됩니다.
아래 예제는 key=value 의 모든 쌍들을 collect 합니다.
     t = {}
     s = "from=world, to=Lua"
     for k, v in string.gmatch(s, "(%w+)=(%w+)") do
       t[k] = v
     end

샘플코드에 있으니까 직접 결과 값을 확인 해 보세요.

string.gsub(s,pattern,repl[,n])
이건 해석하거나 설명하기 좀 복잡하네요.
아래 샘플을 보시죠.
     x = string.gsub("hello world", "(%w+)", "%1 %1")
     --> x="hello hello world world"
     
     x = string.gsub("hello world", "%w+", "%0 %0", 1)
     --> x="hello hello world"
     
     x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
     --> x="world hello Lua from"
     
     x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
     --> x="home = /home/roberto, user = roberto"
     
     x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
           return loadstring(s)()
         end)
     --> x="4+5 = 9"
     
     local t = {name="lua", version="5.1"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     --> x="lua-5.1.tar.gz"

좀 헛갈리네요. 저도 소스를 잘 분석해 봐야겠어요.
뭐 그렇게 쓸일이 있을까? 싶지만요.

아래 함수들은 많이 쓰일 것 같네요.

string.len(s)
s의 length를 반환합니다. 아무 값도 없으면 length는 0 입니다. embeded 0도 카운트 됩니다. 그래서 "a\000bc\000" 의 length 는 5 입니다.

string.lower(s)
스트링을 모두 소문자로 바꿉니다.

string.match(s,pattern[,init])
패턴에 맞는 첫번째 조건을 찾습니다. 찾으면 찾은 값을 리턴해 줍니다. 없으면 nil을 리턴해 줍니다. 패턴이 아무것도 없으면 모든 문자를 리턴합니다. 세번째 옵션값인 init 은 검색을 시작할 위치를 표시합니다.

string.rep(s,n)
n개의 수만큼 s를 표시합니다.

string.reverse(s)
s의 거꾸로 된 값을 리턴합니다.

string.sub(s,i[,j])
i 번째에서 j번째까지의 문자를 표시합니다. i,j는 마이너스가 될 수 있습니다. j가 없으면 -1이 디폴트 입니다. string.sub(s,1,j)는 j에 -1값이 들어가고 string.sub(s,-i)는 length i의 suffix를 리턴합니다.

string.upper(s)
모든 문자를 대문자로 바꿔 줍니다.

### Patterns

Character Class

캐릭터 클래스는 문자들의 세트를 표현합니다.

. (dot) 모든 문자를 표시한다.
%a : 모든 문자를 표시한다.
%c : control character들을 표시한다.
%d : 숫자들을 표시한다.
%l : 소문자를 표시한다.
%p : 기능 문자들을 표시한다.
%s : 스페이스를 표시한다.
%u : 대문자를 표시한다.
%w : 알파뉴메릭을 표시한다.
%x : 헥사디시멀 숫자들을 표시한다.
%z : 0으포 표시되는 문자를 출력한다.
%x (x가 알파벳 문자가 아닌 경우) 캐릭터 x를 표시한다.
[set] : 세트 안의 모든 문자들의 집합 클래스를 표시한다.
%w 나 _%w는 모든 알파뉴메릭 문자와 언더스코어를 표시하고 [0-7] 은 8진수를 [0-7%l%-]은 8진수와 소문자와 -를 표시한다.
[^set] : 위에 해당하는 것의 반대로 표시한다.
 %a , %c 등으로 표시 되는데 이것을 대문자로 표시하면 그것의 반대로 표시한다.
예를 들어 %S 의 경우는 non-space 문자들을 출력한다.

pattern item
패턴 아이템은 아래와 같은 조건을 갖는다.
- 클래스의 싱글 캐릭터에 매치 되는 싱글 캐릭터 클래스
- *로 시작하는 싱글 캐릭터 클래스 0번 이상 반복되는 캐릭터. 이 반복되는 아이템들은 가능한 긴 시퀀스에 매치 된다.
- +로 시작하는 캐릭터. 1번 이상 반복되는 캐릭터. 이 반복되는 아이템들은 가능한 가장 긴 시퀀스에 매치된다.
- -로 시작하는 캐릭터. 0번 이상 반복되는 캐릭터. 이 반복되는 아이템들은 가능한 가장 짧은 시퀀스에 매치된다.
- ? 로 시작하는 캐릭터.
- %n n은 1~9 사이이다. n번째 무자를 말한다.
- %bxy x와 y 는 두개의 구별되는 문자들이다. x에서 시작해서 y에서 끝난다.

이렇게 코로나의 String 다루는 법을 정리했습니다.

다음엔 코로나에서 사용하는 배열인 tables 에 대해 알아보겠습니다.
반응형

코로나 애니메이션 Sprite Sheets.

2011. 10. 8. 20:53 | Posted by 솔웅


반응형
이 Sprite Sheets 는 조금 독특한 것 같습니다.
이전에 플래시 같은데서 썼던 개념인지 어떤지 모르겠지만.
여러 동작을 표현한 하나의 이미지 파일을 불러와서 특정 구역을 지정하면.
코로나는 그 특정구역별로 하나씩 이미지로 인식합니다.
그리고 그것을 Frame 단위로 display 하면서 애니메이션 효과를 냅니다.

글로만 쓰니까 잘 모르겠죠?

이렇게 하나의 이미지파일에 여러 동작을 그려 넣습니다.
개발자는 Sprite Sheets를 이용해서 저 하나하나별로 구역을 나눕니다.
그러면 코로나는 그 개별 구역을 개별 이미지로 인식해서 애니메이션을 연출합니다.

이렇게 하면 하나의 이미지 파일을 불러오니까 메모리 절약 효과도 있겠네요.
Loading 시간도 줄일 수 있겠구요.
그 외에 어떤 장점이 있는지 (MovieClips와 비교해서) 한번 알아보죠.

Corona 는 두가지 형태의 Sprite Sheets 를 제공합니다.
사이즈와 포지션이 일정한 것과 사이즈와 포지션이 일정하지 않은 것이 있습니다.

위에 있는 이미지는 uniformly-sized 프레임 입니다. (이 이미지는 코로나 샘플 파일의 sprites 폴더에 보면 있습니다.)

두번째는 외부 데이터 파일에서 그 크기와 위치를 지정하는 건데요. 이건 ZwoptexTexturePacker 같은 툴을 이용해서 만듭니다.

이 그림은 위에서 말한 샘플 코드 sprites 폴더 아래 있습니다. 그리고 외부 데이터 파일은 uma.lua 이름으로 있습니다.

코로나는 애니메이트 된 캐릭터에 대한 순서나 거꾸로 움직이기 등의 메소드를 제공하는 constructor들을 제공합니다. 이것은 frame-base 가 아니라 time-base로 주로 이용 됩니다.
샘플 코드 중 JungleScene 샘플 을 보시면 퓨마의 움직임하고 사람의 움직임이 약간 다릅니다. 정확히 속도가요.  이것은 두 객체가 다른 frame rate 으로 움직이기 때문입니다.


위에서 말한대로 이 JungleScene은 uniformly-sized frames를 사용했고 HorseAnimation 은 non-uniformly sized and positioned 방법을 사용했습니다.

혹시 다음에 시간 되면 이 샘플 소스들도 자세히 살펴 보도록 하겠습니다.
우선은 이 Sprite Sheets에 대한 진도를 계속 나가겠습니다.
주의 할 점은 이미지 사이즈가 디바이스의 맥시멈 사이즈를 넘으면 그림이 표시 되지 않습니다.
이 정보는 system.getinfo("maxTextureSize")를 통해서 얻을 수 있습니다.

이 sprite 효과를 사용하려면 아래와 같이 합니다.
require "sprite"
그리고 sprite.newSpriteSheet , sprite.newSpriteSheetFromData 이 두 함수는 새로운 sprite sheets를 생성합니다.

spriteSheet = sprite.newSpriteSheet("image.png", fromeWidth, fromeHeight)
이 코드는 uniformly-sized frames sprite sheet를 생성합니다.
spriteSheet = sprite.newSpriteSheetFromData("image.png", spriteData)
이 코드는 외부 파일에서 지정한 frame size와 포지션을 적용할 때 이용합니다.

외부 데이터 파일은 아래와 같은 형식으로 돼 있습니다.
-- test.lua
module (...)
 
function getSpriteSheetData()
 
        local sheet = {
                frames = {
                        {
                        name = "01.png",
                        spriteColorRect = { x = 38, y = 38, width = 50, height = 50 },
                        textureRect = { x = 2, y = 70, width = 50, height = 50 },
                        spriteSourceSize = { width = 128, height = 128 },
                        spriteTrimmed = true,
                        textureRotated = false
                        },
 
                        {
                        name = "02.png",
                        spriteColorRect = { x = 38, y = 36, width = 50, height = 52 }, 
                        textureRect = { x = 2, y = 242, width = 50, height = 52 }, 
                        spriteSourceSize = { width = 128, height = 128 }, 
                        spriteTrimmed = true,
                        textureRotated = false
                        },
                }
        }
        return sheet
end

위 소스는 이미지 파일을 두개의 이미지로 나누는데요. 위치와 사이즈 등을 설정하도록 돼 있습니다.

이 파일 (test.lua) 을 이용하려면 아래와 같이 합니다.
local sprite = require("sprite")
-- In this case, test.lua is exported from Zwoptex
local test = require("test.lua")
-- Method defined by test.lua that returns table data defining the sprites
local spriteData = test.getSpriteSheetData()
-- Load the sprite sheet in test.png using the sprite definitions from spriteData
local spriteSheet = sprite.newSpriteSheetFromData( "test.png", spriteData )

이 경우 밑에 두 줄은 아래와 같이 합칠 수도 있습니다.
local spriteSheet = sprite.newSpriteSheetFromData( "test.png", test.getSpriteSheetData() )

하나의 기본 이미지에 여러 캐릭터들이 들어 있을 때 관련 된 캐릭터들만 따로 세팅을 할 수 있습니다.
spriteSet = sprite.newSpriteSet(spriteSheet, startFrame, frameCount)

sprite에 추가 할 수도 있습니다.
sprite.add(spriteSet, "startFlying", startFrame,frameCount, time,[loopCount])

sprite sheet를 없애고 메모리에서 release시키려면 아래와 같이 합니다.
sprite.Sheet:dispose()
이 경우 removeSelf()도 불려 집니다.

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

si = sprite.newSprite( spriteSet )
sprite의 새로운 인스턴스를 만듭니다. 스프라이트는 Display Object입니다. 스프라이트는 한번에 하나의 애니메이션 시퀀스를 플레이 합니다.

si:prepare([sequence])
현재 플레이 되고 있는 애니메이션 시퀀스를 정지합니다. 현재 시퀀스를 바꿀수도 있고 첫번째 시퀀스의 프레임으로 갈 수도 있습니다.

si:play()
애니메이션 시퀀스를 플레이 합니다.현재의 프레임에서 시작합니다.
 
si:pause()
애니메이션을 정지시킵니다. 맨 마지막 display됐던 프레임이 남아 있게 됩니다.
 
si:addEventListener("sprite", listener)
스프라이트 인스턴스 애니메이션이 이벤트를 일으켰을 때 리스너에 통보 합니다.

이벤트는 리스너에게 다음과 같은 필드들을 전달합니다.

event.sprite
이벤트를 발생한 스프라이트; event.sprite.sequence 같이 현재 스프라이트의 프로퍼티들에 접근 할 수도 있습니다.

event.phase
phase는 다음과 같은 요소들이 있습니다.

"end" - 플레이를 멈춘다.
"loop" - 순차적으로 아니면 거꾸로 스프라이트를 루핑합니다.
"next" - 스프라이트의 다음 프레임이 플레이 됩니다.

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

여기까지 코로나의 Sprite Sheets로 애니메이션을 구현하는 방법에 대해 알아봤습니다.
근데 뭔가 어렵고 불편한 것 같은 느낌이 드네요.
뭔가 숙달이 안 되서 그런건가?

한번 다양한 방법으로 실습을 해 봐야겠습니다.
샘플을 보던지... 내가 직접 만들던지... 어쨌든 그래야 정확하게 내것이 되겠는데요.

일단 다음 주제는 데이터와 파일 단원에 들어가겠습니다.
SQLite (DB) 와 파일 (file) control도 하게 될 텐데요.

이 주제로 강좌를 진행하다가 sprite sheet 직접 실습해보고 sprite sheet 사용  방법을 알기 쉽게 한번 더 다룰께요.

그럼.......
반응형


반응형
코로나 SDK 는 어도브 플래시 (adobe flash)  회사 다니던 사람이 나와서 만든 툴이예요.

아래 링크를 클릭하시면 코로나 SDK를 개발한 Carlos가 자신들이 고생하면서 성장한 얘기에 대해 기술해 놨습니다.
최근 adobe 사에서도 기존의 Flex를 확장해서 모바일 앱 개발 툴을 만든다고 하던데 ..
우리의 Carlos가 이 글을 통해서 adobe (어도비) 사와의 경쟁에서도 자신있다라고 쓰고 있네요.
http://blog.anscamobile.com/2011/10/thoughts-on-flash/

코로나의 강력한 Physics Engine은 Box2D를 사용해서 구현한거고.. 이것에 대해서는 우리가 이미 살펴 봤습니다.

그러면 이제 코로나의 Animation and Motion 기능은 어떤가 한번 살펴 보죠.

이 애니메이션 기능도 아주 강력합니다. 플래시 개발하던 사람이 개발했으니까 당연하겠죠?
우선 Transitions (통과, 이동)에 대해 보겠습니다.
이 Transition Library는 여러분들에게 단 한줄로 쉽게 애니메이션을 만들 수 있게 해 줍니다.
단지 alpha 프로퍼티를 이용해서 객체를 천천히 사라지게 할 수도 있습니다.
transition.to 메소드로 껏을 할 수 있는데요.
한번 보죠.
local square = display.newRect( 0, 0, 100, 100 )
square:setFillColor( 255,255,255 )
 
local w,h = display.contentWidth, display.contentHeight
 
local square = display.newRect( 0, 0, 100, 100 )
square:setFillColor( 255,255,255 )
 
local w,h = display.contentWidth, display.contentHeight
 
-- (1) move square to bottom right corner; subtract half side-length
--     b/c the local origin is at the square's center; fade out square
transition.to( square, { time=1500, alpha=0, x=(w-50), y=(h-50) } )
 
-- (2) fade square back in after 2.5 seconds
transition.to( square, { time=500, delay=2500, alpha=1.0 } )

위 소스를 main.lua로 저장하고 이를 시뮬레이터에서 실행 합니다.
첫줄은 사각형을 그리고 다음에 흰색으로 채웁니다.
w,h 변수에 contentWidth와 contentHeight 값을 넣구요.
그 다음에 두번째 사각형을 그리고 흰색으로 채웁니다.

그리고 transition.to로 이 사각형을 대각선으로 움직입니다.
(이때 사각형은 두번째 지정한 사각형이 됩니다.)
이때 alpha값은 0으로 놨으니까 점점 투명해 지겠죠?

그리고 두번째로 transition.to를 적용했는데요.
이것은 점차 투명도가 없어지는 겁니다.
그러니까 0이었던 alpha 값이 정해진 time과 delay에 맞춰 점차 1로 변하게 됩니다.

실행하면 아래와 같은 화면이 나옵니다.


사각형 하나는 좌상단에 그대로 있고 또 다른 사각형이 좌상단에서 우하단으로 점점 흐려지면서 움직입니다.
다 움직이고 난 후 이 사각형은 점차 뚜렷해 집니다. (delay를 사용했기 때문이죠)

아주 간단한 코드로 애니메이션을 만들었죠?

syntax (신택스)는 아래와 같습니다.
transition.to( target, params )
이 params에 time, alpha, x, y, delay 등이 들어간 겁니다.
정확하게 아래의 파라미터들이 사용가능합니다.
params.time : 밀리세컨드 단위로 동작이 일어나는 시간을 나타냅니다. 디폴트는 500(0.5초) 입니다.
params.transition : 디폴트는 easing.linear 입니다. 이 Easing 에 대해서는 잠시 후 다룰 겁니다.
params.delay : 동작 (코로나에서는 이것을 tween이라고 표현합니다.) 이 시작될 때까지의 시간입니다. 이 시간만큼 기다렸다가 실행 됩니다.
params.delta : boolean값으로 디폴트는 nil로 false를 말합니다.
params.onStart : tween이 시작 되기 직전에 불려집니다. 리스너가 이를 캣치 해서 어떤 동작을 작동 시킬 수 있습니다.
params.onComplete : tween이 끝난 직후 불려집니다. 리스너가 이를 캣치 해서 어떤 동작을 작동 시킬 수 있습니다.

transition.form(target,params)는 transition.to와 비슷한 역할을 합니다. 단지 시작하는 프로퍼티 값이 params table에 특정 지어지고 final값들은 target prior에 상응하는 값을 가진다고 돼 있는데.. 저도 잘 이해가 안 되네요.

아래코드를 보세요.
local rect = display.newRect(  0, 0, 100, 100 )
rect:setFillColor(255,255,255)
 
local tMax =1000+system.getTimer()
local function fadeOut(event)
  local t = event.time
  if t < tMax then
    rect.alpha = 1 - t/tMax
  else
    rect.alpha = 0
    -- done, so remove listener
    Runtime:removeEventListener("enterFrame", fadeOut )
  end
end
 
-- Add listener to begin animation
Runtime:addEventListener( "enterFrame", fadeOut )

사각형을 만들고 흰색으로 채우고
tMax에 현재시각보다 1초 더 많은 시각을 대입하고
fadeOut 함수에는 점차 alpha값을 주는 로직으로 돼 있습니다.
리스너는 Runtime리스너로 enterFrame시 fadeOut을 실행하도록 돼 있구요.
이 리스너는 함수 안에서 alpha 값이 0이 되면서 remove 됩니다.

이와 똑같은 기능을 아래와 같이 단 3줄로 할 수 있습니다.

local rect = display.newRect( 0, 0, 100, 100 )
rect:setFillColor(255,255,255)
 
transition.to(rect, {time=1000, alpha=0})

이제 transition.to의 간편함을 알겠네요.

이외에 아래 기능들이 있습니다.
transition.cancel( tween )    Cancels the tween.
transition.dissolve( src, dst, duration, delayDuration )

이제 아까 살펴 보기로 했던 Easing을 볼까요?

easing library는 transition library에 의해 사용되는 interpolation 함수들의 collection 입니다.
아래와 같은 종류들이 있구요.

easing.linear( t, tMax, start, delta )
easing.inQuad( t, tMax, start, delta )
easing.outQuad( t, tMax, start, delta )
easing.inOutQuad( t, tMax, start, delta )
easing.inExpo( t, tMax, start, delta )
easing.outExpo( t, tMax, start, delta )
easing.inOutExpo( t, tMax, start, delta )

start와 final 값을 만들기 위해 여러분만의 easing 함수를 만들 수도 있습니다.
함수안의 인수들은 다음과 같습니다.
t : transition이 시작된 이후부터 진행된 시간
tMax : transition의 duration
start : 시작 값
delta : 변화 값 (final value = start + delta)

무비클립 (Movieclips)

여러장의 이미지들을 테이블에 넣고 이를 순차적으로 보여주면서 애니메이션 효과를 내는 것이 무비크립입니다.
저는 두 물체가 부딪혔을 때 폭발하는 애니메이션을 이 무비클립으로 효과를 줬었습니다.
플래시 하시는 분들은 플래시에서 구현한 애니메이션을 코로나로 표현할 때 유용할 것 같습니다.
이 movieclip library에 대해 자세히 살펴 보겠습니다.
이 기능을 이용하려면 movieclip.lua 파일이 있어야 합니다.

위 파일을 다운 받으시면 됩니다.
한 400줄 되는 코드인데요. 각자 이 코드를 분석해 보시면 아마 많은 도움이 되실 겁니다.

우선 이 파일을 쓰려면 require()문을 써야합니다.
local movieclip = require("movieclip")
자 그럼 하나 만들어 볼까요?

이 그림들을 이용할 겁니다.

저도 이 그림들을 어떤 샘플 파일에서 받은 것 같은데요.

이름은 explode1.png ~ explode9.png 로 합니다.



local movieclip = require("movieclip")

function bombAni()
    bombAnim = movieclip.newAnim{"images/explode1.png", "images/explode2.png", "images/explode3.png", "images/explode4.png", "images/explode5.png",
    "images/explode6.png","images/explode7.png", "images/explode8.png", "images/explode9.png"};
    bombAnim.x = 200;
    bombAnim.y = 200;

    bombAnim:play{ startFrame=1, endFrame=9, loop=5, remove=true }
end

bombAni()


이 코드를 main.lua 파일에 넣고 실행 합니다.


첫번째 줄은 movieclip 파일을 require 하는 부분입니다.


그 다음 bombAni() 함수가 애니메이션 효과를 주는 부분 입니다.


movieclip.newAnim{} 에다가 사용할 그림들을 다 넣습니다.

그리고 이 값들을 bombAnim이라는 변수에 넣습니다.


bombAnim.x , y 좌표를 줍니다. (폭발하는 위치가 됩니다.)


그리고 bombAnim:play{} 로 애니메이션을 실행 시킵니다.


startFrame은 첫번째 그림부터 한다는 것이고 9번째 그림에서 끝나게 됩니다.

그리고 이 폭발 장면은 5번 반복하게 되고 폭발 장면이 끝난 후에는 remove 됩니다.



이 코드를 실행 시키면 폭발하는 장면이 그럴 듯 하게 나옵니다.

remove 파라미터를 false로 하면 마지막 이미지가 없어지지 않고 계속 남아 있습니다. 디폴트는 false입니다.

그리고 loop 부분을 넣지 않으면 디폴트 값으로 0이 들어가는데요. 이 경우 무한정으로 루프가 반복됩니다.

그리고 startFrame과 endFrame을 적당히 정해주면 중간 이미지부터 시작하거나 끝낼 수 있습니다.


object:reverse() 애니메이션이 거꾸로 실행 됩니다.

play와 똑 같이 파라미터를 넣으시면 됩니다.

object:reverse{startFrame=1, endFrame=9, loop=5, remove=true}


object:nextFrame() : 다음 이미지로 애니메이션이 넘어갑니다.

object:previousFrame() : 이전 이미지로 넘어갑니다. 그리고 정지합니다.

object:stop() :  애니메이션을 현재 프레임에서 정지합니다.

object:stopAtFrame(frame) : 지정한 이미지에서 애니메이션이 정지합니다.


bombAnim:stopAtFrame(2)
bombAnim:play()
 
bombAnim:stopAtFrame(1)
bombAnim:reverse()


이 코드를 추가해 보세요. 뭔가 바뀌었나요? 전 잘 모르겠는데.......

이부분도 많이 시도해 봐야 겠어요.


object:setLabels(labels)

객체에 라벨을 매깁니다.

myAnim:setLabels{ firstLabel=1, anotherLabel=3 }


object:setDrag 이 값이 true가 되면 드래그 할 수 있습니다. 그리고 드래그 범위를 지정하려면 bounds 파라미터를 사용할 수 있습니다.

myAnim:setDrag{ drag=true, limitX=false, limitY=false, onPress=myPressFunction, onDrag=myDragFunction,
                onRelease=myReleaseFunction, bounds={ 10, 10, 200, 50 }}


눌려졌을 경우 myPressFunction 이 실행되고 드래그 될 경우는 myDragFunction이 실행되고 release 됐을 경우는 myReleaseFunction이 실행됩니다.

Drag의 범위는 10,10,200,50 이 되구요.


이 기능으로 아주 다양한 표현을 할 수 있겠는데요...

이 기능을 없애려면 단순하게 myAnim:setDrag{drag=false}를 하시면 됩니다.


이 transition library를 사용하지 않고 애니메이션을 구현해야 할 때가 있습니다.

제 탁구 앱 (Multi Player Ping Pong) 에서 공이 튀는 부분도 프로그램으로 그냥 했거든요.


볼이 벽면에 계속 튀는 코드를 보겠습니다.


local xdirection,ydirection = 1,1
local xpos,ypos = display.contentWidth*0.5,display.contentHeight*0.5
local circle = display.newCircle( xpos, ypos, 20 );
circle:setFillColor(255,0,0,255);
 
local function animate(event)
        xpos = xpos + ( 2.8 * xdirection );
        ypos = ypos + ( 2.2 * ydirection );
 
        if (xpos > display.contentWidth - 20 or xpos < 20) then
                xdirection = xdirection * -1;
        end
        if (ypos > display.contentHeight - 20 or ypos < 20) then
                ydirection = ydirection * -1;
        end
 
        circle:translate( xpos - circle.x, ypos - circle.y)
end
 
Runtime:addEventListener( "enterFrame", animate );


이렇게 하면 저 빨간 공이 움직이면서 벽에 계속 튕깁니다.

제 앱의 경우는 각 벽면들과 공도 객체로 만들어서 addBody를 한 다음에 bounce를 주었습니다.

이렇게 프로그램으로 애니메이션을 하려면 enterFrame 으로 리스너를 달아야 합니다.

그리고 제가 했던 방법으로 하면 Ball에 addBody를 하고 Ball:setLinearVelocity()를 주어도 됩니다.


여기서는 enterFrame을 이용하는 방법을 좀 더 보도록 하죠.


위에 있는 소스는 enterFrame의 Frame-base로 코딩을 한 것입니다.

이 frame rate은 config.lua에서 세팅해 주죠? 그래서 이 세팅 값에 따라 속도가 느려지루도 빨라질 수도 있습니다.


이것을 방지하기 위해서 time-base로 코딩을 해도 됩니다.

만약에 일정한 움직임에 딱 맞는 sound효과를 주려면 위에 Frame base보다는 아래에 있는 time base가 더 낫겠네요.


local xdirection,ydirection = 1,1
local xpos,ypos = display.contentWidth*0.5,display.contentHeight*0.5
local circle = display.newCircle( xpos, ypos, 20 );
circle:setFillColor(255,0,0,255);
 
local tPrevious = system.getTimer()
local function animate(event)
        local tDelta = event.time - tPrevious
        tPrevious = event.time
        xpos = xpos + ( 0.084*xdirection*tDelta );
        ypos = ypos + ( 0.066*ydirection*tDelta );
 
        if (xpos > display.contentWidth - 20 or xpos < 20) then
                xdirection = xdirection * -1;
        end
        if (ypos > display.contentHeight - 20 or ypos < 20) then
                ydirection = ydirection * -1;
        end
 
        circle:translate( xpos - circle.x, ypos - circle.y)
end
 
Runtime:addEventListener( "enterFrame", animate );


실행 결과는 똑 같습니다.

다만 system.getTimer() 와 event.time으로 time base로 볼의 움직임을 콘트롤 한 것이 다릅니다.


이 time base의 경우에도 디바이스가 메모리가 딸려서 버벅 거린다면 문제가 일어날 수 있겠죠?


이렇게 버벅거리는 경우 아래 코드로 보완 할 수 있다고 하네요.

-- Add the following below the code in the previous example
 
local tSuspend
local function onSuspendResume( event )
        if "applicationSuspend" == event.type then
                tSuspend = system.getTimer()
        elseif "applicationResume" == event.type then
                -- add missing time to tPrevious
                tPrevious = tPrevious + ( system.getTimer() - tSuspend )
        end
end
 
Runtime:addEventListener( "system", onSuspendResume );


이 코드를 아까 time base 코드에 덧 붙이세요.

이 경우는 테스트 해 보기가 어려워서 직접 테스트는 못했는데요.

그냥 사용하면 될 것 같습니다.


오늘은 transition library와 Movieclips 그리고 코딩으로 애니메이션 구현하는 법에 대해서 배웠습니다.


다음시간엔 movieclips보다 조금 더 복잡하지만 세밀한 애니메이션 효과를 낼 수 있는 Sprite Sheets 에 대해서 살펴 보겠습니다.


그럼....

반응형

Display Object와 Stage 사용하기

2011. 10. 6. 23:19 | Posted by 솔웅


반응형
오늘은 주로 이론적인 내용들이 많을 것 같네요.
그래서 코로나에서 제공하는 매뉴얼을 번역했습니다.
원문은 아래 링크를 참조하세요.
http://developer.anscamobile.com/content/application-programming-guide-graphics-and-drawing

스크린에 그려지는 모든 객체는 DisplayObjects에 의해 그려진다.
그러므로 스크린에 표시되는 모든 것은 DisplayObject의 인스턴스 들이다.

Display Objects 생성하기

개발자가 DisplayObject를 바료 생성하지는 않는다.
대신 rectangles, circles,images, text, groups 같은 특별한 종료의 DisplayObject를 생성하게 된다.

DisplayObject의 모든 인스턴스들은 Lua 테이블 처럼 다뤄질 수 있다.
이는 DisplayObject가 미리 선점한 프로퍼티를 제외한 개발자만의 프로퍼티들을 생성해서 사용할 수 있다는 얘기다.
다만 DisplayObject 내의 인스턴스를 소팅하는 기능은 안 된다.

페인터 모델

DisplayObject는 Painter's Model을 사용해서 스크린에 그린다.
먼저 그린게 맨 밑에 깔리고 가장 최근에 그린게 가장 위에 나타난다는 얘기다.

Display Hierarchy

그려진 DisplayObject들의 순서를 관리하기 위해서 이 DisplayObjects들은 hierarchy를 갖게 된다.

Group Objects

이 Hierarchy 는 group objects를 사용해서 구성할 수 있다.
group 객체는 DisplayObject로서 child를 가질 수 있다. child들은 배열로서 관리 된다.
첫번째 child는 index 1이다.  group:insert() 메소드를 사용해 객체들을 Group에 넣을 수 있다.

local square = display.newRect( 0, 0, 100, 100 )
local rect = display.newRect( 0, 0, 100, 100 )
local group = display.newGroup()
group:insert( square )
group:insert( rect )
assert( (group[1] == square) and (group[2] == rect) )

Stage Objects

새로운 객체를 생성할 때마다 이 객체들은 hierarchy 최상단에 있는 특별한 group 객체에 암시적으로 add되게 된다.
이 특별한 group object를 stage object라고 부른다.
(현재 코로나에는 오직 하나의 stage만이 있다. 이 스테이지는 화면 전체를 말한다.)

객체들을 앞, 뒤로 위치 시키기

한번 지정된 객체들의 순서는 불변이 아니다. 순서를 바꿀 수 있다.
group:insert()메소드를 사용해서 객체들의 순서를 재배열 할 수 있다.
local square = display.newRect( 0, 0, 100, 100 )        -- Red square is
square:setFillColor( 255, 0, 0 )                        -- at the bottom.
local circle = display.newCircle( 80, 120, 50 )         -- Green circle is
circle:setFillColor( 0, 255, 0 )                        -- in the middle.
local rect = display.newRect( 0, 0, 100, 100 )          -- Blue rect is
rect:setFillColor( 0, 0, 255 )                          -- at the top.
 
-- square,circle,rect all have same parent
local parent = square.parent
 
-- Move to top. Siblings at higher indices are above those at lower ones
parent:insert( square )         -- same as parent:insert( parent.length, square)
 
-- Move below all other siblings
parent:insert( 1, circle )

객체들은 object:toBack , object:toFront 를 사용해서 위치를 재배열 할 수 있다.

Drawing Cycle

각 객체들이 그려지고 난 후 Lua는 Display Objects 내에 있는 객체가 변경될 때 새로 그리게 된다.
이 변경은 adding, removing, changing 프로퍼티 들에 의해 일어나게 된다.
이 Cycle은 1초당 30~60 회 일어난다. 정확한 숫자는 config.lua에 세팅된 frame rate에 의해 결정된다.
각 싸이클의 시작 지점에서 enterFrame 이벤트가 일어나게 된다. 이러한 리스너들에 대한 실행이 끝난 후 스크린은 업데이트 된다.

Coordinates and Transforms



스크린에 무엇인가를 그릴 때의 기준은 좌표이다. 코로나는 좌측 최 상단을 좌표 0,0 으로 인식한다.
각 객체에 대한 좌표도 각 객체의 좌측 상단에서 부터 시작한다.

Coordinates

포지션을 정의하는데에는 Cartesian coordinate system (일명 rectangular coordinate system 또는 데카르트 좌표계) 이 사용된다.
참조 http://blog.naver.com/destinycs?Redirect=Log&logNo=140113963405
       http://www.kyungmoon.com/bookstory/story_1_5.htm
원래의 데카르트 좌표계와는 다르게 스크린의 원점은 좌측 상단이다. 그러므로 오른쪽으로 갈수록 x좌표값은 증가하고 아래쪽으로 갈수록 y좌표값이 증가한다. (y 좌표값이 데카르트 좌표계와 반대이다.)
각 객체들은 좌표값을 이동시키거나 회전시킬수 있다. 이 경우 원래의 좌표값은 따로 가지고 있다. 객체 r을 예로 들면 r.xOrigin, r.yOrigin이 원래의 좌표값이다.
객체가 만들어 졌을 때 그 객체는 screen에 소속 된다. 그 다음 어느 한 그룹에 배속 됐을 때 그 객체는 그룹에 소속되게 된다. 이에 따라 좌표 계산도 달라지게 된다.
객체가 회전 등을 할 경우 이 참조 좌표는 r.xReference, r.yReference 로 알 수 있다.

Changing Position of Object

객체의 포지션을 바꾸려면 x,y 좌표를 이용해서 바꾸면 된다. (xOrigin,yOrigin을 바꿔도 되지만 권장할만한 방법은 아니다.)
Reference Point는 그 객체의 중앙이 된다. 이 레퍼런스 포인트를  좌측상단으로 바꾸려면 아래와 같이 한다.
(object:setReferencePoint(display.TopLeftReferencePoint)

Transforms

Transformation 은 기하학적인 법칙이 적용되고 다음과 같은 규칙을 따른다.
1. 객체의 크기 변형은 reference Point를 사용한다. (object.xScale, object.yScale)
2. 객체의 회전은 object.rotation degree reference Point를 이용한다.
3. origin을 이동실킬 때는 object.x, object.y를 이용한다.

object:scale(), object:rotate() object:translate() 처럼 파라미터를 사용하지 않고 함수를 사용하는 명령어는 origin이 아니라 그 이전 객체 상태에서 변형을 하게 된다.

Object References

객체들의 순서를 재배열 하기 위해서는 integer 값을 사용해서 그룹의 children에 접근한다. 하나의 child를 위로 위치시키면 이 이후의 모든 children들의 순서값도 바뀌게 된다.
태양계 행성들을 예를 들어 설명하겠다.
local planetFiles = { sun="sun.png", mercury="mercury.png",
   venus="venus.png", earth="earth.png", mars="mars.png",
   jupiter="jupiter.png", saturn="saturn.png", neptune="neptune.png",
   uranus="uranus.png", pluto="pluto.png" }
 
local solarSystem = display.newGroup()

이렇게 planetFiles 테이블에 각 행성 이름과 이미지 이름을 대입 시킵니다.
그리고 solarSystem이라는 그룹을 생성합니다.
그 다음은 이미지 객체를 생성하기 위해서 특별한 iterator 인 ipairs를 사용합니다. 이것은 프로퍼티 이름과 파일 이름을 리턴하게 됩니다. 이 파일 이름으로 실제 이미지파일을 로드하게 될 겁니다.
프로퍼티 이름은 그룹에 이 프로퍼티들을 할당하는데 사용합니다. 이럼으로서 단지 숫자로만 테이블 내의 객체들을 다룰 때보다 덜 헛갈리게 작업을 할 수 있습니다.
-- Loop through all the files, load the image, assign property in the group for key,file in pairs( planetFiles ) do
        -- key will be "sun", "mercury", etc.
        -- file will be "sun.png", "mercury.png", etc.
        local planet = display.newImage( file )
   solarSystem:insert( planet )
   solarSystem[key] = planet
end
 
-- Afterwards:
-- solarSystem.sun will refer to the image object for "sun.png",
-- solarSystem.mercury will refer to the image object "mercury.png",
-- etc.
만양 이 중에 하나를 remove하고 싶으면 두가지 작업이 필요합니다.
첫번째로 hierarchy 로부터 이것을 remove합니다.
두번째로 해당 프로퍼티를 nil로 만들어 줍니다.

Removing Objects Properly

객체를 제대로 remove 하는 것은 아주 중요합니다. 왜냐하면 디바이스는 한정된 메모리를 가지고 있기 때문입니다. 이렇게 함으로서 퍼포먼스 향상을 얻을 수 있습니다.
이 해당 객체가 더 이상 스크린에 표시 될 필요가 없다면 다음의 두가지 방법으로 display hierarchy에서 없앱니다.
image.parent:remove( image ) -- remove image from hierarchy
-- or --
image:removeSelf( ) -- same as above

이렇게 되면 이 이미지 객체는 garbage collected 됩니다. 이 이미지를 확실하게 없애려면 한단계 더 필요합니다.
이에 대해서는 아래 variable References에서 살펴 봅니다.
(그룹을 없애면 그 그룹내의 모든 리스너와 texture 들을 없애게 됩니다. 그리고 display object 변수들은 simple 한 루아 테이블로 바뀌게 되고 이 것은 garbage collect에 남게 됩니다. )

variable References

display obeject가 hierarchy에서  remove 되었더라도 아직 그 객체는 존재한 상태 일 수 있습니다. 가비지 컬렉터에서도 이 남아있는 정보들을 없애려면 아래와 같이 합니다.
local sun = solarSystem.sun
sun.parent:remove( sun ) -- remove image from hierarchy
solarSystem.sun = nil -- remove sun as a property of solarSystem

화면에 보이지 않더라도 객체가 계속 display object 에 남아 있는 경우가 있는데 이 경우 nil을 부여해 확실하게 없앨 수 있습니다.

만약에 글로벌 변수가 display object 에 할당 돼 있으면 얘기가 조금 달라집니다.
hierarchy 에 있지 않아도 글로벌 변수는 계속 존재합니다.
이 경우 글로벌 변수에도 따로 nil을 대입해야 합니다.

만약 함수가 함수 밖에 있는 로컬 변수를 참조하고 있다면?

local sun = solarSystem.sun
 
function dimSun()
        sun.alpha = 0.5 -- sun was declared outside the function block
end

보시면 이 함수는 글로벌입니다. 그래서 저 이미지는 계속 남아 있습니다.

아래와 같이 하면 그러한 위험을 없앨 수 있고 또한 이 함수가 다양한 display object를 처리 할 수도 있습니다.

local sun = solarSystem.sun
 
function dim( object )
        object.alpha = 0.5
end

Common Pitfalls 흔히 실수 하는 것들

흔히 그룹 내의 모든 객체들을 확실하지 않게 remove 하는 실수 들을 하게 됩니다.
아래 예제는 제대로 remove 하지 못한 경우입니다.

for i=1,solarSystem.numChildren do
        local child = solarSystem[i]
        child.parent:remove( child )
end

이 경우  우리는 다른 collection (다른 그룹의 children 배열) 수정하게 됩니다. 결과는 다른 collection의 children을 없애게 됩니다.

아래 소스를 실행해 보시고 분석해 보세요. 그리고 위에 소스를 어떻게 고쳐야 할지 생각해 보세요.

local array = {1,2,3,4,5,6,7,8,9,10}
print( table.concat( array, " " ) ) --> 1 2 3 4 5 6 7 8 9 10
 
for i=1,#array do
        table.remove( array, i )
end
 
print( table.concat( array, " " ) ) --> 2 4 6 8 10

위에 소스를 고치려면 아래와 같이 합니다.
for i=solarSystem.numChildren,1,-1 do
        local child = solarSystem[i]
        child.parent:remove( child )
end

이러면 모든 children이 hierarchy에서 remove 됩니다.
물론 가비지 콜렉터에서마저 없애려면 모두 nil값을 대입 시켜야죠.

그냥 대충 보고서는 제가 이해하지 못해서 원문을 그냥 번역해 봤습니다.
이론이고 그림 없이 글자들만 있으니까 이해하기 힘드네요.
하지만 이렇게 번역을 해 보면서 많이 이해 된 것 같습니다.
이걸 가지고 또 실습을 해 보면 더 확실히 이해 하겠죠.

한 김에 Bitmap Mask 까지 하겠습니다.

Bitmap Mask

아래 두개의 이미지를 사용할 겁니다.

그리고 아래 소스를 main.lua에 넣고 실행 해 보세요.

local img=display.newImage("endinggreen.png",50,50)
local mask=graphics.newMask("podium.png")
img:setMask(mask)
img.maskRotation =30
img.maskX = 20
img.maskY=10
--img.maskScaleX=2
--img.maskScaleY=1
--img.maskRotation = 5

그러면 아래와 같은 이미지가 나옵니다.


뭔가 두개의 이미지가 합쳐져서 희한하게 나오죠?
영화 마스크 보면 짐캐리가 마스크를 쓰면 완전히 다른 사람이 되던데.. 그 마스크가 생각나네요.

코로나의 비트맵 마스크는 이미지를 비트맵으로 사용하고 이것을 다른 display object에 적용할 수 있도록 합니다.
코로나는 마스크로 정의된 이미지를 우선 흑백(grayscale)으로 바꿉니다. 흰색은 없앴다네요.
이 비트맵 마스크는 다른 display objects에 적용가능한데요 텍스트와 native display objects에는 안 된다고 하네요.

한번 마스크로 설정된 이미지는 다른 일반 이미지 객체로 불러올수 없다고 합니다. 그 반대도 마찬가지구요.  iOS, 안드로이드, 코로나 시뮬레이터에서 작동합니다.

디바이스에 따라 마스크 갯수에 제한이 있답니다. 넥서스 원은 2개 iPhone 3G 는 8개 입니다.

신택스는 graphics.newMask() ,local mask = graphics.newMask( filename [, baseDir] ) 입니다.

object:setMask() 로 마스크를 적용하고 object:setMask(nil)로 remove 합니다.
프로퍼티들엔 아래와 같은 것들이 있습니다.
object.maskX
object.maskY
object.maskScaleX
object.maskScaleY
object.maskRotation
이 프로퍼티들을 위 소스에 대입하면서 감을 잡으시면 도움이 되실 겁니다.

이상으로 Images, Shapes and Text 에 대한 걸 마쳤습니다.

후반부엔 조금 이론적인 내용들이 많아서 힘들었습니다.

다음엔 코드 작성하고 객체들이 막 움직이고 하는 Animation and Motion 을 하겠습니다.

그럼...
반응형


반응형
어제 애플(Apple)이 아이폰을 발표했는데 iPhone 5 가 아니라 iPhone 4S 였군요. 새로워진 것도 별로 없고... 안드로이드 진영에선 이미 다 구현 한 것들이구.
실망이 이만 저만이 아니겠는데요. 잡스 없는 애플이 이제 내리막 길로 가는 것은 아닌지......
우리나라의 삼성과 LG 에는 아주 좋은 기회가 될 듯 합니다.
1년 넘는 기간 동안 그정도 밖에 준비 못했다니..... 애플 완전 실망...

오늘은 코로나 SDK (Corona SDK) 에서 다각형 그리기 부터 하겠습니다.

코로나에서는 벡터 객체를 사용해서 다각형을 그립니다. 벡터 객체는 DisplayObject의 특별한 타입 입니다.
우선 사각형, 모서리가 둥근 사각형, 원 그리는 신택스를 보겠습니다.

display.newRect( [parentGroup,] left, top, width, height )
parentGroup은 옵션이구요. 이건 나중에 볼께요. 우선 좌측과 상단 값을 넣어서 좌상단 꼭지점의 위치를 정하고 너비와 높이 값을 주어서 사각형을 선언합니다. 여기서 색을 지정하는 건 없네요. 메소드를 사용해서 색을 지정할까요? 잠시후에 보죠.

display.newRoundedRect( [parentGroup,] left, top, width, height, cornerRadius )
모서리가 둥근 사각형은 위의 사각형과 같구요. 다만 맨 마지막에 모서리에 그릴 곡선의 반지름 값을 넣으면 됩니다.

display.newCircle( [parentGroup,] xCenter, yCenter, radius )
원은 원 중심의 x,y 값을 넣고 반지름 값을 넣습니다.
   
strokeWidth (테두리선)는 아래와 같이 파라미터를 사용해서 정해 줍니다.
object.strokeWidth = 픽셀
색 지정은 아래와 같이 메소드를 사용합니다.
object:setFillColor(r,g,b[,a]) , object:setStrokeColor(r,g,b[,a])
a는 알파값(투명도) 로 옵션 입니다. 들어갈 값은 0~255 사이에 있는 수입니다.

라인 그리기는 display.newLine(x1,y1,x2,y2) 을 사용합니다.
그리고 라인과 라인을 연결하면 다각형을 만들 수 있는데요. 이것은 object:append()를 사용합니다.

아래 샘플 코드를 작성하고 실행 해 볼까요.
 local star = display.newLine( 0,-110, 27,-35 )
  star:append( 105,-35, 43,16, 65,90, 0,45, -65,90, -43,15, -105,-35, -27,-35, 0,-110 )
  star:setColor( 255, 102, 102, 255 )
  star.width = 3


별을 그리는 코드인데요.
별 모양이 왼쪽 위에 반만 나와 있죠?
이 라인 위치와 색 그리고 width 등은 여러분들이 바꾸면서 익혀 보세요.
남의 소스를 자기 맘대로 막 고치면서 이해도 더 잘 되고 실력도 더 많이 늘더라구요.

아래는 코로나에서 제공하는 샘플 코드인데요. (Graphics/PolyLines)
한번 실행해 보세요.
--
-- Abstract: PolyLines sample app, demonstrating how to draw shapes using line segments
-- Version: 1.0
-- Sample code is MIT licensed, see http://developer.anscamobile.com/code/license
-- Copyright (C) 2010 ANSCA Inc. All Rights Reserved.
-- Example of shape drawing function
local function newStar()
    -- need initial segment to start
    local star = display.newLine( 0,-110, 27,-35 )
    -- further segments can be added later
    star:append( 105,-35, 43,16, 65,90, 0,45, -65,90, -43,15, -105,-35, -27,-35, 0,-110 )
    -- default color and width (can also be modified later)
    star:setColor( 255, 255, 255, 255 )
    star.width = 3
    return star
end

-- Create stars with random color and position
local stars = {}

for i = 1, 20 do
    local myStar = newStar()
   
    myStar:setColor( math.random(255), math.random(255), math.random(255), math.random(200) + 55 )
    myStar.width = math.random(10)
   
    myStar.x = math.random( display.contentWidth )
    myStar.y = math.random( display.contentHeight )
    myStar.rotation = math.random(360)
   
    myStar.xScale = math.random(150)/100 + 0.5
    myStar.yScale = myStar.xScale
   
    myStar:setReferencePoint( display.CenterReferencePoint )

    local dr = math.random( 1, 4 )
    myStar.dr = dr
    if ( math.random() < 0.5 ) then
        myStar.dr = -dr
    end

    table.insert( stars, myStar )
end

function stars:enterFrame( event )

    for i,v in ipairs( self ) do
        v.rotation = v.rotation + v.dr
    end
end

Runtime:addEventListener( "enterFrame", stars )


저는 사용하던 소스에 위 소스를 덧 붙였더니 위 화면같이 나옵니다.
지금 까지 배운 걸로도 이 소스를 충분히 분석할 수 있을 것 같은데요.
나중에 샘플 코드 분석할 때 시간 되면 이 소스도 함 자세히 분석해 보죠.
오늘은 Text, Group, Stage 등 앞으로 나갈 진도가 더 있어서 계속 진도 나가겠습니다.

TEXT
우리가 작성한 소스에 이미 Text 사용하는게 있는데요.
local textObj = display.newText("A short string", 0,0, nil, 26);
display.newText 를 사용하고 그 안에 텍스트와 x,y 좌표 그리고 폰트(여기선 nil), 글자 사이즈 이렇게 들어갑니다.
현재 사용하고 있는 폰트를 알아내시려면 native.getFontNames() 함수를 사용합니다.
디폴트 폰트는 native.systemFont , native.systemFontBold 입니다.
object.size - 텍스트 사이즈를 정합니다. (=숫자 를 덧붙이시면 됩니다.)
object.setTextColor(r,g,b[,a]) -  텍스트 칼라를 지정합니다.
파라미터는 . 를 사용하고 메소드는 : 를 사용하는건 이제 아시겠죠?
값을 대입 시키는 것도 대부분 파라미터는 = 숫자 , 문자 등등 을 사용하고 메소드는 () 안에 인수값을 넣구요.

원하는 폰트를 사용하시려면 우선 폰트 파일 (.ttf) 이 있어야 되고 이것을 building.settings 파일에 아래와 같이 세팅해 줘야 합니다.
settings =
{
        iphone =
        {
                plist =
                {
                        UIAppFonts =
                        {
                                "Harrowprint.ttf"
                        }
                },
        },
}
이것은 아이폰 일 경우만 해당되고 안드로이드용은 이 build.settings 파일에 세팅을 하지 않아도 됩니다.

Groups

이  Group 객체도 DisplayObject의 특별한 타입 중 하나입니다.
display.newGroup() 으로 그룹을 생성합니다. 그리고 이 그룹에 객체들을 insert 하고 각 객체들에 대한 접근은 테이블(배열) 에서 접근하는 것처럼 index 1번 부터 시작합니다.
그룹 생성 및 인서트는 아래와 같이 합니다.
 local group = display.newGroup()
  group:insert( rect1 ) -- assume rect1 is an existing display object
  group:insert( rect2 ) -- assume rect2 is an existing display object

만약 그룹 2개를 생성하고 한 객체를 두군데 모두 삽입했다면 코로나가 알아서 마지막 insert문만 적용하도록 만듭니다.
local txt=display.newText('Hello',0,0)
local g1=display.newGroup()
local g2=display.newGroup()
             
-- Insert text object into g1
g1:insert(txt)              
-- Insert same text object into g2
g2:insert(txt)
 
print("g1[1]: " .. tostring(g1[1])) -- prints nil
print("g2[1]: " .. tostring(g2[1])) -- prints textObject    

그룹을 없애려면 remove를 사용합니다. group:remove(indexOrChild)

이 그룹은 여러 객체를 하나의 객체처럼 사용할 때 편리합니다.

Stage

스테이지도 GroupObject의 특별한 타입 중 하나입니다.
스테이지는 모든 display 객체들의 root group입니다.
현재의 스테이지는 아래와 같이 접근 할 수 있습니다.
display.getCurrentState()

다른 Display함수들을 보면 아래와 같은 것들이 있습니다.
display.captureScreen(saveToAlbum) 화면을 캡쳐해서 이미지로 만듭니다. saveToAlbum 이 true이면 그 이미지를 디바이스(핸드폰) 의 앨범에 저장합니다.
시뮬레이터일 경우에는 데스크탑 컴퓨터에 저장합니다.

display.save(displayObject,filename[,baseDirectory])
해당 객체를 JPEG 이미지로 filename에서 지정한 이름으로 바꿔 줍니다.

display.setStatusBar(mode) mode가 display.HidenStatusBar이면 아이폰의 스테이터스 바가 화면에 나오지 않습니다. 관련된 argument들엔 다음과 같은 것들이 있습니다.

    * display.HiddenStatusBar
    * display.DefaultStatusBar
    * display.TranslucentStatusBar
    * display.DarkStatusBar

Size 관련된 프로퍼티들엔 아래와 같은 것들이 있습니다.
display.contentWidth - 스크린 너비가 픽셀단위로 나타납니다. config.lua에서 픽셀 말고 다른 것으로 설정해 놨으면 그 값이 나타납니다.
display.contentHeight - 스크린 높이가 나타납니다.
display.viewableContentWidth - 다이나믹 스케일 모드일 때 유용하게 사용할 수 있습니다. 해당 객체의 너비를 나타냅니다.
display.viewableContentHeight - 해당 객체의 높이를 나타냅니다.
display.statusBarHeight - 상태 바의 높이를 픽셀로 나타냅니다. iOS에서만 적용됩니다.

Display Object 대 Table

DisplayObject와 Table은 유사한 점이 많습니다. 다른 점은 Display Object의 메타테이블을 세팅할 수 없다는 겁니다.
내부적으로 코로나의 DisplayObject는 native C++객체를 사용합니다.

오늘은 여기까지 알아봤구요.

다음 시간에 Display Object 와 Stage에 대해 좀 더 자세히 예제와 함께 알아보겠습니다.

감사합니다.
반응형


반응형
오늘은 텍스트와 이미지, 라인, 다각형 같은 모양들 표시하는 방법에 대해 공부하겠습니다.

display.setStatusBar( display.HiddenStatusBar )
local textObj = display.newText("A short string", 0,0, nil, 14);
textObj:setReferencePoint(display.CenterLeftReferencePoint);
textObj.x = 20;

textObj.text = "This string has several more characters than before..."

textObj.text = "This is third sentence."
textObj:setReferencePoint(display.CenterLeftReferencePoint);
textObj.x = 20

우선 첫번째 줄은 아이폰에서 status bar 없애는 거구요.
다음줄 보맨 textObj 변수에 "A short string"이란 텍스트를 담고 위치는 0,0 폰트는 nil로 설정해서 그냥 디폴트가 나오도록 하고 글자 사이즈는 14포인트로 정했습니다.
다음은 textObj에 setReferencePoint를 적용하고 x 좌표를 20으로 했습니다.

그리고 textObj.text 를 이용해서 다른 문구를 적용했습니다.
그리고 다음줄은 또 다른 문구를 적용했구요.

화면에는 위와 같이 맨 마지막 적용된 값이 표시됩니다.

화면에 표시되는 모든 것은 DisplayObjects가 만듭니다. 즉 화면에 표시되는 모든 것은 이 DisplayObjects의 인스턴스 입니다.
사각형, 원, 이미지, 텍스트 같은 모든 것들은 다 DisplayObject를 사용해 표시할 수 있습니다.
이걸 기초로 애니메이션을 만든다던가 터치를 하면 다른 객체를 표시한다던가 하는 일들을 하게 됩니다.
이 DisplayObject는 특별한 함수인 생성자(constructor)를 호출함으로서 생성할 수 있습니다. 직접적으로 생성하지는 않습니다.
예를 들어 display.newRect()는 Vector Object를 생성하게 됩니다.

이러한 display object들은 아래와 같은 프로퍼티들과 메소드들을 사용할 수 있습니다.
프로퍼티들은 . 로 접근 할 수 있습니다.
예를 들어 아까 작성했던 텍스트에 투명도를 주려면
textObj.alpha=0.5 를 주면 됩니다.
자주 사용되는 프로퍼티 몇개를 살펴 보면요.
object.inVisible = true or false
object.height , object.width, object.length 객체 크기와 길이 등을 구할 수 있습니다.
object.isHitTestable 객체가 보이지 않을 때에도 hit 이벤트를 받을 수 있도록 합니다. (true 일 경우)
object.rotation = 숫자 -> 해당 객체를 로테이션 시킵니다.

위 화면은 아까 작성했던 코드에 아래 몇줄을 더 한 겁니다.
textObj.alpha=0.8
oHeight = textObj.height
print("Height = " .. oHeight)
textObj.rotation = 45

object.x, object.y 객체의 x,y 좌표
좀 더 자세한 사항은 아래 페이지를 참고 하세요.
http://developer.anscamobile.com/content/display-objects

다음은 메소드를 살펴 봅니다.
메소드는 : 를 이용해서 접근 할 수 있습니다.

object:rotate() 현재의 로테이션에 ()안에 있는 로테이션 각도를 추가해서 적용합니다.
예를 들어 아까 소스에 textObj:rotate(-25) 를 하면 45-25=20 이니까 20도의 기울기가 나오게 됩니다.
파라미터를 쓰게 되면 (textObj.rotation = -25) 그냥 -25도로 기울기가 표시 됩니다.
object:scale(sx,sy) 객체의 길이와 높이를 세팅합니다.
object:setReferencePoint(referencePoint) 기준점을 정합니다.
reference Point 종류는 아래와 같습니다.
    * display.CenterReferencePoint
    * display.TopLeftReferencePoint
    * display.TopCenterReferencePoint
    * display.TopRightReferencePoint
    * display.CenterRightReferencePoint
    * display.BottomRightReferencePoint
    * display.BottomCenterReferencePoint
    * display.BottomLeftReferencePoint
    * display.CenterLeftReferencePoint

예를 들어 처음의 예제에서 referncePoint를 CenterLeftReferncePoint를 사용했습니다. 그리고 text길이가 다르게 세팅을 했고 x포인트를 20으로 늘렸습니다.
이 레퍼런스 포인트를 어떻게 설정하느냐에 따라 해당 객체의 길이가 달라질 때 화면에 표시되는 위치가 다르게 보일 수가 있습니다.

object:translate(deltaX,deltaY) 현재 위치에서 deltaX,deltaY만큼 이동시킵니다.
object:removeSelf() 객체를 없앱니다. 이는 메모리 할당을 풀어주게 됩니다.


*** 이미지 Images
 Bitmap 이미지 객체도  DisplayObject의 한 타입입니다.

display.newImage(filename[,baseDirectory][,left,top])

저는 이미지테스트 할 때 아래 이미지를 사용하겠습니다.

이름은 endinggreen.png입니다.
이번에 Corona로 만든 간단한 게임에 사용했던 이미지 입니다.
제가 이미지 디자인한건 아니고 동료 디자이너가 디자인 했죠. 저는 개발만 했습니다.
잠깐 홍보 하자면 안드로이드는 아래 링크로 볼 수 있습니다.
https://market.android.com/details?id=nowbyte.corona.multipingpong&feature=search_result
저희 회사에서 제가 만든 앱들을 보려면 아래 링크로 가시면 보실 수 있구요.
https://market.android.com/search?q=effilonNY&so=1&c=apps
대부분이 게임이라서 한국에서는 볼 수 있을 지 모르겠습니다.
아이폰 앱 스토어는 아래 링크로 보실 수 있구요.
http://itunes.apple.com/us/app/multi-player-ping-pong/id466527438?ls=1&mt=8
이것도 게임 카테고리에 들어 있어서 한국에서는 보실 수 있을 지 모르겠습니다.
예전 강좌에 있던 대로 config.lua 파일에서 아이폰 (iPhone) 해상도 (resolution) 인 320 X 480 으로 하고 scale = "letterbox" 로 했기 때문에 안드로이드 폰의 모든 기기 (갤럭시S, 갤럭시S2, 드로이드, 넥서스 원 (NexusOne), 갤럭시 탭) 에서 사용할 수 있습니다. 새로나온 갤럭시 탭이나 이번에 나온 아마존의 킨들 파이어 (Kindle fire)에서는 테스트 해 보지 않았지만 여기서도 돌아갈 겁니다.
그리고 애플 쪽 디바이스은 아이폰, 아이폰 4 , iPod Touch, iPad 에서 다 돌아갑니다. 그리고 오늘 발표 될 iPhone 5 에서도 제대로 나올겁니다.

코로나 SDK (Corona SDK) 의 장점은 Box2D를 사용한 강력한 물리엔진 (Physics Engine)과 한번 개발해서 애플의 아이폰, 아이패드 디바이스와 안드로이드의 모든 모바일 폰 과 태블릿 (갤럭시 탭, 모토로라의 줌 (XOOM), 아이리버의 아이리버 탭 그리고 아마존의 Kindle fire 까지) 에서 사용할 수 있고 해상도도 간편하게 적용할 수 있다는 것에 있습니다.

일단 제가 개발한 앱과 코로나 SDK에 대한 홍보는 여기서 마치구요.
코로나에서 이미지 디스플레이 하는 것을 보도록 하겠습니다.

아까까지 사용했던 소스에 아래 두 줄을 덧 붙이겠습니다.

myImage = display.newImage( "endinggreen.png", 10, 150 )
myImage2 = display.newImage( "endinggreen.png" )


보시는 대로 이미지는 display.newImage 생성자(constructor)를 사용해서 그립니다.
이미지 이름만 파라미터로 넣어도 되고 x,y 좌표도 같이 넣어도 됩니다.
코로나에서는 PNG와 JPEG파일을 지원한다고 나옵니다.
제 기억엔 GIF도 나왔던 것 같은데요.. 잘 모르겠네요. GIF는 나오고 GIF 애니메이션은 지원이 안 되서 그냥 정지된 이미지 하나만 나온 것 같은데요... 궁금하시면 한번 해 보세요.
이미지 크기가 맥시멈을 넘어서면 autoscale됩니다. iPhone 3G 의 경우 1024  X 1024 , iPhone 3GS, iPad 의 경우는 2048X2048 이 맥시멈 사이즈 입니다.

이전 코드에 아래 세줄을 넣어 보세요.
myDynamicImage = display.newImageRect( "endinggreen.png", 60, 200 )
myDynamicImage.x =260
myDynamicImage.y =300

newImageRect 는 이렇게 이미지의 width,height를 다이나믹하게 설정할 때 사용합니다.
이 다이나믹 이미지 해상도(Dynamic Image Resolution) 과 configuring 에 대해 좀 더 자세히 보시려면 아래 링크를 참조하세요.
http://developer.anscamobile.com/content/configuring-projects#Dynamic_Image_Resolution

오늘은 이렇게 텍스트와 이미지 표시하는 법과 displayObject 에서 사용되는 파라미터와 메소드들에 대해 알아봤습니다.

다음 시간엔 사각형, 원 그리고 다각형 그리는 법과 색깔 넣는 법, 글자 폰트 지정하는 법과 새로운 폰트 사용하는 법, Group 사용법 (display.newGroup), stage 객체에 대해 알아보겠습니다.

이 모든 것들이 Display Object 의 객체(인스턴스) 들입니다.

그리고 간단히 Display Object 와 테이블에 대한 기술적인 분석에 대해서도 다룰 거구요.

뭐 별거는 아니지만 오늘 사용했던 코드 전체는 아래와 같습니다.
display.setStatusBar( display.HiddenStatusBar )
-- A text object is created and is aligned left at point x=20
local textObj = display.newText("A short string", 0,0, nil, 26);
textObj:setReferencePoint(display.CenterLeftReferencePoint);
textObj.x = 20;

 
-- Later, the textObj.text property is assigned a new string value of different length,
-- causing the object's width to change, but not its reference point.
-- Consequently, the text is no longer aligned left at point x =20
textObj.text = "This string has several more characters than before..."
 
-- Work-around:
-- Reset the text object's reference point and x position
-- after you update its text property:
textObj.text = "This is third sentence."
textObj:setReferencePoint(display.CenterLeftReferencePoint);
textObj.x = 20
textObj.alpha=0.8
oHeight = textObj.height
print("Height = " .. oHeight)
textObj.rotation = 45
textObj:rotate(-25)

myImage = display.newImage( "endinggreen.png", 10, 150 )
myImage2 = display.newImage( "endinggreen.png" )
myDynamicImage = display.newImageRect( "endinggreen.png", 100, 100 )
myDynamicImage.x =260
myDynamicImage.y =300


그럼 다음 시간에 뵙겠습니다.
반응형


반응형
오늘은 화면에 점수 표시하는 샘플코드를 공부할 겁니다.

이 파일이 전체 소스 파일입니다.
웹 싸이트는 http://techority.com/2011/01/26/how-to-add-a-score-to-your-iphone-app/ 입니다.
이 싸이트로 가면 유튜브 동영상도 있습니다.

이 소스를 보면서 제 식으로 공부 해 보겠습니다.


이 소스를 실행하면 위 화면처럼 나옵니다.
화면을 Tapping 하면 1 포인트씩 숫자가 올라갑니다.

우선 main.lua부터 보겠습니다.

--[[
Related files are score.lua, 1 through 9.png, scorebg.png and space.png
--]]
display.setStatusBar( display.HiddenStatusBar )
local background = display.newImage ("background.png")

score = require ("score")

local border = 5

local scoreInfo = score.getInfo()

score.init({
x = 40,
y = 5}
)
score.setScore(0)

local function addtoit (event)
if event.phase == "ended" then
score.setScore (score.getScore()+1)
end
end
background:addEventListener("touch", addtoit)

처음에 아이폰의 StatusBar를 없앱니다. (원래 소스에는 없는데 그냥 제가 추가 했습니다. 이게 보기에 더 편하더라구요.)
두번째는 백그라운드 이미지를 display 합니다.

세번째는 score.lua 소스를 require (implement) 합니다.
그 다음은 border를 5로 설정합니다.(이 border는 뭐 하는건지 모르겠습니다. 그냥 쓸데없이 있는거 같네요.)

그 다음엔 아까 require했던 score.lua에 있는 getInfo() 함수를 scoreInfo 변수에 담습니다. 이 getInfo() 함수는 이따 score.lua 파일을 살펴 볼 때 어떤 기능인지 공부하겠습니다.

그 다음은 score.lua에 있는 init() 함수에 x,y 파라미터를 넣어서 실행시킵니다.
그리고 setScore()함수에 파라미터 값을 0으로 해서 보냅니다.

로컬 함수 addtoit(event) 함수를 만듭니다.
그 내용은 이벤트가 끝나면 score.lua의 setScore함수에 이전 점수 +1 을 한 파라미터 값을 넣어서 부릅니다.

맨 마지막으로 background 에 touch 리스너를 달구요. 이 이벤트가 일어났을 경우 addtoit() 함수를 실행시킵니다.

지금까지 main.lua의 코드를 분석해 봤습니다.
이제 score.lua 파일을 분석해 보겠습니다.

-- A sample score keeping display
-- This updates a display for a numeric score
-- Example usage:
--    Place the score at 50,50
--         score.init( { x = 50, y = 50 } )
--    Update the score to current value + 10:
--        score.setScore( score.getScore() + 10 )

module(..., package.seeall)
 
-- Init images. This creates a map of characters to the names of their corresponding images.
 local numbers = {
    [string.byte("0")] = "0.png",
    [string.byte("1")] = "1.png",
    [string.byte("2")] = "2.png",
    [string.byte("3")] = "3.png",
    [string.byte("4")] = "4.png",
    [string.byte("5")] = "5.png",
    [string.byte("6")] = "6.png",
    [string.byte("7")] = "7.png",
    [string.byte("8")] = "8.png",
    [string.byte("9")] = "9.png",
    [string.byte(" ")] = "space.png",
}

-- score components
local theScoreGroup = display.newGroup()
local theBackground = display.newImage( "scorebg.png" )
local theBackgroundBorder = 10

theScoreGroup:insert( theBackground )

local numbersGroup = display.newGroup()
theScoreGroup:insert( numbersGroup )

-- the current score
local theScore = 0

-- the location of the score image

-- initialize the score
--         params.x <= X location of the score
--         params.y <= Y location of the score
function init( params )
    theScoreGroup.x = params.x
    theScoreGroup.y = params.y
    setScore( 0 )
end

-- retrieve score panel info
--        result.x <= current panel x
--        result.y <= current panel y
--        result.xmax <= current panel x max
--        result.ymax <= current panel y max
--        result.contentWidth <= panel width
--        result.contentHeight <= panel height
--        result.score <= current score
function getInfo()
    return {
        x = theScoreGroup.x,
        y = theScoreGroup.y,
        xmax = theScoreGroup.x + theScoreGroup.contentWidth,
        ymax = theScoreGroup.y + theScoreGroup.contentHeight,
        contentWidth = theScoreGroup.contentWidth,
        contentHeight = theScoreGroup.contentHeight,
        score = theScore
    }
end

-- update display of the current score.
-- this is called by setScore, so normally this should not be called
function update()
    -- remove old numerals
    theScoreGroup:remove(2)

    local numbersGroup = display.newGroup()
    theScoreGroup:insert( numbersGroup )

    -- go through the score, right to left
    local scoreStr = tostring( theScore )

    local scoreLen = string.len( scoreStr )
    local i = scoreLen   

    -- starting location is on the right. notice the digits will be centered on the background
    local x = theScoreGroup.contentWidth - theBackgroundBorder
    local y = theScoreGroup.contentHeight / 2
   
    while i > 0 do
        -- fetch the digit
        local c = string.byte( scoreStr, i )
        local digitPath = numbers[c]
        local characterImage = display.newImage( digitPath )

        -- put it in the score group
        numbersGroup:insert( characterImage )
       
        -- place the digit
        characterImage.x = x - characterImage.width / 2
        characterImage.y = y
        x = x - characterImage.width
        --
        i = i - 1
    end
end

-- get current score
function getScore()
    return theScore
end

-- set score to value
--    score <= score value
function setScore( score )
    theScore = score
   
    update()
end

score.lua 파일을 보겠습니다.
score.lua 파일 처음에 module(..., package.seeall) 를 넣습니다. 이러면 이 파일은 모듈로 사용될 수 있습니다.
이것을 했기 때문에 main.lua에서 require("score") 를 할 수 있고 이것을 변수 score에 담아서 score.(score.lua에 있는 함수) 방법으로 score.lua 내의 함수를 main.lua에서 사용할 수 있습니다.

그 다음 numbers 라는 테이블 변수에 0~9 의 이미지 파일을 담습니다.

그 다음은 display.newGroup()으로 객체들을 담을 그룹을 만듭니다.
그리고 score를 표시할 백그라운드 이미지를 넣고 theBackgroundBorder 변수에 10을 담습니다.

아까 만들었던  theScoreGroup에 백그라운드 이미지(theBackground)를 insert 합니다.
보시면 아까 theScoreGroup 말고 또다른 Group인 numberGroup을 만듭니다.
그리고 이 numberGrooup 자체를 처음의 theScoreGroup에 insert 합니다.

다음 줄에서 theScore 변수를 0으로 선언합니다. 처음 0점부터 시작하게 됩니다.
이제 함수가 나오네요. 아까 main.lua에서 불렀던 init(params) 함수가 나옵니다.

여기서는 theScoreGroup.x와 y를 전달받은 파라미터 값으로 넣고 setScore()함수에 0을 던져 줍니다.
아까 main에서 x=40, y=5 으로 보냈기 때문에 그 위치에 theScoreGroup의 객체들이 display 될 겁니다.

다음은 getInfo() 함수입니다. main함수에서는 이 함수에서 세팅된 값을 scoreInfo 변수에 담았었습니다.
getInfo() 함수에서는 7개의 값을 return합니다.
Lua (Corona) 에서는 복수개의 값을 return 할 수 있습니다.
theScoreGrooup,x , y 값과 ymax,ymax, contentWidth,contentHeight, score 등의 값을 return합니다.

다음은 update() 함수입니다.
처음엔 이전 score를 없앱니다.
그 다음은 numbersGroup이라는 그룹을 만들고 이를 통째로 theScoreGroup에 넣습니다.
다음줄은 theScore를 String 값으로 변환해서 scoreStr 변수에 담습니다.
Score가 한자리 수 인지 두자리 수인지 세자리 수인지 에 대한 정보를 담는 변수도 있네요.
scoreLen변수에 scoreStr 의 length를 담습니다.
local x, y는 점수가 표시되는 위치 입니다. 한자리 수일때 두자리 수일때 표시되는 위치가 조금씩 다르겠죠?

그 다음 while 문에서 어떤 점수 이미지를 어디에 뿌려줄지 계산하는 로직이 있습니다.
이미지를 출력하고 그 이미지를 numbersGroup에 담습니다.
그리고 나서 이미지의 x,y 좌표를 설정하구요.
이 while 문은 scoreLen 만큼 돌아갈 겁니다.

다음 함수는 getScore() 함수인데 여기서는 theScore를 리턴해 줍니다.

그리고 setScore() 함수에서는 theScore 함수에 score 값을 넣어 준 후 update() 함수를 실행시킵니다.

여기 까지 하면 scoring 샘플이 모두 완성된거구요.
이걸 실행 한 후 background를 누르면 점수가 1씩 증가하게 될 겁니다.

이 소스를 가지고 있으면 나중에 게임 개발할 때 아주 유용하게 사용할 수 있을 겁니다.

그럼.........

반응형