U E D R , A S I H C RSS

WOW Add On/2011년프로젝트/초성퀴즈



1. 목적


2. 과정


2.1. Lua 설치 및 셋팅

WOW의 애드온은 Lua나 Ruby와 같은 스크립트 언어를 사용하는것으로 알고있다.

그래서 Programming in Lua라는 책을 도서관에서 빌려왔다. 아마 빌려온지 1주일은 됬겠지.

Lua 개발환경을 셋팅하는데 좀 걸렸다.

Lua-Eclipse를 받아서 깔고. (LunarEclipse라는 것도 있단다)


Eclipse에서 Java외의 다른것을 돌리려면 당연 인터프리터나 컴파일러를 설치해주어야 한다. 그래서 Lua를 설치하려했다. LuaProfilerLuaInterpreter를 설치해야한다는데 도통 영어를 못읽겠다 나의 무식함이 들어났다.

여튼 어찌어찌해서

http://luaforge.net
에서 루아 Windows 인스톨러를 받아서 설치하게됬다.

5.1.4-35 2010-04-07 13:09
LuaForWindows_v5.1.4-35.exe

얏호. 설치.

설치된경로를 따라 Eclipse의 Profiler말고 Interpreter로 lua.exe로 path를 설정해주면 Eclipse에서 Project를 만든뒤 출력되는 Lua파일을 볼 있다.

2.2. 자바에서 초성빼오기


WOW애드온 초성퀴즈를 만들기위함이기 때문에.

당연히 한글의 초성을 빼올 있어야한다.

그래서 찾아봤더니 UTF-8방식으로 빼올 있다고 한다.

근데 이상한데 UNICODE에서 계산해서 빼오더구만.
추가 : 알고보니 UNICODE를 포함하는 방식중 하나라고한다. 근데 더 모르겠는데... U-00000800 - U-0000FFFF 범위에 들어간다고 하는데??
아악 모르겠어!! 그래서 추가로 링크를 걸어본다.


우선 자바 코드는 다음과 같다.
/////////////////////////////////////////////
package utfencoding;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class UtfEncoding {

final static char[] first = {
'ㄱ','ㄲ','ㄴ','ㄷ','ㄸ','ㄹ','ㅁ','ㅂ','ㅃ','ㅅ','ㅆ','o','ㅈ','ㅉ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
};
final static char[] middle = {
'ㅏ','ㅐ','ㅑ','ㅒ','ㅓ','ㅔ','ㅕ','ㅖ','ㅗ','ㅠ','ㅘ','ㅛ','ㅙ','ㅚ','ㅜ','ㅝ','ㅞ','ㅟ','ㅡ','ㅢ','ㅣ'
};
final static char[] last = {
'\0','ㄱ','ㄲ','ㄳ','ㄴ','ㄵ','ㄶ','ㄷ','ㄹ','ㄺ','ㄻ','ㄽ','ㄾ','ㄿ','ㅀ','ㅁ','ㅂ','ㅄ','ㅅ','ㅆ','o','ㅈ','ㅊ','ㅋ','ㅌ','ㅍ','ㅎ'
};

/**
  • @param args
  • /
public static void main(String[] args) {
String temp = "바";

char a = temp.charAt(0);
a = (char) (a - 0xAC00);
int cho = a/21/28;
int joong = ( (a %(21*28))/28);
int jong = (a%28);
System.out.println(""+ (char)firstcho + (char)middlejoong + (char)lastjong);
}

}
////////////////////////////////////////

보면 알겠지만 자바에서 작성되는건 UNICoDE이다(아마도?)

현재 Eclipse개발환경중 문자 Encoding은 UTF-8방식이다.
거기서 한글을 빼온후 UTF-8에 따라 0xACC00 (<- UTF-8시작) 을 빼고 초성 중성 종성을 다음으로 빼온다

초성 = (원문자 /21 /28)
중성 = ( (원문자%(21*28))/28)
종성 = ( a%28)

이렇게 하고 문자 규칙에 따라 빼온다.
규칙은 위의 코드를 보면 알 있다.

2.3. 첫 와우 Addon 만들기


첫 와우 Addon을 제작하게 되었다.
이름하야 Hello WoW!!

천리길도 Hello World!부터라는 지론에 따라
어떻게 연동되는지를 살펴보겠다.

기본 프로그램 정보를 담고있는 *.toc파일.
그리고 UI와 LUA를 Mapping 시켜주는 *.xml
그리고 연산을 해주는 *.lua

작성을 해보자

기본적으로 "/World of Warcraft/interface/addons/애드온명" 으로 폴더가 만들어져있어야한다.

그리고 HelloWoW.toc으로 기본설정을 잡아야하고
toc에 정보를 넣어주어야한다.
HelloWoW.toc

HelloWoW.xml
HelloWoW.lua

Interface에는 현재 버전 정보가 들어가있다.
http://www.wowwiki.com/Getting_the_current_interface_number
로 현재 인터페이스 정보를 알아볼 있다.
내가 작성하는건 4.2니까 40200이겠지 인터페이스 정보를 갱신안해놓으면 와우 애드온 로드시 '구버전'으로 제대로 로드가 안될 있다.

Title은 짐작대로고
노트는 그냥 설명이고
Author도 있음.

HelloWoW.xml

<UI xmlns="http://www.blizzard.com/wow/ui" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui">
<script file="HelloWoW.lua"/>
<Frame name="HelloWoWFrame">
<Scripts>
<OnLoad>
HelloWoW_ShowMessage();
</OnLoad>
</Scripts>
</Frame>
</UI>


HelloWoW.lua

function HelloWoW_ShowMessage()
message("Hello WOW!");
end

WoW API function을 lua에 넣어준적은 없지만 자동으로 되나보다.
신기한데?

이렇게 작성하면 Hello WOW!가 뜬다.

2.4. WOW API를 이용해서 아이템 이름을 불러오자!


WOW는 WOW API를 소개해주는 wiki를 가지고 있고 API를 가보면 다양한것들이 있다.
http://www.wowwiki.com/World_of_Warcraft_API
또한 Widget과 LuaFunction의 사용정보를 볼 있다.

여기서 아이템 초성 퀴즈의 기본은 아이템 이름 DB를 검색할 있어야한다. 근데 WOW아이템은 현재 아이템 넘버만 7만을 넘는다. 중간중간 비어있는 index도 있다. 이게 뭥미. 아마 suffix때문일것으로 예상된다. suffix란 아이템에 부가적으로 붙은 옵션을 item index에치화 한것을 부르는 말인데 그것에 따라 아이템의 index가 결정되는것 같더라. (아직 정확히는 모른다)

그리고 아이템 리스트를 한꺼번에 뽑는것을 지원해주는 것이 아니라서 누구드랍, 블루아이템 같은 애드온은 심지어 아이템 ID와 캐시 정보까지 구해놓고 있더만.. 까보고 놀랐네.

와우 게임정보 공식 사이트에 가면 아이템은 50760개라고 나와있다.
4.2 전율하는 불의땅의 아이템이 공개 후에 업데이트 된것이다.


저 번호에 아이템 넘버를 넣으면 해당 아이템 정보가 들어가있는 페이지로 이동하게 된다. DB를 WOW안에서와 웹페이지 똑같이 관리 하는것 같은데 이렇게 똑같이 되있으니까 좋다. 사실 Addon에서 페이지에서 하나 빼오는걸로 생각했지만 가장 좋다고 생각하는것은 블루아이템과 누구드랍처럼 아이템 이름을 보관해놓고 Addon을 돌려보는것이 정신건강에 이로울것이라고 생각했다.

아직 WOW addon에 대해서 모르는것도 있고. WOW에서 사용하는 몇몇 자료구조가 특이한건 알겠다. 젠장. Item에 뭔 부가정보가 그렇게 많이 붙어!! 여튼 그것에 대해서는 한번 다시 다루어보아야겠다.

HelloWoW정해서 만든것을 한번 다시 보자

function HelloWoW_ShowMessage()

local baseName
local count = 1
local base = {}
baseName = GetItemInfo(25)
for i=25, 70000 do
baseName = GetItemInfo(i)
if count > 10000 then
break
end

if baseName ~= nil then
basecount = baseName
count = count+1
end
end
for i = 1, #base do
print(basei)
end
end

우선 일단 아직 아이템 번호는 7만을 넘지 않는다 wow아이템정보에 관한건 나중에 조사하기로 하고 그래서 i 는 와우 일반 item의 최소 시작값인 '낡은쇼트단검' 25부터 시작해서 1만개의 아이템을 검색해서 base테이블에 한개씩 저장해서 출력한다.

결과는 잘된다. 조금 느리겠지만 이걸 DB화해서 저장해놓았다가 해도 좋을듯 한다. 최적화따윈 우선 버리고 해보자.


정 했다

아이템 정보 25번 부터 7만 사이의 랜덤변를 불러와서
아이템 정보가 나온다면 출력!
아이템들을 모두 가져오는것 보단 빠르다. 안전하진 않지만..

function HelloWoW_ShowMessage()

local baseName = nil

repeat
i = random(25,70000)
baseName = GetItemInfo(i)
until baseName ~= nil

print(baseName)
end


자 그럼 파싱을 해보자

2.5. Lua에서 초성빼오기


결국 초성을 빼오게 되었다
황현이 준 UTF-8로 첫 바이트로 뒤에 몇바이트를 차지하는 문자인지 보여주는 도표를 가지고 Lua로 바꾸어보았다.

헤더 바이트의 값의 범위로 한 UTF-8 글자의 바이트  알아내기

00000000 ~ 01111111 : 1 byte (0 ~ 127)
11000000 ~ 11011111 : 2 bytes (192 ~ 223)
11100000 ~ 11101111 : 3 bytes (224 ~ 239)
11110000 ~ 11110111 : 4 bytes (240 ~ 247)
11111000 ~ 11111011 : 5 bytes (248 ~ 251)
11111100 ~ 11111101 : 6 bytes (252 ~ 253)

아래 함는 문자열이 들어오면 몇 byte를 차지해주는지 알려주는 함이다
function charLen(str)
	calc = string.byte(str:sub(1,1))
	--lua에서 byte로 char형을 변환하면 0이상이 나온다.
	if(calc >= 0 and calc <= 127) then return 1
	elseif(calc >= 192 and calc <= 223) then return 2
	elseif(calc >= 224 and calc <= 239) then return 3
	elseif(calc >= 240 and calc <= 247) then return 4
	elseif(calc >= 248 and calc <= 251) then return 5
	elseif(calc >= 252 and calc <= 253) then return 6
	else return 0
	end			
end

다시한번 말하자면 한글은 3byte이다.
이제 3byte를 찾을 있으니 영문과 특문자가 섞여있어도 초성만 뽑아내는데는 아무 문제 없을것이다.
자 이제 한글을 받아와서 초성만 뽑아 보자.
~lua
Chosung = {"ㄱ","ㄲ","ㄴ","ㄷ","ㄸ","ㄹ","ㅁ","ㅂ","ㅃ","ㅅ","ㅆ","o","ㅈ","ㅉ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
Middle = {"ㅏ","ㅐ","ㅑ","ㅒ","ㅓ","ㅔ","ㅕ","ㅖ","ㅗ","ㅠ","ㅘ","ㅛ","ㅙ","ㅚ","ㅜ","ㅝ","ㅞ","ㅟ","ㅡ","ㅢ","ㅣ"};
Last = { "\0","ㄱ","ㄲ","ㄳ","ㄴ","ㄵ","ㄶ","ㄷ","ㄹ","ㄺ","ㄻ","ㄽ","ㄾ","ㄿ","ㅀ","ㅁ","ㅂ","ㅄ","ㅅ","ㅆ","o","ㅈ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};	

function samplingFirst(str)
	-- 1110xxxx 10yyyyzz 10zzwwww
	local first = string.byte(str:sub(1,1)) - 224  -- 1110xxxx
	local second = math.floor((string.byte(str:sub(2,2)) - 128)/4) 
	local third = math.floor((string.byte(str:sub(2,2))%4)*4 + (string.byte(str:sub(3,3)) - 128)/16)
	local fourth = math.floor(string.byte(str:sub(3,3))%16)
		
	local unicode = first * 16* 16* 16 + second * 16 * 16 + third * 16 + fourth
	unicode = unicode - 0xAC00
	local cho = math.floor(unicode / 21/ 28)
	local goong = math.floor((unicode % (21*28))/28)
	local jong = math.floor((unicode % 28))

	return Chosung[cho+1];	
end

이제 한글 문자열을 집어넣는다면 초성만 빼와주는것을 발견할 있다. Lua에서 초성 ㄱ은 3byte로 인식되기 때문에 캐릭터형 'ㄱ'으로 감싸면 환경에 따라 에러가 날 있다. 따라서 문자열 " "로 감싸주었다.

이제 이것을 이용하여 "카르가스\:얼라이언스\:Aldiana"를 한글은 초성만 뽑고 그 외는 그냥 보존해서 출력해 보는것을 연습해보자

~lua
function cstc(len, str)
	return {type = len, body = str}; 
end

function makingTable(str)
	
	stringtable = {}
	for i = 1, #str do
		len = charLen(str:sub(i,i))
		if len ~= 0 then -- 이거 안좋은데..
			stringtable[#stringtable+1] = cstc(len, str:sub(i,i+len-1))
		end		
	end
		
	chosungtable = {}
	for i, v in ipairs(stringtable) do		
		if v.type == 3 then
			chosungtable[#chosungtable+1] = cstc(v.type, samplingFirst(v.body)) 
		else 
			chosungtable[#chosungtable+1] = cstc(v.type, v.body)
		end
	end
	
	return stringtable, chosungtable
end


local a = "카르가스\:얼라이언스\:Aldiana"
t,x = makingTable(a)
for i, v in ipairs(t) do
	print(v.type .. " " .. v.body)
end
for i, v in ipairs(x) do
	print(v.type .. " " .. v.body)
end

출력결과 :
카르가스:얼라이언스:Aldiana
ㅋㄹㄱㅅ:ㅇㄹㅇㅇㅅ:Aldiana

자 아이템 목록도 불러왔고 초성도 빼왔으니 합치는걸로 가보자

2.6. WOW Slash Command


와우에는 SlashCommand가 있다. 그게 뭐냐면..

/명령어 란 것이다.

Addon을 만들고 초성퀴즈는 SlashCommand로 시작하게 할려고한다.

/초성퀴즈 시작 30
하면 30개의 문제가 진행된다.

이외에도 기본셋팅이 필요하기도 하고 진행 시나리오도 짜야하지만 우선 SlashCommand로 작동시켜보자.

SlashCommand에 등록시키는것을 알아보자.
~lua
function HelloWoW_OnLoad(self)
	SLASH_HelloWoW1 = '/hiw';
	SLASH_HelloWoW2 = '/hellow';
	SLASH_HelloWoW3 = '/HelloWOW';
	SlashCmdList["HelloWoW"] = function (msg)
         	   HelloWoWF(msg)
	end
end

function HelloWoWF(msg, editbox) -- 4.
	print("Hello WOW")
end

와우 입력 :/hiw
와우 시스템 출력 : Hello WOW
SlashCmdList["애드온명"] = function(msg) 를 하고
SLASH_애드온명# = '/명칭'으로 변를 설정해주면
WOW에서 '/명칭' 명령어는 msg를 들고 function으로 들어가게 된다.

위의 예제에서는 3개의 명칭이 있는것이다

SLASH_HelloWoW1 = '/hiw';
SLASH_HelloWoW2 = '/hellow';
SLASH_HelloWoW3 = '/HelloWOW';

각각 실행하면 같은 결과가 나오는것을 알 있다.

다음에 할일은 String 파싱으로 시나리오를 꾸며보자.

2.7. CommandMessageParsing


자 슬래시 커맨드를 통해 받아온 msg를 출력해보고
parsing해서 원하는 문자만 뽑아보자.

실행 됬을때를 위해서 실행되고 있는지 않는지에 대한 flag를 넣어봤는데
Addon이 적재되면 해당 프로그램 전체가 올라간것이기 때문에 WOW 메모리에 할당이 되있다. 따라서 Addon에서 flag는 따로 메모리를 할당 받았다는 얘기. 따라서 전역 변는 addon의 함 어디서든 접근이 가능하단 소리가 된다.

~lua
SLASH_HelloWoW1 = '/hiw';
SLASH_HelloWoW2 = '/hellow';
SLASH_HelloWoW3 = '/HelloWOW';
flag = false;

function HelloWoW_ShowMessage()
	
	SlashCmdList["HelloWoW"] = function (msg)
		HelloWoWF(msg)
	end;
	
end
	
function HelloWoWF(msg, editbox)
	print(msg)

	local x;

	if string.find(msg,"시작") then		
		if flag == false then
			x = msg:match("시작%s(%d+)$");
			print(x)
			flag = true;			
			if x then
				print("초성퀴즈 시작 " .. x .. " 문제!");
			else print("초성퀴즈 기본 시작 10 문제!");
			end
		else 
			print("초성퀴즈가 이미 실행중입니다");
		end
	elseif msg:find("종료") then
		if flag == true then
			print("초성퀴즈를 종료합니다");
			flag = false;
		else 
			print("초성퀴즈가 실행중이지 않습니다");
		end
	end	
end
WOW 입력 : /hiw 종료
WOW 출력 : 초성퀴즈가 실행중이지 않습니다
WOW 입력 : /hiw 시작 50
WOW 출력 : 초성퀴즈 시작 50 문제!
WOW 입력 : /hiw 종료
WOW 출력 : 초성퀴즈를 종료합니다
WOW 입력 : /hiw 종료
WOW 출력 : 초성퀴즈가 실행중이지 않습니다
WOW 입력 : /hiw 시작
WOW 출력 : 초성퀴즈 기본 시작 10 문제
WOW 입력 : /hiw 시작 50
WOW 출력 : 초성퀴즈가 이미 실행중입니다

string.find(msg,"시작")은 msg에서 "시작"이란 단어가 시작되는곳과 끝을 다중 인자로 2개 넘겨주며 값이 없을때는 nil을 리턴한다. 따라서, if문에서 쓰면 lua에서는 nil을 제외한 모든 값은 (심지어 0과 공백스트링""도) true로 인식한다.

처음에 문제가 생겼었는데 Eclipse에서 테스트하던 string.find(msg,"시작")이 WOW에서 글씨가 깨지며 정상 작동하지 않았다. 그 이유는 무엇이냐 하면 WOW Addon폴더에서 lua파일을 작업할때 메모장을 열고 작업했었는데 메모장의 기본 글자 Encoding타입은 윈도우에서 ANSI이다. 그렇기 때문에 WOW에서 쓰는 UTF-8과는 매칭이 안되는것! 따라서 메모장에서 새로 저장 -> 저장 버튼 밑에 Encoding타입을 UTF-8로 해주면 정상작동 한다. 이래저래 힘들게 한다.

x = string.match(msg,pattern) 는 pattern에 따라 msg를 매칭해주고 서브 패턴에 맞는 인자를 assign 연산자(=)를 통해 넘겨주게 된다.
위의 코드에서는
x = msg:match("시작%s(%d+)$");
으로 해석하자면 시작 다음에 공백이 하나있고 숫자집합 다음에 문자열이 끝나는 문자열에서 숫자 집합을 x로 가져오란 소리다.


패턴 매칭은 여기 있다. Lua에서 Sub pattern에 의해 변를 가져오는건 더 찾아봐야한다.

일단 이런 저런 것을 참조하면 하나 하나 완성하고 있다.

짜증 : 테스트 케이스를 와우에 가서 일일이 입력하는것도 지겹다!!!
쉬운방법없나? 더러운 코드!! 청소해줘!!

다음 : 채팅창과 lua파일을 연동해서 모든 메시지를 처리하는걸 보자.

2.8. FrameEventHandling


가장 빡센 공부가 될걸로 생각된다.

항상 UI를 만지는데 어려운건

동기화, Event Handling, UI꾸미기니깐

WOW API를 뒤지고 뒤져서 우선 Frame을 주고 Frame에 DefaultChatWindow에서 메시지를 받아야 할것 같다.

우선 Lua에서 Frame을 불러오려면 CreateFrame을 해봐야겠지

CreateFrame()
newFrame = CreateFrame("frameType"[, "frameName"[, parentFrame[, "inheritsFrame"]]]);

frameType
Frame 타입에 따라 생성되는게 달라진다 "EditBox","Button"등 UIObject를 String 타입으로 넣어 설정해주면 타입이 결정된다


frameName
이름을 주면 된다 String타입.

parentFrame
부모님을 지정해준다.

inheritsFrame
상속할 Frame을 지정하는것 같은데 없으면 상속 안된단다. 이런..

newFrame:setScript("OnLoad",funcname)
이렇게 하면 OnLoad라는 이벤트때 funcname이 실행된다.

http://www.wowwiki.com/WoW_constants
와우에서 상값으로 가지고 있는 값들은 여기 나와있다.

여기서 MainFrame을 찾아보자.

채팅에 관련해서..
http://www.wowwiki.com/Chat
Event중에 CHAT_MSG_CHANNEL 이 있는데

예제로 들은게 뭐냐하면 도시에서만 2. 거래 채널을 사용할 있는데 밖에 나갔을때 이 채널이 활성화되있지만. CHAT_MSG_CHANNEL 이벤트를 더이상 서버에서 보내지 않는다는것이다.

여튼 메시지를 받으면 발생되는 이벤트란 소리겠지.

다음의 에제는 기본 프레임에 RegisterEvent를 입력시키고 챗이 들어올때마다 출력하는것이다.
function HelloWoW_ShowMessage()
	DEFAULT_CHAT_FRAME:RegisterEvent("CHAT_MSG_CHANNEL");
		local function eventHandler(self, event, ...)
			if (event == "CHAT_MSG_CHANNEL") then
			local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = ...;
				print(arg1);
			end
		end
	DEFAULT_CHAT_FRAME:SetScript("OnEvent", eventHandler);
end

어렵다.

자 이제 채팅 메시지를 다 뽑아버려보자.


이곳에 가면 커뮤니케이션 이벤트를 볼 있는데 내가 이번에 대상으로 하는 이벤트는 채팅 관련으로 "파티","길드","일반","공격대"로 해당 Chat이 발생할때마다 날아오는 Event를 캣치해서 가져온다.

frame을 만들어 놓으면 Event가 발생해서 받는 대상에 상관없이 모든 이벤트가 날아오게 된다.

자.. 다시 xml로 돌아가 보자.

HelloWoW.xml
<UI xmlns="http://www.blizzard.com/wow/ui" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui">
<script file="HelloWoW.lua"/>
<Frame name="HelloWoW">
<Scripts>
	<OnLoad>
		HelloWoW_ShowMessage(self)
	</OnLoad>
	</Scripts>
</Frame>
</UI>
self로 바꼈다!! self는 Frame 자신을 일컫는다.

On_load가 되면 자기 자신을 넘겨서 HelloWoW를 실행시킨다.

HelloWoW.lua
function HelloWoW_ShowMessage(self)

	local frame = self;
		
	frame:RegisterEvent("CHAT_MSG_CHANNEL");
	frame:RegisterEvent("CHAT_MSG_SAY");
	frame:RegisterEvent("CHAT_MSG_PARTY");
	frame:RegisterEvent("CHAT_MSG_YELL");
	frame:RegisterEvent("CHAT_MSG_GUILD");
	
	print("Load OK")
	local function eventHandler(self, event, ...)
		if (event == "CHAT_MSG_CHANNEL" or event == "CHAT_MSG_SAY") then
		local arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 = ...;
			print(arg1);
			print(arg2);
		end
	end
	
	frame:SetScript("OnEvent", eventHandler);
end

이렇게 하면 이벤트 등록한것은
frame:RegisterEvent("CHAT_MSG_CHANNEL");
frame:RegisterEvent("CHAT_MSG_SAY");
frame:RegisterEvent("CHAT_MSG_PARTY");
frame:RegisterEvent("CHAT_MSG_YELL");
frame:RegisterEvent("CHAT_MSG_GUILD");
다섯가지지만 CHAT_MSG_CHANNEL CHAT_MSG_SAY 이것 두가지만 출력하는것을 알 있다.

자 이제 이것을 실행하면 로드가 되면 Load_OK라고 하면서 진행된다.

이제 채팅창에 올라오는 모든 문자를 print해준다.

eventHandler(self, event, ...)

self는 이벤트를 받은 frame이나 객체를 말하고 event는 해당 이벤트. 그리고 가변 인자 ... 에서 뽑아온 arg1~arg9는


Communication Event가 발생될때 건네주는 인자를 받아와주는것으로 대체적으로 arg1에는 msg arg2에는 author가 들어가 있다.

이제 다음에는 타이머를 쓰는것을 배워보자.

Timer + CommandMessageParsing + FrameEventHandling로 초성퀴즈를 만들 있다.


2.9. 자동 Update함 찾고 timer찾기


여행갔다와서 다 까먹은다음에 하는 Addon만들기.

Timer는 어떻게 만들까?

우선 WOW API에서 지원해주는것은 getTime()..
출력은 잘된다.
~lua
	seconds = GetTime();
	
	print("Current system uptime is: "..seconds.." seconds!");


우선 와우에서는 API설명상. Addon에 Sleep을 걸어놓으면 전체 시스템이 멈추는것으로 되어있다. 그렇다면 쓰레드를 만들어서 Sleep을 했다가. 복귀하면 하는건 어떨까? 우선 Sleep을 Lua의 System함 패키지인 OS에서는 지원해주지 않는다.

그래서 Lua 홈페이지에서 찾아보겠다.


이곳에서는 Lua로 작성된 다양한 환경의 sleep을 볼 있다.

Coroutine을 이용해서 만드는듯 한데 뭔가 이상하다.

Coroutine이 다른 쓰레드를 하는건가? 이상하다. Busy Wait로 만든 Sleep을 해서 하는건데 Thread해서 다른 타이밍에 나오는것 같지가 않다???

뭐여 이거.
~lua
local clock = os.clock
function sleep(n)  -- seconds
	local t0 = clock()
	while clock() - t0 <= n do end
end

co = coroutine.create(function()
	print("what")
	sleep(5)
	end
	
)

print("what")
print("main",coroutine.resume(co));
이걸 실행하면 what이 먼저나오는것 맞다.

그리고 sleep(5)를 하면 5초뒤에 실행이다. 주의. Millisecond가 아니다.


시뮬을 돌려본 결과 Coroutine은 다른 Thread 단위로 돌아가는것이 아니라 main에서 같이 돌아가는것으로 나왔다. 이거 진짜 어떤경우에 쓰는건지.. 여튼.

2주를 찾아서 자동으로 0.1초나 0.2초사이에 자동으로 Check를 해주는 함를 찾아해매었다.

그러다가 OnUpdate라는 Event를 발견하게 됬는데 설명을 보자
{{{
How Often Is It Called

The game engine will call your OnUpdate function once each frame. This is (in most cases) extremely excessive.
Ctrl + R to see the FPS on screen.
}}
FPS... Frame Per Second마다 불린단다. 그러니까 대략 1/60초 마다 한번 불린단다.

그래서 OnUpdate를 Frame에 등록하고 사용해보았다.

{{{
<UI xmlns="http://www.blizzard.com/wow/ui" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui">
  <script file="HelloWoW.lua"/>
  <Frame name="HelloWoW">
    <Scripts>
      <OnLoad>self.TimeSinceLastUpdate = 0 </OnLoad>
      <OnUpdate function="HelloWoW_OnUpdate"></OnUpdate>
    </Scripts>
  </Frame>
</UI>
}}}
자 Frame에 OnUpdate라는게 있고 OnUpdate가 실행될때마다 HelloWoW_OnUpdate라는 함를 실행해준다. 

근데 실행해보면..?

{{{
function HelloWoW_OnUpdate(self, elapsed)
  self.TimeSinceLastUpdate = self.TimeSinceLastUpdate + elapsed; 	

  if (self.TimeSinceLastUpdate > MyAddon_UpdateInterval) then
    --
    -- Insert your OnUpdate code here
    --
	TimeSet();
    self.TimeSinceLastUpdate = 0;
  end
end
	
function TimeSet(){
	seconds = GetTime();
	
	print("Current system uptime is: "..seconds.." seconds!");

}	
}}}

근데...

실행이 안된다?
이유를 보니깐.
{{{
When Is It Called Edit

OnUpdate is not called on any hidden frames, only while they are shown on-screen. OnUpdate will also never be called on a virtual frame, but will be called on any frames that inherit from one.
}}}

가상 Frame에선 안돌아간다는것이다. 아우.. 직접 Frame을 나오게 하고 만들어야 된다.

그래서 나는 예제로 배우는 프로그래밍 루아 라는 책을 도서관에서 빌려서 WOW Addon Studio를 깔게 되었다.

WOW Addon Studio는 WOW Addon의 UI디자인과 이벤트 헨들링을 도와주는 유용한 툴이다. 

Addon Studio는 크게 V1.0.1과 V2.0으로 나눌 있는데
1.0.1은 Visual Studio 2008을 지원하고 V2.0은 Visual Studio 2010을 지원한다 당연히 V2.0이 지원하는 기능은 더 많다고 써있다.

사이트는 http://www.codeplex.com/WarcraftAddOnStudio 에서 다운 받을  있다.

Basic Addon UI나 ACE UI를 지원해준다.

Project를 Visual Studio의 기본과 같이 만들으면 Frame이 하나 뜨고 Frame을 누르게 되면 우측 하단에 Properties에서 EventHandling을 하게 된다. 

넣고 싶은 이벤트를 누르고 Create버튼을 누르면 Function을 제작할  있게되고 Lua파일에 자동으로 Script가 등록되게 된다.

뭐 그래서.. 

OnUpdate에 적용할 함를 만들고 가상 프레임이 아닌 실제 프레임을 툴로 만들어 생성하게 하면
{{{
Frame.xml
<Ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.blizzard.com/wow/ui/">
	<Script file="Frame.lua" />
	<Frame name="Frame1" parent="UIParent" toplevel="true" movable="true" enableMouse="true">
		<Size>
			<AbsDimension x="359" y="303" />
		</Size>
		<Anchors>
			<Anchor point="TOPLEFT">
				<Offset>
					<AbsDimension x="100" y="-47" />
				</Offset>
			</Anchor>
		</Anchors>
		<Scripts>
			<OnUpdate>Frame1_OnUpdate();</OnUpdate>
			<OnMouseDown>self:StartMoving();</OnMouseDown>
			<OnMouseUp>self:StopMovingOrSizing();</OnMouseUp>
		</Scripts>
		<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
			<BackgroundInsets>
				<AbsInset left="11" right="12" top="12" bottom="11" />
			</BackgroundInsets>
			<TileSize>
				<AbsValue val="32" />
			</TileSize>
			<EdgeSize>
				<AbsValue val="32" />
			</EdgeSize>
		</Backdrop>
	</Frame>
</Ui>
}}}

이렇게 만들어주게 된다.

{{{

Frame.lua
-- Author      : June
-- Create Date : 2011-08-12 오전 2:23:05


function Frame1_OnUpdate()
	--put your event handler logic here
	seconds = GetTime();
	
	print("Current system uptime is: "..seconds.." seconds!");
end
}}}
{{{
## X-AutoGenerated: true
## X-GeneratorComment: Basic project properties and project files will be automatically added during deployment. Properties added by the user will be copied without changes.
## Interface: 40200
## Title: WowAddon1
## Notes: Basic WoW Addon
## Author: June
## Version: 0.1Beta
Frame.xml
Frame.lua
}}}

이렇게 자동생성된다.

해당 해드온을 돌리면 초당 60회씩 Time을 채크하는걸 볼 있다.

이제..

모든 준비가 맞춰졌다!!!


다음에 할일 : 프로그램 시나리오 및 구성


== 남기고 싶은 말 ==

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:28:23
Processing time 0.0685 sec