-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #41 from godot-rust/feature/godot-api
Godot API tutorial (builtins, objects, functions)
- Loading branch information
Showing
16 changed files
with
644 additions
and
149 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
<!-- | ||
~ Copyright (c) godot-rust; Bromeon and contributors. | ||
~ This Source Code Form is subject to the terms of the Mozilla Public | ||
~ License, v. 2.0. If a copy of the MPL was not distributed with this | ||
~ file, You can obtain one at https://mozilla.org/MPL/2.0/. | ||
--> | ||
|
||
# Built-in types | ||
|
||
The so-called "built-in types" or just "builtins" are the basic types that Godot provides. Notably, these are not _classes_. | ||
See also [basic built-in types in Godot][godot-docs-builtins]. | ||
|
||
|
||
## Table of contents | ||
<!-- toc --> | ||
|
||
|
||
## List of types | ||
|
||
Here is an exhaustive list of all built-in types, by category. We use the GDScript names; below, we explain how they map to Rust. | ||
|
||
**Simple types** | ||
|
||
- Boolean: `bool` | ||
- Numeric: `int`, `float` | ||
|
||
**Composite types** | ||
|
||
- Variant (able to hold anything): `Variant` | ||
- String types: `String`, `StringName`, `NodePath` | ||
- Ref-counted containers: `Array` (`Array[T]`), `Dictionary` | ||
- Packed arrays: `Packed*Array` for following element types: | ||
`Byte`, `Int32`, `Int64`, `Float32`, `Float64`, `Vector2`, `Vector3`, `Color`, `String` | ||
- Functional: `Callable`, `Signal` | ||
|
||
**Geometric types** | ||
|
||
- Vectors: `Vector2`, `Vector2i`, `Vector3`, `Vector3i`, `Vector4`, `Vector4i` | ||
- Bounding boxes: `Rect2`, `Rect2i`, `AABB` | ||
- Matrices: `Transform2D`, `Transform3D`, `Basis`, `Projection` | ||
- Rotation: `Quaternion` | ||
- Geometric objects: `Plane` | ||
|
||
**Miscellaneous** | ||
|
||
- Color: `Color` | ||
- Resource ID: `RID` | ||
|
||
|
||
### Rust mapping | ||
|
||
Rust types in the gdext API represent the corresponding Godot types in the closest way possible. They are used in parameter and return type | ||
position of API functions, for example. | ||
|
||
Most builtins have a 1:1 equivalent (e.g. `Vector2f`, `Color` etc.). The following list highlights some noteworthy mappings: | ||
|
||
| GDScript type | Rust type | Rust example expression | | ||
|---------------------------|---------------------------------------|---------------------------| | ||
| `int` | `i64` | `-12345` | | ||
| `float` | `f64` | `3.14159` | | ||
| `real` | `real` (either `f32` or `f64`) | `real!(3.14159)` | | ||
| `String` | `GString` | `"Some string".into()` | | ||
| `StringName` | `StringName` | `"MyClass".into()` | | ||
| `NodePath` | `NodePath` | `Nodes/MyNode".into()` | | ||
| `Array[T]` | `Array<T>` | `array![1, 2, 3]` | | ||
| `Array` | `VariantArray`<br>or `Array<Variant>` | `varray![1, "two", true]` | | ||
| `Dictionary` | `Dictionary` | `dict!{"key": "value"}` | | ||
| `AABB` | `Aabb` | `Aabb::new(pos, size)` | | ||
| `Object` | `Gd<Object>` | `Object::new_alloc()` | | ||
| `SomeClass` | `Gd<SomeClass>` | `Resource::new_gd()` | | ||
| `SomeClass` (nullable) | `Option<Gd<SomeClass>>` | `None` | | ||
| `Variant` (also implicit) | `Variant` | `Variant::nil()` | | ||
|
||
Note that Godot does not have nullability information in its class API yet. This means that we have to conservatively assume that objects can | ||
be null, and thus use `Option<Gd<T>>` instead of `Gd<T>` for object return types. This often needs unnecessary unwrapping. | ||
|
||
Nullable types are being looked into [on Godot side][godot-nullability-issue]. If there is no upstream solution for a while, we may consider our | ||
own workarounds, but it may come with manual annotation of many APIs. | ||
|
||
|
||
## String types | ||
|
||
Godot provides three string types: `String` ([`GString`][api-gstring] in Rust), [`StringName`][api-stringname], and [`NodePath`][api-nodepath]. | ||
`GString` is used as a general-purpose string, while `StringName` is often used for identifiers like class or action names. | ||
The idea is that `StringName` is cheap to construct and compare.[^string-name-Rust] | ||
|
||
These types all support `From` traits to convert to/from Rust `String`, and from `&str`. You can thus use `"some_string".into()` expressions. | ||
If you need more explicit typing, use `StringName::from("some_string")`. | ||
|
||
`StringName` in particular provides a direct conversion from C-string literals such as `c"string"`, [introduced in Rust 1.77][rust-c-strings]. | ||
This can be used for _static_ C-strings, i.e. ones that remain allocated for the entire program lifetime. Don't use them for short-lived ones. | ||
|
||
|
||
## Arrays and dictionaries | ||
|
||
Godot's linear collection type is `Array<T>`. It is generic over its element type `T`, which can be one of the supported Godot types (generally | ||
anything that can be represented by `Variant`). A special type `VariantArray` is provided as an alias for `Array<Variant>`, which is used when | ||
the element type is dynamically typed. | ||
|
||
`Dictionary` is a key-value store, where both keys and values are `Variant`. Godot does currently not support generic dictionaries, although | ||
this feature is [under discussion][godot-generic-dicts]. | ||
|
||
Arrays and dictionaries can be constructed using three macros: | ||
|
||
```rust | ||
let a = array![1, 2, 3]; // Array<i64> | ||
let b = varray![1, "two", true]; // Array<Variant> | ||
let c = dict!{"key": "value"}; // Dictionary | ||
``` | ||
|
||
Their API is similar, but not identical to Rust's standard types `Vec` and `HashMap`. An important difference is that `Array` and `Dictionary` | ||
are reference-counted, which means that `clone()` will not create an independent copy, but another reference to the same instance. Furthermore, | ||
since internal elements are stored as variants, they are not accessible by reference, which is why the `[]` operator (`Index/IndexMut` traits) | ||
is absent. | ||
|
||
```rust | ||
let a = array![0, 11, 22]; | ||
|
||
assert_eq!(a.len(), 3); | ||
assert_eq!(a.get(1), Some(11)); // Note: by value, not Some(&11). | ||
assert_eq!(a.at(1), 11); // Panics on out-of-bounds. | ||
|
||
let mut b = a.clone(); // Increment reference-count. | ||
b.set(2, 33); // Modify new ref. | ||
assert_eq!(a.at(2), 33); // Original array has changed. | ||
|
||
b.clear(); | ||
assert_eq!(b, Array::new()); // new() creates an empty array. | ||
``` | ||
|
||
```rust | ||
let c = dict! { | ||
"str": "hello", | ||
"int": 42, | ||
"bool": true, | ||
}; | ||
|
||
assert_eq!(c.len(), 3); | ||
assert_eq!(c.get("str"), Some(&"hello".into())); | ||
|
||
let mut d = c.clone(); // Increment reference-count. | ||
d.insert("float", 3.14); // Modify new ref. | ||
assert!(c.contains_key("float")); // Original dict has changed. | ||
``` | ||
|
||
To iterate, you can use `iter_shared()`. This method works almost like `iter()` on Rust collections, but the name highlights that you do not | ||
have unique access to the collection during iteration, since there might exist another reference. This also means it's your responsibility to | ||
ensure that the collection is not modified in unintended ways during iteration (which should be safe, but may lead to data inconsistencies). | ||
|
||
```rust | ||
let a = array!["one", "two", "three"]; | ||
let d = dict!{"one": 1, "two": 2.0, "three": Vector3::ZERO}; | ||
|
||
for elem in a.iter_shared() { | ||
println!("Element: {elem}"); | ||
}bu | ||
|
||
for (key, value) in d.iter_shared() { | ||
println!("Key: {key}, value: {value}"); | ||
} | ||
``` | ||
|
||
|
||
## Packed arrays | ||
|
||
`Packed*Array` types are used for storing elements space-efficiently in contiguous memory ("packed"). The `*` stands for the element type, e.g. | ||
`PackedByteArray` or `PackedVector3Array`. | ||
|
||
```rust | ||
// Create from slices. | ||
let bytes = PackedByteArray::from(&[0x0A, 0x0B, 0x0C]); | ||
let ints = PackedInt32Array::from(&[1, 2, 3]); | ||
|
||
// Access as Rust shared/mutable slices. | ||
let bytes_slice: &[u8] = b.as_slice(); | ||
let ints_slice: &mut [i32] = i.as_mut_slice(); | ||
``` | ||
|
||
Unlike `Array`, packed arrays use copy-on-write instead of reference counting. When you clone a packed array, you get a new independent instance. | ||
Cloning is cheap as long as you don't modify either instance. Once you use a write operation (anything with `&mut self`), the packed array will | ||
allocate its own memory and copy the data. | ||
|
||
<br> | ||
|
||
--- | ||
|
||
**Footnotes** | ||
|
||
[^string-name-Rust]: When constructing `StringName` from `&str` or `String`, the conversion is rather expensive, since it has to go through a | ||
conversion to UTF-32. As Rust recently introduced C-string literals (`c"hello"`), we can now directly construct from them. This is more | ||
efficient, but keeps memory allocated until shutdown, so don't use it for rarely used temporaries. | ||
See [API docs][api-stringname] and [issue #531][issue-stringname-perf] for more information. | ||
|
||
[api-gstring]: https://godot-rust.github.io/docs/gdext/master/godot/builtin/struct.GString.html | ||
[api-nodepath]: https://godot-rust.github.io/docs/gdext/master/godot/builtin/struct.NodePath.html | ||
[api-stringname]: https://godot-rust.github.io/docs/gdext/master/godot/builtin/struct.StringName.html | ||
[godot-docs-builtins]: https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#basic-built-in-types | ||
[godot-generic-dicts]: https://github.com/godotengine/godot/pull/78656 | ||
[godot-nullability-issue]: https://github.com/godotengine/godot-proposals/issues/162 | ||
[issue-stringname-perf]: https://github.com/godot-rust/gdext/issues/531 | ||
[rust-c-strings]: https://doc.rust-lang.org/nightly/edition-guide/rust-2021/c-string-literals.html#c-string-literals |
Oops, something went wrong.