Chiril
Welt-Boss
- Mitglied seit
- 17.07.2008
- Beiträge
- 1.226
- Reaktionspunkte
- 1
- Kommentare
- 53
- Buffs erhalten
- 57
[font="verdana, sans-serif"]Dieses kleine Tutorial soll die Grundlagen der Erstellung von ein Pixel breiten Linien erklären und richtet sich damit eher an Anfänger, wenn auch Lua-Kenntnisse erforderlich sind. Vielleicht finden aber auch die einen oder anderen "Pros" hier etwas Neues
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Ich denke, die meisten kennen es: Man will nur eben schnell im Interface eine kleine, feine Linie ziehen, die genau einen Pixel breit ist. Genau ein Pixel ist nicht zu dick, sondern genau richtig um einzelne Elemente einfach aber klar abzugrenzen. Kombiniert mit den richtigen Texturen und Schriftarten führt das (meist) zu einem sehr minimalistischem, aufgeräumtem und eigentlich sauberem Interface. „Eigentlich sauber"? Ja, leider. Blizard legt uns da ein paar Steine in den Weg und macht uns oft unnötig das Leben schwer. Ich möchte euch helfen, diese Steine aus dem Weg zu räumen und das Interface wirklich „sauber" zu machen, nicht eigentlich
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Als erstes treffen wir ein paar Vorbereitungen, bevor wir zu den Linien an sich kommen.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]1) Multisampling ausschalten.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Ich mag eigentlich Multisampling. Dadurch werden die Kanten geglättet und das Spiel sieht besser aus. Aus mir unersichtlichen Gründen wird dadurch aber auch das Interface geglättet und saubere einen Pixel breite Linien fast unmöglich. Ich würde mich verdammt freuen, wenn ich irgendwann mal in den Patch Notes lesen würde, dass man das jetzt selber bestimmen kann, aber dazu wird es wohl nie kommen. Leider müssen wir jetzt die Entscheidung „Schönes Spiel oder schönes Interface?" treffen. Ich denke, die Antwort die ich voraussetze ich klar
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]2) Die richtige UI-Skalierung[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Mit Hilfe der UI-Skalierung lässt sich die Größe jedes Elementes des Interfaces, oder besser, des Interfaces an sich, regulieren. Das ist an sich eine nette Idee und für das Standard Interface ein sehr tolles Feature. Wir wollen aber einen Pixel breite Linien auch als einen Pixel breite Linien im Spiel sehen und wenn wir sie durch die UI-Skalierung skalieren lassen würden, ginge das nicht. Wir setzen die Skalierung deshalb auf den Wert, der eine im Quellcode als „eins" angegebene Länge auch „eins" lang im Spiel darstellt.[/font]
[font="verdana, sans-serif"]Der Wert lässt sich ganz einfach ermitteln: Wir teilen 768 durch die Y-Auflösung unseres Monitors. An meinem wäre es 768/1050, also etwa 0.73. Da uns „etwa" aber nicht reicht müssen wir den Bruch als solches irgendwo angeben. Dazu öffnen wir ingame den Chat und geben Folgendes ein:[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]
[/font]/run SetCVar('uiScale', 768 / 1050)
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Das setzt die UI-Skalierung auf den Bruch, den wir gerne hätten.Wir können uns auch ein dynamisches Makro machen, das die Höhe automatisch anpasst (Danke an Wertzu):[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]
(ungetestet)[/font]/script SetCVar("uiScale", 768/string.match(({GetScreenResolutions()})[GetCurrentResolution()], "%d+x(%d+)"))
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Nach Beachtung dieser beiden Punkte können wir endlich richtig losgehen. Endlich wird eine Linie, der wir im Code die Höhe „eins" zuweisen auch genau einen Pixel hoch. Wunderbar
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Unsere Absicht ganz zu Anfang war etwas wie ein Rand, um unsere Elemente schön abzugrenzen. Da wir aber unterschiedliche Elemente haben, die es abzugrenzen gilt, müssen wir auch auf unterschiedliche Methoden, Wege und Funktionen zurückgreifen.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Fangen wir mit der einfachsten an:[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]1) Die SetBackdrop()-Funktion[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Diese Funktion erstellt mit Hilfe der ihr zugewiesenen „bgFile" eine farbige Fläche hinter den Objekt auf das man sie anwendet. Ihren „insets" kann man dann Werte eintragen, die den „Überstand" von eigentlichen Objekt festlegen, also die Randdicke. Da wir alle faul sind machen wir es uns auch einfach:[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]
[/font]
[font="verdana, sans-serif"]self:SetBackdrop{ bgFile = „Interface\\ChatFrame\\ChatFrameBackground", tile = true, tilesize= 16,[/font][font="verdana, sans-serif"] insets = {left = -1, right = -1, top = -1, bottom = -1},}[/font][font="verdana, sans-serif"]self:SetBackdropColor(0,0,0,1)
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Wir greifen dabei auf eine .blp von Blizzard zurück (Den ChatFrameBackground), da sie einfarbig ist uns für unsere Wünsche völlig reicht. Die Insets sind überall „-1", da wir einen Rand von einem Pixel haben wollen. Die Farbe lässt sich natürlich beliebig ändern, aber ich finde, dass alles außer schwarz doof aussieht
.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Diese Methode eignet sich am besten für Elemente, die undurchsichtig sind, also alle möglichen Leisten, wie UnitFrames, Timer und Ähnliches oder ganz einfache Frames.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Ergebnis bei einer Leiste:[/font]
[font="verdana, sans-serif"]http://i7chy.i7.funpic.de/wow/px/bar.png[/font]
[font="verdana, sans-serif"][/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]2) Einen echten Rand erstellen[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Manchmal ist man allerdings auch in der Situation, dass man ein durchsichtiges Frame hat, dem man einen schicken Rand verpassen möchte. Dafür eignet sich eine Möglichkeit, die ich mal vor Ewigkeiten in einer nMinimap gefunden hab und die mittlerweile eh jeder benutzt.Wir nehmen hierfür eine Textur, zum Beispiel eine für einen Button und zerschneiden sie mit Hilfe der SetTexCoord()-Funktion in acht Stücke; vier Ecken und vier Kanten. Vorteil: Der Rand passt sich automatisch der Größe an und sieht immer „richtig" aus.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"](Im Beispiel ist „m" das Frame, das den Rand bekommt)[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"][/font]
[font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local color = {r = 1, g = 1, b = 1}[/font][font="verdana, sans-serif"]local scale = 1[/font][font="verdana, sans-serif"]local pos = 1 –- dieser Wert gibt den Abstand vom Frame an[/font][font="verdana, sans-serif"]local frameborder = "Interface\\AddOns\\AddonName\\media\\frameborder" –- selbstverständlich alles anpassen[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local TopLeft = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]TopLeft:SetTexture(frameborder)[/font][font="verdana, sans-serif"]TopLeft:SetTexCoord(0, 1/3, 0, 1/3)[/font][font="verdana, sans-serif"]TopLeft:SetPoint("TOPLEFT", m, -pos, pos)[/font][font="verdana, sans-serif"]TopLeft:SetWidth(scale) TopLeft:SetHeight(scale)[/font][font="verdana, sans-serif"]TopLeft:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]TopLeft:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local TopRight = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]TopRight:SetTexture(frameborder)[/font][font="verdana, sans-serif"]TopRight:SetTexCoord(2/3, 1, 0, 1/3)[/font][font="verdana, sans-serif"]TopRight:SetPoint("TOPRIGHT", m, pos, pos)[/font][font="verdana, sans-serif"]TopRight:SetWidth(scale) TopRight:SetHeight(scale)[/font][font="verdana, sans-serif"]TopRight:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]TopRight:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local BottomLeft = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]BottomLeft:SetTexture(frameborder)[/font][font="verdana, sans-serif"]BottomLeft:SetTexCoord(0, 1/3, 2/3, 1)[/font][font="verdana, sans-serif"]BottomLeft:SetPoint("BOTTOMLEFT", m, -pos, -pos+1)[/font][font="verdana, sans-serif"]BottomLeft:SetWidth(scale) BottomLeft:SetHeight(scale)[/font][font="verdana, sans-serif"]BottomLeft:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]BottomLeft:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local BottomRight = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]BottomRight:SetTexture(frameborder)[/font][font="verdana, sans-serif"]BottomRight:SetTexCoord(2/3, 1, 2/3, 1)[/font][font="verdana, sans-serif"]BottomRight:SetPoint("BOTTOMRIGHT", m, pos, -pos+1)[/font][font="verdana, sans-serif"]BottomRight:SetWidth(scale) BottomRight:SetHeight(scale)[/font][font="verdana, sans-serif"]BottomRight:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]BottomRight:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local TopEdge = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]TopEdge:SetTexture(frameborder)[/font][font="verdana, sans-serif"]TopEdge:SetTexCoord(1/3, 2/3, 0, 1/3)[/font][font="verdana, sans-serif"]TopEdge:SetPoint("TOPLEFT", TopLeft, "TOPRIGHT")[/font][font="verdana, sans-serif"]TopEdge:SetPoint("TOPRIGHT", TopRight, "TOPLEFT")[/font][font="verdana, sans-serif"]TopEdge:SetHeight(scale)[/font][font="verdana, sans-serif"]TopEdge:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]TopEdge:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local BottomEdge = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]BottomEdge:SetTexture(frameborder)[/font][font="verdana, sans-serif"]BottomEdge:SetTexCoord(1/3, 2/3, 2/3, 1)[/font][font="verdana, sans-serif"]BottomEdge:SetPoint("BOTTOMLEFT", BottomLeft, "BOTTOMRIGHT")[/font][font="verdana, sans-serif"]BottomEdge:SetPoint("BOTTOMRIGHT", BottomRight, "BOTTOMLEFT")[/font][font="verdana, sans-serif"]BottomEdge:SetHeight(scale)BottomEdge:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]BottomEdge:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local LeftEdge = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]LeftEdge:SetTexture(frameborder)[/font][font="verdana, sans-serif"]LeftEdge:SetTexCoord(0, 1/3, 1/3, 2/3)[/font][font="verdana, sans-serif"]LeftEdge:SetPoint("TOPLEFT", TopLeft, "BOTTOMLEFT")[/font][font="verdana, sans-serif"]LeftEdge:SetPoint("BOTTOMLEFT", BottomLeft, "TOPLEFT")[/font][font="verdana, sans-serif"]LeftEdge:SetWidth(scale)[/font][font="verdana, sans-serif"]LeftEdge:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]LeftEdge:SetDrawLayer("BORDER")[/font][font="verdana, sans-serif"]
[/font][font="verdana, sans-serif"]local RightEdge = m:CreateTexture(nil, "BORDER")[/font][font="verdana, sans-serif"]RightEdge:SetTexture(frameborder)[/font][font="verdana, sans-serif"]RightEdge:SetTexCoord(2/3, 1, 1/3, 2/3)[/font][font="verdana, sans-serif"]RightEdge:SetPoint("TOPRIGHT", TopRight, "BOTTOMRIGHT")[/font][font="verdana, sans-serif"]RightEdge:SetPoint("BOTTOMRIGHT", BottomRight, "TOPRIGHT")[/font][font="verdana, sans-serif"]RightEdge:SetWidth(scale)[/font][font="verdana, sans-serif"]RightEdge:SetVertexColor(color.r,color.g,color.b)[/font][font="verdana, sans-serif"]RightEdge:SetDrawLayer("BORDER")[/font]
[font="verdana, sans-serif"][/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Ergebnis bei einem transparenten Frame:[/font]
[font="verdana, sans-serif"]http://i7chy.i7.funpic.de/wow/px/quadrat.png[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Die meisten erstellen für diese Methode eine eigene Funktion in einem Core-Addon, wo man all so ein Zeug reinpackt. BeautyCase von Neal zum Beispiel. Das sparrt eine Menge Code.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Diese Methode lässt sich ausnahmslos überall erfolgreich anwenden.Eine sehr tückische Stelle sind dagegen Buttons. Auf Buttons wendet man für gewöhnlich (so wie zum Beispiel in rActionButtonStyler) Funktionen wie SetNormalTexture() oder SetCheckedTexture() an. Wir sind faul, also ist das völlig ok. Wenn wir dann den Button aber noch skalieren, wird unser toller 1-Pxiel-Skin (einer der tausend auf wowinterface, wie etwa Lunas, Skullflowers oder Qauns) schön unscharf. Wir haben dann nur die Möglichkeit, die Buttons einzeln ein halben Pixelschritten zu verschieben, solange bis sie scharf werden.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Da wir aber alle faul sind, habe ich mich mal drangesetzt, die „Einen echten Rand erstellen" Methode in einem ActionBar-Addon zu verwirklichen. Bin auch relativ stolz auf das Ergebnis, der Rand ist nämlich perfekt
. Das Addon allerdings ... nicht wirklich
[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"]Das sind alle Wege, die ich zum Ziel perfekter einpixeliger Linien gehe und ich finde, dass ich schon fast am Ziel bin bin
Für Vorschläge, insbesondere Ergänzungen oder Korrekturen bin ich offen.[/font]
[font="verdana, sans-serif"]
[/font]
[/font]
[font="verdana, sans-serif"](c) 2010, Chiril[/font]
Zuletzt bearbeitet von einem Moderator: