mirror of
https://github.com/minetest/minetest_docs.git
synced 2024-11-19 22:13:52 +01:00
Document Raycast
This commit is contained in:
parent
f9b5d6408a
commit
ec2a2c1bf8
223
doc/classes/Raycast.adoc
Normal file
223
doc/classes/Raycast.adoc
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
= Raycast
|
||||||
|
include::../../include/config.adoc[]
|
||||||
|
include::../../include/types.adoc[]
|
||||||
|
:description: Iterator of pointed things in a raycast / "line of sight"
|
||||||
|
:keywords: ray, raycast, selection, pointing, shooting, line of sight
|
||||||
|
|
||||||
|
Raycasts are used to simulate rays and determine which selection boxes they intersect with.
|
||||||
|
|
||||||
|
With some limitations, they work almost exactly like the player pointing at things.
|
||||||
|
|
||||||
|
== Pointed Thing
|
||||||
|
|
||||||
|
Pointed things in-game as passed to various callbacks are either
|
||||||
|
|
||||||
|
* Nothing (`{type = "nothing"}`), or
|
||||||
|
* Objects (entities or players), or
|
||||||
|
* Nodes (solids or liquids)
|
||||||
|
|
||||||
|
Pointed thing tables are thus a "tagged union" type, taking the following forms for objects & nodes:
|
||||||
|
|
||||||
|
=== Objects
|
||||||
|
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `type` | `{type-string}` | `"object"`
|
||||||
|
| `ref` | ObjectRef | The pointed ObjectRef
|
||||||
|
|===
|
||||||
|
|
||||||
|
=== Nodes
|
||||||
|
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `type` | `{type-string}` | `"node"`
|
||||||
|
| `under` | `{type-vector}` | The position of the pointed node
|
||||||
|
(the node which would be dug if using an appropriate tool)
|
||||||
|
| `above` | `{type-vector}` | The position of the pointed node plus the pointed face normal
|
||||||
|
(the position of the node which would be placed if you were building)
|
||||||
|
|===
|
||||||
|
|
||||||
|
=== Additional Raycast Fields
|
||||||
|
|
||||||
|
These fields are only available in pointed things returned by raycasts.
|
||||||
|
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `box_id` | `{type-number}` | Only relevant for nodes: 1-indexed ID of the pointed selection box of the node
|
||||||
|
| `intersection_normal` | `{type-vector}` | Normal vector pointing out of the face of the pointed thing the ray first hit
|
||||||
|
| `intersection_point` | `{type-vector}` | Point on both face & ray where the ray & the selection box(es) of the pointed thing first intersect
|
||||||
|
(may be in the selection box if the raycast starts in the box)
|
||||||
|
|===
|
||||||
|
|
||||||
|
Raycasts do not emit "nothing" pointed things.
|
||||||
|
|
||||||
|
== `minetest.line_of_sight(pos1, pos2)`
|
||||||
|
|
||||||
|
Checks whether any non-air node is blocking the line of sight between two positions.
|
||||||
|
|
||||||
|
You may use this to determine e.g. whether a target would be visible to a mob.
|
||||||
|
Raycasts will often be preferable since they provide more fine-grained control
|
||||||
|
(objects, liquids, looping over possibly blocking things in the line of sight).
|
||||||
|
|
||||||
|
=== Arguments
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `pos1` | `{type-vector}` | Starting position of the line ("segment") of sight
|
||||||
|
| `pos2` | `{type-vector}` | Ending position of the line ("segment") of sight
|
||||||
|
|===
|
||||||
|
|
||||||
|
=== Returns
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `free_sight` | `{type-bool}` | Whether any node is blocking the sight
|
||||||
|
| `pos` | `{type-vector}` | Position of the "first" node (the node closest to `pos1`)
|
||||||
|
blocking the sight (only returned if `free_sight` is `false`)
|
||||||
|
|===
|
||||||
|
|
||||||
|
== `Raycast(from_pos, to_pos, include_objects, include_liquid_nodes)`
|
||||||
|
|
||||||
|
Creates a raycast object; OOP-constructor-style alias for `minetest.raycast`.
|
||||||
|
|
||||||
|
IMPORTANT: https://github.com/minetest/minetest/issues/12673[Raycasts work on selection-, not collision boxes,
|
||||||
|
making them coherent with player pointing but not physics (collisions) unless selection- and collision boxes are identical.]
|
||||||
|
|
||||||
|
TIP: Have selection boxes, collision boxes (and ideally even visuals) match for all nodes & entities if possible.
|
||||||
|
|
||||||
|
IMPORTANT: https://github.com/minetest/minetest/issues/10304[Serverside raycasts do not support attachments properly;
|
||||||
|
the server is unaware of model specificities, doesn't keep track of automatic rotation etc.]
|
||||||
|
|
||||||
|
=== Arguments
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `from_pos` | `{type-vector}` | Starting position of the raycast (usually eye position)
|
||||||
|
| `to_pos` | `{type-vector}` | Ending position of the raycast (usually eye position + look direction times range)
|
||||||
|
| `include_objects` | `{type-bool}` | Whether objects are included (considered pointable). Optional, defaults to `true`.
|
||||||
|
| `include_liquids` | `{type-bool}` | Whether liquids are included (considered pointable). Optional, defaults to `true`.
|
||||||
|
|===
|
||||||
|
|
||||||
|
=== Returns
|
||||||
|
[%autowidth, frame=none]
|
||||||
|
|===
|
||||||
|
| `ray` | Raycast iterator object | Non-restartable, stateful iterator of pointed things
|
||||||
|
|===
|
||||||
|
|
||||||
|
The Raycast iterator object is callable. Calling `ray()` is syntactic sugar for `ray:next()`.
|
||||||
|
|
||||||
|
`ray:next()` returns the next pointed thing or `{type-nil}` if there is none and advances the iterator state.
|
||||||
|
|
||||||
|
Pointed things are returned sorted by the intersection point, from `from_pos` to `to_pos`, from nearest to farthest.
|
||||||
|
|
||||||
|
=== Usage
|
||||||
|
|
||||||
|
Since raycasts are iterators, you can loop over them using a `for`-loop. You will usually use the concise
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local ray = Raycast(...)
|
||||||
|
for pointed_thing in ray do
|
||||||
|
-- do something
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
which is just shorthand for
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local ray = Raycast(...)
|
||||||
|
for pointed_thing in ray.next, ray do
|
||||||
|
-- do something
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
which again is syntactic sugar for
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local ray = Raycast(...)
|
||||||
|
local pointed_thing = ray:next()
|
||||||
|
while pointed_thing ~= nil do
|
||||||
|
-- do something
|
||||||
|
pointed_thing = ray:next()
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
There is absolutely no need to manually step through raycasts
|
||||||
|
using the latter two more verbose loops.
|
||||||
|
|
||||||
|
[IMPORTANT]
|
||||||
|
.Iterator Restartability
|
||||||
|
====
|
||||||
|
Restartability is the ability of an iterator to start again. For example, `ipairs` is resumable:
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local t = {1, 2, 3}
|
||||||
|
for _ = 1, 3 do
|
||||||
|
for i, v in ipairs(t) do
|
||||||
|
print(i, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
will work just expected, printing the contents of `t` three times.
|
||||||
|
|
||||||
|
*Raycasts are not restartable.* Iterating them _"consumes"_ the pointed things;
|
||||||
|
they can't be iterated again. The following will not work as expected:
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local ray = Raycast(...)
|
||||||
|
for _ = 1, 3 do
|
||||||
|
for pt in ray do
|
||||||
|
print(pt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
this will print the pointed things (table addresses) exactly once:
|
||||||
|
after the first run of the inner `for` loop, all pointed things have been consumed;
|
||||||
|
subsequent runs exit immediately, not entering the `for` loop at all.
|
||||||
|
|
||||||
|
One advantage of this is that Raycasts remember their looping state. The following is thus possible:
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
local ray = Raycast(...)
|
||||||
|
for pt in ray do
|
||||||
|
if ... then break end
|
||||||
|
end
|
||||||
|
for pt in ray do
|
||||||
|
-- will resume looping with the next pointed thing
|
||||||
|
end
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
== Examples
|
||||||
|
|
||||||
|
=== Redo tool raycasts
|
||||||
|
|
||||||
|
Useful to obtain the additional raycast pointed thing fields,
|
||||||
|
to dynamically decide what is pointable and what is not,
|
||||||
|
to determine the pointed thing periodically ("what am I looking at?") etc.
|
||||||
|
|
||||||
|
[source,lua]
|
||||||
|
----
|
||||||
|
-- Calculate eye position including eye offset
|
||||||
|
local eye_pos = player:get_pos()
|
||||||
|
eye_pos.y = eye_pos.y + player:get_properties().eye_height
|
||||||
|
local first, third = player:get_eye_offset()
|
||||||
|
if not vector.equals(first, third) then
|
||||||
|
minetest.log("warning", "First & third person eye offsets don't match, assuming first person")
|
||||||
|
end
|
||||||
|
eye_pos = vector.add(eye_pos, vector.divide(first, 10)) -- eye offsets are in block space (10x), transform them back to metric
|
||||||
|
|
||||||
|
local def = player:get_wielded_item():get_definition()
|
||||||
|
|
||||||
|
for pointed_thing in minetest.raycast(eye_pos, vector.add(eye_pos, vector.multiply(player:get_look_dir(), def.range or 4)), true, def.liquids_pointable) do
|
||||||
|
if pointed_thing.ref ~= player then -- exclude the player
|
||||||
|
-- do something
|
||||||
|
end
|
||||||
|
end
|
||||||
|
----
|
||||||
|
|
||||||
|
// TODO compare players by player name rather than by reference in the above example?
|
Loading…
Reference in New Issue
Block a user