2018-09-12 17:29:15 +03:00
|
|
|
---
|
|
|
|
title: Objects, Players, and Entities
|
|
|
|
layout: default
|
|
|
|
root: ../..
|
2018-10-19 16:29:07 +03:00
|
|
|
idx: 3.4
|
2018-09-12 17:29:15 +03:00
|
|
|
description: Using an ObjectRef
|
2019-07-01 23:01:38 +03:00
|
|
|
degrad:
|
|
|
|
level: warning
|
|
|
|
title: Degrees and Radians
|
|
|
|
message: Attachment rotation is set in degrees, whereas object rotation is in radians.
|
|
|
|
Make sure to convert to the correct angle measurement.
|
2018-09-12 17:29:15 +03:00
|
|
|
---
|
|
|
|
|
2019-05-31 20:32:40 +03:00
|
|
|
## Introduction <!-- omit in toc -->
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
In this chapter, you will learn how to manipulate objects and how to define your
|
|
|
|
own.
|
|
|
|
|
2019-08-13 17:09:51 +03:00
|
|
|
- [What are Objects, Players, and Entities?](#what-are-objects-players-and-entities)
|
|
|
|
- [Position and Velocity](#position-and-velocity)
|
|
|
|
- [Object Properties](#object-properties)
|
|
|
|
- [Entities](#entities)
|
2021-01-25 13:53:27 +03:00
|
|
|
- [Health and Damage](#health-and-damage)
|
2023-05-01 14:22:02 +03:00
|
|
|
- [Health Points (HP)](#health-points-hp)
|
|
|
|
- [Punch, Damage Groups, and Armor Groups](#punch-damage-groups-and-armor-groups)
|
|
|
|
- [Example Damage Calculation](#example-damage-calculation)
|
2019-08-13 17:09:51 +03:00
|
|
|
- [Attachments](#attachments)
|
|
|
|
- [Your Turn](#your-turn)
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
## What are Objects, Players, and Entities?
|
|
|
|
|
2021-01-25 13:53:27 +03:00
|
|
|
Players and Entities are both types of Objects. An object is something that can move
|
2018-09-12 17:29:15 +03:00
|
|
|
independently of the node grid and has properties such as velocity and scale.
|
2018-10-20 03:37:41 +03:00
|
|
|
Objects aren't items, and they have their own separate registration system.
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
There are a few differences between Players and Entities.
|
|
|
|
The biggest one is that Players are player-controlled, whereas Entities are mod-controlled.
|
|
|
|
This means that the velocity of a player cannot be set by mods - players are client-side,
|
|
|
|
and entities are server-side.
|
|
|
|
Another difference is that Players will cause map blocks to be loaded, whereas Entities
|
|
|
|
will just be saved and become inactive.
|
|
|
|
|
2019-07-01 23:01:38 +03:00
|
|
|
This distinction is muddied by the fact that Entities are controlled using a table
|
2020-03-28 22:59:39 +03:00
|
|
|
which is referred to as a Lua entity, as discussed later.
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
## Position and Velocity
|
|
|
|
|
|
|
|
`get_pos` and `set_pos` exist to allow you to get and set an entity's position.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
local object = minetest.get_player_by_name("bob")
|
|
|
|
local pos = object:get_pos()
|
|
|
|
object:set_pos({ x = pos.x, y = pos.y + 1, z = pos.z })
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
`set_pos` immediately sets the position, with no animation. If you'd like to
|
|
|
|
smoothly animate an object to the new position, you should use `move_to`.
|
|
|
|
This, unfortunately, only works for entities.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
object:move_to({ x = pos.x, y = pos.y + 1, z = pos.z })
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
An important thing to think about when dealing with entities is network latency.
|
|
|
|
In an ideal world, messages about entity movements would arrive immediately,
|
|
|
|
in the correct order, and with a similar interval as to how you sent them.
|
|
|
|
However, unless you're in singleplayer, this isn't an ideal world.
|
|
|
|
Messages will take a while to arrive. Position messages may arrive out of order,
|
|
|
|
resulting in some `set_pos` calls being skipped as there's no point going to
|
|
|
|
a position older than the current known position.
|
|
|
|
Moves may not be similarly spaced, which makes it difficult to use them for animation.
|
|
|
|
All this results in the client seeing different things to the server, which is something
|
|
|
|
you need to be aware of.
|
|
|
|
|
|
|
|
## Object Properties
|
|
|
|
|
2020-03-28 22:59:39 +03:00
|
|
|
Object properties are used to tell the client how to render and deal with an
|
|
|
|
object. It's not possible to define custom properties, because the properties are
|
|
|
|
for the engine to use, by definition.
|
|
|
|
|
2018-09-12 17:29:15 +03:00
|
|
|
Unlike nodes, objects have a dynamic rather than set appearance.
|
|
|
|
You can change how an object looks, among other things, at any time by updating
|
|
|
|
its properties.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
object:set_properties({
|
|
|
|
visual = "mesh",
|
|
|
|
mesh = "character.b3d",
|
|
|
|
textures = {"character_texture.png"},
|
|
|
|
visual_size = {x=1, y=1},
|
|
|
|
})
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
The updated properties will be sent to all players in range.
|
|
|
|
This is very useful to get a large amount of variety very cheaply, such as having
|
|
|
|
different skins per-player.
|
|
|
|
|
2018-10-20 03:37:41 +03:00
|
|
|
As shown in the next section, entities can have initial properties
|
|
|
|
provided in their definition.
|
2018-09-12 17:29:15 +03:00
|
|
|
The default Player properties are defined in the engine, however, so you'll
|
|
|
|
need to use `set_properties()` in `on_joinplayer` to set the properties for newly
|
|
|
|
joined players.
|
|
|
|
|
|
|
|
## Entities
|
|
|
|
|
2020-03-28 22:59:39 +03:00
|
|
|
An Entity has a definition table that resembles an item definition table.
|
|
|
|
This table can contain callback methods, initial object properties, and custom
|
|
|
|
members.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
local MyEntity = {
|
|
|
|
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 MyEntity:set_message(msg)
|
|
|
|
self.message = msg
|
|
|
|
end
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2020-12-24 16:25:31 +03:00
|
|
|
Entity definitions differ in one very important way from Item definitions.
|
|
|
|
When an entity is emerged (ie: loaded or created), a new table is created for
|
|
|
|
that entity that *inherits* from the definition table.
|
|
|
|
|
|
|
|
<!--
|
2021-01-05 23:51:13 +03:00
|
|
|
This inheritance is done using a metatables.
|
|
|
|
Metatables are an important Lua feature that you will need to be aware of, as it
|
|
|
|
is an essential part of the Lua language. In layman's terms, a metatable allows
|
|
|
|
you to control how the table behaves when using certain Lua syntax. The most
|
|
|
|
common use of metatables is the ability to use another table as a prototype,
|
|
|
|
defaulting to the other table's properties and methods when they do not exist in
|
|
|
|
the current table.
|
2020-12-24 16:25:31 +03:00
|
|
|
Say you want to access `a.x`. If the table `a` has that member, then it will be
|
|
|
|
returned as normal. However, if the table doesn't have that member and the
|
2021-01-05 23:51:13 +03:00
|
|
|
metatable lists a table `b` as a prototype, then table `b` will be checked to
|
|
|
|
see if it has that member.
|
2020-12-24 16:25:31 +03:00
|
|
|
-->
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
Both an ObjectRef and an entity table provide ways to get the counterpart:
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
local entity = object:get_luaentity()
|
|
|
|
local object = entity.object
|
|
|
|
print("entity is at " .. minetest.pos_to_string(object:get_pos()))
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
There are a number of available callbacks for use with entities.
|
2023-05-01 14:22:02 +03:00
|
|
|
A complete list can be found in [lua_api.md](https://minetest.gitlab.io/minetest/minetest-namespace-reference/#registered-definition-tables).
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
function MyEntity:on_step(dtime)
|
2018-09-24 19:16:00 +03:00
|
|
|
local pos = self.object:get_pos()
|
|
|
|
local pos_down = vector.subtract(pos, vector.new(0, 1, 0))
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
local delta
|
2018-09-24 19:16:00 +03:00
|
|
|
if minetest.get_node(pos_down).name == "air" then
|
2018-09-12 17:29:15 +03:00
|
|
|
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 MyEntity:on_punch(hitter)
|
|
|
|
minetest.chat_send_player(hitter:get_player_name(), self.message)
|
|
|
|
end
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
Now, if you were to spawn and use this entity, you'd notice that the message
|
|
|
|
would be forgotten when the entity becomes inactive then active again.
|
|
|
|
This is because the message isn't saved.
|
|
|
|
Rather than saving everything in the entity table, Minetest gives you control over
|
|
|
|
how to save things.
|
2018-10-20 03:37:41 +03:00
|
|
|
Staticdata is a string which contains all the custom information that
|
2018-09-12 17:29:15 +03:00
|
|
|
needs to stored.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
function MyEntity:get_staticdata()
|
|
|
|
return minetest.write_json({
|
|
|
|
message = self.message,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
function MyEntity: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
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2018-10-20 03:37:41 +03:00
|
|
|
Minetest may call `get_staticdata()` as many times as it wants and at any time.
|
2018-09-12 17:29:15 +03:00
|
|
|
This is because Minetest doesn't wait for a MapBlock to become inactive to save
|
|
|
|
it, as this would result in data loss. MapBlocks are saved roughly every 18
|
|
|
|
seconds, so you should notice a similar interval for `get_staticdata()` being called.
|
|
|
|
|
|
|
|
`on_activate()`, on the other hand, will only be called when an entity becomes
|
|
|
|
active either from the MapBlock becoming active or from the entity spawning.
|
|
|
|
This means that staticdata could be empty.
|
|
|
|
|
|
|
|
Finally, you need to register the type table using the aptly named `register_entity`.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-27 17:37:35 +03:00
|
|
|
minetest.register_entity("mymod:entity", MyEntity)
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2018-09-27 17:37:35 +03:00
|
|
|
The entity can be spawned by a mod like so:
|
|
|
|
|
|
|
|
```lua
|
|
|
|
local pos = { x = 1, y = 2, z = 3 }
|
|
|
|
local obj = minetest.add_entity(pos, "mymod:entity", nil)
|
|
|
|
```
|
|
|
|
|
|
|
|
The third parameter is the initial staticdata.
|
|
|
|
To set the message, you can use the entity table method:
|
|
|
|
|
|
|
|
```lua
|
|
|
|
obj:get_luaentity():set_message("hello!")
|
|
|
|
```
|
|
|
|
|
|
|
|
Players with the *give* [privilege](../players/privileges.html) can
|
|
|
|
use a [chat command](../players/chat.html) to spawn entities:
|
|
|
|
|
|
|
|
/spawnentity mymod:entity
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2021-01-25 13:53:27 +03:00
|
|
|
|
|
|
|
## Health and Damage
|
|
|
|
|
2021-01-27 21:43:06 +03:00
|
|
|
### Health Points (HP)
|
2021-01-25 13:53:27 +03:00
|
|
|
|
2021-01-27 21:43:06 +03:00
|
|
|
Each object has a Health Points (HP) number, which represents the current health.
|
2021-01-25 13:53:27 +03:00
|
|
|
Players have a maximum hp set using the `hp_max` object property.
|
|
|
|
An object will die if its hp reaches 0.
|
|
|
|
|
|
|
|
```lua
|
|
|
|
local hp = object:get_hp()
|
|
|
|
object:set_hp(hp + 3)
|
|
|
|
```
|
|
|
|
|
|
|
|
### Punch, Damage Groups, and Armor Groups
|
|
|
|
|
|
|
|
Damage is the reduction of an object's HP. An object can *punch* another object to
|
|
|
|
inflict damage. A punch isn't necessarily an actual punch - it can be an
|
|
|
|
explosion, a sword slash, or something else.
|
|
|
|
|
|
|
|
The total damage is calculated by multiplying the punch's damage groups with the
|
|
|
|
target's vulnerabilities. This is then limited depending on how recent the last
|
|
|
|
punch was. We will go over an example of this calculation in a bit.
|
|
|
|
|
|
|
|
Just like [node dig groups](../items/nodes_items_crafting.html#tools-capabilities-and-dig-types),
|
|
|
|
these groups can take any name and do not need to be registered. However, it's
|
|
|
|
common to use the same group names as with node digging.
|
|
|
|
|
|
|
|
How vulnerable an object is to particular types of damage depends on its
|
|
|
|
`armor_groups` [object property](#object-properties). Despite its misleading
|
|
|
|
name, `armor_groups` specify the percentage damage taken from particular damage
|
|
|
|
groups, not the resistance. If a damage group is not listed in an object's armor
|
|
|
|
groups, that object is completely invulnerable to it.
|
|
|
|
|
|
|
|
```lua
|
|
|
|
target:set_properties({
|
|
|
|
armor_groups = { fleshy = 90, crumbly = 50 },
|
|
|
|
})
|
|
|
|
```
|
|
|
|
|
|
|
|
In the above example, the object will take 90% of `fleshy` damage and 50% of
|
|
|
|
`crumbly` damage.
|
|
|
|
|
|
|
|
When a player punches an object, the damage groups come from the item they are
|
|
|
|
currently wielding. In other cases, mods decide which damage groups are used.
|
|
|
|
|
|
|
|
### 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-09-12 17:29:15 +03:00
|
|
|
## 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.
|
|
|
|
|
2018-09-19 14:04:51 +03:00
|
|
|
```lua
|
2018-09-12 17:29:15 +03:00
|
|
|
child:set_attach(parent, bone, position, rotation)
|
2018-09-19 14:04:51 +03:00
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2021-01-25 13:53:27 +03:00
|
|
|
An object's `get_pos()` will always return the global position of the object, no
|
2018-09-12 17:29:15 +03:00
|
|
|
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.
|
2018-10-20 03:37:41 +03:00
|
|
|
So, `0,5,0` would be half a node above the parent's origin.
|
2018-09-12 17:29:15 +03:00
|
|
|
|
2019-07-01 23:01:38 +03:00
|
|
|
{% include notice.html notice=page.degrad %}
|
|
|
|
|
2018-09-12 17:29:15 +03:00
|
|
|
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
|
2019-05-31 20:32:40 +03:00
|
|
|
each bone can be given a position and rotation to change the model, for example,
|
2018-09-12 17:29:15 +03:00
|
|
|
to move the arm.
|
2019-07-01 23:01:38 +03:00
|
|
|
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
|
|
|
|
```
|
2018-09-12 17:29:15 +03:00
|
|
|
|
|
|
|
## Your Turn
|
|
|
|
|
|
|
|
* Make a windmill by combining nodes and an entity.
|
2019-07-01 23:01:38 +03:00
|
|
|
* Make a mob of your choice (using just the entity API, and without using any other mods).
|