--- title: Objekte, Spieler, und Entities layout: default root: ../.. idx: 3.4 description: Nutzung eines ObjectRef degrad: level: warning title: Grad and Radiant message: Die Drehung von Anbauteilen wird in Grad angegeben, während die Drehung von Objekten in Radiant angegeben wird. Stellen Sie sicher, dass Sie das richtige Winkelmaß verwenden. --- ## Einleitung In diesem Kapitel lernen Sie, wie man Objekte manipuliert und eigene Objekte definiert. - [Was sind Objekte, Spieler, und Entities?](#was-sind-objekte-spieler-und-entities) - [Position und Geschwindigkeit](#position-und-geschwindigkeit) - [Objekt-Eigenschaften](#objekt-eigenschaften) - [Entities](#entities) - [Leben und Schaden](#leben-und-schaden) - [Attachments](#attachments) - [Your Turn](#your-turn) ## Was sind Objekte, Spieler, und Entities? Spieler und Entities sind beide Arten von Objekten. Ein Objekt ist etwas, das sich unabhängig vom Block-Raster bewegen kann und Eigenschaften wie Geschwindigkeit und Skalierung besitzt. Objekte sind keine Gegenstände, und sie haben ihr eigenes Registrierungssystem. Es gibt ein paar Unterschiede zwischen Spielern und Entities. Der größte ist, dass Spieler von Spielern gesteuert werden, während Entities von Mods gesteuert werden. Das bedeutet, dass die Geschwindigkeit eines Spielers nicht von Mods eingestellt werden kann - Spieler sind client-seitig, und Entities sind serverseitig. Ein weiterer Unterschied ist, dass Spieler das Laden von Kartenblöcken verursachen, während Entities nur gespeichert werden und inaktiv werden. Diese Unterscheidung wird durch die Tatsache erschwert, dass Entities über eine Tabelle gesteuert werden, die als Lua entity bezeichnet wird, wie später erläutert wird. ## Position und Geschwindigkeit `get_pos` und `set_pos` existieren, um die Position eines Entitys zu ermitteln und zu setzen. ```lua local objekt = minetest.get_player_by_name("bob") local pos = objekt:get_pos() objekt:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z }) ``` `set_pos` setzt die Position sofort und ohne Animation. Wenn Sie ein Objekt sanft an die neue Position animieren möchten, sollte man `move_to` verwenden. Dies funktioniert leider nur für Entities. ```lua objekt:move_to({ x = pos.x, y = pos.y + 1, z = pos.z }) ``` Ein wichtiger Punkt beim Umgang mit Entities ist die Latenzzeit im Netz. In einer idealen Welt würden Nachrichten über Entitybewegungen sofort ankommen, in der richtigen Reihenfolge und in einem ähnlichen Intervall ankommen, wie Sie sie gesendet haben. Solange man sich jedoch nicht im Einzelspielermodus befindet, ist dies keine ideale Welt. Nachrichten brauchen eine Weile, bis sie ankommen. Positionsnachrichten können in der falschen Reihenfolge ankommen, was dazu führt, dass einige `set_pos`-Aufrufe übersprungen werden, da es keinen Sinn macht, zu einer Position zu gehen, die älter ist als die aktuell bekannte Position. Bewegungen können nicht in ähnlichen Abständen erfolgen, was es schwierig macht, sie für Animationen zu verwenden. All dies führt dazu, dass der Client andere Dinge sieht als der Server, und das ist etwas das Sie beachten müssen. ## Objekt-Eigenschaften Objekt-Eigenschaften werden verwendet, um dem Client mitzuteilen, wie ein Objekt zu rendern und zu behandeln ist. Es ist nicht möglich, benutzerdefinierte Eigenschaften zu definieren, denn die Eigenschaften sind per Definition von der Engine zu verwenden. Im Gegensatz zu Blöcken haben Objekte ein dynamisches und kein festes Aussehen. Sie können unter anderem das Aussehen eines Objekts jederzeit ändern, indem Sie seine Eigenschaften ändern. ```lua object:set_properties({ visual = "mesh", mesh = "character.b3d", textures = {"character_texture.png"}, visual_size = {x=1, y=1}, }) ``` Die aktualisierten Eigenschaften werden an alle Spieler in Reichweite gesendet. Dies ist sehr nützlich, um eine große Menge an Vielfalt sehr billig zu bekommen, wie zum Beispiel verschiedene Skins pro Spieler. Wie im nächsten Abschnitt gezeigt wird, können Entities Erst-Eigenschaften haben die in ihrer Definition angegeben werden. Die Standardeigenschaften des Spielers sind jedoch in der Engine definiert, so dass man `on_joinplayer` die Funktion `set_properties()` verwenden kann, um die Eigenschaften für neue Spieler zu setzen. ## Entities Ein Entity hat eine Definitionstabelle, die einer Objektdefinitionstabelle ähnelt. Diese Tabelle kann Callback-Methoden, anfängliche Objekteigenschaften und benutzerdefinierte Mitglieder enthalten. ```lua local MeinEntity = { initial_properties = { hp_max = 1, physical = true, collide_with_objects = false, collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3}, visual = "wielditem", visual_size = {x = 0.4, y = 0.4}, textures = {""}, spritediv = {x = 1, y = 1}, initial_sprite_basepos = {x = 0, y = 0}, }, message = "Default message", } function MeinEntity:set_message(msg) self.message = msg end ``` Entity-Definitionen unterscheiden sich in einem sehr wichtigen Punkt von Item-Definitionen. Wenn ein Entity auftaucht (d.h.: geladen oder erstellt wird), wird eine neue Tabelle für diese Entity erstellt, die von der Definitionstabelle *erbt*. Sowohl ein ObjectRef als auch eine Entity-Tabelle bieten Möglichkeiten, das Gegenstück zu erhalten: ```lua local entity = object:get_luaentity() local objekt = entity.objekt print("Entity ist bei " .. minetest.pos_to_string(objekt:get_pos())) ``` Es gibt eine Reihe von Callbacks für die Verwendung mit Entities. Eine vollständige Liste findet sich in [lua_api.txt](https://minetest.gitlab.io/minetest/minetest-namespace-reference/#registered-definition-tables). ```lua function MeinEntity:on_step(dtime) local pos = self.object:get_pos() local pos_drunter = vector.subtract(pos, vector.new(0, 1, 0)) local delta if minetest.get_node(pos_drunter).name == "air" then delta = vector.new(0, -1, 0) elseif minetest.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 MeinEntity:on_punch(hitter) minetest.chat_send_player(hitter:get_player_name(), self.message) end ``` Wenn Sie nun diese Entity spawnen und verwenden würden, würden Sie feststellen, dass die Nachricht vergessen wird, wenn die Entity inaktiv und dann wieder aktiv wird. Das liegt daran, dass die Nachricht nicht gespeichert wird. Anstatt alles in der Entity-Tabelle zu speichern, gibt Minetest Ihnen die Kontrolle darüber wie die Dinge gespeichert werden sollen. Staticdata ist ein String, der alle benutzerdefinierten Informationen enthält, die gespeichert werden müssen. ```lua function MeinEntity:get_staticdata() return minetest.write_json({ message = self.message, }) end function MeinEntity:on_activate(staticdata, dtime_s) if staticdata ~= "" and staticdata ~= nil then local data = minetest.parse_json(staticdata) or {} self:set_message(data.message) end end ``` Minetest kann `get_staticdata()` so oft wie gewünscht und zu jeder Zeit aufrufen. Der Grund dafür ist, dass Minetest nicht darauf wartet, dass ein MapBlock inaktiv wird, um ihn zu speichern, da dies zu Datenverlusten führen würde. MapBlocks werden ungefähr alle 18 Sekunden gespeichert, also sollten Sie ein ähnliches Intervall für den Aufruf von `get_staticdata()` feststellen. `on_activate()` wird dagegen nur aufgerufen, wenn eine Entity aktiv wird, entweder wenn der MapBlock aktiv wird oder wenn die Entity spawnen wird. Dies bedeutet, dass staticdata leer sein könnte. Schließlich müssen Sie die Typentabelle mit der treffenden Bezeichnung `register_entity` registrieren. ```lua minetest.register_entity("meinemod:entity", MeinEntity) ``` Die Entity kann von einem Mod wie folgt erzeugt werden: ```lua local pos = { x = 1, y = 2, z = 3 } local obj = minetest.add_entity(pos, "meinemod:entity", nil) ``` Der dritte Parameter sind die anfänglichen statischen Daten. Um die Nachricht einzustellen, können Sie die Methode der Entity-Tabelle verwenden: ```lua obj:get_luaentity():set_message("hello!") ``` Spieler mit den *give* [privilege](../players/privileges.html) können einen [chat command](../players/chat.html) zum spawnen von entities benutzen: /spawnentity mymod:entity ## Leben und Schaden ### Lebenspunkte (HP) Jedes Objekt hat eine Anzahl von Lebenspunkten (HP), die die aktuelle Gesundheit darstellt. Spieler haben eine maximale Lebenspunktzahl, die mit der Objekteigenschaft `hp_max` festgelegt wird. Ein Objekt stirbt, wenn seine HP 0 erreichen. ```lua local hp = object:get_hp() object:set_hp(hp + 3) ``` ### Schlagen, Damage Groups, und Armor Groups Schaden ist die Verringerung der HP eines Objekts. Ein Objekt kann ein anderes Objekt *schlagen*, um Schaden zuzufügen. Ein Schlag muss nicht unbedingt ein echter Schlag sein - es kann auch eine Explosion, ein Schwerthieb oder etwas anderes sein. Der Gesamtschaden wird durch Multiplikation der Schadensgruppen des Schlags mit den Verwundbarkeiten des Ziels berechnet. Dies wird dann begrenzt, je nachdem, wie lange der letzte Schlag her war. Wir werden gleich ein Beispiel für diese Berechnung erläutern. Genau wie [Node Grabungsgruppen](../items/nodes_items_crafting.html#tools-capabilities-and-dig-types), können diese Gruppen jeden Namen annehmen und müssen nicht registriert werden. Es ist jedoch üblich, dieselben Gruppennamen wie bei Node digging zu verwenden. Wie anfällig ein Objekt für bestimmte Arten von Schaden ist, hängt von seiner `armor_groups` [Objekteigenschaft](#object-properties). Trotz seines irreführenden Namen gibt `armor_groups` den prozentualen Schaden von bestimmten Schadensgruppen an,und nicht den Widerstand. Wenn eine Schadensgruppe nicht in den Armor Groups eines Objekts aufgeführt ist, ist das Objekt völlig unverwundbar. ```lua target:set_properties({ armor_groups = { fleshy = 90, crumbly = 50 }, }) ``` Im obigen Beispiel erleidet das Objekt 90 % `fleshy` Schadens und 50 % des `crumbly`-Schaden. Wenn ein Spieler ein Objekt schlägt, stammen die Schadensgruppen von dem Gegenstand, das er gerade trägt. In anderen Fällen entscheiden die Mods, welche Schadensgruppen verwendet werden. ### Example Damage Calculation Let's punch the `target` object: ```lua local tool_capabilities = { full_punch_interval = 0.8, damage_groups = { fleshy = 5, choppy = 10 }, -- This is only used for digging nodes, but is still required max_drop_level=1, groupcaps={ fleshy={times={[1]=2.5, [2]=1.20, [3]=0.35}, uses=30, maxlevel=2}, }, } local time_since_last_punch = tool_capabilities.full_punch_interval target:punch(object, time_since_last_punch, tool_capabilities) ``` Now, let's work out what the damage will be. The punch's damage groups are `fleshy=5` and `choppy=10`, and `target` will take 90% damage from fleshy and 0% from choppy. First, we multiply the damage groups by the vulnerability and sum the result. We then multiply by a number between 0 or 1 depending on the `time_since_last_punch`. ```lua = (5*90/100 + 10*0/100) * limit(time_since_last_punch / full_punch_interval, 0, 1) = (5*90/100 + 10*0/100) * 1 = 4.5 ``` As HP is an integer, the damage is rounded to 5 points. ## Attachments Attached objects will move when the parent - the object they are attached to - is moved. An attached object is said to be a child of the parent. An object can have an unlimited number of children, but at most one parent. ```lua child:set_attach(parent, bone, position, rotation) ``` An object's `get_pos()` will always return the global position of the object, no matter whether it is attached or not. `set_attach` takes a relative position, but not as you'd expect. The attachment position is relative to the parent's origin as scaled up by 10 times. So, `0,5,0` would be half a node above the parent's origin. {% include notice.html notice=page.degrad %} For 3D models with animations, the bone argument is used to attach the entity to a bone. 3D animations are based on skeletons - a network of bones in the model where each bone can be given a position and rotation to change the model, for example, to move the arm. Attaching to a bone is useful if you want to make a character hold something: ```lua obj:set_attach(player, "Arm_Right", -- default bone {x=0.2, y=6.5, z=3}, -- default position {x=-100, y=225, z=90}) -- default rotation ``` ## Your Turn * Make a windmill by combining nodes and an entity. * Make a mob of your choice (using just the entity API, and without using any other mods).