본문 바로가기

[ C/ C++ 프로그래밍 ]/[ 루아 ]

[ 루아 ] 루아, 좀더 알아보기

[ 참고 및 출처 ] 
루아를 이용한 민첩하고 효과적인 게임 개발




○ 함수
  함수는 게임 개발 스크립트의 행동을 조직화하는 기본적인 수단이다.
함수는 하나의 식별(실제로는 하나의 변수)를 통해서 호출할 수  있는 루아 코드 블록으로, 작업에 필요한 매개변수를 전달받거나 작업의 결과를 돌려기도 한다.

  함수들이 정의되어 있는 스크립트 파일을 dofile 등을 dofile 등을 이용해서 불러 올때, 그 함수들을이 실제로 실행되는 것은 아니고, 함수의 코드를
메모리에 올리고 그것을 함수 이름 변수에 배정하는 작업만 한다.

- 단일 인수

fuctin  SetName ( myString )
    print(" ")
    print(" Your Name is : ", myString )
    print(" ")
end

※ myString는 함수 안에서 지역변수만 쓰인다. 함수의 실행이 끝나면 폐기

- 다중 인수

fuctin  SetName ( myString, myAge )
    print(" ")
    print(" Your Name is : ", myString )
   print(" Your Age is : ", myAge )
    print(" ")
end


-  가변적인 인수



※  주어진 인수들은 모두 arg라는 테이블에 들어가며, 그 개수는 arg.n으로 알아낸다. 아무것도 넣지 않으면 무시 된다.

- 반환값
 루아에서 함수가 결과를 돌려줄때에는 return문을 사용,  C/C++과 달리 여러 개의 값들을  반환하는 것도 가능

return d1, d2, d3, d4, myT;


함수 호출과 관련해서 루아의 또 다른 한가지 흥미로운 특징은 return 문에서(함수 본문 중간에서가 아니라) 다른 함수를 호출하는 경우 루아는 그런 return문을
그 함수 처리의 물리적인 끝으로 보고, 그 함수를 스텍에서 제거한 후 즉시 다음 함수로 건너 뛴다.

이러한 능력은 함수 호출들이 꼬리에 꼬리를 물고 있는 상황이라도 루아의 스택이 넘치는 일을 방지할 수 있다는 점에서 유용하다.
함수 호출시 루아는 호출에 관련된 값들과 변수들을 '스택'에 쌓는다. 그 값들은 호출이 끝나면 다시 스택에서 뽑혀진다. 그런데 함수 안에서 또 다른 함수를
호출하는 '호출연쇄'가 길게 이어지면, 크기가 제한된 스택에 꽉 차서 루아 인터프리터가 죽는 일이 발생한다.

그러나 앞에서 언급한 특징을 이용하면 그런 일을 피할 수 있다.


ㅇ 표준 라이브러리

- assert(청크)()
 컴파일 된 루아 스크립트 청크를 하나의 처리 함수처럼 실행할 수 있도록 하는 수단이다.
컴파일된 스크립트 청크를 가리키는 변수를 인수로 해서 assert를 호출 하면 그 청크가 즉시 실행된다.
컴파일된 스크립트 청크를 가리키는 변수를 인수로 해서 assert를 호출하면 그 청크가 즉시 실행된다.
루아 스크립트 청크는 loasstring 이나 loadfine 함수로적재, 컴파일 할 수 있다.

게임 개발에서는 loadfile은 그리 유용하지 않음 ==> 좀 더 간편한 dofile이 있음 , 그러나 loadstring과 assert 함수를 결합하면 루아 스크립트 안에서 청크를 만들고 그것을 
즉시 실행한다는  유용한 능력을 얻을 수 있다.

loadstring 함수만으로도 임의의 청크를 실행할 수 있다. 다음이 그러한 예이다.
assert와 다른 점은, 문자열로 된 코드에 문제가 있을 때 assert는 그 문제를 직접 언급하는 오류를 메시지를 보여주지만 이런 경우에는 그리 도움이 되지 않는 오류 메시지를 출력한다는
것이다.


- dofile (파일 이름)
 루아 스크립트 파일을 적재하고 즉시 처리한다. 일반적으로 이 함수는 이후에 호출될 함수 정의들을 담은 파일을 적재할 때 사용하나, 자료 파일을 적재하거나 파일에 담긴 루아 코드를
즉시 실행하는 용도로도 사용할 수 있다.
 dofile 함수는 프로그램 실행 파일이 있는 디렉토리를 현재 디렉토리로 간주한다.


 ㅇ 수학 함수들
 표준 C라이브러리의 수학 함수들에 해당하는 함수들을 제공한다. 이들은 대부분은 그냥 해당 C 함수에 대한 루아글루 인터페이스일 뿐이다. 
이 수학 함수들은 math 테이블 안에 들어 있다.

math.abs,   math.max,  mat.asin, math.mod, math.atan, math.atan2, math.pow, math.pow, math.rad, math.ceil math.sin
math.cos, math.sqrt, math.deg, mat.tan, math.exp, mathfrexp, math.floor, math.random, math.log10, math.randomseed
math.pi(원주율)

 다음은 게임 개발에 유용한 함수 몇가지

- math.floor()
 주어진 수의 소수부를 버리고 정수로 변환한다. 반올림이 필요하다면 다음 예처럼 0.5를 더한 값을 이 함수에 넣으면된다.
 



- math.random()
 아무 인수도 지정하지 않은 경우 math.random() 함수는 (대부분의 다른 언어들의 난수 함수와 마찬가지로) 0에서 1사이의 의사난수를 돌려준다.
이 함수의 유용한 기능은 생성할 난수의 최대, 최소값을 지정할 수 있다는 것이다.  그런 경우 이함수는 그 범위안의 정수 난수를 돌려준다.



이 난수 함수로 좀 더 무작위로 보이는 값들을 얻으려면, 프로그램을 시작할 떄 난수 발생기에 고유한 값을 종자값(seed)으로 제공할 필요가 있다. 
이런 작업은 스크립트쪽에서 게임을 환경을 초기화할떄 사용됨, 디버깅을 하는 도중에 어떤 고정된 난수 종자값을 계속 사용하는 게 도움이 된다.
그러면 게임을 실행할때마다 매번 동일한 난수열을 얻게 되므로 디버깅이 편해진다.


- math.min(), math.max)
 게임 개발에서는 어떤 값들의 집합에서 가장 큰 값 또는 가장 작은 값을 알아내야 하는 경우가 많이 있다.
예를 들면 주인공 캐릭터의 큰 능력치라던가, 선거인단 수가 가장 큰주 등)  그런 목적으로 사용할 수 있는 것이 math.min 과 math.max이다.
이 함수는 임의의 개수의 매개변수들을 받고 그 중 가장 큰것(math.max) 또는 가장 작은 것(math.min)을 돌려준다.

루아에서는 자료를 테이블에 담아두는 것이 대부분이다. 그런데 이 함수들을에 테이블을  그대로 사용할 수 없다.
방법은 테이블에 담긴 값들을 쉼표로 연결한 문자열로 적절한 호출문 청크를 만들어서 실행하는 것이다. 

\

소스 다운



○ 문자열 고급
 안정적인 문자열 처리 능력은 루아의 커다란 장점들 중에 하나이다. 루아는 기본적인 문자열 조작 함수들뿐만 아니라 다양한 패턴 부합 함수들도 제공한다.
여기서는 게임 개발과 관련된 큰 몇가지만 살펴본다. 모든 루아 문자열 조작함수들에 대한 좀더 자세한 정보는 루아 온라인 매뉴얼에서 찾을 수 있다.


- 형식 변환

 tonumber, tostring :   문자열을 수치로 변환할 때, 수치를 문자열로 변환할떄



 string.char(n1, n2 , ... ) : 주어진 수치들을 ASCII 값들로 해석하고 그 ASCII 값들에 해당하는 문자들로 하나의 문자열을 만들어서 돌려준다
                                     자주 쓰이지는 않지만, 게임 저장 파일에서 파일을 사람이 좀더 읽기 쉽도록 만들기 위해서 라인피드(linefeed)문자를 삽입할 때 유용
                                      ex)  myFile:wrtie(string.char (10) ) -- 열린 파일에 라인피드를 출력

string.len(myString) : 주어진 문자열에 담긴 문자들의 개수를 돌려 준다. "1234"일경우 --> 4를 출력

string.sub(myString, start, end) : 문자열 안이 한부분을 돌려준다. start는 그 부분문자열의 시작, end는 끝 위치




 string.format()  : 문자열을 포매팅하는 함수, 여러 변수들의 값을 포함하는 문자열을 만들때 편리
 



 string.find(sourceString, findString)
 string.find 함수는 첫번째 인수로 주어진 원본 문자열에서 두 번째 인수로 주어진 대상 문자열이 처음 나타난 부분의 시작과 끝 위치를 돌려준다.
 대상 문자열을 찾을 수 없으면 nil을 돌려준다.




- 문자열과 패턴
 루아의 가장 강력한 물자열 함수들은 패턴을 사용하는 함수들이다. 패턴이란 루아가 문자열로부터 의미 있는 결과를 걸러내도록 하는 일종의 틀이다.



위의 filter는 우리가 원하는 결과만을 통과시키는 틀이다. 달러표시와 소수점은 정확히 일치해야 하지만, 숫자는 어떤 것이든 상관없다.

해당 패턴 문자를 대문자로 쓰면 반대의 의미가 된다. 즉, %d는 모든 숫자와 부함 %D는 숫자가 아닌 모든 문자와 부합

루아의 패턴을 만드는 데 쓰이는 요소들
 .    :  모든문자
 %a : 영문자
 %c : 제어문자
 %d : 숫자
 %l : 영문 소문문자
 %p : 문방 부호
 %s : 공백 문자
 %u :  영문 대분자
 %w : 영수문자( 영문자와 숫자) )
 %x  : 16진수 숫자
 %z : 코드값 0에 해당하는 문자
 

string.gsub ( sourecString, paten, replacementString) :  sourceString으로 주어진 문자열에서 pattern으로 지정된 패턴에 부합하는 부분을 replacemenstString으로
 주어진 문자열로 치환한다. 



추가적인  인수를 통해서 최대 치환 횟수를 지정하는 것도 가능 




h로 시작하는 단어를 찾는다. &a+는 빈캄이나 문장부호, 숫자등이 나오기 전까지의 임의의 개수의 영문자들을 의미, 마지막 인수2는 그런 부분을 처으 두개만 치환하라는 뜻


string.gfind( sourceString, pattern ) : 첫번째 주어진 문자열에서 두번째 인수로 주어진 패턴을 한번에 하나씩 차례로 찾아나간다.



%a+ 패턴은 영문자로 된 개별 단어와 부합한다.(게임 자료를 피싱할 때 매우 유용하다)

○ 테이블 고급
 테이블의 가장 단순한 용법은, 다른 언어들에서 볼 수 있는 보통의 배열처럼 사용하는 것이다.

 - table.getn(myTable) : 주어진 테이블의 요수 개수를 돌려준다. 다만 돌려주는 것은 연속적인 수치 색인 요소들의 개수라는 점을 주의
    문자열 색인 요소들이나색인 수치가 멀리 떨어져 있는 요소들은 개수에 포함되지 않는다.




- table.sort(myTable) : 테이블의 요소들을의 크기를 비교해서 오름차순으로 정렬,
                                 크기 비교 판정을 수행하는 함수의 이름을 두번째 인수로 지정해서 크기 방식을 임의로 바꾸는 것도 가능





소스 다운 :



- table.insert(myTable, position, value) : table.insert() 함수는 테이블에 새 요소를 추가한다. position인수는 생략할 수 있다.
   생략하면 value 인수로 주어진 요소를 테이블의 끝에 추가한다. position 인수를 지정하면, 그 위치에 요소를 삽입하고 다른 요소들의 색인을 적절히 조정한다.
  
   ex) table.inset( myTable, position )

-  table.remove(myTable, position) : 테이블에서 특정 요소를 제거하고 그 요소를 돌려준다. 필요하다면 색인을 다시 조정한다. position 매개변수는 생략할 수 있으며, 생략한
    경우 테이블의 마지막 요소를 제거한다.

    ex) print(table.remove(myTable, 25) )


ㅇ 테이블 요소의 간결한 참조 표기
루아의 테이블은 소위 연관 배열(associative array)이라고 하는 것으로, 정수만 아니라 어떠한 값도 색인으로 사용할 수 있으며, 대괄호 대신 마침표를
이용해서 색인(이런 경우 '키key'라고 부른다)을 지정할 수 있다. 다음 예제를 보자

 myData = {}
 myData.name = "Thardwick"
 myData.class = "barbarian"
 myData.str = math.random(3, 18)
 myData.dex = math.random(3, 18)

 마치 C에서 구조체 필드들에 접근하는 것과 비슷한 방식이다. 그리고 이런 식의 이름있는 색인을 가진 테이블에, 다음 처럼 보토의 숫자 색인을 추가하는 것도 가능하다.

 myData[1] = 17
 myData[2] = 34
 myData[3] = 24

 루아의 테이블은 구조체에 임의의 개수의 자료 값들이 추가된 형태의 강력한 자료구조로 사용할 수 있을 정도로 유연한다.

- 다차원배열
 다차원 배열을 만드는 것 역시 간단함. 다차원 배열은 테이블 안에 테이블이 들어 있는 것으로 생각할 수 있다.

  widget =  {}
  widget.name = {}
  widget.cost = {}
  widget.name[1] = "Can opener"
  widget.cost[1] = "$12, 75"
  widget.name[2] = "Scissors"
  widget.cost[2] = "$8.99"

 widget.cost[1]이라는 표현은 우선 widget 안에 있는, 키가 cost인 요소를 가리킨다. 그 요소는 하나의테이블을 가리키며, [1]은 그 테이블의 첫번째 요소에 해당한다.
위의 코드를  실행한 후 table.getn(widget.cost)를 실행하면 2가 나오는데, 이는 cost가 가리키는 테이블에 요소가 두개가 있다는 뜻이다.


- pairs()
  pairs() 함수는 테이블의 요소를 한번에 하나씩 접근할 때 사용,
 
   myNames = { "Fred", "Ethel", "Lucy", "Ricky", "Tockey", "Betsy", "Bill" }
 
  for  index , value in pairs(myNames) do
   print(index, value)
end

테이블의 끝에 도달해서 더 이상 돌려줄 요소가 없으면 반복이 끝난다.

table.getn(myNames) 도 같은 일을 할 수 있지만 이 방식으로는 정수가 아닌 색인을 가진 요소들에 접근할 수 없다. 그런 요소들을 포함한 모든 요소들에 접근하기 위해서는
paris()를 사용해야 한다.



ㅇ 기본적인 입출력
 루아를 저장/적재 시스템으로 사용하면 자료를 파싱(parsing)하는 과정을 완전히 생략할 수 있다는 장점이 생긴다.
게임 자료 자체를 유효한 루아 스크립트 파일 형태로 만들어 두면, 자료를 파싱하는 일은 루아가 알아서 처리해 준다.

파일을 여는 방법 io.open() 함수를 사용
myFile = io.open("test_data.lua", "w")
첫번째 인수는 열고자 또는 만들고자하는 파일의 이름,  두번째 파일을 어떻게 사용할 것인지를 결정하는 문자열
파일을 기록 할때는 w를 지정, 주어진 파일 이름에 해당하는 파일이 없으면 새로 만든다. 기존의 파일이 존재하면 기존의 파일 내용은 사라진다.
a는 기존 내용을 보존하고 파일의 끝에 새로운 내용을 추가한다.

파일을 열거나 생성하는 도중에 뭔가 오류가 발생한 경우 io.open()은 nill을 돌려준다.




소스 다운 :


실행결과 Test_data 파일을 열어보면




이 예제는 파일을 열고 write() 함수로 파일에 문자열을 기록한다. 파일을 읽기 쉽도록 줄바꿈도 추가하는데, 이때 사용하는 것은 앞서 이야기한 string.char()를 사용한다.
문자열을 다 출력한 후에는 io.close()로 파일을 닫는다.