20.03.2025
Ich hatte es schon im letzten Post angekündigt. Dieses mal hatten wir uns Entities angeschaut. Und wie diese in Luanti implementiert werden.
Jetzt stellt sich aber natürlich eine wichtige Frage: Was genau sind Entities? Entities können fast alles sein. Mit ihnen kann man Blöcke, Monster oder auch Tiere erstellen wobei die zwei letzteren, die Hauptverwendung von Entities sein sollten. Eine tiefgründige Erklärung, wie Entities von Items zum Beispiel verschieden sind, gibt es hier in dem sehr guten und anfängerfreundlichem Guide von Ruben Wardy. Da er auf English ist, erkläre ich das gröbste.
Zu aller erst definieren wir uns erstmal eine Entity. Wir legen einen Lua Tabelle an und definieren hier jetzt Anfangswerte, wie z.B. die Anzahl an Leben, die unsere Entity hat, Funktionen, die beschreiben wie z.B. die Entity sich bewegt, und andere Daten, die wir benötigen für unsere Entity. Hier im Beispiel eine Nachricht (message), die abgesendet werden soll, wenn die Entity geschlagen wird.
local entity = {
initial_properties = {
hp_max = 20,
physical = true,
collide_with_objects = true,
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
visual = "mesh",
visual_size = {x = 1, y = 1, z = 1},
mesh = "Steve.obj",
textures = {"steve.png"},
spritediv = {x = 1, y = 1},
initial_sprite_basepos = {x = 0, y = 0},
},
message = "I got hit",
}
Das wichtigste ist, wenn eine Entity entweder geladen oder neu erstellt(gespawnt wird), kopiert diese Entity ihre Definitionstabell. Nehmen wir an, ein Villager wird in unserer Welt gespawnt, dann bekommt sie alle Werte aus der oberen Tabelle. Das passiert aber auch wenn dieser Villager geladen wird. Sagen wir wir hätten die Nachricht des Villagers durch z.B. Dialog mit ihm geändert, würden wir ihn jetzt entladen und wieder laden, dann würde ein Gespräch mit ihm wieder die aller erste Nachricht aufbringen. Wie man das umgeht übersteigt für heute aber den Rahmen dieses Posts und wir werden das in der Zukunft uns eh noch anschauen müssen. Wenn ihr aber Lust habt euch das jetzt schon anzuschauen empfehle ich den Guide von Ruben Wardy aber der Stelle hier
Jetzt zur Erklärung des Codes von oben. Dabei möchte ich nur auf ein paar wichtige Eigenschaften eingehen. Wenn ihr eine volle Übersicht über alle Eigenschaften, die ihr setzen könnt haben wollt, gibt es die hier.
initial_properties = {
:- initial_properties ist ein von luanti erforderte Lua Tabelle die wichtige basis Eigenschaften für die Luanti Engine birgt
visual = "mesh"
:"mesh"
bedeutet, dass wir ein 3D Model benutzen, um die Entity anzuzeigen.
visual_size = {x = 1, y = 1, z = 1}
:- setzt die Skalierung des 3D Model’s in x (Nord/Süd), y (Oben/Unten) und z (Ost/West) Richtung
mesh = "Steve.obj"
:- Hier geben wir an, welches 3D Model wir benutzen möchten für unsere Entity
textures = {"steve.png"}
:- Ähnlich zu tiles von unserem Block, setzt die Texturen für die Entity. Hier können auch mehrere Bentuzt werden, welche dann im 3D Model bestimmt gemappt werden müssen
message = "I got hit"
:- benutzerdefinierte Eigenschaft. Wir benutzen diese hier um eine Nachricht in den Chat zu senden, wenn die Entity geschlagen wird
Jetzt haben wir erst einmal Standardeigenschaften für die Entity definiert. Wenn wir die Entity jetzt so kreieren würden, würde sie nur dastehen und nichts machen. Wir müssen also Logik für sie erstellen.
function entity:on_step(dtime)
local pos = self.object:get_pos()
local pos_down = vector.subtract(pos, vector.new(0, 1, 0))
local delta
if core.get_node(pos_down).name == "air" then
delta = vector.new(0, -1, 0)
elseif core.get_node(pos).name == "air" then
delta = vector.new(0, 0, 1)
else
delta = vector.new(0, 1, 0)
end
delta = vector.multiply(delta, dtime)
self.object:move_to(vector.add(pos, delta))
end
function entity:on_punch(hitter)
core.chat_send_player(hitter:get_player_name(), self.message)
end
Wir definieren zwei Funktionen auf der Entity: entity:on_step
und entity:on_punch
, welche jeweils jeden Tick und jedes mal, wenn die Entity geschlagen wird, aufgerufen werden.
In on_step
holen wir uns die Position von uns selbst mit self.object:get_pos()
und kalkulieren die Position, des Blockes unter der Entity. Anschließend berechnen wir unsere Bewegung. Wenn der Block unter der Entity Luft ist, dann soll sich die Entity nach unten Bewegen. Wenn die Entity an ihrer geradigen Position kein Block hat (sie also auf einer Ebenen ist), dann soll sie sich in z Richtung (Ost/Westen) bewegen. Und wenn unter uns und an unserer Position ein Block ist, dann müssen wir an der Kante eines anderen Blockes der Höher als die Entity ist sein, die Entity muss sich also nach oben bewegen.
Um eine fließende Bewegung zu implementieren, weil das Spiel kann ja an zufälligen Stellen hängen, müssen wir die Positionsveränderung der Entity mit der Zeit, die abgelaufen ist multiplizieren. Dannach ändern wir die Position der Entity mit self.object:move_to(vector.add(pos, delta))
und haben somit die Entity in eine bestimmte Richtung bewegt.
In on_punch
bekommen wir als Parameter den Spieler oder die Entity, die unsere Entity geschlagen hat. Mit dem namen des Spielers können wir jetzt mit core.chat_send_player
die Nachricht self.message
an den Spieler senden.
Um jetzt zu guter letzt unsere Entity zu registrieren und zu spawnen rufen wir core.register_entity
auf und übergeben mit dem Namen der Entity auch unsere Definitionstabelle der Entity.
core.register_entity("entity_mod:entity", entity)
core.add_entity({x = 1, y = 2, z = 3}, "entity_mod:entity", nil)
core.add_entity
spawnt dann die Entity an einer bestimmten Stelle in der Welt. Hier ist das {x = , y = 2, z = 3}
. Man kann die Entity dann aber auch über den Command: /spawnentity entity_mod:entity
spawnen.
Zusammenfassung
Diese Woche haben wir gelernt, wie man Entities definiert, Logik auf ihnen implementiert und die Entities dann auch im Spiel spawnt. Es gibt noch vieles hier zu machen, wie z.B. das Laufen bei der Entity zu verbessern. Oder wenn man z.B. einen Zombie machen möchte diesen auf den Spieler zulaufen zu lassen.