Das Ziel dieses Projektes ist es, GUIs möglichst einfach erstellen zu können. Dabei wird großer Wert darauf gelegt, dass auch komplexe GUIs (z.B. mit Animationen) unterstützt werden. Die grundlegende Komponenten-Struktur ermöglicht es, einzelne Komponenten in verschiedenen GUIs wiederzuverwenden und aus externen Quellen zu beziehen. Dies führt zu einem stark reduzierten Aufwand, da häufig verwendete GUI-Strukturen (z.B. Listen mit mehreren Seiten) nicht immer erneut programmiert werden müssen.
Die GUI kann auf verschiedenen Oberflächen angezeigt werden. Momentan werden drei Oberflächen in Minecraft-Inventaren unterstützt.
Dies ist die wahrscheinlich populärste Oberfläche. Sie besteht aus dem Inventar einer Kiste und kann eine bis sechs Zeilen groß sein. Unter ihr wird das aktuelle Spieler-Inventar angezeigt. Dieses gehört allerdings nicht zu der Oberfläche und die Nutzung davon kann deaktiviert werden.
Diese Oberfläche verbindet das ChestInventory mit dem darunterliegenden PlayerInventory. Alle zuvor im Spieler-Inventar vorhandenen Items werden beim Öffnen des GUI entfernt und beim Schließen wiederhergestellt.
Diese Oberfläche besteht aus dem Spieler-Inventar des Spielers und besitzt vier Zeilen. Alle zuvor im Inventar vorhandenen Items werden beim Öffnen des GUI entfernt und beim Schließen wiederhergestellt.
Alle Elemente des GUI bestehen aus Komponenten. Komponenten als Ganzes können wiederum als Unterkomponenten in andere Komponenten eingefügt werden. Komponenten können jegliche Form annehmen und auch nicht miteinander zusammenhängende Slots besitzen. Der Index einer Komponente verläuft immer von der obersten Zeile nach unten und jeweils pro Zeile von links nach rechts.
ReservedSlots legen fest, wie Komponenten im Zweidimensionalen aufgebaut sind. Dafür wird ein zweidimensionales Array an booleans benutzt. Jeder boolean, welcher true ist, markiert einen Slot, welcher der Komponente zugehörig ist. Jeder boolean, welcher auf false gesetzt wurde, gehört wiederum nicht zu der Komponente und dient als Abstandshalter für einen zugehörigen Slot.
Dieses Beispiel zeigt die ReservedSlots der roten Komponente aus dem Beispiel von Verschachtelung.
ReservedSlots -> true
ReservedSlots -> false
Index der Komponente
Die ReservedSlots dieser Komponente:
[
[false, true],
[true, false, true],
[false, true]
]
Komponenten können Unterkomponenten jeglicher Form enthalten, solange alle ReservedSlots der Unterkomponente auch in der eigentlichen Komponente reserviert sind. Jede Komponente besitzt dabei einen eigenen Index, welcher sich über alle in der Komponente reservierten Slots erstreckt.
Dieses Beispiel zeigt eine Komponente (grün), welche einige Unterkomponenten besitzt. Dazu werden in den jeweiligen Farben der Komponenten die Indizes aller Komponenten angegeben.
Abgrenzung der Komponente einer Farbe
Index der Komponente einer Farbe
Start-Index der Komponente einer Farbe
Um eine Unterkomponente einzufügen, wird ein Start-Index benötigt, welcher als Ankerpunkt der Unterkomponente auf der übergeordneten Komponente dient. Auf dem angegebenen Start-Index liegt der erste Eintrag der ReservedSlots. Es macht dabei keinen Unterschied, ob der erste Eintrag reserviert ist, oder nicht.
Generell kann sich gemerkt werden: Wenn man um die ganze Komponente ein Rechteck zieht, ist der Start-Index immer der Slot oben links in der Ecke.
Die ItemComponent ist eine Komponente, welche einen Minecraft-ItemStack abbildet. Sie muss benutzt werden, um einzelne Items in die Komponenten-Struktur einzubringen.
Der Component-Tick ist eine periodische Routine, welche den Aufruf der GuiComponent#onComponentTick Methode in allen (Unter-)Komponenten zur Folge hat. Der Tick muss auf der globalen Ebene aktiviert werden und die Schnelligkeit kann auf Komponenten-Ebene eingestellt werden. Der erste Tick wird noch direkt vor dem Öffnen des GUI mit dem Wert 0 gestartet.
Vor jedem Rendervorgang (Aktualisierung) des GUI wird die GuiComponent#beforeRender Methode in allen (Unter-)Komponenten aufgerufen.
Teilweise ist es nötig, die GUI noch nach dem initialen Öffnen zu verändern. Das kann zum Beispiel bei Animationen, oder bei gewünschten Veränderungen durch einen Klick, der Fall sein.
On-Demand-Rendering bietet eine Möglichkeit, die GUI bei Bedarf zu aktualisieren. Das Feature kann über die GuiComponent#triggerReRender Methode verwendet werden. Dabei gibt es zwei Möglichkeiten zur Nutzung:
Dieses Feature ermöglicht es, die Aktualisierung innerhalb von spätestens X Ticks anzufordern. Wenn eine andere Komponente eine frühere Aktualisierung anfordert, kann die Änderung auch bereits vorher gerendert werden.
Dieses Feature wird benötigt, damit eine Vielzahl von gleichzeitig angeforderten Aktualisierungen nicht für unnötig viele Rendervorgänge sorgen.
Das Feature muss auf globaler Ebene aktiviert werden und ermöglicht die Nutzung der Methode mit dem Wert 0
.
Sobald die Methode so aufgerufen wird, aktualisiert sich die GUI unverzüglich.
Das Feature muss auf globaler Ebene aktiviert werden und ermöglicht die automatische Aktualisierung bei einer erkannten Änderung des GUI. Die Aktualisierung findet spätestens nach einer auf oberster Komponenten-Ebene eingestellten Zeit statt.
Der Rendervorgang kann in einigen Punkten angepasst werden, was zu einer niedrigeren Renderzeit führen kann.
Wenn eine Komponente als static markiert wurde, wird entgegen aller anderen Einstellungen keine Aktualisierung vorgenommen.
Dieses Feature erkennt Veränderungen an Komponenten und ermöglicht es, nur Komponenten mit Veränderungen neu zu rendern. Smart-Rendering muss auf Komponenten-Ebene aktiviert werden.
Das folgende Beispiel wurde in Kotlin programmiert. Genaue Erklärungen, welche die volle Funktionalität der API erläutern, sind im Wiki zu finden.
import net.bestlinuxgamers.guiApi.component.essentials.ItemComponent
import net.bestlinuxgamers.guiApi.gui.ChestInventoryGui
import net.bestlinuxgamers.guiApi.provider.GuiInstancesProvider
import org.bukkit.Material
import org.bukkit.entity.Player
import org.bukkit.inventory.ItemStack
/**
* This example creates a GUI that slowly fills itself with sticks.
* A stick will disappear when you click on it.
*/
class ReadmeGui(player: Player, guiInstancesProvider: GuiInstancesProvider) :
ChestInventoryGui(
player,
"TITLE",
6, //lines
guiInstancesProvider.eventHandler,
guiInstancesProvider.schedulerProvider,
componentTick = true,
tickSpeed = 20, //every second
autoRender = true //changes will be automatically updated
) {
override fun beforeRender(frame: Long) {}
/**
* Executed every [tickSpeed] ticks.
*/
override fun onComponentTick(tick: Long, frame: Long) {
// Calculate the next slot to fill with a stick
val slot = tick.toInt() % reservedSlots.totalReserved
// If there is not already a stick in the slot
if (getComponentOfIndex(slot) == null) {
// Put a new stick in the next slot
setComponent(getRemovableStick(), slot)
}
}
/**
* Creates a stick component, which will be removed by a click.
*/
private fun getRemovableStick(): ItemComponent {
val component = ItemComponent(ItemStack(Material.STICK))
component.setClickable { event, _ ->
event.whoClicked.sendMessage("Stick removed!")
// Remove self from parent component
removeComponent(component)
}
return component
}
}
val gui = ReadmeGui(player, guiInstancesProvicer)
//open
gui.open()
//close
gui.close()