// 들어가며 :
단계별로 수정하며 github에 업로드하고 중간 버전의 링크를 글에 달아둘 것입니다.
다운로드하고 적용하는 방법은 지난 글에 다루었습니다.
링크로 들어가시면 파일 구조가 나오는데 해당 파일의 전체 코드를 확인하려면 파일명을, 이전 버전에서 바뀐 부분을 확인하려면 변경사항 커멘트를 달아둔 부분을 클릭하면 됩니다.
블리자드 기본 미디어 파일들은 별로 안예쁘므로 상태바 텍스쳐, 테두리 이미지, 폰트 등을 다른데서 가져다 사용할 것입니다.
텍스쳐 이미지들은 'wow interface media pack' 뭐 이런 식으로 검색해보면 구할 수 있습니다.
저는 oUF_Simple 레이아웃과 Ferous Media Pack의 미디어들을 사용했습니다.
oUF: 유닛프레임 제작 #1 :: 코드 구조 정리, 기본 elements 사용법
v0.2 : 코드 체계화, 플레이어와 대상 유닛프레임
v0.3 : 소환수, 대상의대상, 주시대상, 보스 프레임 추가
v0.4 : 시전바, 초상화, 버프/디버프 아이콘 추가
v0.1에서는 전체 코드를 한 파일에 넣었습니다. 코드가 길어지면 한 파일에 있을 때 원하는 부분을 찾아가는데 애로사항이 꽃피기 때문에 이제 이 예제도 코드를 여러 파일로 분리하겠습니다.
주된 구조는
설정값이 저장되는 config.lua
명령어 함수가 모여있는 api.lua
스타일 함수(유닛프레임 외형을 생성하는 함수)와 생성 명령을 포함한 core.lua
이렇게 세 부분으로 나누겠습니다.
Tukui나 Elvui는 module\unitframes 폴더 아래에 파일 하나에(Tukui는 core.lua) api와 생성, 크기와 위치 지정하는 코드가 들어있습니다.
각 유닛별 스타일 함수는 하위폴더 Units 에 유닛별로 파일 하나씩 분리되어 있습니다.
* .xml을 이용한 파일 추가, Name space table
.toc 파일은 애드온 재시작만으로 다시 불러지지 않습니다.
어짜피 애드온에 새 파일을 넣으면 와우를 껐다 켜야 하지만 파일은 미리 만들어두고 로딩하도록 .toc에 넣지만 않았다거나, 불러오는 순서를 바꿔야할 때 이건 좀 불편합니다.
.toc에서는 .xml 파일을 하나 불러오고 .xml에서 나머지 파일들을 불러오게 하면 약간 편해집니다.
이 예제에서는 embeds.xml을 만들었습니다.
<Ui>
</Ui>
사이에
.lua 파일은 <Script file="config.lua"/> 와 같이,
다른 .xml 파일은 <Include file="oUF\oUF.xml"/> 와 같은 구조로 넣으면 됩니다.
애드온을 구성하는 매 .lua 파일에서 ...을 호출하면 두 개의 인수가 지정되어있습니다.
하나는 애드온 폴더명과 같은 문자열, 나머지 하나는 이 애드온의 모든 파일이 공유하는 테이블인데 이걸 보통 name space라고 부릅니다.
이 애드온에서는 ...이 "oUF_Tutor", {} 를 나타냅니다.
코드 파일 중 처음 수행되는 config.lua에서
local addon, ns = ...
local C, A, M = CreateFrame("Frame"), CreateFrame("Frame"), CreateFrame("Frame")
table.insert(ns, C)
table.insert(ns, A)
table.insert(ns, M)
네임스페이스 테이블을 ns로 잡아오고 여기에 C, A, M이라는 프레임(테이블로 사용할 것인데 혹시 몰라 프레임으로 만들었습니다)을 넣어둡니다.
각각 config, api, main 테이블입니다.
다른 파일에서 여기에 접근하려면
local addon, ns = ...
local C, A, M = unpack(ns)
이렇게 쓰면 됩니다.
* 코드의 모듈화
이 예제에서는 oUF_Simple의 예제를 참고하여 여러 유닛의 스타일 함수에 공통으로 사용할 수 있는 구조를 사용합니다.
즉, api.lua에 체력바, 마나바, 시전바, 텍스트 등을 생성하는 코드를 미리 분리해서 만들어두고 각 유닛별 스타일 함수에서 이것들 중 필요한 것을 수행하고 필요에 따라 해당 유닛에 적용할 변경사항만을 추가합니다.
예를 들어 v0.4의 소환수와 대상 스타일 함수를 비교하면
M.styles["pet"] = function(f, unit)
A.InitButton(f, unit)
A.CreateHealth(f)
A.CreatePower(f)
A.CreateTexts(f)
end
M.styles["target"] = function(f, unit)
A.InitButton(f, unit)
A.CreateHealth(f)
A.CreatePower(f)
A.CreatePortrait(f)
A.CreateTexts(f)
A.CreateCastBar(f)
A.CreateAura(f, 24)
A.AddSettings(f.Health, "colorTapping", "colorDisconnected", "colorReaction")
A.AddSettings(f.Power, "colorTapping", "colorDisconnected", "colorReaction")
end
위와 같이 몇 개의 api 함수들을 수행하며 기본 요소를 생성합니다.
A.InitButton : 툴팁 기능 추가, 배경 및 테두리 생성
A.CreateHealth : 생명력 바 생성
A.Power : 자원바 생성
...
처럼 각 기능을 분리함으로써 여러 유닛의 유닛프레임을 서로 다르게 구성하면서도 공통된 부분은 같이 사용해서 코드 길이를 줄이고 변경 사항이 있을 때 모두에 한 번에 적용되게 하였습니다.
각각의 api 함수들도 어떤 유닛인가에 따라 다르게 동작하는 것들이 있는데 다음의 코드는 시전바를 생성하는 A.CreateCastBar의 일부입니다.
local unit = f.unit
...
if unit == "player" then
bg:SetSize(C["playerCastbar"]["width"], C["playerCastbar"]["height"])
local rel1, anchor, rel2, offx, offy = unpack(C["playerCastbar"]["pos"])
bg:SetPoint(rel1, anchor, rel2, offx, offy)
else
bg:SetPoint("TOPLEFT", f, "BOTTOMLEFT", 0, -4)
bg:SetPoint("TOPRIGHT", f, "BOTTOMRIGHT", 0, -4)
bg:SetHeight(unit == "target" and 22 or 18)
end
플레이어 시전바는 화면 하단에, 그 외 유닛의 시전바는 유닛프레임 아래에 위치시키도록 되어있습니다.
Tukui는 각 유닛별로 스타일 함수가 파일 하나씩 차지하며 나뉘어 있고 공통으로 사용하는 코드가 거의 없습니다.
서로 완전히 다르게 꾸밀 수 있으나 여러 유닛프레임에 공통으로 적용할 변경사항이 있으면 일이 조금 늘어나는 번거로움이 있습니다.
* 스타일 함수 구조
스타일 함수 구조는 Tukui의 것을 차용했습니다.
이전 글에서 본 것과 같이 유닛프레임의 생성 순서는
1. 스타일 함수 등록 / 활성화
2. Spawn으로 유닛프레임 생성
입니다.
여러개의 유닛프레임을 생성할 때 스타일 함수를 따로 만들어서 생성할 때마다 바꾸어가며 생성할 수도 있습니다.
oUF:RegisterStyle("oUF_SimplePlayer", CreatePlayerStyle)
oUF:SetActiveStyle("oUF_SimplePlayer")
oUF:Spawn("player", "oUF_Simple_PlayerFrame")
oUF:RegisterStyle("oUF_SimpleTarget", CreateTargetStyle)
oUF:SetActiveStyle("oUF_SimpleTarget")
oUF:Spawn("target", "oUF_Simple_TargetFrame")
oUF:RegisterStyle("oUF_SimpleToT", CreateToTStyle)
oUF:SetActiveStyle("oUF_SimpleToT")
oUF:Spawn("targettarget", "oUF_Simple_ToTFrame")
...
플레이어 스타일 함수를 등록하고 / 활성화하고 / 플레이어 생성
대상 스타일 함수를 등록하고 / 활성화하고 / 대상 생성
대상의 대상 스타일 함수를 등록하고 / 활성화하고 / 대상의 대상 생성
...
이 예제의 스타일 함수는 한 번만 등록되는데 v0.2 기준으로 다음과 같습니다.
M.styles = {}
M.styles["player"] = function(f, unit)
...
end
M.styles["target"] = function(f, unit)
...
end
M.Styler = function(f, unit)
if M.styles[unit] then
M.styles[unit](f, unit)
end
end
oUF:RegisterStyle("oUF_TutorStyle", M.Styler)
oUF:Spawn("player", M.GlobalName.player)
oUF:Spawn("target", M.GlobalName.target)
M.Styler가 등록된 스타일 함수이고 얘는 호출되면 어떤 유닛의 스타일을 원하는지에 따라 위에서 생성한(oUF에 등록되지는 않은) 해당 유닛의 스타일 함수를 대신 수행합니다.
* 기본적인 element
이제 기본적인 element 종류와 그 사용법을 봅시다.
< 체력바 > self.Health / status bar
유닛버튼 개체 아래에 .Health라는 키로 status bar를 만들어두면 oUF가 생명력 수치에 맞는 비율로 바를 채웁니다.
.Health.bg 라는 텍스쳐를 만들어두면 배경으로 등록되어서 생명력 바의 색상에 따른 색으로 업데이트됩니다. 단, .Health.bg.multiplier 라는 값을 설정하지 않으면 생명력 바와 정확히 같은 색이 되어서 바 길이 구분이 안됩니다.
multiplier는 0~1 사이의 숫자를 넣는데 이 값이 0.2라면 생명력 바 색상이 (1, 8, 0)일 때(노란 색) 배경의 색상은 (0.2, 0.16, 0)으로 지정됩니다.
설정값 : 원하는 옵션들을 true로 지정하며 색상 관련입니다. 선택된 옵션들을 앞쪽부터 체크해서 색을 부여합니다.
.colorTapping : 다른 플레이어가 선점했을 때 회색으로 표시합니다.
.colorDisconnected : 접종 상태의 유저일 때 회색으로 표시합니다.
.colorClass : 플레이어 유닛이면 직업 색상으로
.colorClassNPC : NPC 유닛도 직업이 있다면(UnitClass 반환값) 직업 색상 사용
.colorClassPet : 소환수 유닛도 직업이 있다면 직업 색상 사용
.colorReaction : 우호 / 중립 / 적대 상태에 따른 색상 적용
.colorSmooth : 생명력 비율에 따라 변화하는 색상을 적용
.colorHealth : 이도저도 아니면 녹색 적용
< 자원바 > self.Power / status bar
유닛버튼 개체 아래에 .Power라는 키로 status bar를 만들면 해당 유닛의 자원 상황에 맞추어 업데이트합니다.
.Power.bg, multiplier 생명력 바와 같습니다.
설정값 :
.displayAltPower : 해당 유닛에 alternate power가 있으면 (아트라메데스 소음 등) 자원 대신 그것을 표시합니다. 이걸 설정해서 자원 바에 표시할 수도 있으나 alt power element가 따로 있으므로 따로 만들어도 됩니다.
색상 지정 설정은 생명력 바와 거의 같습니다.
.colorPower : 자원 종류에 따라 해당 색상을 적용합니다. 마나, 분노, 기력 등
.colorClass
.colorClassNPC
.colorClassPet
.colorReaction
.colorSmooth
< 시전바 > self.Castbar / status bar
유닛버튼 개체 아래에 .Castbar라는 키로 status bar를 만들면 시전 상황에 맞게 표시 / 숨김, 업데이트합니다.
만들면 자동으로 활성화되는 하위개체들은
.Castbar.Text : font string 개체로 만들면 주문명을 표시합니다.
.Castbar.Icon : 텍스쳐. 주문 아이콘이 표시됩니다.
.Castbar.Time : font string / 시전 시간
.Castbar.Shield : 텍스쳐. 차단 불가능한 스킬일 때 표시됩니다.
.Castbar.SafeZone : 텍스쳐. 시전바 끄트머리에 붙여두면 지연시간에 맞게 폭이 조절됩니다.
.Castbar.Spark : 텍스쳐. 시전바 높이와 같은 정사각형으로 생성하면 시전바 위치 반짝임이 표시됩니다.
설정값은 없습니다.
< 초상화 > self.Portrait / player model 또는 texture
유닛버튼 개체 아래에 .Portrait라는 키로 Player Model 프레임이나 텍스쳐를 만들면 해당 유닛 3D 모델 / 초상화가 표시됩니다. 이 예제에서는 3D 모델로 만들었습니다.
< 강화/약화 효과 > self.Auras, self.Buffs, self.Debuffs / 프레임
여러 개의 오라 아이콘을 배열할 배경 프레임을 만듭니다. .Auras라면 버프/디버프 모두, .Buff / .Debuffs는 강화 / 약화 효과만 표시합니다.
아이콘 각각을 만드는 것이 아니라 지정된 배열 방식대로 배치될 틀을 만드는 것입니다.
배경 프레임의 크기와 아이콘의 크기에 따라서 한 줄에 배치될 아이콘 갯수가 정해져서 그 이상은 다음 줄에 표시됩니다.
설정값
공통 :
.disableCooldown : true이면 남은 시간 표시를 숨깁니다.
.size : 숫자. 오라 아이콘 각각의 가로 세로 크기
.onlyShowPlayer : true이면 내가 시전한 것만 표시
.showStealableBuffs : true이면 훔치기 가능한 것에 강조 표시
.spacing : 숫자. 오라 아이콘 사이 가로세로 간격
.['spacing-x'] : 숫자. 가로 방향에만 적용할 간격
.['spacing-y'] : 숫자. 세로 방향에만 적용할 간격
.['growth-x'] : 다음 오라 아이콘이 배치될 방향. "LEFT" 또는 "RIGHT"
.['growth-y'] : 다음 줄이 배치될 방향. "UP" 또는 "DOWN"
.initialAnchor : 오라 프레임에 첫 아이콘이 배치될 위치. 여기에서부터 growth 방향으로 추가로 배열됩니다.
.filter : UnitAura의 필터 설정. 기본값은 Auras에는 없고 Buffs라면 "HELPFUL", Debuffs라면 "HARMFUL"입니다.
.Auras 설정값 :
.numBuffs : 표시할 버프의 최대 갯수
.numDebuffs : 표시할 디버프의 최대 갯수
.gap : true이면 버프와 디버프 사이에 공간을 띄웁니다
.buffFilter : 버프에 적용할 UnitAura 함수 필터
.debuffFilter : 디버프에 적용할 UnitAura 함수 필터
.Buffs / .Debuffs 설정값 :
.num : 버프 / 디버프의 최대 갯수
이 예제에서는 .Buffs와 .Debuffs를 구분해서 만들었습니다.
< 문자열 > self:Tag(object, "tag") 로 등록 / fontstring
이름, 생명력 수치, 레벨, 상태 등의 문자열을 업데이트하려면 fontstring을 만들고 Tag 명령으로 종류를 등록하게 됩니다.
이 예제에는
f:Tag(name, "[level] [name]") -- "레벨 이름" 형식
f:Tag(name, "[name]") -- "이름"
f:Tag(healthVal, "[curhp] | [perhp]%") -- "생명력수치 | 생명력비율%"
f:Tag(healthVal, "[perhp]%") -- "생명력비율%"
f:Tag(powerVal, "[curpp]") -- "자원 수치"
등이 쓰였습니다. [ ] 로 둘러쌓인 부분이 업데이트됩니다.
< 그 외 >
일단 v0.4까지 사용한 것들만 설명하였습니다.
드루이드 변신폼일 때 인간폼 마나량 추적, 일월식 바 , 연계점수 바, 클래스 아이콘(수도사 기, 기사 신성한 힘, 암사 구슬 등), 치유량 예측 바 등등의 다양한 element들이 있습니다. oUF 폴더 아래 elements에 각각의 파일로 들어있고 맨 앞에 간략하게 주석으로 설명되어 있습니다.
< Element의 Post 함수 >
각각의 element 아래에 .PostUpdate라는 키값으로 함수를 등록해두면 해당 개체를 업데이트한 뒤에 등록된 함수를 수행합니다.
이 예제에는 대상 유닛프레임의 .Buffs에 PostUpdate가 등록되어 있습니다.
A.CreateAura = function(f, size)
if f.unit == "target" then
...
b.PostUpdate = A.UpdateTargetDebuffHeader
...
end
A.UpdateTargetDebuffHeader = function(Buffs)
local rows = ceil(Buffs.visibleBuffs / Buffs.numRow)
local Debuffs = Buffs:GetParent()["Debuffs"]
local gap = rows == 0 and 0 or 10
Debuffs:SetPoint("TOPLEFT", Buffs, "TOPLEFT", 0, -(rows * (Buffs.size + Buffs.spacing) + gap) )
end
대상 프레임은 버프 아래에 디버프가 위치해야 하는데 버프가 몇 줄 표시되는가에 따라서 디버프의 위치가 위아래로 달라져야 합니다.
그래서 버프에 변동이 있으면 저 함수를 추가로 수행해서 버프의 줄수에 맞는 간격만큼 디버프 프레임 위치를 조절하는 것입니다.
Tukui쪽에서는 oUF의 기본 기능이 성에 차지 않는지 아주 여러 곳에서 PostUpdate를 사용하고 있습니다.
예를 들어 .Health의 PostUpdate는 생명력 %에 따라서 생명력 수치 문자열의 표시 형식과 색상을 바꿉니다(oUF:Tag 기능은 색상을 바꾸지 못합니다)
PostUpdate 외에도 각각 element에 따라서 몇가지 Post... 함수들을 쓸 수 있는데 예제에서는 버프 / 디버프에 PostCreateIcon을 사용했습니다.
말씀드렸듯이 .Buff / .Debuff는 오라 아이콘 각각이 아니라 배열할 캔버스에 해당하며 오라 아이콘은 필요에 따라 생성됩니다. 예를 들어 버프가 두 개만 걸려있으면 .Buffs에는 아이콘이 두 개만 생성되어 있다가 세 번째 버프가 들어오면 그 때 생성해서 사용합니다.
따라서 레이아웃 도중에는 아직 만들어지지 않은 오라 아이콘의 외형을 바꿀 수 없기 때문에 아이콘이 생성된 직후에 한 번 수행할 후처리 함수를 등록해서 외형을 바꾸게 됩니다.
A.CreateAura = function(f, size)
...
local b = CreateFrame("Frame", nil, f)
...
b.PostCreateIcon = A.PostCreateAura
local d = CreateFrame("Frame", nil, f)
...
d.PostCreateIcon = A.PostCreateAura
...
end
A.PostCreateAura = function(buffheader, aura)
aura.icon:SetTexCoord(.1, .9, .1, .9)
aura.cd:SetReverse(true)
aura.bg = aura:CreateTexture(nil, "BACKGROUND")
aura.bg:SetPoint("TOPLEFT", -1, 1)
aura.bg:SetPoint("BOTTOMRIGHT", 1, -1)
aura.bg:SetTexture(.6, .6, .6)
end
- 아이콘을 조금 줌인
- 남은 시간 표시 방식 반전
- 배경 생성
* 예제 애드온의 코드 수행 순서 확인하기
Ok. 이제 여러가지 유닛 프레임 중에 대상 유닛프레임 생성하는 코드를 죽 따라가봅시다. v0.4 기준입니다.
대상 프레임 생성 명령을 내리는 것은 core.lua의 111번 줄부터입니다.
oUF:Spawn("target", M.GlobalName.target)
oUF는 대상 유닛의 유닛버튼을 생성하고 손봐주라고 스타일 함수에 넘깁니다.
활성화된 스타일 함수는 M.Styler입니다.
core.lua 90번 줄
M.Styler = function(f, unit)
if M.styles[unit] then
M.styles[unit](f, unit)
elseif unit:find("boss") then
M.styles["boss"](f, unit)
elseif unit:find("raid") then
local parent = f:GetParent():GetName()
if parent:find("party") then
elseif parent:find("raid") then
end
end
end
조건 분기에 따라 굵은 글씨쪽만 진행합니다.
M.styles["target"]이 실행됩니다.
core.lua 34번 줄
M.styles["target"] = function(f, unit)
A.InitButton(f, unit)
A.CreateHealth(f)
A.CreatePower(f)
A.CreatePortrait(f)
A.CreateTexts(f)
A.CreateCastBar(f)
A.CreateAura(f, 24)
A.AddSettings(f.Health, "colorTapping", "colorDisconnected", "colorReaction")
A.AddSettings(f.Power, "colorTapping", "colorDisconnected", "colorReaction")
end
A.으로 시작하는 함수들은 모두 api.lua에 지정되어 있습니다.
:: A.InitButton : 유닛프레임 전체 크기 지정, 배경 설정 등 기초 작업
첫 두 줄은 마우스 가져다댈 때 툴팁 띄우기 위한 코드입니다.
FrameStrata는 다른 인터페이스 창보다 조금 아래쪽으로 수정합니다.
설정 파일인 config.lua의 C["target"]에 가로 세로 크기가 지정되어있습니다. 유닛버튼의 크기를 맞춥니다.
배경을 진회색으로 설정하고 테두리에 그림자 모양 border를 두릅니다.
SetBackdrop은 다음 링크 참조
:: A.CreateHealth : 생명력 바 생성
생성하고 FrameStrata 설정한 뒤
왼쪽 위와 오른쪽 위 모서리는 유닛버튼에 맞추고
높이는 유닛버튼 전체 높이 - 10으로 설정합니다. (아래에는 자원 바가 1 띄우고 9 높이로 들어갑니다)
플레이어, 대상, 보스 유닛 외에는 자원 바 높이가 6이라서 7을 빼는 것을 볼 수 있습니다.
배경 설정하고 .bg로 등록합니다.
A.AddSettings는 true / false 타입 설정값들 넣는 함수를 짠 것입니다.(세 개를 한 줄에 넣고 있어서 이 부분 코드가 짧아집니다)
생명력 바를 .Health로 등록
:: A.CreatePower : 자원 바 생성
자원 바는 아래쪽 모서리를 맞추고 높이는 9로 설정됩니다.
:: A.CreatePortrait : 초상화 생성
플레이어와 대상에만 들어있습니다.
생명력 바의 왼쪽을 64만큼 안쪽으로 들여놓고 빈 공간에 초상화를 넣습니다.
초상화를 반투명하게 만들어서 생명력 바에 덮어씌우는 방법도 있습니다.
저는 3D 모델로 CreateFrame("PlayerModel", nil, f) 로 생성했습니다.
:: A.CreateTexts : 이름, 생명력, 자원 문자열 생성
플레이어 / 대상 / 보스는 세 가지를 모두 사용하고 그 외 유닛은 자원 수치 표시가 없습니다.
사용해보니 보스 이름을 조금 더 올리든지 유닛프레임을 더 높게 만들어야겠습니다.
:: A.CreateCastBar : 시전바 생성
플레이어는 화면 하단, 그 외 유닛은 모두 유닛프레임 아래에 생성됩니다.
시전바를 먼저 만들고 배경을 만들지만 위치 지정은 배경을 먼저 유닛프레임 폭에 맞추고 시전바는 아이콘 들어갈만큼 공간을 두고 배치합니다.
:: A.CreateAura : 오라 생성
.Buffs와 .Debuffs 배경 프레임을 생성합니다. 아이콘 크기를 24로 지정했으며 지정안된 유닛은 20 사이즈가 됩니다.
대상 유닛에 대해서만 시전바 아래로 버프(최대 두 줄), 그 아래 디버프(최대 네 줄)이 생성됩니다.
플레이어는 생성하지 않으며 나머지 유닛들은 유닛프레임 위에 오른쪽에서 안쪽으로 버프 세 개, 왼쪽에서 안쪽으로 디버프 세 개가 표시됩니다.
보스 유닛에는 내가 시전한 디버프만 표시됩니다.
(다만 v0.4에는 보스 유닛 스타일 함수에 디버프 생성을 빼먹었네요)
PostCreateIcon 함수가 지정되어서 오라 아이콘들 외형을 약간 수정합니다.
대상 프레임의 .Buffs에는 PostUpdate 함수가 지정되어서 표시중인 버프 갯수에 따라서 디버프의 위치를 조정합니다.
:: A.AddSettings : 대상에 적용할 설정값 등록
플레이어는 타인 선점시 회색, 접종 색상, 적대적 색상 등이 필요 없으므로 이쪽에서 등록합니다.
여기까지가 스타일 함수였습니다.
이제 core.lua의 더 뒤로 가면 132번 줄에서 위치를 지정합니다.
for unit, name in pairs(M.GlobalName) do
local f = _G[name]
if f then
A.Position(f)
end
end
매 유닛프레임 만들 때마다 Spawn 명령 바로 뒤에서 각자 지정해도 되는데 이 방식은 모든 유닛프레임을 생성한 뒤에 전부 위치를 지정하게 되어있습니다. 소환수 유닛프레임 위치가 플레이어 유닛프레임 기준으로 잡히는 것처럼 서로 참조할 때 생성 순서에 따라서 아직 안만든 유닛프레임 기준을 잡을 수는 없으므로 맨 뒤에서 한 번에 레이아웃을 잡습니다.
:: A.Position
config.lua에 지정된 매 유닛의 pos 설정값대로 유닛프레임 위치를 잡아줍니다.
그러면 결과가 이러합니다.
위쪽은 플레이어(펫), 대상과 대상의 대상
왼쪽은 주시와 주시의 대상
오른쪽에 표시되는 보스 프레임을 잘라왔습니다.
보스 프레임에 버프 / 디버프 아이콘 등록하는 한 줄 빼먹었습니다.
보스 이름과 생명력 수치가 겹치니 약간 더 조절해야겠군요.
아직 남은 내용은
- Plugin 사용(드래그 이동 기능 플러그인 포함)
- 그룹헤더 사용
입니다.
가능하면 한 개 글에 넣게 될 것 같습니다.