More block cleanups.

This commit is contained in:
Relintai 2023-01-12 22:32:46 +01:00
parent 8e5258c966
commit 469fb55c5e
53 changed files with 1106 additions and 981 deletions

View File

@ -28,9 +28,9 @@ Usage is generally as follows
Obtaining a ResourceInteractiveLoader Obtaining a ResourceInteractiveLoader
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: cpp ```
Ref( ResourceInteractiveLoader> ResourceLoader::load_interactive(String p_path); Ref( ResourceInteractiveLoader> ResourceLoader::load_interactive(String p_path);
```
This method will give you a ResourceInteractiveLoader that you will use This method will give you a ResourceInteractiveLoader that you will use
to manage the load operation. to manage the load operation.
@ -38,9 +38,9 @@ to manage the load operation.
Polling Polling
~~~~~~~ ~~~~~~~
.. code-block:: cpp ```
Error ResourceInteractiveLoader::poll(); Error ResourceInteractiveLoader::poll();
```
Use this method to advance the progress of the load. Each call to Use this method to advance the progress of the load. Each call to
`poll` will load the next stage of your resource. Keep in mind that `poll` will load the next stage of your resource. Keep in mind that
@ -55,10 +55,10 @@ Load progress (optional)
To query the progress of the load, use the following methods: To query the progress of the load, use the following methods:
.. code-block:: cpp ```
int ResourceInteractiveLoader::get_stage_count() const; int ResourceInteractiveLoader::get_stage_count() const;
int ResourceInteractiveLoader::get_stage() const; int ResourceInteractiveLoader::get_stage() const;
```
`get_stage_count` returns the total number of stages to load. `get_stage_count` returns the total number of stages to load.
`get_stage` returns the current stage being loaded. `get_stage` returns the current stage being loaded.
@ -66,9 +66,9 @@ To query the progress of the load, use the following methods:
Forcing completion (optional) Forcing completion (optional)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: cpp ```
Error ResourceInteractiveLoader::wait(); Error ResourceInteractiveLoader::wait();
```
Use this method if you need to load the entire resource in the current Use this method if you need to load the entire resource in the current
frame, without any more steps. frame, without any more steps.
@ -76,9 +76,9 @@ frame, without any more steps.
Obtaining the resource Obtaining the resource
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: cpp ```
Ref<Resource> ResourceInteractiveLoader::get_resource();
Ref( Resource> ResourceInteractiveLoader::get_resource(); ```
If everything goes well, use this method to retrieve your loaded If everything goes well, use this method to retrieve your loaded
resource. resource.
@ -92,8 +92,7 @@ context of the `doc_singletons_autoload` example.
First, we set up some variables and initialize the `current_scene` First, we set up some variables and initialize the `current_scene`
with the main scene of the game: with the main scene of the game:
:: ```
var loader var loader
var wait_frames var wait_frames
var time_max = 100 # msec var time_max = 100 # msec
@ -103,6 +102,7 @@ with the main scene of the game:
func _ready(): func _ready():
var root = get_tree().get_root() var root = get_tree().get_root()
current_scene = root.get_child(root.get_child_count() -1) current_scene = root.get_child(root.get_child_count() -1)
```
The function `goto_scene` is called from the game when the scene The function `goto_scene` is called from the game when the scene
needs to be switched. It requests an interactive loader, and calls needs to be switched. It requests an interactive loader, and calls
@ -110,8 +110,7 @@ needs to be switched. It requests an interactive loader, and calls
callback. It also starts a "loading" animation, which could show a callback. It also starts a "loading" animation, which could show a
progress bar or loading screen. progress bar or loading screen.
:: ```
func goto_scene(path): # Game requests to switch to this scene. func goto_scene(path): # Game requests to switch to this scene.
loader = ResourceLoader.load_interactive(path) loader = ResourceLoader.load_interactive(path)
if loader == null: # Check for errors. if loader == null: # Check for errors.
@ -125,6 +124,7 @@ progress bar or loading screen.
get_node("animation").play("loading") get_node("animation").play("loading")
wait_frames = 1 wait_frames = 1
```
`process` is where the loader is polled. `poll` is called, and then `process` is where the loader is polled. `poll` is called, and then
we deal with the return value from that call. `OK` means keep polling, we deal with the return value from that call. `OK` means keep polling,
@ -138,8 +138,7 @@ to cram more than one call to `poll` in one frame; some might take way
more than your value for `time_max`, so keep in mind we won't have more than your value for `time_max`, so keep in mind we won't have
precise control over the timings. precise control over the timings.
:: ```
func _process(time): func _process(time):
if loader == null: if loader == null:
# no need to process anymore # no need to process anymore
@ -168,6 +167,7 @@ precise control over the timings.
show_error() show_error()
loader = null loader = null
break break
```
Some extra helper functions. `update_progress` updates a progress bar, Some extra helper functions. `update_progress` updates a progress bar,
or can also update a paused animation (the animation represents the or can also update a paused animation (the animation represents the
@ -176,8 +176,7 @@ newly loaded scene on the tree. Because it's a scene being loaded,
`instance()` needs to be called on the resource obtained from the `instance()` needs to be called on the resource obtained from the
loader. loader.
:: ```
func update_progress(): func update_progress():
var progress = float(loader.get_stage()) / loader.get_stage_count() var progress = float(loader.get_stage()) / loader.get_stage_count()
# Update your progress bar? # Update your progress bar?
@ -194,6 +193,7 @@ loader.
func set_new_scene(scene_resource): func set_new_scene(scene_resource):
current_scene = scene_resource.instance() current_scene = scene_resource.instance()
get_node("/root").add_child(current_scene) get_node("/root").add_child(current_scene)
```
Using multiple threads Using multiple threads
---------------------- ----------------------
@ -224,34 +224,34 @@ Example class
You can find an example class for loading resources in threads here: You can find an example class for loading resources in threads here:
:download:`resource_queue.gd ( files/resource_queue.gd )`. Usage is as follows: :download:`resource_queue.gd ( files/resource_queue.gd )`. Usage is as follows:
:: ```
func start() func start()
```
Call after you instance the class to start the thread. Call after you instance the class to start the thread.
:: ```
func queue_resource(path, p_in_front = false) func queue_resource(path, p_in_front = false)
```
Queue a resource. Use optional argument "p_in_front" to put it in Queue a resource. Use optional argument "p_in_front" to put it in
front of the queue. front of the queue.
:: ```
func cancel_resource(path) func cancel_resource(path)
```
Remove a resource from the queue, discarding any loading done. Remove a resource from the queue, discarding any loading done.
:: ```
func is_ready(path) func is_ready(path)
```
Returns `true` if a resource is fully loaded and ready to be retrieved. Returns `true` if a resource is fully loaded and ready to be retrieved.
:: ```
func get_progress(path) func get_progress(path)
```
Get the progress of a resource. Returns -1 if there was an error (for example if the Get the progress of a resource. Returns -1 if there was an error (for example if the
resource is not in the queue), or a number between 0.0 and 1.0 with the resource is not in the queue), or a number between 0.0 and 1.0 with the
@ -259,9 +259,9 @@ progress of the load. Use mostly for cosmetic purposes (updating
progress bars, etc), use `is_ready` to find out if a resource is progress bars, etc), use `is_ready` to find out if a resource is
actually ready. actually ready.
:: ```
func get_resource(path) func get_resource(path)
```
Returns the fully loaded resource, or `null` on error. If the resource is Returns the fully loaded resource, or `null` on error. If the resource is
not fully loaded (`is_ready` returns `false`), it will block your thread not fully loaded (`is_ready` returns `false`), it will block your thread
@ -271,8 +271,7 @@ and finish the load. If the resource is not on the queue, it will call
Example: Example:
~~~~~~~~ ~~~~~~~~
:: ```
# Initialize. # Initialize.
queue = preload("res://resource_queue.gd").new() queue = preload("res://resource_queue.gd").new()
queue.start() queue.start()
@ -301,6 +300,7 @@ Example:
# When the user walks away from the trigger zone in your Metroidvania game: # When the user walks away from the trigger zone in your Metroidvania game:
queue.cancel_resource("res://zone_2.tscn") queue.cancel_resource("res://zone_2.tscn")
```
**Note**: this code, in its current form, is not tested in real world **Note**: this code, in its current form, is not tested in real world
scenarios. If you run into any issues, ask for help in one of scenarios. If you run into any issues, ask for help in one of

View File

@ -20,10 +20,12 @@ little-endian-encoded. All packets have a 4-byte header representing an
integer, specifying the type of data. integer, specifying the type of data.
The lowest value two bytes are used to determine the type, while the highest value The lowest value two bytes are used to determine the type, while the highest value
two bytes contain flags:: two bytes contain flags
```
base_type = val & 0xFFFF; base_type = val & 0xFFFF;
flags = val >> 16; flags = val >> 16;
```
+--------+--------------------------+ +--------+--------------------------+
| Type | Value | | Type | Value |

View File

@ -11,15 +11,15 @@ As an example if `t` is 0, then the state is A. If `t` is 1, then the state is B
Between two real (floating-point) numbers, a simple interpolation is usually described as: Between two real (floating-point) numbers, a simple interpolation is usually described as:
:: ```
interpolation = A * (1 - t) + B * t interpolation = A * (1 - t) + B * t
```
And often simplified to: And often simplified to:
:: ```
interpolation = A + (B - A) * t interpolation = A + (B - A) * t
```
The name of this type of interpolation, which transforms a value into another at *constant speed* is *"linear"*. So, when you hear about *Linear Interpolation*, you know they are referring to this simple formula. The name of this type of interpolation, which transforms a value into another at *constant speed* is *"linear"*. So, when you hear about *Linear Interpolation*, you know they are referring to this simple formula.

View File

@ -280,8 +280,9 @@ could get the same fruit three or more times in a row.
You can accomplish this using the *shuffle bag* pattern. It works by removing an You can accomplish this using the *shuffle bag* pattern. It works by removing an
element from the array after choosing it. After multiple selections, the array element from the array after choosing it. After multiple selections, the array
ends up empty. When that happens, you reinitialize it to its default value:: ends up empty. When that happens, you reinitialize it to its default value:
```
var _fruits = ["apple", "orange", "pear", "banana"] var _fruits = ["apple", "orange", "pear", "banana"]
# A copy of the fruits array so we can restore the original value into `fruits`. # A copy of the fruits array so we can restore the original value into `fruits`.
var _fruits_full = [] var _fruits_full = []
@ -307,6 +308,7 @@ ends up empty. When that happens, you reinitialize it to its default value::
var random_fruit = _fruits.pop_front() var random_fruit = _fruits.pop_front()
# Prints "apple", "orange", "pear", or "banana" every time the code runs. # Prints "apple", "orange", "pear", or "banana" every time the code runs.
return random_fruit return random_fruit
```
When running the above code, there is a chance to get the same fruit twice in a When running the above code, there is a chance to get the same fruit twice in a
row. Once we picked a fruit, it will no longer be a possible return value unless row. Once we picked a fruit, it will no longer be a possible return value unless

View File

@ -84,37 +84,37 @@ To create that object, it first has to be initialized as a server or client.
Initializing as a server, listening on the given port, with a given maximum number of peers: Initializing as a server, listening on the given port, with a given maximum number of peers:
:: ```
var peer = NetworkedMultiplayerENet.new() var peer = NetworkedMultiplayerENet.new()
peer.create_server(SERVER_PORT, MAX_PLAYERS) peer.create_server(SERVER_PORT, MAX_PLAYERS)
get_tree().network_peer = peer get_tree().network_peer = peer
```
Initializing as a client, connecting to a given IP and port: Initializing as a client, connecting to a given IP and port:
:: ```
var peer = NetworkedMultiplayerENet.new() var peer = NetworkedMultiplayerENet.new()
peer.create_client(SERVER_IP, SERVER_PORT) peer.create_client(SERVER_IP, SERVER_PORT)
get_tree().network_peer = peer get_tree().network_peer = peer
```
Get the previously set network peer: Get the previously set network peer:
:: ```
get_tree().get_network_peer() get_tree().get_network_peer()
```
Checking whether the tree is initialized as a server or client: Checking whether the tree is initialized as a server or client:
:: ```
get_tree().is_network_server() get_tree().is_network_server()
```
Terminating the networking feature: Terminating the networking feature:
:: ```
get_tree().network_peer = null get_tree().network_peer = null
```
(Although it may make sense to send a message first to let the other peers know you're going away instead of letting the connection close or timeout, depending on your game.) (Although it may make sense to send a message first to let the other peers know you're going away instead of letting the connection close or timeout, depending on your game.)
@ -189,8 +189,7 @@ Back to lobby
Let's get back to the lobby. Imagine that each player that connects to the server will tell everyone about it. Let's get back to the lobby. Imagine that each player that connects to the server will tell everyone about it.
:: ```
# Typical lobby implementation; imagine this being in /root/lobby. # Typical lobby implementation; imagine this being in /root/lobby.
extends Node extends Node
@ -232,12 +231,13 @@ Let's get back to the lobby. Imagine that each player that connects to the serve
player_info[id] = info player_info[id] = info
# Call function to update lobby UI here # Call function to update lobby UI here
```
You might have already noticed something different, which is the usage of the `remote` keyword on the `register_player` function: You might have already noticed something different, which is the usage of the `remote` keyword on the `register_player` function:
:: ```
remote func register_player(info): remote func register_player(info):
```
This keyword is one of many that allow a function to be called by a remote procedure call (RPC). There are six of them total: This keyword is one of many that allow a function to be called by a remote procedure call (RPC). There are six of them total:
@ -260,10 +260,10 @@ The `master` keyword means a call can be made from any network puppet to the net
If `sync` is included, the call can also be made locally. For example, to allow the network master to change the player's position on all peers: If `sync` is included, the call can also be made locally. For example, to allow the network master to change the player's position on all peers:
:: ```
puppetsync func update_position(new_position): puppetsync func update_position(new_position):
position = new_position position = new_position
```
Tip: Tip:
You can also use `SceneTree.get_rpc_sender_id()` to have more advanced rules on how an rpc can be called. You can also use `SceneTree.get_rpc_sender_id()` to have more advanced rules on how an rpc can be called.
@ -292,8 +292,7 @@ node represents each player ID.
The solution is to simply name the *root nodes of the instanced player scenes as their network ID*. This way, they will be the same in The solution is to simply name the *root nodes of the instanced player scenes as their network ID*. This way, they will be the same in
every peer and RPC will work great! Here is an example: every peer and RPC will work great! Here is an example:
:: ```
remote func pre_configure_game(): remote func pre_configure_game():
var selfPeerID = get_tree().get_network_unique_id() var selfPeerID = get_tree().get_network_unique_id()
@ -317,7 +316,7 @@ every peer and RPC will work great! Here is an example:
# Tell server (remember, server is always ID=1) that this peer is done pre-configuring. # Tell server (remember, server is always ID=1) that this peer is done pre-configuring.
# The server can call get_tree().get_rpc_sender_id() to find out who said they were done. # The server can call get_tree().get_rpc_sender_id() to find out who said they were done.
rpc_id(1, "done_preconfiguring") rpc_id(1, "done_preconfiguring")
```
Note: Note:
Depending on when you execute pre_configure_game(), you may need to change any calls to `add_child()` Depending on when you execute pre_configure_game(), you may need to change any calls to `add_child()`
@ -329,16 +328,15 @@ Synchronizing game start
Setting up players might take different amounts of time for every peer due to lag, different hardware, or other reasons. Setting up players might take different amounts of time for every peer due to lag, different hardware, or other reasons.
To make sure the game will actually start when everyone is ready, pausing the game until all players are ready can be useful: To make sure the game will actually start when everyone is ready, pausing the game until all players are ready can be useful:
:: ```
remote func pre_configure_game(): remote func pre_configure_game():
get_tree().set_pause(true) # Pre-pause get_tree().set_pause(true) # Pre-pause
# The rest is the same as in the code in the previous section (look above) # The rest is the same as in the code in the previous section (look above)
```
When the server gets the OK from all the peers, it can tell them to start, as for example: When the server gets the OK from all the peers, it can tell them to start, as for example:
:: ```
var players_done = [] var players_done = []
remote func done_preconfiguring(): remote func done_preconfiguring():
var who = get_tree().get_rpc_sender_id() var who = get_tree().get_rpc_sender_id()
@ -358,7 +356,7 @@ When the server gets the OK from all the peers, it can tell them to start, as fo
get_tree().set_pause(false) get_tree().set_pause(false)
# Game starts now! # Game starts now!
```
Synchronizing the game Synchronizing the game
---------------------- ----------------------
@ -380,8 +378,7 @@ Checking that a specific node instance on a peer is the network master for this
If you have paid attention to the previous example, it's possible you noticed that each peer was set to have network master authority for their own player (Node) instead of the server: If you have paid attention to the previous example, it's possible you noticed that each peer was set to have network master authority for their own player (Node) instead of the server:
:: ```
[...] [...]
# Load my player # Load my player
var my_player = preload("res://player.tscn").instance() var my_player = preload("res://player.tscn").instance()
@ -396,7 +393,7 @@ If you have paid attention to the previous example, it's possible you noticed th
player.set_network_master(p) # Each other connected peer has authority over their own player. player.set_network_master(p) # Each other connected peer has authority over their own player.
get_node("/root/world/players").add_child(player) get_node("/root/world/players").add_child(player)
[...] [...]
```
Each time this piece of code is executed on each peer, the peer makes itself master on the node it controls, and all other nodes remain as puppets with the server being their network master. Each time this piece of code is executed on each peer, the peer makes itself master on the node it controls, and all other nodes remain as puppets with the server being their network master.
@ -416,16 +413,15 @@ Similarly to the `remote` keyword, functions can also be tagged with them:
Example bomb code: Example bomb code:
:: ```
for p in bodies_in_area: for p in bodies_in_area:
if p.has_method("exploded"): if p.has_method("exploded"):
p.rpc("exploded", bomb_owner) p.rpc("exploded", bomb_owner)
```
Example player code: Example player code:
:: ```
puppet func stun(): puppet func stun():
stunned = true stunned = true
@ -438,6 +434,7 @@ Example player code:
# Stun this player instance for myself as well; could instead have used # Stun this player instance for myself as well; could instead have used
# the remotesync keyword above (in place of puppet) to achieve this. # the remotesync keyword above (in place of puppet) to achieve this.
stun() stun()
```
In the above example, a bomb explodes somewhere (likely managed by whoever is the master of this bomb-node, e.g. the host). In the above example, a bomb explodes somewhere (likely managed by whoever is the master of this bomb-node, e.g. the host).
The bomb knows the bodies (player nodes) in the area, so it checks that they contain an `exploded` method before calling it. The bomb knows the bodies (player nodes) in the area, so it checks that they contain an `exploded` method before calling it.
@ -472,9 +469,9 @@ any player in the bomb area get stunned on the screens of all the peers.
Note that you could also send the `stun()` message only to a specific player by using `rpc_id(<id>, "exploded", bomb_owner)`. Note that you could also send the `stun()` message only to a specific player by using `rpc_id(<id>, "exploded", bomb_owner)`.
This may not make much sense for an area-of-effect case like the bomb, but might in other cases, like single target damage. This may not make much sense for an area-of-effect case like the bomb, but might in other cases, like single target damage.
:: ```
rpc_id(TARGET_PEER_ID, "stun") # Only stun the target peer rpc_id(TARGET_PEER_ID, "stun") # Only stun the target peer
```
Exporting for dedicated servers Exporting for dedicated servers
------------------------------- -------------------------------

View File

@ -61,9 +61,9 @@ and it will work.
If you are using Linux, you can use the supplied certs file, generally If you are using Linux, you can use the supplied certs file, generally
located in: located in:
.. code-block:: none ```
/etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
```
This file allows HTTPS connections to virtually any website (i.e., This file allows HTTPS connections to virtually any website (i.e.,
Google, Microsoft, etc.). Google, Microsoft, etc.).

View File

@ -52,8 +52,7 @@ Minimal connection example
This example will show you how to create a WebRTC connection between two peers in the same application. This example will show you how to create a WebRTC connection between two peers in the same application.
This is not very useful in real life, but will give you a good overview of how a WebRTC connection is set up. This is not very useful in real life, but will give you a good overview of how a WebRTC connection is set up.
:: ```
extends Node extends Node
# Create the two peers # Create the two peers
@ -96,21 +95,21 @@ This is not very useful in real life, but will give you a good overview of how a
print("P1 received: ", ch1.get_packet().get_string_from_utf8()) print("P1 received: ", ch1.get_packet().get_string_from_utf8())
if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0: if ch2.get_ready_state() == ch2.STATE_OPEN and ch2.get_available_packet_count() > 0:
print("P2 received: ", ch2.get_packet().get_string_from_utf8()) print("P2 received: ", ch2.get_packet().get_string_from_utf8())
```
This will print: This will print:
:: ```
P1 received: Hi from P1 P1 received: Hi from P1
P2 received: Hi from P2 P2 received: Hi from P2
```
Local signaling example Local signaling example
^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
This example expands on the previous one, separating the peers in two different scenes, and using a `singleton ( doc_singletons_autoload )` as a signaling server. This example expands on the previous one, separating the peers in two different scenes, and using a `singleton ( doc_singletons_autoload )` as a signaling server.
:: ```
# An example P2P chat client (chat.gd) # An example P2P chat client (chat.gd)
extends Node extends Node
@ -146,14 +145,14 @@ This example expands on the previous one, separating the peers in two different
func send_message(message): func send_message(message):
channel.put_packet(message.to_utf8()) channel.put_packet(message.to_utf8())
```
And now for the local signaling server: And now for the local signaling server:
Note: Note:
This local signaling server is supposed to be used as a `singleton ( doc_singletons_autoload )` to connect two peers in the same scene. This local signaling server is supposed to be used as a `singleton ( doc_singletons_autoload )` to connect two peers in the same scene.
:: ```
# A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling) # A local signaling server. Add this to autoloads with name "Signaling" (/root/Signaling)
extends Node extends Node
@ -183,11 +182,11 @@ Note:
var other = _find_other(path) var other = _find_other(path)
assert(other != "") assert(other != "")
get_node(other).peer.add_ice_candidate(mid, index, sdp) get_node(other).peer.add_ice_candidate(mid, index, sdp)
```
Then you can use it like this: Then you can use it like this:
:: ```
# Main scene (main.gd) # Main scene (main.gd)
extends Node extends Node
@ -204,13 +203,14 @@ Then you can use it like this:
# Wait a second and send message from P2 # Wait a second and send message from P2
yield(get_tree().create_timer(1), "timeout") yield(get_tree().create_timer(1), "timeout")
p2.send_message("Hi from %s" % p2.get_path()) p2.send_message("Hi from %s" % p2.get_path())
```
This will print something similar to this: This will print something similar to this:
:: ```
/root/main/@@3 received: Hi from /root/main/@@2 /root/main/@@3 received: Hi from /root/main/@@2
/root/main/@@2 received: Hi from /root/main/@@3 /root/main/@@2 received: Hi from /root/main/@@3
```
Remote signaling with WebSocket Remote signaling with WebSocket
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -33,7 +33,7 @@ Minimal client example
This example will show you how to create a WebSocket connection to a remote server, and how to send and receive data. This example will show you how to create a WebSocket connection to a remote server, and how to send and receive data.
:: ```
extends Node extends Node
@ -83,20 +83,22 @@ This example will show you how to create a WebSocket connection to a remote serv
# Call this in _process or _physics_process. Data transfer, and signals # Call this in _process or _physics_process. Data transfer, and signals
# emission will only happen when calling this function. # emission will only happen when calling this function.
_client.poll() _client.poll()
```
This will print: This will print:
:: ```
Connected with protocol: Connected with protocol:
Got data from server: Test packet Got data from server: Test packet
```
Minimal server example Minimal server example
^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^
This example will show you how to create a WebSocket server that listens for remote connections, and how to send and receive data. This example will show you how to create a WebSocket server that listens for remote connections, and how to send and receive data.
:: ```
extends Node extends Node
@ -149,13 +151,15 @@ This example will show you how to create a WebSocket server that listens for rem
# Call this in _process or _physics_process. # Call this in _process or _physics_process.
# Data transfer, and signals emission will only happen when calling this function. # Data transfer, and signals emission will only happen when calling this function.
_server.poll() _server.poll()
```
This will print (when a client connects) something similar to this: This will print (when a client connects) something similar to this:
:: ```
Client 1348090059 connected with protocol: selected-protocol Client 1348090059 connected with protocol: selected-protocol
Got data from client 1348090059: Test packet ... echoing Got data from client 1348090059: Test packet ... echoing
```
Advanced chat demo Advanced chat demo
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^

View File

@ -113,11 +113,13 @@ objects are rendered from back to front, consider 3 objects `A`, `B` and
![](img/overlap1.png) ![](img/overlap1.png)
In painter's order they are ordered:: In painter's order they are ordered:
```
A - wood A - wood
B - grass B - grass
C - wood C - wood
```
Because of the texture changes, they can't be batched and will be rendered in 3 Because of the texture changes, they can't be batched and will be rendered in 3
draw calls. draw calls.
@ -140,11 +142,12 @@ looking ahead to decide whether items can be reordered. The number of items to
lookahead for reordering can be set in project settings (see below), in order to lookahead for reordering can be set in project settings (see below), in order to
balance the costs and benefits in your project. balance the costs and benefits in your project.
:: ```
A - wood A - wood
C - wood C - wood
B - grass B - grass
```
Since the texture only changes once, we can render the above in only 2 draw Since the texture only changes once, we can render the above in only 2 draw
calls. calls.
@ -161,7 +164,7 @@ lights, they would be drawn as follows, each line being a draw call:
![](img/lights_overlap.png) ![](img/lights_overlap.png)
:: ```
A A
A - light 1 A - light 1
@ -171,6 +174,7 @@ lights, they would be drawn as follows, each line being a draw call:
B - light 1 B - light 1
B - light 2 B - light 2
B - light 3 B - light 3
```
That is a lot of draw calls: 8 for only 2 sprites. Now, consider we are drawing That is a lot of draw calls: 8 for only 2 sprites. Now, consider we are drawing
1,000 sprites. The number of draw calls quickly becomes astronomical and 1,000 sprites. The number of draw calls quickly becomes astronomical and
@ -185,12 +189,13 @@ so the drawing process is as follows:
![](img/lights_separate.png) ![](img/lights_separate.png)
:: ```
AB AB
AB - light 1 AB - light 1
AB - light 2 AB - light 2
AB - light 3 AB - light 3
```
That is only 4 draw calls. Not bad, as that is a 2× reduction. However, consider That is only 4 draw calls. Not bad, as that is a 2× reduction. However, consider
@ -413,8 +418,7 @@ as intended, and help you fix these situations to get the best possible performa
Reading a diagnostic Reading a diagnostic
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^
.. code-block:: cpp ```
canvas_begin FRAME 2604 canvas_begin FRAME 2604
items items
joined_item 1 refs joined_item 1 refs
@ -434,6 +438,7 @@ Reading a diagnostic
batch D 0-0 batch D 0-0
batch R 0-2560 [0 - 144] {158 193 0 104 } MULTI batch R 0-2560 [0 - 144] {158 193 0 104 } MULTI
canvas_end canvas_end
```
This is a typical diagnostic. This is a typical diagnostic.
@ -455,8 +460,9 @@ Default batches
^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
The second number following default batches is the number of commands in the The second number following default batches is the number of commands in the
batch, and it is followed by a brief summary of the contents:: batch, and it is followed by a brief summary of the contents:
```
l - line l - line
PL - polyline PL - polyline
r - rect r - rect
@ -469,6 +475,7 @@ batch, and it is followed by a brief summary of the contents::
c - circle c - circle
t - transform t - transform
CI - clip_ignore CI - clip_ignore
```
You may see "dummy" default batches containing no commands; you can ignore those. You may see "dummy" default batches containing no commands; you can ignore those.
@ -544,10 +551,12 @@ value to the power of 4.
For example, on a screen size of 1920×1080, there are 2,073,600 pixels. For example, on a screen size of 1920×1080, there are 2,073,600 pixels.
At a threshold of 1,000 pixels, the proportion would be:: At a threshold of 1,000 pixels, the proportion would be:
```
1000 / 2073600 = 0.00048225 1000 / 2073600 = 0.00048225
0.00048225 ^ (1/4) = 0.14819 0.00048225 ^ (1/4) = 0.14819
```
So a `scissor_area_threshold So a `scissor_area_threshold
( ProjectSettings_property_rendering/batching/lights/scissor_area_threshold )` ( ProjectSettings_property_rendering/batching/lights/scissor_area_threshold )`
@ -555,10 +564,12 @@ of `0.15` would be a reasonable value to try.
Going the other way, for instance with a `scissor_area_threshold Going the other way, for instance with a `scissor_area_threshold
( ProjectSettings_property_rendering/batching/lights/scissor_area_threshold )` ( ProjectSettings_property_rendering/batching/lights/scissor_area_threshold )`
of `0.5`:: of `0.5`:
```
0.5 ^ 4 = 0.0625 0.5 ^ 4 = 0.0625
0.0625 * 2073600 = 129600 pixels 0.0625 * 2073600 = 129600 pixels
```
If the number of pixels saved is greater than this threshold, the scissor is If the number of pixels saved is greater than this threshold, the scissor is
activated. activated.

View File

@ -105,8 +105,7 @@ using a profiler, is to manually time the function or area under test.
The specifics vary depending on the language, but in GDScript, you would do The specifics vary depending on the language, but in GDScript, you would do
the following: the following:
:: ```
var time_start = OS.get_ticks_usec() var time_start = OS.get_ticks_usec()
# Your function you want to time # Your function you want to time
@ -114,6 +113,7 @@ the following:
var time_end = OS.get_ticks_usec() var time_end = OS.get_ticks_usec()
print("update_enemies() took %d microseconds" % time_end - time_start) print("update_enemies() took %d microseconds" % time_end - time_start)
```
When manually timing functions, it is usually a good idea to run the function When manually timing functions, it is usually a good idea to run the function
many times (1,000 or more times), instead of just once (unless it is a very slow many times (1,000 or more times), instead of just once (unless it is a very slow

View File

@ -244,17 +244,19 @@ The proverb *"a chain is only as strong as its weakest link"* applies directly t
performance optimization. If your project is spending 90% of the time in performance optimization. If your project is spending 90% of the time in
function `A`, then optimizing `A` can have a massive effect on performance. function `A`, then optimizing `A` can have a massive effect on performance.
.. code-block:: none ```
A: 9 ms A: 9 ms
Everything else: 1 ms Everything else: 1 ms
Total frame time: 10 ms Total frame time: 10 ms
```
.. code-block:: none ```
A: 1 ms A: 1 ms
Everything else: 1ms Everything else: 1ms
Total frame time: 2 ms Total frame time: 2 ms
```
In this example, improving this bottleneck `A` by a factor of 9× decreases In this example, improving this bottleneck `A` by a factor of 9× decreases
overall frame time by 5× while increasing frames per second by 5×. overall frame time by 5× while increasing frames per second by 5×.
@ -262,17 +264,19 @@ overall frame time by 5× while increasing frames per second by 5×.
However, if something else is running slowly and also bottlenecking your However, if something else is running slowly and also bottlenecking your
project, then the same improvement can lead to less dramatic gains: project, then the same improvement can lead to less dramatic gains:
.. code-block:: none ```
A: 9 ms A: 9 ms
Everything else: 50 ms Everything else: 50 ms
Total frame time: 59 ms Total frame time: 59 ms
```
.. code-block:: none ```
A: 1 ms A: 1 ms
Everything else: 50 ms Everything else: 50 ms
Total frame time: 51 ms Total frame time: 51 ms
```
In this example, even though we have hugely optimized function `A`, In this example, even though we have hugely optimized function `A`,
the actual gain in terms of frame rate is quite small. the actual gain in terms of frame rate is quite small.
@ -281,17 +285,19 @@ In games, things become even more complicated because the CPU and GPU run
independently of one another. Your total frame time is determined by the slower independently of one another. Your total frame time is determined by the slower
of the two. of the two.
.. code-block:: none ```
CPU: 9 ms CPU: 9 ms
GPU: 50 ms GPU: 50 ms
Total frame time: 50 ms Total frame time: 50 ms
```
.. code-block:: none ```
CPU: 1 ms CPU: 1 ms
GPU: 50 ms GPU: 50 ms
Total frame time: 50 ms Total frame time: 50 ms
```
In this example, we optimized the CPU hugely again, but the frame time didn't In this example, we optimized the CPU hugely again, but the frame time didn't
improve because we are GPU-bottlenecked. improve because we are GPU-bottlenecked.

View File

@ -23,21 +23,21 @@ Scene tree
Interacting with the active scene tree is **NOT** thread-safe. Make sure to use mutexes when sending data between threads. If you want to call functions from a thread, the *call_deferred* function may be used: Interacting with the active scene tree is **NOT** thread-safe. Make sure to use mutexes when sending data between threads. If you want to call functions from a thread, the *call_deferred* function may be used:
:: ```
# Unsafe: # Unsafe:
node.add_child(child_node) node.add_child(child_node)
# Safe: # Safe:
node.call_deferred("add_child", child_node) node.call_deferred("add_child", child_node)
```
However, creating scene chunks (nodes in tree arrangement) outside the active tree is fine. This way, parts of a scene can be built or instantiated in a thread, then added in the main thread: However, creating scene chunks (nodes in tree arrangement) outside the active tree is fine. This way, parts of a scene can be built or instantiated in a thread, then added in the main thread:
:: ```
var enemy_scene = load("res://enemy_scene.scn") var enemy_scene = load("res://enemy_scene.scn")
var enemy = enemy_scene.instance() var enemy = enemy_scene.instance()
enemy.add_child(weapon) # Set a weapon. enemy.add_child(weapon) # Set a weapon.
world.call_deferred("add_child", enemy) world.call_deferred("add_child", enemy)
```
Still, this is only really useful if you have **one** thread loading data. Still, this is only really useful if you have **one** thread loading data.
Attempting to load or create scene chunks from multiple threads may work, but you risk Attempting to load or create scene chunks from multiple threads may work, but you risk

View File

@ -53,19 +53,19 @@ world orientation.
In order to control the speed of the animation, we will start by defining our own time variable using `TIME`. In order to control the speed of the animation, we will start by defining our own time variable using `TIME`.
.. code-block:: glsl ```
//time_scale is a uniform float //time_scale is a uniform float
float time = TIME * time_scale; float time = TIME * time_scale;
```
The first motion we will implement is the side to side motion. It can be made by offsetting `VERTEX.x` by The first motion we will implement is the side to side motion. It can be made by offsetting `VERTEX.x` by
`cos` of `TIME`. Each time the mesh is rendered, all the vertices will move to the side by the amount `cos` of `TIME`. Each time the mesh is rendered, all the vertices will move to the side by the amount
of `cos(time)`. of `cos(time)`.
.. code-block:: glsl ```
//side_to_side is a uniform float //side_to_side is a uniform float
VERTEX.x += cos(time) * side_to_side; VERTEX.x += cos(time) * side_to_side;
```
The resulting animation should look something like this: The resulting animation should look something like this:
@ -76,18 +76,18 @@ rotation matrix for it to rotate around the center of the fish.
We construct a rotation matrix like so: We construct a rotation matrix like so:
.. code-block:: glsl ```
//angle is scaled by 0.1 so that the fish only pivots and doesn't rotate all the way around //angle is scaled by 0.1 so that the fish only pivots and doesn't rotate all the way around
//pivot is a uniform float //pivot is a uniform float
float pivot_angle = cos(time) * 0.1 * pivot; float pivot_angle = cos(time) * 0.1 * pivot;
mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle))); mat2 rotation_matrix = mat2(vec2(cos(pivot_angle), -sin(pivot_angle)), vec2(sin(pivot_angle), cos(pivot_angle)));
```
And then we apply it in the `x` and `z` axes by multiplying it by `VERTEX.xz`. And then we apply it in the `x` and `z` axes by multiplying it by `VERTEX.xz`.
.. code-block:: glsl ```
VERTEX.xz = rotation_matrix * VERTEX.xz; VERTEX.xz = rotation_matrix * VERTEX.xz;
```
With only the pivot applied you should see something like this: With only the pivot applied you should see something like this:
@ -96,18 +96,18 @@ With only the pivot applied you should see something like this:
The next two motions need to pan down the spine of the fish. For that, we need a new variable, `body`. The next two motions need to pan down the spine of the fish. For that, we need a new variable, `body`.
`body` is a float that is `0` at the tail of the fish and `1` at its head. `body` is a float that is `0` at the tail of the fish and `1` at its head.
.. code-block:: glsl ```
float body = (VERTEX.z + 1.0) / 2.0; //for a fish centered at (0, 0) with a length of 2 float body = (VERTEX.z + 1.0) / 2.0; //for a fish centered at (0, 0) with a length of 2
```
The next motion is a cosine wave that moves down the length of the fish. To make The next motion is a cosine wave that moves down the length of the fish. To make
it move along the spine of the fish, we offset the input to `cos` by the position it move along the spine of the fish, we offset the input to `cos` by the position
along the spine, which is the variable we defined above, `body`. along the spine, which is the variable we defined above, `body`.
.. code-block:: glsl ```
//wave is a uniform float //wave is a uniform float
VERTEX.x += cos(time + body) * wave; VERTEX.x += cos(time + body) * wave;
```
This looks very similar to the side to side motion we defined above, but in this one, by This looks very similar to the side to side motion we defined above, but in this one, by
using `body` to offset `cos` each vertex along the spine has a different position in using `body` to offset `cos` each vertex along the spine has a different position in
@ -118,18 +118,18 @@ the wave making it look like a wave is moving along the fish.
The last motion is the twist, which is a panning roll along the spine. Similarly to the pivot, The last motion is the twist, which is a panning roll along the spine. Similarly to the pivot,
we first construct a rotation matrix. we first construct a rotation matrix.
.. code-block:: glsl ```
//twist is a uniform float //twist is a uniform float
float twist_angle = cos(time + body) * 0.3 * twist; float twist_angle = cos(time + body) * 0.3 * twist;
mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle))); mat2 twist_matrix = mat2(vec2(cos(twist_angle), -sin(twist_angle)), vec2(sin(twist_angle), cos(twist_angle)));
```
We apply the rotation in the `xy` axes so that the fish appears to roll around its spine. For We apply the rotation in the `xy` axes so that the fish appears to roll around its spine. For
this to work, the fish's spine needs to be centered on the `z` axis. this to work, the fish's spine needs to be centered on the `z` axis.
.. code-block:: glsl ```
VERTEX.xy = twist_matrix * VERTEX.xy; VERTEX.xy = twist_matrix * VERTEX.xy;
```
Here is the fish with twist applied: Here is the fish with twist applied:
@ -145,10 +145,10 @@ panning motions to the back half of the fish. To do this, we create a new variab
`mask` is a float that goes from `0` at the front of the fish to `1` at the end using `mask` is a float that goes from `0` at the front of the fish to `1` at the end using
`smoothstep` to control the point at which the transition from `0` to `1` happens. `smoothstep` to control the point at which the transition from `0` to `1` happens.
.. code-block:: glsl ```
//mask_black and mask_white are uniforms //mask_black and mask_white are uniforms
float mask = smoothstep(mask_black, mask_white, 1.0 - body); float mask = smoothstep(mask_black, mask_white, 1.0 - body);
```
Below is an image of the fish with `mask` used as `COLOR`: Below is an image of the fish with `mask` used as `COLOR`:
@ -156,10 +156,10 @@ Below is an image of the fish with `mask` used as `COLOR`:
For the wave, we multiply the motion by `mask` which will limit it to the back half. For the wave, we multiply the motion by `mask` which will limit it to the back half.
.. code-block:: glsl ```
//wave motion with mask //wave motion with mask
VERTEX.x += cos(time + body) * mask * wave; VERTEX.x += cos(time + body) * mask * wave;
```
In order to apply the mask to the twist, we use `mix`. `mix` allows us to mix the In order to apply the mask to the twist, we use `mix`. `mix` allows us to mix the
vertex position between a fully rotated vertex and one that is not rotated. We need to vertex position between a fully rotated vertex and one that is not rotated. We need to
@ -167,10 +167,10 @@ use `mix` instead of multiplying `mask` by the rotated `VERTEX` because we are n
adding the motion to the `VERTEX` we are replacing the `VERTEX` with the rotated adding the motion to the `VERTEX` we are replacing the `VERTEX` with the rotated
version. If we multiplied that by `mask`, we would shrink the fish. version. If we multiplied that by `mask`, we would shrink the fish.
.. code-block:: glsl ```
//twist motion with mask //twist motion with mask
VERTEX.xy = mix(VERTEX.xy, twist_matrix * VERTEX.xy, mask); VERTEX.xy = mix(VERTEX.xy, twist_matrix * VERTEX.xy, mask);
```
Putting the four motions together gives us the final animation. Putting the four motions together gives us the final animation.
@ -218,12 +218,12 @@ and is described in the `MultiMeshInstance tutorial ( doc_using_multi_mesh_insta
The second is to loop over all the instances and set their transforms in code. Below, we use GDScript The second is to loop over all the instances and set their transforms in code. Below, we use GDScript
to loop over all the instances and set their transform to a random position. to loop over all the instances and set their transform to a random position.
:: ```
for i in range($School.multimesh.instance_count): for i in range($School.multimesh.instance_count):
var position = Transform() var position = Transform()
position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25)) position = position.translated(Vector3(randf() * 100 - 50, randf() * 50 - 25, randf() * 50 - 25))
$School.multimesh.set_instance_transform(i, position) $School.multimesh.set_instance_transform(i, position)
```
Running this script will place the fish in random positions in a box around the position of the Running this script will place the fish in random positions in a box around the position of the
MultiMeshInstance. MultiMeshInstance.
@ -244,26 +244,25 @@ swim cycle, we only need to offset `time`.
We do that by adding the per-instance custom value `INSTANCE_CUSTOM` to `time`. We do that by adding the per-instance custom value `INSTANCE_CUSTOM` to `time`.
.. code-block:: glsl ```
float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x); float time = (TIME * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
Next, we need to pass a value into `INSTANCE_CUSTOM`. We do that by adding one line into Next, we need to pass a value into `INSTANCE_CUSTOM`. We do that by adding one line into
the `for` loop from above. In the `for` loop we assign each instance a set of four the `for` loop from above. In the `for` loop we assign each instance a set of four
random floats to use. random floats to use.
:: ```
$School.multimesh.set_instance_custom_data(i, Color(randf(), randf(), randf(), randf())) $School.multimesh.set_instance_custom_data(i, Color(randf(), randf(), randf(), randf()))
```
Now the fish all have unique positions in the swim cycle. You can give them a little more Now the fish all have unique positions in the swim cycle. You can give them a little more
individuality by using `INSTANCE_CUSTOM` to make them swim faster or slower by multiplying individuality by using `INSTANCE_CUSTOM` to make them swim faster or slower by multiplying
by `TIME`. by `TIME`.
.. code-block:: glsl ```
//set speed from 50% - 150% of regular speed //set speed from 50% - 150% of regular speed
float time = (TIME * (0.5 + INSTANCE_CUSTOM.y) * time_scale) + (6.28318 * INSTANCE_CUSTOM.x); float time = (TIME * (0.5 + INSTANCE_CUSTOM.y) * time_scale) + (6.28318 * INSTANCE_CUSTOM.x);
```
You can even experiment with changing the per-instance color the same way you changed the per-instance You can even experiment with changing the per-instance color the same way you changed the per-instance
custom value. custom value.

View File

@ -21,14 +21,13 @@ First create a Particles node. Then, under "Draw Passes" set the Particle's "Dra
Set the `shader_type` to `particles`. Set the `shader_type` to `particles`.
.. code-block:: glsl ```
shader_type particles shader_type particles
```
Then add the following two functions: Then add the following two functions:
.. code-block:: glsl ```
float rand_from_seed(in uint seed) { float rand_from_seed(in uint seed) {
int k; int k;
int s = int(seed); int s = int(seed);
@ -48,6 +47,7 @@ Then add the following two functions:
x = (x >> uint(16)) ^ x; x = (x >> uint(16)) ^ x;
return x; return x;
} }
```
These functions come from the default `ParticlesMaterial`. These functions come from the default `ParticlesMaterial`.
They are used to generate a random number from each particle's `RANDOM_SEED`. They are used to generate a random number from each particle's `RANDOM_SEED`.
@ -66,8 +66,7 @@ built-in variable `RESTART` which becomes `true` for one frame when the particle
From a high level, this looks like: From a high level, this looks like:
.. code-block:: glsl ```
void vertex() { void vertex() {
if (RESTART) { if (RESTART) {
//Initialization code goes here //Initialization code goes here
@ -75,34 +74,35 @@ From a high level, this looks like:
//per-frame code goes here //per-frame code goes here
} }
} }
```
Next, we need to generate 4 random numbers: 3 to create a random position and one for the random Next, we need to generate 4 random numbers: 3 to create a random position and one for the random
offset of the swim cycle. offset of the swim cycle.
First, generate 4 seeds inside the `RESTART` block using the `hash` function provided above: First, generate 4 seeds inside the `RESTART` block using the `hash` function provided above:
.. code-block:: glsl ```
uint alt_seed1 = hash(NUMBER + uint(1) + RANDOM_SEED); uint alt_seed1 = hash(NUMBER + uint(1) + RANDOM_SEED);
uint alt_seed2 = hash(NUMBER + uint(27) + RANDOM_SEED); uint alt_seed2 = hash(NUMBER + uint(27) + RANDOM_SEED);
uint alt_seed3 = hash(NUMBER + uint(43) + RANDOM_SEED); uint alt_seed3 = hash(NUMBER + uint(43) + RANDOM_SEED);
uint alt_seed4 = hash(NUMBER + uint(111) + RANDOM_SEED); uint alt_seed4 = hash(NUMBER + uint(111) + RANDOM_SEED);
```
Then, use those seeds to generate random numbers using `rand_from_seed`: Then, use those seeds to generate random numbers using `rand_from_seed`:
.. code-block:: glsl ```
CUSTOM.x = rand_from_seed(alt_seed1); CUSTOM.x = rand_from_seed(alt_seed1);
vec3 position = vec3(rand_from_seed(alt_seed2) * 2.0 - 1.0, vec3 position = vec3(rand_from_seed(alt_seed2) * 2.0 - 1.0,
rand_from_seed(alt_seed3) * 2.0 - 1.0, rand_from_seed(alt_seed3) * 2.0 - 1.0,
rand_from_seed(alt_seed4) * 2.0 - 1.0); rand_from_seed(alt_seed4) * 2.0 - 1.0);
```
Finally, assign `position` to `TRANSFORM[3].xyz`, which is the part of the transform that holds Finally, assign `position` to `TRANSFORM[3].xyz`, which is the part of the transform that holds
the position information. the position information.
.. code-block:: glsl ```
TRANSFORM[3].xyz = position * 20.0; TRANSFORM[3].xyz = position * 20.0;
```
Remember, all this code so far goes inside the `RESTART` block. Remember, all this code so far goes inside the `RESTART` block.
@ -113,27 +113,27 @@ or by writing to `VELOCITY`.
Let's transform the fish by setting their `VELOCITY`. Let's transform the fish by setting their `VELOCITY`.
.. code-block:: glsl ```
VELOCITY.z = 10.0; VELOCITY.z = 10.0;
```
This is the most basic way to set `VELOCITY` every particle (or fish) will have the same velocity. This is the most basic way to set `VELOCITY` every particle (or fish) will have the same velocity.
Just by setting `VELOCITY` you can make the fish swim however you want. For example, try the code Just by setting `VELOCITY` you can make the fish swim however you want. For example, try the code
below. below.
.. code-block:: glsl ```
VELOCITY.z = cos(TIME + CUSTOM.x * 6.28) * 4.0 + 6.0; VELOCITY.z = cos(TIME + CUSTOM.x * 6.28) * 4.0 + 6.0;
```
This will give each fish a unique speed between `2` and `10`. This will give each fish a unique speed between `2` and `10`.
If you used `CUSTOM.y` in the last tutorial, you can also set the speed of the swim animation based If you used `CUSTOM.y` in the last tutorial, you can also set the speed of the swim animation based
on the `VELOCITY`. Just use `CUSTOM.y`. on the `VELOCITY`. Just use `CUSTOM.y`.
.. code-block:: glsl ```
CUSTOM.y = VELOCITY.z * 0.1; CUSTOM.y = VELOCITY.z * 0.1;
```
This code gives you the following behavior: This code gives you the following behavior:

View File

@ -60,8 +60,7 @@ Example manual Camera script
Here is an example of a simple fixed Camera which follows an interpolated target: Here is an example of a simple fixed Camera which follows an interpolated target:
.. code-block:: python ```
extends Camera extends Camera
# Node that the camera will follow # Node that the camera will follow
@ -88,6 +87,7 @@ Here is an example of a simple fixed Camera which follows an interpolated target
# Fixed camera position, but it will follow the target # Fixed camera position, but it will follow the target
look_at(_target_pos, Vector3(0, 1, 0)) look_at(_target_pos, Vector3(0, 1, 0))
```
Mouse look Mouse look
^^^^^^^^^^ ^^^^^^^^^^

View File

@ -69,10 +69,10 @@ If our physics ticks are happening 10 times per second (for this example), what
First of all, we have to calculate how far through the physics tick we want the object to be. If the last physics tick took place at 0.1 seconds, we are 0.02 seconds *(0.12 - 0.1)* through a tick that we know will take 0.1 seconds (10 ticks per second). The fraction through the tick is thus: First of all, we have to calculate how far through the physics tick we want the object to be. If the last physics tick took place at 0.1 seconds, we are 0.02 seconds *(0.12 - 0.1)* through a tick that we know will take 0.1 seconds (10 ticks per second). The fraction through the tick is thus:
.. code-block:: python ```
fraction = 0.02 / 0.10 fraction = 0.02 / 0.10
fraction = 0.2 fraction = 0.2
```
This is called the **physics interpolation fraction**, and is handily calculated for you by Godot. It can be retrieved on any frame by calling `Engine.get_physics_interpolation_fraction( Engine_method_get_physics_interpolation_fraction )`. This is called the **physics interpolation fraction**, and is handily calculated for you by Godot. It can be retrieved on any frame by calling `Engine.get_physics_interpolation_fraction( Engine_method_get_physics_interpolation_fraction )`.
@ -81,17 +81,17 @@ Calculating the interpolated position
Once we have the interpolation fraction, we can insert it into a standard linear interpolation equation. The X coordinate would thus be: Once we have the interpolation fraction, we can insert it into a standard linear interpolation equation. The X coordinate would thus be:
.. code-block:: python ```
x_interpolated = x_prev + ((x_curr - x_prev) * 0.2) x_interpolated = x_prev + ((x_curr - x_prev) * 0.2)
```
So substituting our `x_prev` as 10, and `x_curr` as 30: So substituting our `x_prev` as 10, and `x_curr` as 30:
.. code-block:: python ```
x_interpolated = 10 + ((30 - 10) * 0.2) x_interpolated = 10 + ((30 - 10) * 0.2)
x_interpolated = 10 + 4 x_interpolated = 10 + 4
x_interpolated = 14 x_interpolated = 14
```
Let's break that down: Let's break that down:

View File

@ -162,8 +162,9 @@ can use binary, hexadecimal, or decimal notation for layer masks, depending
on your preference. on your preference.
The code equivalent of the above example where layers 1, 3 and 4 were enabled The code equivalent of the above example where layers 1, 3 and 4 were enabled
would be as follows:: would be as follows:
```
# Example: Setting mask value for enabling layers 1, 3 and 4 # Example: Setting mask value for enabling layers 1, 3 and 4
# Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0. # Binary - set the bit corresponding to the layers you want to enable (1, 3, and 4) to 1, set all other bits to 0.
@ -178,6 +179,7 @@ would be as follows::
# Decimal - Add the results of 2 to the power of (layer to be enabled - 1). # Decimal - Add the results of 2 to the power of (layer to be enabled - 1).
# (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13 # (2^(1-1)) + (2^(3-1)) + (2^(4-1)) = 1 + 4 + 8 = 13
pow(2, 1) + pow(2, 3) + pow(2, 4) pow(2, 1) + pow(2, 3) + pow(2, 4)
```
Area2D Area2D

View File

@ -106,8 +106,7 @@ gdscript GDScript
The `result` dictionary when a collision occurs contains the following The `result` dictionary when a collision occurs contains the following
data: data:
:: ```
{ {
position: Vector2 # point in world space for collision position: Vector2 # point in world space for collision
normal: Vector2 # normal in world space for collision normal: Vector2 # normal in world space for collision
@ -117,6 +116,7 @@ data:
shape: int # shape index of collider shape: int # shape index of collider
metadata: Variant() # metadata of collider metadata: Variant() # metadata of collider
} }
```
The data is similar in 3D space, using Vector3 coordinates. The data is similar in 3D space, using Vector3 coordinates.

View File

@ -46,8 +46,7 @@ Getting started
To use the `GodotGooglePlayBilling` API you first have to get the `GodotGooglePlayBilling` To use the `GodotGooglePlayBilling` API you first have to get the `GodotGooglePlayBilling`
singleton and start the connection: singleton and start the connection:
:: ```
var payment var payment
func _ready(): func _ready():
@ -71,6 +70,7 @@ singleton and start the connection:
payment.startConnection() payment.startConnection()
else: else:
print("Android IAP support is not enabled. Make sure you have enabled 'Custom Build' and the GodotGooglePlayBilling plugin in your Android export settings! IAP will not work.") print("Android IAP support is not enabled. Make sure you have enabled 'Custom Build' and the GodotGooglePlayBilling plugin in your Android export settings! IAP will not work.")
```
All API methods only work if the API is connected. You can use `payment.isReady()` to check the connection status. All API methods only work if the API is connected. You can use `payment.isReady()` to check the connection status.
@ -82,14 +82,14 @@ As soon as the API is connected, you can query SKUs using `querySkuDetails`.
Full example: Full example:
:: ```
func _on_connected(): func _on_connected():
payment.querySkuDetails(["my_iap_item"], "inapp") # "subs" for subscriptions payment.querySkuDetails(["my_iap_item"], "inapp") # "subs" for subscriptions
func _on_sku_details_query_completed(sku_details): func _on_sku_details_query_completed(sku_details):
for available_sku in sku_details: for available_sku in sku_details:
print(available_sku) print(available_sku)
```
Purchase an item Purchase an item
@ -99,14 +99,13 @@ To initiate the purchase flow for an item, call `purchase`.
You **must** query the SKU details for an item before you can You **must** query the SKU details for an item before you can
initiate the purchase flow for it. initiate the purchase flow for it.
:: ```
payment.purchase("my_iap_item") payment.purchase("my_iap_item")
```
Then, wait for the `on_purchases_updated` callback and handle the purchase result: Then, wait for the `on_purchases_updated` callback and handle the purchase result:
:: ```
func _on_purchases_updated(purchases): func _on_purchases_updated(purchases):
for purchase in purchases: for purchase in purchases:
if purchase.purchase_state == 1: # 1 means "purchased", see https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState#constants_1 if purchase.purchase_state == 1: # 1 means "purchased", see https://developer.android.com/reference/com/android/billingclient/api/Purchase.PurchaseState#constants_1
@ -115,6 +114,7 @@ Then, wait for the `on_purchases_updated` callback and handle the purchase resul
payment.acknowledgePurchase(purchase.purchase_token) # call if non-consumable product payment.acknowledgePurchase(purchase.purchase_token) # call if non-consumable product
if purchase.sku in list_of_consumable_products: if purchase.sku in list_of_consumable_products:
payment.consumePurchase(purchase.purchase_token) # call if consumable product payment.consumePurchase(purchase.purchase_token) # call if consumable product
```
Check if the user purchased an item Check if the user purchased an item
@ -126,8 +126,7 @@ and either an array of purchases or an error message. Only active subscriptions
Full example: Full example:
:: ```
var query = payment.queryPurchases("inapp") # Or "subs" for subscriptions var query = payment.queryPurchases("inapp") # Or "subs" for subscriptions
if query.status == OK: if query.status == OK:
for purchase in query.purchases: for purchase in query.purchases:
@ -136,6 +135,7 @@ Full example:
if !purchase.is_acknowledged: if !purchase.is_acknowledged:
payment.acknowledgePurchase(purchase.purchase_token) payment.acknowledgePurchase(purchase.purchase_token)
# Or wait for the _on_purchase_acknowledged callback before giving the user what they bought # Or wait for the _on_purchase_acknowledged callback before giving the user what they bought
```
Consumables Consumables
@ -147,8 +147,7 @@ Call `queryPurchases` to get the purchase token. Calling `consumePurchase` autom
acknowledges a purchase. acknowledges a purchase.
Consuming a product allows the user to purchase it again, and removes it from appearing in subsequent `queryPurchases` calls. Consuming a product allows the user to purchase it again, and removes it from appearing in subsequent `queryPurchases` calls.
:: ```
var query = payment.queryPurchases("inapp") # Or "subs" for subscriptions var query = payment.queryPurchases("inapp") # Or "subs" for subscriptions
if query.status == OK: if query.status == OK:
for purchase in query.purchases: for purchase in query.purchases:
@ -156,6 +155,7 @@ Consuming a product allows the user to purchase it again, and removes it from ap
# enable_premium(purchase.sku) # add coins, save token on server, etc. # enable_premium(purchase.sku) # add coins, save token on server, etc.
payment.consumePurchase(purchase.purchase_token) payment.consumePurchase(purchase.purchase_token)
# Or wait for the _on_purchase_consumed callback before giving the user what they bought # Or wait for the _on_purchase_consumed callback before giving the user what they bought
```
Subscriptions Subscriptions
************* *************

View File

@ -68,11 +68,13 @@ The instructions below assumes that you're using Android Studio.
- Open the plugin `AndroidManifest.xml` file. - Open the plugin `AndroidManifest.xml` file.
- Add the `<application></application )` tag if it's missing. - Add the `<application></application )` tag if it's missing.
- In the `<application )` tag, add a `<meta-data )` tag setup as follow:: - In the `<application )` tag, add a `<meta-data )` tag setup as follow:
```
<meta-data <meta-data
android:name="org.godotengine.plugin.v1.[PluginName]" android:name="org.godotengine.plugin.v1.[PluginName]"
android:value="[plugin.init.ClassFullName]" /> android:value="[plugin.init.ClassFullName]" />
```
Where `PluginName` is the name of the plugin, and `plugin.init.ClassFullName` is the full name (package + class name) of the plugin loading class. Where `PluginName` is the name of the plugin, and `plugin.init.ClassFullName` is the full name (package + class name) of the plugin loading class.
@ -85,8 +87,9 @@ The instructions below assumes that you're using Android Studio.
6. Create a Godot Android Plugin configuration file to help the system detect and load your plugin: 6. Create a Godot Android Plugin configuration file to help the system detect and load your plugin:
- The configuration file extension must be `gdap` (e.g.: `MyPlugin.gdap`). - The configuration file extension must be `gdap` (e.g.: `MyPlugin.gdap`).
- The configuration file format is as follow:: - The configuration file format is as follow:
```
[config] [config]
name="MyPlugin" name="MyPlugin"
@ -98,6 +101,7 @@ The instructions below assumes that you're using Android Studio.
local=["local_dep1.aar", "local_dep2.aar"] local=["local_dep1.aar", "local_dep2.aar"]
remote=["example.plugin.android:remote-dep1:0.0.1", "example.plugin.android:remote-dep2:0.0.1"] remote=["example.plugin.android:remote-dep1:0.0.1", "example.plugin.android:remote-dep2:0.0.1"]
custom_maven_repos=["http://repo.mycompany.com/maven2"] custom_maven_repos=["http://repo.mycompany.com/maven2"]
```
The `config` section and fields are required and defined as follow: The `config` section and fields are required and defined as follow:
@ -129,11 +133,11 @@ The Godot editor will automatically parse all `.gdap` files in the `res://androi
From your script: From your script:
.. code:: ```
if Engine.has_singleton("MyPlugin"): if Engine.has_singleton("MyPlugin"):
var singleton = Engine.get_singleton("MyPlugin") var singleton = Engine.get_singleton("MyPlugin")
print(singleton.myPluginFunction("World")) print(singleton.myPluginFunction("World"))
```
Bundling GDNative resources Bundling GDNative resources

View File

@ -22,8 +22,7 @@ The default HTML page is available in the Godot Engine repository at
`/misc/dist/html/full-size.html ( https://github.com/godotengine/godot/blob/master/misc/dist/html/full-size.html )` `/misc/dist/html/full-size.html ( https://github.com/godotengine/godot/blob/master/misc/dist/html/full-size.html )`
but the following template can be used as a much simpler example: but the following template can be used as a much simpler example:
.. code-block:: html ```
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
@ -39,6 +38,7 @@ but the following template can be used as a much simpler example:
</script> </script>
</body> </body>
</html> </html>
```
Setup Setup
----- -----
@ -88,8 +88,7 @@ However, in the simplest case all you need to do is to create an instance of the
class with the exported configuration, and then call the :js:meth:`engine.startGame <Engine.prototype.startGame )` method class with the exported configuration, and then call the :js:meth:`engine.startGame <Engine.prototype.startGame )` method
optionally overriding any :js:attr:`EngineConfig` parameters. optionally overriding any :js:attr:`EngineConfig` parameters.
.. code-block:: js ```
const engine = new Engine($GODOT_CONFIG); const engine = new Engine($GODOT_CONFIG);
engine.startGame({ engine.startGame({
/* optional override configuration, eg. */ /* optional override configuration, eg. */
@ -97,6 +96,7 @@ optionally overriding any :js:attr:`EngineConfig` parameters.
// canvasResizePolicy: 0, // canvasResizePolicy: 0,
// ... // ...
}); });
```
This snippet of code automatically loads and initializes the engine before starting the game. This snippet of code automatically loads and initializes the engine before starting the game.
It uses the given configuration to to load the engine. The :js:meth:`engine.startGame <Engine.prototype.startGame )` It uses the given configuration to to load the engine. The :js:meth:`engine.startGame <Engine.prototype.startGame )`
@ -113,8 +113,7 @@ the module initialization, but before the engine starts.
This process is a bit more complex, but gives you full control over the engine startup process. This process is a bit more complex, but gives you full control over the engine startup process.
.. code-block:: js ```
const myWasm = 'mygame.wasm'; const myWasm = 'mygame.wasm';
const myPck = 'mygame.pck'; const myPck = 'mygame.pck';
const engine = new Engine(); const engine = new Engine();
@ -129,6 +128,7 @@ This process is a bit more complex, but gives you full control over the engine s
}).then(() => { }).then(() => {
console.log('Engine has started!'); console.log('Engine has started!');
}); });
```
To load the engine manually the :js:meth:`Engine.load` static method must be called. As To load the engine manually the :js:meth:`Engine.load` static method must be called. As
this method is static, multiple engine instances can be spawned if the share the same `wasm`. this method is static, multiple engine instances can be spawned if the share the same `wasm`.
@ -161,10 +161,10 @@ By default, the first canvas element on the page is used for rendering. To use a
element the :js:attr:`canvas` override option can be used. It requires a reference to the DOM element the :js:attr:`canvas` override option can be used. It requires a reference to the DOM
element itself. element itself.
.. code-block:: js ```
const canvasElement = document.querySelector("#my-canvas-element"); const canvasElement = document.querySelector("#my-canvas-element");
engine.startGame({ canvas: canvasElement }); engine.startGame({ canvas: canvasElement });
```
The way the engine resize the canvas can be configured via the :js:attr:`canvasResizePolicy` The way the engine resize the canvas can be configured via the :js:attr:`canvasResizePolicy`
override option. override option.
@ -173,12 +173,12 @@ If your game takes some time to load, it may be useful to display a custom loadi
the progress. This can be achieved with the :js:attr:`onProgress` callback option, which the progress. This can be achieved with the :js:attr:`onProgress` callback option, which
allows to set up a callback function that will be called regularly as the engine loads new bytes. allows to set up a callback function that will be called regularly as the engine loads new bytes.
.. code-block:: js ```
function printProgress(current, total) { function printProgress(current, total) {
console.log("Loaded " + current + " of " + total + " bytes"); console.log("Loaded " + current + " of " + total + " bytes");
} }
engine.startGame({ onProgress: printProgress }); engine.startGame({ onProgress: printProgress });
```
Be aware that in some cases `total` can be `0`. This means that it cannot be calculated. Be aware that in some cases `total` can be `0`. This means that it cannot be calculated.
@ -197,8 +197,7 @@ behavior can be customized by setting your own functions to handle messages.
Use the :js:attr:`onPrint` override option to set a callback function for the output stream, Use the :js:attr:`onPrint` override option to set a callback function for the output stream,
and the :js:attr:`onPrintError` override option to set a callback function for the error stream. and the :js:attr:`onPrintError` override option to set a callback function for the error stream.
.. code-block:: js ```
function print(text) { function print(text) {
console.log(text); console.log(text);
} }
@ -206,6 +205,7 @@ and the :js:attr:`onPrintError` override option to set a callback function for t
console.warn(text); console.warn(text);
} }
engine.startGame({ onPrint: print, onPrintError: printError }); engine.startGame({ onPrint: print, onPrintError: printError });
```
When handling the engine output keep in mind, that it may not be desirable to print it out in the When handling the engine output keep in mind, that it may not be desirable to print it out in the
finished product. finished product.

View File

@ -20,11 +20,13 @@ An iOS plugin requires a `.gdip` configuration file, a binary file which can be
![](img/ios_export_preset_plugins_section.png) ![](img/ios_export_preset_plugins_section.png)
When a plugin is active, you can access it in your using `Engine.get_singleton()`:: When a plugin is active, you can access it in your using `Engine.get_singleton()`:
```
if Engine.has_singleton("MyPlugin"): if Engine.has_singleton("MyPlugin"):
var singleton = Engine.get_singleton("MyPlugin") var singleton = Engine.get_singleton("MyPlugin")
print(singleton.foo()) print(singleton.foo())
```
Creating an iOS plugin Creating an iOS plugin
---------------------- ----------------------
@ -59,24 +61,25 @@ To build an iOS plugin:
3. In the `Build Settings` tab, specify the compilation flags for your static library in `OTHER_CFLAGS`. The most important ones are `-fcxx-modules`, `-fmodules`, and `-DDEBUG` if you need debug support. Other flags should be the same you use to compile Godot. For instance: 3. In the `Build Settings` tab, specify the compilation flags for your static library in `OTHER_CFLAGS`. The most important ones are `-fcxx-modules`, `-fmodules`, and `-DDEBUG` if you need debug support. Other flags should be the same you use to compile Godot. For instance:
:: ```
-DPTRCALL_ENABLED -DDEBUG_ENABLED -DDEBUG_MEMORY_ALLOC -DDISABLE_FORCED_INLINE -DTYPED_METHOD_BIND -DPTRCALL_ENABLED -DDEBUG_ENABLED -DDEBUG_MEMORY_ALLOC -DDISABLE_FORCED_INLINE -DTYPED_METHOD_BIND
```
4. Add the required logic for your plugin and build your library to generate a `.a` file. You will probably need to build both `debug` and `release` target `.a` files. Depending on your needs, pick either or both. If you need both debug and release `.a` files, their name should match following pattern: `[PluginName].[TargetType].a`. You can also build the static library with your SCons configuration. 4. Add the required logic for your plugin and build your library to generate a `.a` file. You will probably need to build both `debug` and `release` target `.a` files. Depending on your needs, pick either or both. If you need both debug and release `.a` files, their name should match following pattern: `[PluginName].[TargetType].a`. You can also build the static library with your SCons configuration.
5. The iOS plugin system also supports `.xcframework` files. To generate one, you can use a command such as: 5. The iOS plugin system also supports `.xcframework` files. To generate one, you can use a command such as:
:: ```
xcodebuild -create-xcframework -library [DeviceLibrary].a -library [SimulatorLibrary].a -output [PluginName].xcframework xcodebuild -create-xcframework -library [DeviceLibrary].a -library [SimulatorLibrary].a -output [PluginName].xcframework
```
6. Create a Godot iOS Plugin configuration file to help the system detect and load your plugin: 6. Create a Godot iOS Plugin configuration file to help the system detect and load your plugin:
- The configuration file extension must be `gdip` (e.g.: `MyPlugin.gdip`). - The configuration file extension must be `gdip` (e.g.: `MyPlugin.gdip`).
- The configuration file format is as follow:: - The configuration file format is as follow:
```
[config] [config]
name="MyPlugin" name="MyPlugin"
binary="MyPlugin.a" binary="MyPlugin.a"
@ -106,6 +109,7 @@ To build an iOS plugin:
</array> </array>
" "
StringPlistKeyToInput:string_input="Type something" StringPlistKeyToInput:string_input="Type something"
```
The `config` section and fields are required and defined as follow: The `config` section and fields are required and defined as follow:

View File

@ -19,8 +19,7 @@ returns a registered singleton.
Here's an example of how to do this in GDScript: Here's an example of how to do this in GDScript:
:: ```
var in_app_store var in_app_store
var game_center var game_center
@ -34,6 +33,7 @@ Here's an example of how to do this in GDScript:
game_center = Engine.get_singleton("GameCenter") game_center = Engine.get_singleton("GameCenter")
else: else:
print("iOS Game Center plugin is not available on this platform.") print("iOS Game Center plugin is not available on this platform.")
```
Asynchronous methods Asynchronous methods
@ -42,9 +42,9 @@ Asynchronous methods
When requesting an asynchronous operation, the method will look like When requesting an asynchronous operation, the method will look like
this: this:
:: ```
Error purchase(Variant params); Error purchase(Variant params);
```
The parameter will usually be a Dictionary, with the information The parameter will usually be a Dictionary, with the information
necessary to make the request, and the call will have two phases. First, necessary to make the request, and the call will have two phases. First,
@ -54,8 +54,7 @@ locally (no internet connection, API incorrectly configured, etc). If
the error value is 'OK', a response event will be produced and added to the error value is 'OK', a response event will be produced and added to
the 'pending events' queue. Example: the 'pending events' queue. Example:
:: ```
func on_purchase_pressed(): func on_purchase_pressed():
var result = InAppStore.purchase({ "product_id": "my_product" }) var result = InAppStore.purchase({ "product_id": "my_product" })
if result == OK: if result == OK:
@ -72,6 +71,7 @@ the 'pending events' queue. Example:
show_success(event.product_id) show_success(event.product_id)
else: else:
show_error() show_error()
```
Remember that when a call returns OK, the API will *always* produce an Remember that when a call returns OK, the API will *always* produce an
event through the pending_event interface, even if it's an error, or a event through the pending_event interface, even if it's an error, or a
@ -97,20 +97,20 @@ It is initialized automatically.
The following methods are available and documented below: The following methods are available and documented below:
:: ```
Error purchase(Variant params) Error purchase(Variant params)
Error request_product_info(Variant params) Error request_product_info(Variant params)
Error restore_purchases() Error restore_purchases()
void set_auto_finish_transaction(bool enable) void set_auto_finish_transaction(bool enable)
void finish_transaction(String product_id) void finish_transaction(String product_id)
```
and the pending events interface: and the pending events interface:
:: ```
int get_pending_event_count() int get_pending_event_count()
Variant pop_pending_event() Variant pop_pending_event()
```
`purchase` `purchase`
~~~~~~~~~~~~ ~~~~~~~~~~~~
@ -125,9 +125,9 @@ Parameters
Takes a dictionary as a parameter, with one field, `product_id`, a Takes a dictionary as a parameter, with one field, `product_id`, a
string with your product ID. Example: string with your product ID. Example:
:: ```
var result = in_app_store.purchase({ "product_id": "my_product" }) var result = in_app_store.purchase({ "product_id": "my_product" })
```
Response event Response event
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -136,23 +136,23 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "purchase", "type": "purchase",
"result": "error", "result": "error",
"product_id": "the product ID requested", "product_id": "the product ID requested",
} }
```
On success: On success:
:: ```
{ {
"type": "purchase", "type": "purchase",
"result": "ok", "result": "ok",
"product_id": "the product ID requested", "product_id": "the product ID requested",
} }
```
`request_product_info` `request_product_info`
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
@ -165,17 +165,16 @@ Parameters
Takes a dictionary as a parameter, with a single `product_ids` key to which a Takes a dictionary as a parameter, with a single `product_ids` key to which a
string array of product IDs is assigned. Example: string array of product IDs is assigned. Example:
:: ```
var result = in_app_store.request_product_info({ "product_ids": ["my_product1", "my_product2"] }) var result = in_app_store.request_product_info({ "product_ids": ["my_product1", "my_product2"] })
```
Response event Response event
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
The response event will be a dictionary with the following fields: The response event will be a dictionary with the following fields:
:: ```
{ {
"type": "product_info", "type": "product_info",
"result": "ok", "result": "ok",
@ -186,6 +185,7 @@ The response event will be a dictionary with the following fields:
"prices": [ list of valid product prices ], "prices": [ list of valid product prices ],
"localized_prices": [ list of valid product localized prices ], "localized_prices": [ list of valid product localized prices ],
} }
```
`restore_purchases` `restore_purchases`
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -198,13 +198,13 @@ Response event
The response events will be dictionaries with the following fields: The response events will be dictionaries with the following fields:
:: ```
{ {
"type": "restore", "type": "restore",
"result": "ok", "result": "ok",
"product_id": "product ID of restored purchase", "product_id": "product ID of restored purchase",
} }
```
`set_auto_finish_transaction` `set_auto_finish_transaction`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -218,9 +218,9 @@ Parameters
Takes a boolean as a parameter which specifies if purchases should be Takes a boolean as a parameter which specifies if purchases should be
automatically finalized. Example: automatically finalized. Example:
:: ```
in_app_store.set_auto_finish_transaction(true) in_app_store.set_auto_finish_transaction(true)
```
`finish_transaction` `finish_transaction`
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -235,9 +235,9 @@ Parameters
Takes a string `product_id` as an argument. `product_id` specifies what product to Takes a string `product_id` as an argument. `product_id` specifies what product to
finalize the purchase on. Example: finalize the purchase on. Example:
:: ```
in_app_store.finish_transaction("my_product1") in_app_store.finish_transaction("my_product1")
```
Game Center Game Center
----------- -----------
@ -247,8 +247,7 @@ Implemented in `Godot iOS GameCenter plugin ( https://github.com/godotengine/god
The Game Center API is available through the "GameCenter" singleton. It The Game Center API is available through the "GameCenter" singleton. It
has the following methods: has the following methods:
:: ```
Error authenticate() Error authenticate()
bool is_authenticated() bool is_authenticated()
Error post_score(Variant score) Error post_score(Variant score)
@ -258,13 +257,14 @@ has the following methods:
void request_achievement_descriptions() void request_achievement_descriptions()
Error show_game_center(Variant params) Error show_game_center(Variant params)
Error request_identity_verification_signature() Error request_identity_verification_signature()
```
and the pending events interface: and the pending events interface:
:: ```
int get_pending_event_count() int get_pending_event_count()
Variant pop_pending_event() Variant pop_pending_event()
```
`authenticate` `authenticate`
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -278,24 +278,24 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "authentication", "type": "authentication",
"result": "error", "result": "error",
"error_code": the value from NSError::code, "error_code": the value from NSError::code,
"error_description": the value from NSError::localizedDescription, "error_description": the value from NSError::localizedDescription,
} }
```
On success: On success:
:: ```
{ {
"type": "authentication", "type": "authentication",
"result": "ok", "result": "ok",
"player_id": the value from GKLocalPlayer::playerID, "player_id": the value from GKLocalPlayer::playerID,
} }
```
`post_score` `post_score`
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
@ -312,9 +312,9 @@ Takes a dictionary as a parameter, with two fields:
Example: Example:
:: ```
var result = game_center.post_score({ "score": 100, "category": "my_leaderboard", }) var result = game_center.post_score({ "score": 100, "category": "my_leaderboard", })
```
Response event Response event
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -323,23 +323,23 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "post_score", "type": "post_score",
"result": "error", "result": "error",
"error_code": the value from NSError::code, "error_code": the value from NSError::code,
"error_description": the value from NSError::localizedDescription, "error_description": the value from NSError::localizedDescription,
} }
```
On success: On success:
:: ```
{ {
"type": "post_score", "type": "post_score",
"result": "ok", "result": "ok",
} }
```
`award_achievement` `award_achievement`
~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
@ -359,9 +359,9 @@ Takes a Dictionary as a parameter, with 3 fields:
Example: Example:
:: ```
var result = award_achievement({ "name": "hard_mode_completed", "progress": 6.1 }) var result = award_achievement({ "name": "hard_mode_completed", "progress": 6.1 })
```
Response event Response event
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -370,22 +370,22 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "award_achievement", "type": "award_achievement",
"result": "error", "result": "error",
"error_code": the error code taken from NSError::code, "error_code": the error code taken from NSError::code,
} }
```
On success: On success:
:: ```
{ {
"type": "award_achievement", "type": "award_achievement",
"result": "ok", "result": "ok",
} }
```
`reset_achievements` `reset_achievements`
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -399,22 +399,22 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "reset_achievements", "type": "reset_achievements",
"result": "error", "result": "error",
"error_code": the value from NSError::code, "error_code": the value from NSError::code,
} }
```
On success: On success:
:: ```
{ {
"type": "reset_achievements", "type": "reset_achievements",
"result": "ok", "result": "ok",
} }
```
`request_achievements` `request_achievements`
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
@ -429,24 +429,24 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "achievements", "type": "achievements",
"result": "error", "result": "error",
"error_code": the value from NSError::code, "error_code": the value from NSError::code,
} }
```
On success: On success:
:: ```
{ {
"type": "achievements", "type": "achievements",
"result": "ok", "result": "ok",
"names": [ list of the name of each achievement ], "names": [ list of the name of each achievement ],
"progress": [ list of the progress made on each achievement ], "progress": [ list of the progress made on each achievement ],
} }
```
`request_achievement_descriptions` `request_achievement_descriptions`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -461,18 +461,17 @@ The response event will be a dictionary with the following fields:
On error: On error:
:: ```
{ {
"type": "achievement_descriptions", "type": "achievement_descriptions",
"result": "error", "result": "error",
"error_code": the value from NSError::code, "error_code": the value from NSError::code,
} }
```
On success: On success:
:: ```
{ {
"type": "achievement_descriptions", "type": "achievement_descriptions",
"result": "ok", "result": "ok",
@ -484,6 +483,7 @@ On success:
"hidden": [ list of booleans indicating whether each achievement is initially visible ], "hidden": [ list of booleans indicating whether each achievement is initially visible ],
"replayable": [ list of booleans indicating whether each achievement can be earned more than once ], "replayable": [ list of booleans indicating whether each achievement can be earned more than once ],
} }
```
`show_game_center` `show_game_center`
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
@ -506,10 +506,10 @@ Takes a Dictionary as a parameter, with two fields:
Examples: Examples:
:: ```
var result = show_game_center({ "view": "leaderboards", "leaderboard_name": "best_time_leaderboard" }) var result = show_game_center({ "view": "leaderboards", "leaderboard_name": "best_time_leaderboard" })
var result = show_game_center({ "view": "achievements" }) var result = show_game_center({ "view": "achievements" })
```
Response event Response event
^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^
@ -518,12 +518,12 @@ The response event will be a dictionary with the following fields:
On close: On close:
:: ```
{ {
"type": "show_game_center", "type": "show_game_center",
"result": "ok", "result": "ok",
} }
```
Multi-platform games Multi-platform games
-------------------- --------------------
@ -536,8 +536,7 @@ you need inside a conditional block, you need to also define them as
valid identifiers (local variable or class member). This is an example valid identifiers (local variable or class member). This is an example
of how to work around this in a class: of how to work around this in a class:
:: ```
var GameCenter = null # define it as a class member var GameCenter = null # define it as a class member
func post_score(score): func post_score(score):
@ -555,3 +554,4 @@ of how to work around this in a class:
if Globals.has_singleton("GameCenter"): if Globals.has_singleton("GameCenter"):
GameCenter = Globals.get_singleton("GameCenter") GameCenter = Globals.get_singleton("GameCenter")
# connect your timer here to the "check_events" function # connect your timer here to the "check_events" function
```

View File

@ -23,9 +23,10 @@ color, and the resulting color will be used as the albedo (main color) of the
imported material. In this example it will contain the pure blue color imported material. In this example it will contain the pure blue color
(zero red, zero green, and full blue): (zero red, zero green, and full blue):
.. code-block:: none
```
0,0,255 0,0,255
```
Configuration Configuration
------------- -------------
@ -33,8 +34,7 @@ Configuration
First we need a generic plugin that will handle the initialization and First we need a generic plugin that will handle the initialization and
destruction of our import plugin. Let's add the `plugin.cfg` file first: destruction of our import plugin. Let's add the `plugin.cfg` file first:
.. code-block:: ini ```
[plugin] [plugin]
name="Silly Material Importer" name="Silly Material Importer"
@ -42,12 +42,12 @@ destruction of our import plugin. Let's add the `plugin.cfg` file first:
author="Yours Truly" author="Yours Truly"
version="1.0" version="1.0"
script="material_import.gd" script="material_import.gd"
```
Then we need the `material_import.gd` file to add and remove the import plugin Then we need the `material_import.gd` file to add and remove the import plugin
when needed: when needed:
:: ```
# material_import.gd # material_import.gd
tool tool
extends EditorPlugin extends EditorPlugin
@ -64,6 +64,7 @@ when needed:
func _exit_tree(): func _exit_tree():
remove_import_plugin(import_plugin) remove_import_plugin(import_plugin)
import_plugin = null import_plugin = null
```
When this plugin is activated, it will create a new instance of the import When this plugin is activated, it will create a new instance of the import
plugin (which we'll soon make) and add it to the editor using the plugin (which we'll soon make) and add it to the editor using the
@ -88,8 +89,7 @@ with files.
Let's begin to code our plugin, one method at time: Let's begin to code our plugin, one method at time:
:: ```
# import_plugin.gd # import_plugin.gd
tool tool
extends EditorImportPlugin extends EditorImportPlugin
@ -97,6 +97,7 @@ Let's begin to code our plugin, one method at time:
func get_importer_name(): func get_importer_name():
return "demos.sillymaterial" return "demos.sillymaterial"
```
The first method is the The first method is the
`get_importer_name()( EditorImportPlugin_method_get_importer_name )`. This is a `get_importer_name()( EditorImportPlugin_method_get_importer_name )`. This is a
@ -104,10 +105,10 @@ unique name for your plugin that is used by Godot to know which import was used
in a certain file. When the files needs to be reimported, the editor will know in a certain file. When the files needs to be reimported, the editor will know
which plugin to call. which plugin to call.
:: ```
func get_visible_name(): func get_visible_name():
return "Silly Material" return "Silly Material"
```
The `get_visible_name()( EditorImportPlugin_method_get_visible_name )` method is The `get_visible_name()( EditorImportPlugin_method_get_visible_name )` method is
responsible for returning the name of the type it imports and it will be shown to the responsible for returning the name of the type it imports and it will be shown to the
@ -117,10 +118,10 @@ You should choose this name as a continuation to "Import as", e.g. *"Import as
Silly Material"*. You can name it whatever you want but we recommend a Silly Material"*. You can name it whatever you want but we recommend a
descriptive name for your plugin. descriptive name for your plugin.
:: ```
func get_recognized_extensions(): func get_recognized_extensions():
return ["mtxt"] return ["mtxt"]
```
Godot's import system detects file types by their extension. In the Godot's import system detects file types by their extension. In the
`get_recognized_extensions()( EditorImportPlugin_method_get_recognized_extensions )` `get_recognized_extensions()( EditorImportPlugin_method_get_recognized_extensions )`
@ -134,10 +135,10 @@ Tip:
for the game and should not be imported. You have to be careful when for the game and should not be imported. You have to be careful when
importing to validate the data. Never expect the file to be well-formed. importing to validate the data. Never expect the file to be well-formed.
:: ```
func get_save_extension(): func get_save_extension():
return "material" return "material"
```
The imported files are saved in the `.import` folder at the project's root. The imported files are saved in the `.import` folder at the project's root.
Their extension should match the type of resource you are importing, but since Their extension should match the type of resource you are importing, but since
@ -150,10 +151,10 @@ resource types. If you are importing a scene, you can use `scn`. Generic
resources can use the `res` extension. However, this is not enforced in any resources can use the `res` extension. However, this is not enforced in any
way by the engine. way by the engine.
:: ```
func get_resource_type(): func get_resource_type():
return "SpatialMaterial" return "SpatialMaterial"
```
The imported resource has a specific type, so the editor can know which property The imported resource has a specific type, so the editor can know which property
slot it belongs to. This allows drag and drop from the FileSystem dock to a slot it belongs to. This allows drag and drop from the FileSystem dock to a
@ -180,8 +181,7 @@ shows how the options will appear in the editor:
Since there might be many presets and they are identified with a number, it's a Since there might be many presets and they are identified with a number, it's a
good practice to use an enum so you can refer to them using names. good practice to use an enum so you can refer to them using names.
:: ```
tool tool
extends EditorImportPlugin extends EditorImportPlugin
@ -190,28 +190,29 @@ good practice to use an enum so you can refer to them using names.
... ...
```
Now that the enum is defined, let's keep looking at the methods of an import Now that the enum is defined, let's keep looking at the methods of an import
plugin: plugin:
:: ```
func get_preset_count(): func get_preset_count():
return Presets.size() return Presets.size()
```
The `get_preset_count()` method The `get_preset_count()` method
returns the amount of presets that this plugins defines. We only have one preset returns the amount of presets that this plugins defines. We only have one preset
now, but we can make this method future-proof by returning the size of our now, but we can make this method future-proof by returning the size of our
`Presets` enumeration. `Presets` enumeration.
:: ```
func get_preset_name(preset): func get_preset_name(preset):
match preset: match preset:
Presets.DEFAULT: Presets.DEFAULT:
return "Default" return "Default"
_: _:
return "Unknown" return "Unknown"
```
Here we have the Here we have the
@ -227,8 +228,7 @@ count you defined, it's always better to be on the safe side.
If you have only one preset you could simply return its name directly, but if If you have only one preset you could simply return its name directly, but if
you do this you have to be careful when you add more presets. you do this you have to be careful when you add more presets.
:: ```
func get_import_options(preset): func get_import_options(preset):
match preset: match preset:
Presets.DEFAULT: Presets.DEFAULT:
@ -238,6 +238,7 @@ you do this you have to be careful when you add more presets.
}] }]
_: _:
return [] return []
```
This is the method which defines the available options. This is the method which defines the available options.
`get_import_options()` returns `get_import_options()` returns
@ -273,10 +274,10 @@ Warning:
have to return an array even it's empty, otherwise you can get have to return an array even it's empty, otherwise you can get
errors. errors.
:: ```
func get_option_visibility(option, options): func get_option_visibility(option, options):
return true return true
```
For the For the
`get_option_visibility()` `get_option_visibility()`
@ -293,8 +294,7 @@ The heavy part of the process, responsible for converting the files into
resources, is covered by the `import()` resources, is covered by the `import()`
method. Our sample code is a bit long, so let's split in a few parts: method. Our sample code is a bit long, so let's split in a few parts:
:: ```
func import(source_file, save_path, options, r_platform_variants, r_gen_files): func import(source_file, save_path, options, r_platform_variants, r_gen_files):
var file = File.new() var file = File.new()
var err = file.open(source_file, File.READ) var err = file.open(source_file, File.READ)
@ -304,6 +304,7 @@ method. Our sample code is a bit long, so let's split in a few parts:
var line = file.get_line() var line = file.get_line()
file.close() file.close()
```
The first part of our import method opens and reads the source file. We use the The first part of our import method opens and reads the source file. We use the
`File` class to do that, passing the `source_file` `File` class to do that, passing the `source_file`
@ -312,8 +313,7 @@ parameter which is provided by the editor.
If there's an error when opening the file, we return it to let the editor know If there's an error when opening the file, we return it to let the editor know
that the import wasn't successful. that the import wasn't successful.
:: ```
var channels = line.split(",") var channels = line.split(",")
if channels.size() != 3: if channels.size() != 3:
return ERR_PARSE_ERROR return ERR_PARSE_ERROR
@ -323,6 +323,7 @@ that the import wasn't successful.
color = Color8(255, 0, 0) color = Color8(255, 0, 0)
else: else:
color = Color8(int(channels[0]), int(channels[1]), int(channels[2])) color = Color8(int(channels[0]), int(channels[1]), int(channels[2]))
```
This code takes the line of the file it read before and splits it in pieces This code takes the line of the file it read before and splits it in pieces
that are separated by a comma. If there are more or less than the three values, that are separated by a comma. If there are more or less than the three values,
@ -332,18 +333,18 @@ Then it creates a new `Color` variable and sets its values
according to the input file. If the `use_red_anyway` option is enabled, then according to the input file. If the `use_red_anyway` option is enabled, then
it sets the color as a pure red instead. it sets the color as a pure red instead.
:: ```
var material = SpatialMaterial.new() var material = SpatialMaterial.new()
material.albedo_color = color material.albedo_color = color
```
This part makes a new `SpatialMaterial` that is the This part makes a new `SpatialMaterial` that is the
imported resource. We create a new instance of it and then set its albedo color imported resource. We create a new instance of it and then set its albedo color
as the value we got before. as the value we got before.
:: ```
return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], material) return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], material)
```
This is the last part and quite an important one, because here we save the made This is the last part and quite an important one, because here we save the made
resource to the disk. The path of the saved file is generated and informed by resource to the disk. The path of the saved file is generated and informed by
@ -376,10 +377,10 @@ editor can know that you did.
For example, let's say we save a different material for a mobile platform. We For example, let's say we save a different material for a mobile platform. We
would need to do something like the following: would need to do something like the following:
:: ```
r_platform_variants.push_back("mobile") r_platform_variants.push_back("mobile")
return ResourceSaver.save("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material) return ResourceSaver.save("%s.%s.%s" % [save_path, "mobile", get_save_extension()], mobile_material)
```
The `r_gen_files` argument is meant for extra files that are generated during The `r_gen_files` argument is meant for extra files that are generated during
your import process and need to be kept. The editor will look at it to your import process and need to be kept. The editor will look at it to
@ -390,8 +391,7 @@ This is also an array and should be filled with full paths of the files you
save. As an example, let's create another material for the next pass and save it save. As an example, let's create another material for the next pass and save it
in a different file: in a different file:
:: ```
var next_pass = SpatialMaterial.new() var next_pass = SpatialMaterial.new()
next_pass.albedo_color = color.inverted() next_pass.albedo_color = color.inverted()
var next_pass_path = "%s.next_pass.%s" % [save_path, get_save_extension()] var next_pass_path = "%s.next_pass.%s" % [save_path, get_save_extension()]
@ -400,6 +400,7 @@ in a different file:
if err != OK: if err != OK:
return err return err
r_gen_files.push_back(next_pass_path) r_gen_files.push_back(next_pass_path)
```
Trying the plugin Trying the plugin
----------------- -----------------

View File

@ -26,8 +26,7 @@ The plugin script will come with `enter_tree()` and `exit_tree()`
methods, but for a main screen plugin we need to add a few extra methods. methods, but for a main screen plugin we need to add a few extra methods.
Add five extra methods such that the script looks like this: Add five extra methods such that the script looks like this:
:: ```
tool tool
extends EditorPlugin extends EditorPlugin
@ -54,6 +53,7 @@ Add five extra methods such that the script looks like this:
func get_plugin_icon(): func get_plugin_icon():
return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons") return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
```
The important part in this script is the `has_main_screen()` function, The important part in this script is the `has_main_screen()` function,
which is overloaded so it returns `true`. This function is automatically which is overloaded so it returns `true`. This function is automatically
@ -75,14 +75,14 @@ Next, let's add a button to our example main screen plugin.
Add a `Button` node, and set the text to "Print Hello" or similar. Add a `Button` node, and set the text to "Print Hello" or similar.
Add a script to the button like this: Add a script to the button like this:
:: ```
tool tool
extends Button extends Button
func _on_PrintHello_pressed(): func _on_PrintHello_pressed():
print("Hello from the main screen plugin!") print("Hello from the main screen plugin!")
```
Then connect the "pressed" signal to itself. If you need help with signals, Then connect the "pressed" signal to itself. If you need help with signals,
see the `doc_signals` article. see the `doc_signals` article.
@ -96,8 +96,7 @@ We need to update the `main_screen_plugin.gd` script so the plugin
instances our main panel scene and places it where it needs to be. instances our main panel scene and places it where it needs to be.
Here is the full plugin script: Here is the full plugin script:
:: ```
tool tool
extends EditorPlugin extends EditorPlugin
@ -136,6 +135,7 @@ Here is the full plugin script:
func get_plugin_icon(): func get_plugin_icon():
# Must return some kind of Texture for the icon. # Must return some kind of Texture for the icon.
return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons") return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
```
A couple of specific lines were added. `MainPanel` is a constant that holds A couple of specific lines were added. `MainPanel` is a constant that holds
a reference to the scene, and we instance it into `main_panel_instance`. a reference to the scene, and we instance it into `main_panel_instance`.

View File

@ -330,8 +330,7 @@ an autoload.
Use the following code to register a singleton from an editor plugin: Use the following code to register a singleton from an editor plugin:
:: ```
tool tool
extends EditorPlugin extends EditorPlugin
@ -346,3 +345,4 @@ Use the following code to register a singleton from an editor plugin:
func _exit_tree(): func _exit_tree():
remove_autoload_singleton(AUTOLOAD_NAME) remove_autoload_singleton(AUTOLOAD_NAME)
```

View File

@ -27,18 +27,17 @@ the gizmo can be hidden or not.
This would be a basic setup: This would be a basic setup:
:: ```
# MyCustomGizmoPlugin.gd # MyCustomGizmoPlugin.gd
extends EditorSpatialGizmoPlugin extends EditorSpatialGizmoPlugin
func get_name(): func get_name():
return "CustomNode" return "CustomNode"
```
:: ```
# MyCustomEditorPlugin.gd # MyCustomEditorPlugin.gd
tool tool
extends EditorPlugin extends EditorPlugin
@ -55,6 +54,7 @@ This would be a basic setup:
func _exit_tree(): func _exit_tree():
remove_spatial_gizmo_plugin(gizmo_plugin) remove_spatial_gizmo_plugin(gizmo_plugin)
```
For simple gizmos, just inheriting `EditorSpatialGizmoPlugin` For simple gizmos, just inheriting `EditorSpatialGizmoPlugin`
@ -68,8 +68,7 @@ Simple approach
The first step is to, in our custom gizmo plugin, override the `has_gizmo()( EditorSpatialGizmoPlugin_method_has_gizmo )` The first step is to, in our custom gizmo plugin, override the `has_gizmo()( EditorSpatialGizmoPlugin_method_has_gizmo )`
method so that it returns `true` when the spatial parameter is of our target type. method so that it returns `true` when the spatial parameter is of our target type.
:: ```
# ... # ...
@ -78,12 +77,12 @@ method so that it returns `true` when the spatial parameter is of our target typ
# ... # ...
```
Then we can override methods like `redraw()( EditorSpatialGizmoPlugin_method_redraw )` Then we can override methods like `redraw()( EditorSpatialGizmoPlugin_method_redraw )`
or all the handle related ones. or all the handle related ones.
:: ```
# ... # ...
@ -112,6 +111,7 @@ or all the handle related ones.
# ... # ...
```
Note that we created a material in the `init` method, and retrieved it in the `redraw` Note that we created a material in the `init` method, and retrieved it in the `redraw`
method using `get_material()( EditorSpatialGizmoPlugin_method_get_material )`. This method using `get_material()( EditorSpatialGizmoPlugin_method_get_material )`. This
@ -120,8 +120,7 @@ method retrieves one of the material's variants depending on the state of the gi
So the final plugin would look somewhat like this: So the final plugin would look somewhat like this:
:: ```
extends EditorSpatialGizmoPlugin extends EditorSpatialGizmoPlugin
@ -158,6 +157,7 @@ So the final plugin would look somewhat like this:
# You should implement the rest of handle-related callbacks # You should implement the rest of handle-related callbacks
# (get_handle_name(), get_handle_value(), commit_handle()...). # (get_handle_name(), get_handle_value(), commit_handle()...).
```
Note that we just added some handles in the redraw method, but we still need to implement Note that we just added some handles in the redraw method, but we still need to implement
the rest of handle-related callbacks in `EditorSpatialGizmoPlugin` the rest of handle-related callbacks in `EditorSpatialGizmoPlugin`
@ -174,8 +174,7 @@ In these cases all we need to do is, in our new gizmo plugin, override
`create_gizmo()( EditorSpatialGizmoPlugin_method_create_gizmo )`, so it returns our custom gizmo implementation `create_gizmo()( EditorSpatialGizmoPlugin_method_create_gizmo )`, so it returns our custom gizmo implementation
for the Spatial nodes we want to target. for the Spatial nodes we want to target.
:: ```
# MyCustomGizmoPlugin.gd # MyCustomGizmoPlugin.gd
extends EditorSpatialGizmoPlugin extends EditorSpatialGizmoPlugin
@ -194,12 +193,12 @@ for the Spatial nodes we want to target.
return MyCustomGizmo.new() return MyCustomGizmo.new()
else: else:
return null return null
```
This way all the gizmo logic and drawing methods can be implemented in a new class extending This way all the gizmo logic and drawing methods can be implemented in a new class extending
`EditorSpatialGizmo( EditorSpatialGizmo )`, like so: `EditorSpatialGizmo( EditorSpatialGizmo )`, like so:
:: ```
# MyCustomGizmo.gd # MyCustomGizmo.gd
extends EditorSpatialGizmo extends EditorSpatialGizmo
@ -232,6 +231,7 @@ This way all the gizmo logic and drawing methods can be implemented in a new cla
# You should implement the rest of handle-related callbacks # You should implement the rest of handle-related callbacks
# (get_handle_name(), get_handle_value(), commit_handle()...). # (get_handle_name(), get_handle_value(), commit_handle()...).
```
Note that we just added some handles in the redraw method, but we still need to implement Note that we just added some handles in the redraw method, but we still need to implement
the rest of handle-related callbacks in `EditorSpatialGizmo( EditorSpatialGizmo )` the rest of handle-related callbacks in `EditorSpatialGizmo( EditorSpatialGizmo )`

View File

@ -30,8 +30,7 @@ Don't forget to change its mode to "CanvasItem" (if you are using a Sprite):
Create a script which derives from `VisualShaderNodeCustom`. This is Create a script which derives from `VisualShaderNodeCustom`. This is
all you need to initialize your plugin. all you need to initialize your plugin.
:: ```
# PerlinNoise3D.gd # PerlinNoise3D.gd
tool tool
extends VisualShaderNodeCustom extends VisualShaderNodeCustom
@ -189,6 +188,7 @@ all you need to initialize your plugin.
func _get_code(input_vars, output_vars, mode, type): func _get_code(input_vars, output_vars, mode, type):
return output_vars[0] + " = cnoise(vec3((%s.xy + %s.xy) * %s, %s)) * 0.5 + 0.5;" % [input_vars[0], input_vars[1], input_vars[2], input_vars[3]] return output_vars[0] + " = cnoise(vec3((%s.xy + %s.xy) * %s, %s)) * 0.5 + 0.5;" % [input_vars[0], input_vars[1], input_vars[2], input_vars[3]]
```
Save it and open the Visual Shader. You should see your new node type within the member's dialog (if you can't see your new node, try restarting the editor): Save it and open the Visual Shader. You should see your new node type within the member's dialog (if you can't see your new node, try restarting the editor):

View File

@ -399,9 +399,11 @@ If the game has a very high base resolution (e.g. 3840×2160), aliasing might
appear when downsampling to something considerably lower like 1280×720. appear when downsampling to something considerably lower like 1280×720.
Aliasing can be made less visible by shrinking all images by a factor of 2 Aliasing can be made less visible by shrinking all images by a factor of 2
upon loading. This can be done by calling the method below before upon loading. This can be done by calling the method below before
the game data is loaded:: the game data is loaded:
```
VisualServer.texture_set_shrink_all_x2_on_set_data(true) VisualServer.texture_set_shrink_all_x2_on_set_data(true)
```
Alternatively, you can also enable mipmaps on all your 2D textures. However, Alternatively, you can also enable mipmaps on all your 2D textures. However,
enabling mipmaps will increase memory usage which may be problematic on low-end enabling mipmaps will increase memory usage which may be problematic on low-end

View File

@ -76,9 +76,9 @@ There can only be one active camera per `Viewport`, so if there is more
than one, make sure that the desired one has the "current" property set, than one, make sure that the desired one has the "current" property set,
or make it the current camera by calling: or make it the current camera by calling:
:: ```
camera.make_current() camera.make_current()
```
By default, cameras will render all objects in their world. In 3D, cameras can use their By default, cameras will render all objects in their world. In 3D, cameras can use their
`cull_mask` property combined with the `cull_mask` property combined with the
@ -95,10 +95,10 @@ these values are overridden, but for all others, this sets their resolution.
It is also possible to scale the 2D content and make the `Viewport` resolution It is also possible to scale the 2D content and make the `Viewport` resolution
different from the one specified in size, by calling: different from the one specified in size, by calling:
:: ```
viewport.set_size_override(true, Vector2(width, height)) # Custom size for 2D. viewport.set_size_override(true, Vector2(width, height)) # Custom size for 2D.
viewport.set_size_override_stretch(true) # Enable stretch for custom size. viewport.set_size_override_stretch(true) # Enable stretch for custom size.
```
The root `Viewport` uses this for the stretch options in the project The root `Viewport` uses this for the stretch options in the project
settings. For more information on scaling and stretching visit the `Multiple Resolutions Tutorial ( doc_multiple_resolutions )` settings. For more information on scaling and stretching visit the `Multiple Resolutions Tutorial ( doc_multiple_resolutions )`
@ -136,8 +136,7 @@ It is possible to query a capture of the `Viewport` contents. For the root
`Viewport`, this is effectively a screen capture. This is done with the `Viewport`, this is effectively a screen capture. This is done with the
following code: following code:
:: ```
# Retrieve the captured Image using get_data(). # Retrieve the captured Image using get_data().
var img = get_viewport().get_texture().get_data() var img = get_viewport().get_texture().get_data()
# Flip on the Y axis. # Flip on the Y axis.
@ -148,16 +147,17 @@ following code:
tex.create_from_image(img) tex.create_from_image(img)
# Set Sprite Texture. # Set Sprite Texture.
$sprite.texture = tex $sprite.texture = tex
```
But if you use this in `ready()` or from the first frame of the `Viewport's` initialization, But if you use this in `ready()` or from the first frame of the `Viewport's` initialization,
you will get an empty texture because there is nothing to get as texture. You can deal with you will get an empty texture because there is nothing to get as texture. You can deal with
it using (for example): it using (for example):
:: ```
# Wait until the frame has finished before getting the texture. # Wait until the frame has finished before getting the texture.
yield(VisualServer, "frame_post_draw") yield(VisualServer, "frame_post_draw")
# You can get the image after this. # You can get the image after this.
```
Viewport Container Viewport Container
------------------ ------------------
@ -224,11 +224,11 @@ When rendering to a `Viewport`, whatever is inside will not be
visible in the scene editor. To display the contents, you have to draw the `Viewport's` somewhere. visible in the scene editor. To display the contents, you have to draw the `Viewport's` somewhere.
This can be requested via code using (for example): This can be requested via code using (for example):
:: ```
# This gives us the ViewportTexture. # This gives us the ViewportTexture.
var rtt = viewport.get_texture() var rtt = viewport.get_texture()
sprite.texture = rtt sprite.texture = rtt
```
Or it can be assigned in the editor by selecting "New ViewportTexture" Or it can be assigned in the editor by selecting "New ViewportTexture"

View File

@ -46,11 +46,11 @@ Using C# from GDScript doesn't need much work. Once loaded
(see `doc_gdscript_classes_as_resources`), the script can be instantiated (see `doc_gdscript_classes_as_resources`), the script can be instantiated
with `new()`. with `new()`.
:: ```
var my_csharp_script = load("res://path_to_cs_file.cs") var my_csharp_script = load("res://path_to_cs_file.cs")
var my_csharp_node = my_csharp_script.new() var my_csharp_node = my_csharp_script.new()
print(my_csharp_node.str2) # barbar print(my_csharp_node.str2) # barbar
```
Warning: Warning:
@ -71,10 +71,10 @@ Instantiating GDScript nodes from C#
From the C# side, everything work the same way. Once loaded, the GDScript can From the C# side, everything work the same way. Once loaded, the GDScript can
be instantiated with `GDScript.New()`. be instantiated with `GDScript.New()`.
.. code-block:: csharp ```
GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd"); GDScript MyGDScript = (GDScript) GD.Load("res://path_to_gd_file.gd");
Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object Object myGDScriptNode = (Godot.Object) MyGDScript.New(); // This is a Godot.Object
```
Here we are using an `Object`, but you can use type conversion like Here we are using an `Object`, but you can use type conversion like
explained in `doc_c_sharp_features_type_conversion_and_casting`. explained in `doc_c_sharp_features_type_conversion_and_casting`.
@ -88,14 +88,14 @@ Accessing C# fields from GDScript
Accessing C# fields from GDScript is straightforward, you shouldn't have Accessing C# fields from GDScript is straightforward, you shouldn't have
anything to worry about. anything to worry about.
:: ```
print(my_csharp_node.str1) # bar print(my_csharp_node.str1) # bar
my_csharp_node.str1 = "BAR" my_csharp_node.str1 = "BAR"
print(my_csharp_node.str1) # BAR print(my_csharp_node.str1) # BAR
print(my_csharp_node.str2) # barbar print(my_csharp_node.str2) # barbar
# my_csharp_node.str2 = "BARBAR" # This line will hang and crash # my_csharp_node.str2 = "BARBAR" # This line will hang and crash
```
Note that it doesn't matter if the field is defined as a property or an Note that it doesn't matter if the field is defined as a property or an
attribute. However, trying to set a value on a property that does not define attribute. However, trying to set a value on a property that does not define
@ -108,14 +108,14 @@ As C# is statically typed, accessing GDScript from C# is a bit more
convoluted, you will have to use `Object.Get()` convoluted, you will have to use `Object.Get()`
and `Object.Set()`. The first argument is the name of the field you want to access. and `Object.Set()`. The first argument is the name of the field you want to access.
.. code-block:: csharp ```
GD.Print(myGDScriptNode.Get("str1")); // foo GD.Print(myGDScriptNode.Get("str1")); // foo
myGDScriptNode.Set("str1", "FOO"); myGDScriptNode.Set("str1", "FOO");
GD.Print(myGDScriptNode.Get("str1")); // FOO GD.Print(myGDScriptNode.Get("str1")); // FOO
GD.Print(myGDScriptNode.Get("str2")); // foofoo GD.Print(myGDScriptNode.Get("str2")); // foofoo
// myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything // myGDScriptNode.Set("str2", "FOOFOO"); // This line won't do anything
```
Keep in mind that when setting a field value you should only use types the Keep in mind that when setting a field value you should only use types the
GDScript side knows about. GDScript side knows about.
@ -132,8 +132,7 @@ marshalling process will do its best to cast the arguments to match
function signatures. function signatures.
If that's impossible, you'll see the following error: `Invalid call. Nonexistent function `FunctionName```. If that's impossible, you'll see the following error: `Invalid call. Nonexistent function `FunctionName```.
:: ```
my_csharp_node.PrintNodeName(self) # myGDScriptNode my_csharp_node.PrintNodeName(self) # myGDScriptNode
# my_csharp_node.PrintNodeName() # This line will fail. # my_csharp_node.PrintNodeName() # This line will fail.
@ -141,6 +140,7 @@ If that's impossible, you'll see the following error: `Invalid call. Nonexistent
my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c my_csharp_node.PrintArray(["a", "b", "c"]) # a, b, c
my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3 my_csharp_node.PrintArray([1, 2, 3]) # 1, 2, 3
```
Calling GDScript methods from C# Calling GDScript methods from C#
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -150,8 +150,7 @@ To call GDScript methods from C# you'll need to use
name of the method you want to call. The following arguments will be passed name of the method you want to call. The following arguments will be passed
to said method. to said method.
.. code-block:: csharp ```
myGDScriptNode.Call("print_node_name", this); // my_csharp_node myGDScriptNode.Call("print_node_name", this); // my_csharp_node
// myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out. // myGDScriptNode.Call("print_node_name"); // This line will fail silently and won't error out.
@ -164,6 +163,7 @@ to said method.
myGDScriptNode.Call("print_array", (object)arr); // a, b, c myGDScriptNode.Call("print_array", (object)arr); // a, b, c
myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3 myGDScriptNode.Call("print_array", (object)new int[] { 1, 2, 3 }); // 1, 2, 3
// Note how the type of each array entry does not matter as long as it can be handled by the marshaller // Note how the type of each array entry does not matter as long as it can be handled by the marshaller
```
Warning: Warning:

View File

@ -22,12 +22,14 @@ Note:
Basic usage Basic usage
----------- -----------
To evaluate a mathematical expression, use:: To evaluate a mathematical expression, use:
```
var expression = Expression.new() var expression = Expression.new()
expression.parse("20 + 10*2 - 5/2.0") expression.parse("20 + 10*2 - 5/2.0")
var result = expression.execute() var result = expression.execute()
print(result) # 37.5 print(result) # 37.5
```
The following operators are available: The following operators are available:
@ -55,8 +57,9 @@ applies. Use parentheses to override the order of operations if needed.
All the Variant types supported in Godot can be used: integers, floating-point All the Variant types supported in Godot can be used: integers, floating-point
numbers, strings, arrays, dictionaries, colors, vectors, … numbers, strings, arrays, dictionaries, colors, vectors, …
Arrays and dictionaries can be indexed like in GDScript:: Arrays and dictionaries can be indexed like in GDScript:
```
# Returns 1. # Returns 1.
[1, 2][0] [1, 2][0]
@ -70,14 +73,16 @@ Arrays and dictionaries can be indexed like in GDScript::
Vector3(5, 6, 7)[2] Vector3(5, 6, 7)[2]
Vector3(5, 6, 7)["z"] Vector3(5, 6, 7)["z"]
Vector3(5, 6, 7).z Vector3(5, 6, 7).z
```
Passing variables to an expression Passing variables to an expression
---------------------------------- ----------------------------------
You can pass variables to an expression. These variables will then You can pass variables to an expression. These variables will then
become available in the expression's "context" and will be substituted when used become available in the expression's "context" and will be substituted when used
in the expression:: in the expression:
```
var expression = Expression.new() var expression = Expression.new()
# Define the variable names first in the second parameter of `parse()`. # Define the variable names first in the second parameter of `parse()`.
# In this example, we use `x` for the variable name. # In this example, we use `x` for the variable name.
@ -86,6 +91,7 @@ in the expression::
# Here, `x` is assigned the integer value 5. # Here, `x` is assigned the integer value 5.
var result = expression.execute([5]) var result = expression.execute([5])
print(result) # 30 print(result) # 30
```
Both the variable names and variable values **must** be specified as an array, Both the variable names and variable values **must** be specified as an array,
even if you only define one variable. Also, variable names are **case-sensitive**. even if you only define one variable. Also, variable names are **case-sensitive**.
@ -98,8 +104,9 @@ expression has no base instance associated to it.
When calling `Expression.execute()`, When calling `Expression.execute()`,
you can set the value of the `base_instance` parameter to a specific object you can set the value of the `base_instance` parameter to a specific object
instance such as `self`, another script instance or even a singleton:: instance such as `self`, another script instance or even a singleton:
```
func double(number): func double(number):
return number * 2 return number * 2
@ -116,6 +123,7 @@ instance such as `self`, another script instance or even a singleton::
# as the base instance. # as the base instance.
result = expression.execute([], self) result = expression.execute([], self)
print(result) # 20 print(result) # 20
```
Associating a base instance allows doing the following: Associating a base instance allows doing the following:
@ -135,8 +143,9 @@ Warning:
Example script Example script
-------------- --------------
The script below demonstrates what the Expression class is capable of:: The script below demonstrates what the Expression class is capable of:
```
const DAYS_IN_YEAR = 365 const DAYS_IN_YEAR = 365
var script_member_variable = 1000 var script_member_variable = 1000
@ -179,9 +188,11 @@ The script below demonstrates what the Expression class is capable of::
# The method's return value is also the expression's return value. # The method's return value is also the expression's return value.
return 0 return 0
```
The output from the script will be:: The output from the script will be:
```
4 4
160 160
1.570796 1.570796
@ -196,6 +207,7 @@ The output from the script will be::
You called 'call_me()' in the expression text. You called 'call_me()' in the expression text.
Argument passed: some string Argument passed: some string
0 0
```
Built-in functions Built-in functions
------------------ ------------------

View File

@ -27,13 +27,14 @@ with SVN, Git, Mercurial, Perforce, etc.
Example of file system contents: Example of file system contents:
.. code-block:: none
```
/project.godot /project.godot
/enemy/enemy.tscn /enemy/enemy.tscn
/enemy/enemy.gd /enemy/enemy.gd
/enemy/enemysprite.png) /enemy/enemysprite.png)
/player/player.gd /player/player.gd
```
project.godot project.godot
------------- -------------

View File

@ -57,19 +57,19 @@ assignment. Example:
Static: Static:
.. code-block:: cpp ```
int a; // Value uninitialized. int a; // Value uninitialized.
a = 5; // This is valid. a = 5; // This is valid.
a = "Hi!"; // This is invalid. a = "Hi!"; // This is invalid.
```
Dynamic: Dynamic:
:: ```
var a # 'null' by default. var a # 'null' by default.
a = 5 # Valid, 'a' becomes an integer. a = 5 # Valid, 'a' becomes an integer.
a = "Hi!" # Valid, 'a' changed to a string. a = "Hi!" # Valid, 'a' changed to a string.
```
As function arguments: As function arguments:
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -79,8 +79,7 @@ different arguments, for example:
Static: Static:
.. code-block:: cpp ```
void print_value(int value) { void print_value(int value) {
printf("value is %i\n", value); printf("value is %i\n", value);
@ -90,11 +89,11 @@ Static:
print_value(55); // Valid. print_value(55); // Valid.
print_value("Hello"); // Invalid. print_value("Hello"); // Invalid.
```
Dynamic: Dynamic:
:: ```
func print_value(value): func print_value(value):
print(value) print(value)
@ -102,6 +101,7 @@ Dynamic:
print_value(55) # Valid. print_value(55) # Valid.
print_value("Hello") # Valid. print_value("Hello") # Valid.
```
Pointers & referencing: Pointers & referencing:
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
@ -119,8 +119,7 @@ too. Some Examples:
- C++: - C++:
.. code-block:: cpp ```
void use_class(SomeClass *instance) { void use_class(SomeClass *instance) {
instance->use(); instance->use();
@ -132,11 +131,11 @@ too. Some Examples:
use_class(instance); // Passed as pointer. use_class(instance); // Passed as pointer.
delete instance; // Otherwise it will leak memory. delete instance; // Otherwise it will leak memory.
} }
```
- Java: - Java:
.. code-block:: java ```
@Override @Override
public final void use_class(SomeClass instance) { public final void use_class(SomeClass instance) {
@ -150,11 +149,11 @@ too. Some Examples:
// Garbage collector will get rid of it when not in // Garbage collector will get rid of it when not in
// use and freeze your game randomly for a second. // use and freeze your game randomly for a second.
} }
```
- GDScript: - GDScript:
:: ```
func use_class(instance): # Does not care about class type func use_class(instance): # Does not care about class type
instance.use() # Will work with any class that has a ".use()" method. instance.use() # Will work with any class that has a ".use()" method.
@ -162,6 +161,7 @@ too. Some Examples:
var instance = SomeClass.new() # Created as reference. var instance = SomeClass.new() # Created as reference.
use_class(instance) # Passed as reference. use_class(instance) # Passed as reference.
# Will be unreferenced and deleted. # Will be unreferenced and deleted.
```
In GDScript, only base types (int, float, String and PoolArray types) In GDScript, only base types (int, float, String and PoolArray types)
are passed by value to functions (value is copied). Everything else are passed by value to functions (value is copied). Everything else
@ -175,8 +175,9 @@ Note:
A value is **passed by value** when it is copied every time it's specified A value is **passed by value** when it is copied every time it's specified
as a function parameter. One consequence of this is that the function cannot as a function parameter. One consequence of this is that the function cannot
modify the parameter in a way that is visible from outside the function:: modify the parameter in a way that is visible from outside the function:
```
func greet(text): func greet(text):
text = "Hello " + text text = "Hello " + text
@ -189,14 +190,16 @@ Note:
greet(example) greet(example)
print(example) # Godot print(example) # Godot
```
A value is **passed by reference** when it is *not* copied every time it's A value is **passed by reference** when it is *not* copied every time it's
specified as a function parameter. This allows modifying a function specified as a function parameter. This allows modifying a function
parameter within a function body (and having the modified value accessible parameter within a function body (and having the modified value accessible
outside the function). The downside is that the data passed as a function outside the function). The downside is that the data passed as a function
parameter is no longer guaranteed to be immutable, which can cause parameter is no longer guaranteed to be immutable, which can cause
difficult-to-track bugs if not done carefully:: difficult-to-track bugs if not done carefully:
```
func greet(text): func greet(text):
text.push_front("Hello") text.push_front("Hello")
@ -210,6 +213,7 @@ Note:
greet(example) greet(example)
print(example) # [Hello, Godot] (Array with 2 String elements) print(example) # [Hello, Godot] (Array with 2 String elements)
```
Compared to passing by value, passing by reference can perform better when Compared to passing by value, passing by reference can perform better when
using large objects since copying large objects in memory can be slow. using large objects since copying large objects in memory can be slow.
@ -225,8 +229,7 @@ Arrays in dynamically typed languages can contain many different mixed
datatypes inside and are always dynamic (can be resized at any time). datatypes inside and are always dynamic (can be resized at any time).
Compare for example arrays in statically typed languages: Compare for example arrays in statically typed languages:
.. code-block:: cpp ```
int *array = new int[4]; // Create array. int *array = new int[4]; // Create array.
array[0] = 10; // Initialize manually. array[0] = 10; // Initialize manually.
array[1] = 20; // Can't mix types. array[1] = 20; // Can't mix types.
@ -247,33 +250,34 @@ Compare for example arrays in statically typed languages:
array.resize(3); // Can be resized. array.resize(3); // Can be resized.
use_array(array); // Passed reference or value. use_array(array); // Passed reference or value.
// Freed when stack ends. // Freed when stack ends.
```
And in GDScript: And in GDScript:
:: ```
var array = [10, "hello", 40, 60] # Simple, and can mix types. var array = [10, "hello", 40, 60] # Simple, and can mix types.
array.resize(3) # Can be resized. array.resize(3) # Can be resized.
use_array(array) # Passed as reference. use_array(array) # Passed as reference.
# Freed when no longer in use. # Freed when no longer in use.
```
In dynamically typed languages, arrays can also double as other In dynamically typed languages, arrays can also double as other
datatypes, such as lists: datatypes, such as lists:
:: ```
var array = [] var array = []
array.append(4) array.append(4)
array.append(5) array.append(5)
array.pop_front() array.pop_front()
```
Or unordered sets: Or unordered sets:
:: ```
var a = 20 var a = 20
if a in [10, 20, 30]: if a in [10, 20, 30]:
print("We have a winner!") print("We have a winner!")
```
Dictionaries Dictionaries
------------ ------------
@ -292,25 +296,24 @@ will go as far as implementing arrays as dictionaries.
Example of Dictionary: Example of Dictionary:
:: ```
var d = {"name": "John", "age": 22} # Simple syntax. var d = {"name": "John", "age": 22} # Simple syntax.
print("Name: ", d["name"], " Age: ", d["age"]) print("Name: ", d["name"], " Age: ", d["age"])
```
Dictionaries are also dynamic, keys can be added or removed at any point Dictionaries are also dynamic, keys can be added or removed at any point
at little cost: at little cost:
:: ```
d["mother"] = "Rebecca" # Addition. d["mother"] = "Rebecca" # Addition.
d["age"] = 11 # Modification. d["age"] = 11 # Modification.
d.erase("name") # Removal. d.erase("name") # Removal.
```
In most cases, two-dimensional arrays can often be implemented more In most cases, two-dimensional arrays can often be implemented more
easily with dictionaries. Here's a simple battleship game example: easily with dictionaries. Here's a simple battleship game example:
:: ```
# Battleship Game # Battleship Game
const SHIP = 0 const SHIP = 0
@ -338,14 +341,14 @@ easily with dictionaries. Here's a simple battleship game example:
missile(Vector2(1, 1)) missile(Vector2(1, 1))
missile(Vector2(5, 8)) missile(Vector2(5, 8))
missile(Vector2(2, 3)) missile(Vector2(2, 3))
```
Dictionaries can also be used as data markup or quick structures. While Dictionaries can also be used as data markup or quick structures. While
GDScript's dictionaries resemble python dictionaries, it also supports Lua GDScript's dictionaries resemble python dictionaries, it also supports Lua
style syntax and indexing, which makes it useful for writing initial style syntax and indexing, which makes it useful for writing initial
states and quick structs: states and quick structs:
:: ```
# Same example, lua-style support. # Same example, lua-style support.
# This syntax is a lot more readable and usable. # This syntax is a lot more readable and usable.
# Like any GDScript identifier, keys written in this form cannot start # Like any GDScript identifier, keys written in this form cannot start
@ -362,14 +365,14 @@ states and quick structs:
d["mother"] = "Rebecca" d["mother"] = "Rebecca"
d.mother = "Caroline" # This would work too to create a new key. d.mother = "Caroline" # This would work too to create a new key.
```
For & while For & while
----------- -----------
Iterating in some statically typed languages can be quite complex: Iterating in some statically typed languages can be quite complex:
.. code-block:: cpp ```
const char* strings = new const char*[50]; const char* strings = new const char*[50];
[..] [..]
@ -385,51 +388,51 @@ Iterating in some statically typed languages can be quite complex:
std::cout << *it << std::endl; std::cout << *it << std::endl;
} }
```
This is usually greatly simplified in dynamically typed languages: This is usually greatly simplified in dynamically typed languages:
:: ```
for s in strings: for s in strings:
print(s) print(s)
```
Container datatypes (arrays and dictionaries) are iterable. Dictionaries Container datatypes (arrays and dictionaries) are iterable. Dictionaries
allow iterating the keys: allow iterating the keys:
:: ```
for key in dict: for key in dict:
print(key, " -> ", dict[key]) print(key, " -> ", dict[key])
```
Iterating with indices is also possible: Iterating with indices is also possible:
:: ```
for i in range(strings.size()): for i in range(strings.size()):
print(strings[i]) print(strings[i])
```
The range() function can take 3 arguments: The range() function can take 3 arguments:
:: ```
range(n) # Will go from 0 to n-1. range(n) # Will go from 0 to n-1.
range(b, n) # Will go from b to n-1. range(b, n) # Will go from b to n-1.
range(b, n, s) # Will go from b to n-1, in steps of s. range(b, n, s) # Will go from b to n-1, in steps of s.
```
Some statically typed programming language examples: Some statically typed programming language examples:
.. code-block:: cpp ```
for (int i = 0; i < 10; i++) {} for (int i = 0; i < 10; i++) {}
for (int i = 5; i < 10; i++) {} for (int i = 5; i < 10; i++) {}
for (int i = 5; i < 10; i += 2) {} for (int i = 5; i < 10; i += 2) {}
```
Translate to: Translate to:
:: ```
for i in range(10): for i in range(10):
pass pass
@ -438,32 +441,33 @@ Translate to:
for i in range(5, 10, 2): for i in range(5, 10, 2):
pass pass
```
And backwards looping is done through a negative counter: And backwards looping is done through a negative counter:
:: ```
for (int i = 10; i > 0; i--) {} for (int i = 10; i > 0; i--) {}
```
Becomes: Becomes:
:: ```
for i in range(10, 0, -1): for i in range(10, 0, -1):
pass pass
```
While While
----- -----
while() loops are the same everywhere: while() loops are the same everywhere:
:: ```
var i = 0 var i = 0
while i < strings.size(): while i < strings.size():
print(strings[i]) print(strings[i])
i += 1 i += 1
```
Custom iterators Custom iterators
---------------- ----------------
@ -471,8 +475,7 @@ You can create custom iterators in case the default ones don't quite meet your
needs by overriding the Variant class's `iter_init`, `iter_next`, and `iter_get` needs by overriding the Variant class's `iter_init`, `iter_next`, and `iter_get`
functions in your script. An example implementation of a forward iterator follows: functions in your script. An example implementation of a forward iterator follows:
:: ```
class ForwardIterator: class ForwardIterator:
var start var start
var current var current
@ -498,14 +501,15 @@ functions in your script. An example implementation of a forward iterator follow
func _iter_get(arg): func _iter_get(arg):
return current return current
```
And it can be used like any other iterator: And it can be used like any other iterator:
:: ```
var itr = ForwardIterator.new(0, 6, 2) var itr = ForwardIterator.new(0, 6, 2)
for i in itr: for i in itr:
print(i) # Will print 0, 2, and 4. print(i) # Will print 0, 2, and 4.
```
Make sure to reset the state of the iterator in `iter_init`, otherwise nested Make sure to reset the state of the iterator in `iter_init`, otherwise nested
for-loops that use custom iterators will not work as expected. for-loops that use custom iterators will not work as expected.
@ -522,12 +526,12 @@ As an example, imagine a situation where a big rock is falling down a
tunnel, smashing everything on its way. The code for the rock, in a tunnel, smashing everything on its way. The code for the rock, in a
statically typed language would be something like: statically typed language would be something like:
.. code-block:: cpp ```
void BigRollingRock::on_object_hit(Smashable *entity) { void BigRollingRock::on_object_hit(Smashable *entity) {
entity->smash(); entity->smash();
} }
```
This way, everything that can be smashed by a rock would have to This way, everything that can be smashed by a rock would have to
inherit Smashable. If a character, enemy, piece of furniture, small rock inherit Smashable. If a character, enemy, piece of furniture, small rock
@ -541,10 +545,10 @@ With dynamically typed languages, this is not a problem. Duck typing
makes sure you only have to define a `smash()` function where required makes sure you only have to define a `smash()` function where required
and that's it. No need to consider inheritance, base classes, etc. and that's it. No need to consider inheritance, base classes, etc.
:: ```
func _on_object_hit(object): func _on_object_hit(object):
object.smash() object.smash()
```
And that's it. If the object that hit the big rock has a smash() method, And that's it. If the object that hit the big rock has a smash() method,
it will be called. No need for inheritance or polymorphism. Dynamically it will be called. No need for inheritance or polymorphism. Dynamically
@ -566,11 +570,11 @@ Some dynamically typed languages simply ignore a method call when it
doesn't exist, but GDScript is stricter, so checking if the function doesn't exist, but GDScript is stricter, so checking if the function
exists is desirable: exists is desirable:
:: ```
func _on_object_hit(object): func _on_object_hit(object):
if object.has_method("smash"): if object.has_method("smash"):
object.smash() object.smash()
```
Then, simply define that method and anything the rock touches can be Then, simply define that method and anything the rock touches can be
smashed. smashed.

File diff suppressed because it is too large Load Diff

View File

@ -9,11 +9,13 @@ Introduction to exports
In Godot, class members can be exported. This means their value gets saved along In Godot, class members can be exported. This means their value gets saved along
with the resource (such as the `scene`) they're with the resource (such as the `scene`) they're
attached to. They will also be available for editing in the property editor. attached to. They will also be available for editing in the property editor.
Exporting is done by using the `export` keyword:: Exporting is done by using the `export` keyword:
```
extends Button extends Button
export var number = 5 # Value will be saved and visible in the property editor. export var number = 5 # Value will be saved and visible in the property editor.
```
An exported variable must be initialized to a constant expression or have an An exported variable must be initialized to a constant expression or have an
export hint in the form of an argument to the `export` keyword (see the export hint in the form of an argument to the `export` keyword (see the
@ -36,8 +38,7 @@ Note:
Examples Examples
-------- --------
:: ```
# If the exported value assigns a constant or constant expression, # If the exported value assigns a constant or constant expression,
# the type will be inferred and used in the editor. # the type will be inferred and used in the editor.
@ -136,6 +137,7 @@ Examples
export(AnimationNode) var resource export(AnimationNode) var resource
# The drop-down menu will be limited to AnimationNode and all # The drop-down menu will be limited to AnimationNode and all
# its inherited classes. # its inherited classes.
```
It must be noted that even if the script is not being run while in the It must be noted that even if the script is not being run while in the
editor, the exported properties are still editable. This can be used editor, the exported properties are still editable. This can be used
@ -146,22 +148,26 @@ Exporting bit flags
Integers used as bit flags can store multiple `true`/`false` (boolean) Integers used as bit flags can store multiple `true`/`false` (boolean)
values in one property. By using the export hint `int, FLAGS, ...`, they values in one property. By using the export hint `int, FLAGS, ...`, they
can be set from the editor:: can be set from the editor:
```
# Set any of the given flags from the editor. # Set any of the given flags from the editor.
export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0 export(int, FLAGS, "Fire", "Water", "Earth", "Wind") var spell_elements = 0
```
You must provide a string description for each flag. In this example, `Fire` You must provide a string description for each flag. In this example, `Fire`
has value 1, `Water` has value 2, `Earth` has value 4 and `Wind` has value 1, `Water` has value 2, `Earth` has value 4 and `Wind`
corresponds to value 8. Usually, constants should be defined accordingly (e.g. corresponds to value 8. Usually, constants should be defined accordingly (e.g.
`const ELEMENT_WIND = 8` and so on). `const ELEMENT_WIND = 8` and so on).
Export hints are also provided for the physics and render layers defined in the project settings:: Export hints are also provided for the physics and render layers defined in the project settings:
```
export(int, LAYERS_2D_PHYSICS) var layers_2d_physics export(int, LAYERS_2D_PHYSICS) var layers_2d_physics
export(int, LAYERS_2D_RENDER) var layers_2d_render export(int, LAYERS_2D_RENDER) var layers_2d_render
export(int, LAYERS_3D_PHYSICS) var layers_3d_physics export(int, LAYERS_3D_PHYSICS) var layers_3d_physics
export(int, LAYERS_3D_RENDER) var layers_3d_render export(int, LAYERS_3D_RENDER) var layers_3d_render
```
Using bit flags requires some understanding of bitwise operations. Using bit flags requires some understanding of bitwise operations.
If in doubt, use boolean variables instead. If in doubt, use boolean variables instead.
@ -175,8 +181,7 @@ If the exported array specifies a type which inherits from Resource, the array
values can be set in the inspector by dragging and dropping multiple files values can be set in the inspector by dragging and dropping multiple files
from the FileSystem dock at once. from the FileSystem dock at once.
:: ```
# Default value must be a constant expression. # Default value must be a constant expression.
export var a = [1, 2, 3] export var a = [1, 2, 3]
@ -207,6 +212,7 @@ from the FileSystem dock at once.
# be exported. # be exported.
var c = [a, 2, 3] var c = [a, 2, 3]
```
Setting exported variables from a tool script Setting exported variables from a tool script
--------------------------------------------- ---------------------------------------------
@ -245,8 +251,7 @@ Properties
To understand how to better use the sections below, you should understand To understand how to better use the sections below, you should understand
how to make properties with advanced exports. how to make properties with advanced exports.
:: ```
func _get_property_list(): func _get_property_list():
var properties = [] var properties = []
# Same as "export(int) var my_property" # Same as "export(int) var my_property"
@ -255,6 +260,7 @@ how to make properties with advanced exports.
type = TYPE_INT type = TYPE_INT
}) })
return properties return properties
```
* The `get_property_list()` function gets called by the inspector. You * The `get_property_list()` function gets called by the inspector. You
can override it for more advanced exports. You must return an `Array` can override it for more advanced exports. You must return an `Array`
@ -276,8 +282,7 @@ property or else you may need to override the
`set()` and `set()` and
`get()` methods. Attaching `get()` methods. Attaching
a variable to to a property also gives you the ability to give it a default state. a variable to to a property also gives you the ability to give it a default state.
:: ```
# This variable is determined by the function below. # This variable is determined by the function below.
# This variable acts just like a regular gdscript export. # This variable acts just like a regular gdscript export.
@ -291,6 +296,7 @@ a variable to to a property also gives you the ability to give it a default stat
type = TYPE_INT type = TYPE_INT
}) })
return properties return properties
```
Adding default values for properties Adding default values for properties
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -301,8 +307,7 @@ To define default values for advanced exports, you need to override the `propert
* The `property_get_revert()` method takes the name of a property and must return the default value for that property. * The `property_get_revert()` method takes the name of a property and must return the default value for that property.
:: ```
func _get_property_list(): func _get_property_list():
var properties = [] var properties = []
properties.append({ properties.append({
@ -319,6 +324,7 @@ To define default values for advanced exports, you need to override the `propert
func property_get_revert(property): func property_get_revert(property):
if property == "my_property": if property == "my_property":
return 5 return 5
```
Adding script categories Adding script categories
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~
@ -326,8 +332,7 @@ Adding script categories
For better visual distinguishing of properties, a special script category can be For better visual distinguishing of properties, a special script category can be
embedded into the inspector to act as a separator. `Script Variables` is one embedded into the inspector to act as a separator. `Script Variables` is one
example of a built-in category. example of a built-in category.
:: ```
func _get_property_list(): func _get_property_list():
var properties = [] var properties = []
properties.append({ properties.append({
@ -342,6 +347,7 @@ example of a built-in category.
type = TYPE_BOOL type = TYPE_BOOL
}) })
return properties return properties
```
* `name` is the name of a category to be added to the inspector; * `name` is the name of a category to be added to the inspector;
@ -356,8 +362,7 @@ Grouping properties
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
A list of properties with similar names can be grouped. A list of properties with similar names can be grouped.
:: ```
func _get_property_list(): func _get_property_list():
var properties = [] var properties = []
properties.append({ properties.append({
@ -380,6 +385,7 @@ A list of properties with similar names can be grouped.
type = TYPE_COLOR type = TYPE_COLOR
}) })
return properties return properties
```
* `name` is the name of a group which is going to be displayed as collapsible * `name` is the name of a group which is going to be displayed as collapsible
list of properties; list of properties;

View File

@ -21,8 +21,7 @@ Usage in GDScript
Examine this concrete GDScript example: Examine this concrete GDScript example:
:: ```
# Define a format string with placeholder '%s' # Define a format string with placeholder '%s'
var format_string = "We're waiting for %s." var format_string = "We're waiting for %s."
@ -31,6 +30,7 @@ Examine this concrete GDScript example:
print(actual_string) print(actual_string)
# Output: "We're waiting for Godot." # Output: "We're waiting for Godot."
```
Placeholders always start with a `%`, but the next character or characters, Placeholders always start with a `%`, but the next character or characters,
the *format specifier*, determines how the given value is converted to a the *format specifier*, determines how the given value is converted to a
@ -52,8 +52,7 @@ matters when the index or mixed style of Array is used.
A quick example in GDScript: A quick example in GDScript:
:: ```
# Define a format string # Define a format string
var format_string = "We're waiting for {str}" var format_string = "We're waiting for {str}"
@ -62,6 +61,7 @@ A quick example in GDScript:
print(actual_string) print(actual_string)
# Output: "We're waiting for Godot" # Output: "We're waiting for Godot"
```
There are other `format specifiers`, but they are only applicable when using There are other `format specifiers`, but they are only applicable when using
the `%` operator. the `%` operator.
@ -74,13 +74,13 @@ Format strings may contain multiple placeholders. In such a case, the values
are handed in the form of an array, one value per placeholder (unless using a are handed in the form of an array, one value per placeholder (unless using a
format specifier with `*`, see `dynamic padding`): format specifier with `*`, see `dynamic padding`):
:: ```
var format_string = "%s was reluctant to learn %s, but now he enjoys it." var format_string = "%s was reluctant to learn %s, but now he enjoys it."
var actual_string = format_string % ["Estragon", "GDScript"] var actual_string = format_string % ["Estragon", "GDScript"]
print(actual_string) print(actual_string)
# Output: "Estragon was reluctant to learn GDScript, but now he enjoys it." # Output: "Estragon was reluctant to learn GDScript, but now he enjoys it."
```
Note the values are inserted in order. Remember all placeholders must be Note the values are inserted in order. Remember all placeholders must be
replaced at once, so there must be an appropriate number of values. replaced at once, so there must be an appropriate number of values.
@ -158,40 +158,40 @@ used.
To pad a string to a minimum length, add an integer to the specifier: To pad a string to a minimum length, add an integer to the specifier:
:: ```
print("%10d" % 12345) print("%10d" % 12345)
# output: " 12345" # output: " 12345"
# 5 leading spaces for a total length of 10 # 5 leading spaces for a total length of 10
```
If the integer starts with `0`, integral values are padded with zeroes If the integer starts with `0`, integral values are padded with zeroes
instead of white space: instead of white space:
:: ```
print("%010d" % 12345) print("%010d" % 12345)
# output: "0000012345" # output: "0000012345"
```
Precision can be specified for real numbers by adding a `.` (*dot*) with an Precision can be specified for real numbers by adding a `.` (*dot*) with an
integer following it. With no integer after `.`, a precision of 0 is used, integer following it. With no integer after `.`, a precision of 0 is used,
rounding to integral value. The integer to use for padding must appear before rounding to integral value. The integer to use for padding must appear before
the dot. the dot.
:: ```
# Pad to minimum length of 10, round to 3 decimal places # Pad to minimum length of 10, round to 3 decimal places
print("%10.3f" % 10000.5555) print("%10.3f" % 10000.5555)
# Output: " 10000.556" # Output: " 10000.556"
# 1 leading space # 1 leading space
```
The `-` character will cause padding to the right rather than the left, The `-` character will cause padding to the right rather than the left,
useful for right text alignment: useful for right text alignment:
:: ```
print("%-10d" % 12345678) print("%-10d" % 12345678)
# Output: "12345678 " # Output: "12345678 "
# 2 trailing spaces # 2 trailing spaces
```
Dynamic padding Dynamic padding
@ -202,21 +202,21 @@ without modifying the format string. It is used in place of an integer in the
format specifier. The values for padding and precision are then passed when format specifier. The values for padding and precision are then passed when
formatting: formatting:
:: ```
var format_string = "%*.*f" var format_string = "%*.*f"
# Pad to length of 7, round to 3 decimal places: # Pad to length of 7, round to 3 decimal places:
print(format_string % [7, 3, 8.8888]) print(format_string % [7, 3, 8.8888])
# Output: " 8.889" # Output: " 8.889"
# 2 leading spaces # 2 leading spaces
```
It is still possible to pad with zeroes in integer placeholders by adding `0` It is still possible to pad with zeroes in integer placeholders by adding `0`
before `*`: before `*`:
:: ```
print("%0*d" % [2, 3]) print("%0*d" % [2, 3])
# Output: "03" # Output: "03"
```
Escape sequence Escape sequence
@ -225,11 +225,11 @@ Escape sequence
To insert a literal `%` character into a format string, it must be escaped to To insert a literal `%` character into a format string, it must be escaped to
avoid reading it as a placeholder. This is done by doubling the character: avoid reading it as a placeholder. This is done by doubling the character:
:: ```
var health = 56 var health = 56
print("Remaining health: %d%%" % health) print("Remaining health: %d%%" % health)
# Output: "Remaining health: 56%" # Output: "Remaining health: 56%"
```
Format method examples Format method examples

View File

@ -25,8 +25,7 @@ Note:
Here is a complete class example based on these guidelines: Here is a complete class example based on these guidelines:
:: ```
class_name StateMachine class_name StateMachine
extends Node extends Node
# Hierarchical State machine for the player. # Hierarchical State machine for the player.
@ -88,6 +87,7 @@ Here is a complete class example based on these guidelines:
func _on_state_changed(previous, new): func _on_state_changed(previous, new):
print("state changed") print("state changed")
emit_signal("state_changed") emit_signal("state_changed")
```
.. _formatting: .. _formatting:
@ -109,47 +109,46 @@ Each indent level should be one greater than the block containing it.
**Good**: **Good**:
:: ```
for i in range(10): for i in range(10):
print("hello") print("hello")
```
**Bad**: **Bad**:
:: ```
for i in range(10): for i in range(10):
print("hello") print("hello")
for i in range(10): for i in range(10):
print("hello") print("hello")
```
Use 2 indent levels to distinguish continuation lines from Use 2 indent levels to distinguish continuation lines from
regular code blocks. regular code blocks.
**Good**: **Good**:
:: ```
effect.interpolate_property(sprite, "transform/scale", effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3, sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT) Tween.TRANS_QUAD, Tween.EASE_OUT)
```
**Bad**: **Bad**:
:: ```
effect.interpolate_property(sprite, "transform/scale", effect.interpolate_property(sprite, "transform/scale",
sprite.get_scale(), Vector2(2.0, 2.0), 0.3, sprite.get_scale(), Vector2(2.0, 2.0), 0.3,
Tween.TRANS_QUAD, Tween.EASE_OUT) Tween.TRANS_QUAD, Tween.EASE_OUT)
```
Exceptions to this rule are arrays, dictionaries, and enums. Use a single Exceptions to this rule are arrays, dictionaries, and enums. Use a single
indentation level to distinguish continuation lines: indentation level to distinguish continuation lines:
**Good**: **Good**:
:: ```
var party = [ var party = [
"Godot", "Godot",
"Godette", "Godette",
@ -168,11 +167,11 @@ indentation level to distinguish continuation lines:
TILE_SPIKE, TILE_SPIKE,
TILE_TELEPORT, TILE_TELEPORT,
} }
```
**Bad**: **Bad**:
:: ```
var party = [ var party = [
"Godot", "Godot",
"Godette", "Godette",
@ -191,6 +190,7 @@ indentation level to distinguish continuation lines:
TILE_SPIKE, TILE_SPIKE,
TILE_TELEPORT, TILE_TELEPORT,
} }
```
Trailing comma Trailing comma
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
@ -201,47 +201,46 @@ line doesn't need to be modified when adding new elements.
**Good**: **Good**:
:: ```
enum Tiles { enum Tiles {
TILE_BRICK, TILE_BRICK,
TILE_FLOOR, TILE_FLOOR,
TILE_SPIKE, TILE_SPIKE,
TILE_TELEPORT, TILE_TELEPORT,
} }
```
**Bad**: **Bad**:
:: ```
enum Tiles { enum Tiles {
TILE_BRICK, TILE_BRICK,
TILE_FLOOR, TILE_FLOOR,
TILE_SPIKE, TILE_SPIKE,
TILE_TELEPORT TILE_TELEPORT
} }
```
Trailing commas are unnecessary in single-line lists, so don't add them in this case. Trailing commas are unnecessary in single-line lists, so don't add them in this case.
**Good**: **Good**:
:: ```
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT} enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT}
```
**Bad**: **Bad**:
:: ```
enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,} enum Tiles {TILE_BRICK, TILE_FLOOR, TILE_SPIKE, TILE_TELEPORT,}
```
Blank lines Blank lines
~~~~~~~~~~~ ~~~~~~~~~~~
Surround functions and class definitions with two blank lines: Surround functions and class definitions with two blank lines:
:: ```
func heal(amount): func heal(amount):
health += amount health += amount
health = min(health, max_health) health = min(health, max_health)
@ -252,6 +251,7 @@ Surround functions and class definitions with two blank lines:
health -= amount health -= amount
health = max(0, health) health = max(0, health)
emit_signal("health_changed", health) emit_signal("health_changed", health)
```
Use one blank line inside functions to separate logical sections. Use one blank line inside functions to separate logical sections.
@ -276,27 +276,27 @@ not even with a single line conditional statement.
**Good**: **Good**:
:: ```
if position.x > width: if position.x > width:
position.x = 0 position.x = 0
if flag: if flag:
print("flagged") print("flagged")
```
**Bad**: **Bad**:
:: ```
if position.x > width: position.x = 0 if position.x > width: position.x = 0
if flag: print("flagged") if flag: print("flagged")
```
The only exception to that rule is the ternary operator: The only exception to that rule is the ternary operator:
:: ```
next_state = "fall" if not is_on_floor() else "idle" next_state = "fall" if not is_on_floor() else "idle"
```
Format multiline statements for readability Format multiline statements for readability
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -317,8 +317,7 @@ end of the previous line.
**Good**: **Good**:
:: ```
var angle_degrees = 135 var angle_degrees = 135
var quadrant = ( var quadrant = (
"northeast" if angle_degrees <= 90 "northeast" if angle_degrees <= 90
@ -333,17 +332,18 @@ end of the previous line.
and position.y > 300 and position.y < 400 and position.y > 300 and position.y < 400
): ):
pass pass
```
**Bad**: **Bad**:
:: ```
var angle_degrees = 135 var angle_degrees = 135
var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest" var quadrant = "northeast" if angle_degrees <= 90 else "southeast" if angle_degrees <= 180 else "southwest" if angle_degrees <= 270 else "northwest"
var position = Vector2(250, 350) var position = Vector2(250, 350)
if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400: if position.x > 200 and position.x < 400 and position.y > 300 and position.y < 400:
pass pass
```
Avoid unnecessary parentheses Avoid unnecessary parentheses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -354,17 +354,17 @@ they only reduce readability.
**Good**: **Good**:
:: ```
if is_colliding(): if is_colliding():
queue_free() queue_free()
```
**Bad**: **Bad**:
:: ```
if (is_colliding()): if (is_colliding()):
queue_free() queue_free()
```
Boolean operators Boolean operators
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -379,17 +379,17 @@ This can make long expressions easier to read.
**Good**: **Good**:
:: ```
if (foo and bar) or baz: if (foo and bar) or baz:
print("condition is true") print("condition is true")
```
**Bad**: **Bad**:
:: ```
if foo && bar || baz: if foo && bar || baz:
print("condition is true") print("condition is true")
```
Comment spacing Comment spacing
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
@ -399,17 +399,17 @@ This helps differentiate text comments from disabled code.
**Good**: **Good**:
:: ```
# This is a comment. # This is a comment.
#print("This is disabled code") #print("This is disabled code")
```
**Bad**: **Bad**:
:: ```
#This is a comment. #This is a comment.
# print("This is disabled code") # print("This is disabled code")
```
Note: Note:
@ -426,31 +426,31 @@ in dictionary references and function calls.
**Good**: **Good**:
:: ```
position.x = 5 position.x = 5
position.y = target_position.y + 10 position.y = target_position.y + 10
dict["key"] = 5 dict["key"] = 5
my_array = [4, 5, 6] my_array = [4, 5, 6]
print("foo") print("foo")
```
**Bad**: **Bad**:
:: ```
position.x=5 position.x=5
position.y = mpos.y+10 position.y = mpos.y+10
dict ["key"] = 5 dict ["key"] = 5
myarray = [4,5,6] myarray = [4,5,6]
print ("foo") print ("foo")
```
Don't use spaces to align expressions vertically: Don't use spaces to align expressions vertically:
:: ```
x = 100 x = 100
y = 100 y = 100
velocity = 500 velocity = 500
```
Quotes Quotes
~~~~~~ ~~~~~~
@ -458,8 +458,7 @@ Quotes
Use double quotes unless single quotes make it possible to escape fewer Use double quotes unless single quotes make it possible to escape fewer
characters in a given string. See the examples below: characters in a given string. See the examples below:
:: ```
# Normal string. # Normal string.
print("hello world") print("hello world")
@ -471,6 +470,7 @@ characters in a given string. See the examples below:
# Both quote styles would require 2 escapes; prefer double quotes if it's a tie. # Both quote styles would require 2 escapes; prefer double quotes if it's a tie.
print("'hello' \"world\"") print("'hello' \"world\"")
```
Numbers Numbers
~~~~~~~ ~~~~~~~
@ -479,45 +479,51 @@ Don't omit the leading or trailing zero in floating-point numbers. Otherwise,
this makes them less readable and harder to distinguish from integers at a this makes them less readable and harder to distinguish from integers at a
glance. glance.
**Good**:: **Good**:
```
var float_number = 0.234 var float_number = 0.234
var other_float_number = 13.0 var other_float_number = 13.0
```
**Bad**:: **Bad**:
```
var float_number = .234 var float_number = .234
var other_float_number = 13. var other_float_number = 13.
```
Use lowercase for letters in hexadecimal numbers, as their lower height makes Use lowercase for letters in hexadecimal numbers, as their lower height makes
the number easier to read. the number easier to read.
**Good**:: **Good**:
```
var hex_number = 0xfb8c0b var hex_number = 0xfb8c0b
```
**Bad**:: **Bad**:
```
var hex_number = 0xFB8C0B var hex_number = 0xFB8C0B
```
Take advantage of GDScript's underscores in literals to make large numbers more Take advantage of GDScript's underscores in literals to make large numbers more
readable. readable.
**Good**:: **Good**:
```
var large_number = 1_234_567_890 var large_number = 1_234_567_890
var large_hex_number = 0xffff_f8f8_0000 var large_hex_number = 0xffff_f8f8_0000
var large_bin_number = 0b1101_0010_1010 var large_bin_number = 0b1101_0010_1010
# Numbers lower than 1000000 generally don't need separators. # Numbers lower than 1000000 generally don't need separators.
var small_number = 12345 var small_number = 12345
```
**Bad**:: **Bad**:
```
var large_number = 1234567890 var large_number = 1234567890
var large_hex_number = 0xfffff8f80000 var large_hex_number = 0xfffff8f80000
var large_bin_number = 0b110100101010 var large_bin_number = 0b110100101010
# Numbers lower than 1000000 generally don't need separators. # Numbers lower than 1000000 generally don't need separators.
var small_number = 12_345 var small_number = 12_345
```
.. _naming_conventions: .. _naming_conventions:
@ -532,17 +538,18 @@ File names
~~~~~~~~~~ ~~~~~~~~~~
Use snake_case for file names. For named classes, convert the PascalCase class Use snake_case for file names. For named classes, convert the PascalCase class
name to snake_case:: name to snake_case:
```
# This file should be saved as `weapon.gd`. # This file should be saved as `weapon.gd`.
class_name Weapon class_name Weapon
extends Node extends Node
```
:: ```
# This file should be saved as `yaml_parser.gd`. # This file should be saved as `yaml_parser.gd`.
class_name YAMLParser class_name YAMLParser
extends Object extends Object
```
This is consistent with how C++ files are named in Godot's source code. This This is consistent with how C++ files are named in Godot's source code. This
also avoids case sensitivity issues that can crop up when exporting a project also avoids case sensitivity issues that can crop up when exporting a project
@ -553,43 +560,43 @@ Classes and nodes
Use PascalCase for class and node names: Use PascalCase for class and node names:
:: ```
extends KinematicBody extends KinematicBody
```
Also use PascalCase when loading a class into a constant or a variable: Also use PascalCase when loading a class into a constant or a variable:
:: ```
const Weapon = preload("res://weapon.gd") const Weapon = preload("res://weapon.gd")
```
Functions and variables Functions and variables
~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~
Use snake\_case to name functions and variables: Use snake\_case to name functions and variables:
:: ```
var particle_effect var particle_effect
func load_level(): func load_level():
```
Prepend a single underscore (\_) to virtual methods functions the user must Prepend a single underscore (\_) to virtual methods functions the user must
override, private functions, and private variables: override, private functions, and private variables:
:: ```
var _counter = 0 var _counter = 0
func _recalculate_path(): func _recalculate_path():
```
Signals Signals
~~~~~~~ ~~~~~~~
Use the past tense to name signals: Use the past tense to name signals:
:: ```
signal door_opened signal door_opened
signal score_changed signal score_changed
```
Constants and enums Constants and enums
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~
@ -597,21 +604,21 @@ Constants and enums
Write constants with CONSTANT\_CASE, that is to say in all caps with an Write constants with CONSTANT\_CASE, that is to say in all caps with an
underscore (\_) to separate words: underscore (\_) to separate words:
:: ```
const MAX_SPEED = 200 const MAX_SPEED = 200
```
Use PascalCase for enum *names* and CONSTANT\_CASE for their members, as they Use PascalCase for enum *names* and CONSTANT\_CASE for their members, as they
are constants: are constants:
:: ```
enum Element { enum Element {
EARTH, EARTH,
WATER, WATER,
AIR, AIR,
FIRE, FIRE,
} }
```
Code order Code order
@ -622,8 +629,7 @@ This first section focuses on code order. For formatting, see
We suggest to organize GDScript code this way: We suggest to organize GDScript code this way:
:: ```
01. tool 01. tool
02. class_name 02. class_name
03. extends 03. extends
@ -642,6 +648,7 @@ We suggest to organize GDScript code this way:
14. remaining built-in virtual methods 14. remaining built-in virtual methods
15. public methods 15. public methods
16. private methods 16. private methods
```
We optimized the order to make it easy to read the code from top to bottom, to We optimized the order to make it easy to read the code from top to bottom, to
help developers reading the code for the first time understand how it works, and help developers reading the code for the first time understand how it works, and
@ -672,12 +679,12 @@ Following that, you should have the class's optional docstring as comments. You
can use that to explain the role of your class to your teammates, how it works, can use that to explain the role of your class to your teammates, how it works,
and how other developers should use it, for example. and how other developers should use it, for example.
:: ```
class_name MyNode class_name MyNode
extends Node extends Node
# A brief description of the class's role and functionality. # A brief description of the class's role and functionality.
# Longer description. # Longer description.
```
Signals and properties Signals and properties
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -691,8 +698,7 @@ properties.
Then, write constants, exported variables, public, private, and onready Then, write constants, exported variables, public, private, and onready
variables, in that order. variables, in that order.
:: ```
signal spawn_player(position) signal spawn_player(position)
enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN} enum Jobs {KNIGHT, WIZARD, ROGUE, HEALER, SHAMAN}
@ -709,6 +715,7 @@ variables, in that order.
onready var sword = get_node("Sword") onready var sword = get_node("Sword")
onready var gun = get_node("Gun") onready var gun = get_node("Gun")
```
Note: Note:
@ -752,8 +759,7 @@ interactions with the game engine.
The rest of the class's interface, public and private methods, come after that, The rest of the class's interface, public and private methods, come after that,
in that order. in that order.
:: ```
func _init(): func _init():
add_to_group("state_machine") add_to_group("state_machine")
@ -783,6 +789,7 @@ in that order.
func _on_state_changed(previous, new): func _on_state_changed(previous, new):
print("state changed") print("state changed")
emit_signal("state_changed") emit_signal("state_changed")
```
Static typing Static typing
@ -795,24 +802,24 @@ Declared types
To declare a variable's type, use `<variable>: <type )`: To declare a variable's type, use `<variable>: <type )`:
:: ```
var health: int = 0 var health: int = 0
```
To declare the return type of a function, use `-> <type )`: To declare the return type of a function, use `-> <type )`:
:: ```
func heal(amount: int) -> void: func heal(amount: int) -> void:
```
Inferred types Inferred types
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
In most cases you can let the compiler infer the type, using `:=`: In most cases you can let the compiler infer the type, using `:=`:
:: ```
var health := 0 # The compiler will use the int type. var health := 0 # The compiler will use the int type.
```
However, in a few cases when context is missing, the compiler falls back to However, in a few cases when context is missing, the compiler falls back to
the function's return type. For example, `get_node()` cannot infer a type the function's return type. For example, `get_node()` cannot infer a type
@ -821,26 +828,26 @@ should set the type explicitly.
**Good**: **Good**:
:: ```
onready var health_bar: ProgressBar = get_node("UI/LifeBar") onready var health_bar: ProgressBar = get_node("UI/LifeBar")
```
Alternatively, you can use the `as` keyword to cast the return type, and Alternatively, you can use the `as` keyword to cast the return type, and
that type will be used to infer the type of the var. that type will be used to infer the type of the var.
.. rst-class:: code-example-good .. rst-class: code-example-good
::
```
onready var health_bar := get_node("UI/LifeBar") as ProgressBar onready var health_bar := get_node("UI/LifeBar") as ProgressBar
# health_bar will be typed as ProgressBar # health_bar will be typed as ProgressBar
```
This option is also considered more `type-safe( doc_gdscript_static_typing_safe_lines )` than the first. This option is also considered more `type-safe( doc_gdscript_static_typing_safe_lines )` than the first.
**Bad**: **Bad**:
:: ```
# The compiler can't infer the exact type and will use Node # The compiler can't infer the exact type and will use Node
# instead of ProgressBar. # instead of ProgressBar.
onready var health_bar := get_node("UI/LifeBar") onready var health_bar := get_node("UI/LifeBar")
```

View File

@ -32,8 +32,7 @@ node, then an `Inventory`. To add items to the inventory, the people
who work with your code should always pass an `Item` to the who work with your code should always pass an `Item` to the
`Inventory.add` method. With types, you can enforce this: `Inventory.add` method. With types, you can enforce this:
:: ```
# In 'Item.gd'. # In 'Item.gd'.
class_name Item class_name Item
# In 'Inventory.gd'. # In 'Inventory.gd'.
@ -46,6 +45,7 @@ who work with your code should always pass an `Item` to the
item = _instance_item_from_db(reference) item = _instance_item_from_db(reference)
item.amount += amount item.amount += amount
```
Another significant advantage of typed GDScript is the new **warning Another significant advantage of typed GDScript is the new **warning
system**. From version 3.1, Godot gives you warnings about your code as system**. From version 3.1, Godot gives you warnings about your code as
@ -89,19 +89,19 @@ To define the type of a variable or a constant, write a colon after the
variable's name, followed by its type. E.g. `var health: int`. This variable's name, followed by its type. E.g. `var health: int`. This
forces the variable's type to always stay the same: forces the variable's type to always stay the same:
:: ```
var damage: float = 10.5 var damage: float = 10.5
const MOVE_SPEED: float = 50.0 const MOVE_SPEED: float = 50.0
```
Godot will try to infer types if you write a colon, but you omit the Godot will try to infer types if you write a colon, but you omit the
type: type:
:: ```
var life_points := 4 var life_points := 4
var damage := 10.5 var damage := 10.5
var motion := Vector2() var motion := Vector2()
```
Currently you can use three types of… types: Currently you can use three types of… types:
@ -123,26 +123,26 @@ You can use any class, including your custom classes, as types. There
are two ways to use them in scripts. The first method is to preload the are two ways to use them in scripts. The first method is to preload the
script you want to use as a type in a constant: script you want to use as a type in a constant:
:: ```
const Rifle = preload("res://player/weapons/Rifle.gd") const Rifle = preload("res://player/weapons/Rifle.gd")
var my_rifle: Rifle var my_rifle: Rifle
```
The second method is to use the `name` keyword when you create. The second method is to use the `name` keyword when you create.
For the example above, your Rifle.gd would look like this: For the example above, your Rifle.gd would look like this:
:: ```
extends Node2D extends Node2D
class_name Rifle class_name Rifle
```
If you use `name`, Godot registers the Rifle type globally in If you use `name`, Godot registers the Rifle type globally in
the editor, and you can use it anywhere, without having to preload it the editor, and you can use it anywhere, without having to preload it
into a constant: into a constant:
:: ```
var my_rifle: Rifle var my_rifle: Rifle
```
Variable casting Variable casting
~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~
@ -162,14 +162,14 @@ casting keyword, and using the colon `:` again to force the variable
to use this type. This forces the variable to stick to the to use this type. This forces the variable to stick to the
`PlayerController` type: `PlayerController` type:
:: ```
func _on_body_entered(body: PhysicsBody2D) -> void: func _on_body_entered(body: PhysicsBody2D) -> void:
var player := body as PlayerController var player := body as PlayerController
if not player: if not player:
return return
player.damage() player.damage()
```
As we're dealing with a custom type, if the `body` doesn't extend As we're dealing with a custom type, if the `body` doesn't extend
`PlayerController`, the `player`\ variable will be set to `null`. `PlayerController`, the `player`\ variable will be set to `null`.
@ -221,24 +221,23 @@ Define the return type of a function with the arrow ->
To define the return type of a function, write a dash and a right angle To define the return type of a function, write a dash and a right angle
bracket `- )` after its declaration, followed by the return type: bracket `- )` after its declaration, followed by the return type:
:: ```
func _process(delta: float) -> void: func _process(delta: float) -> void:
pass pass
```
The type `void` means the function does not return anything. You can The type `void` means the function does not return anything. You can
use any type, as with variables: use any type, as with variables:
:: ```
func hit(damage: float) -> bool: func hit(damage: float) -> bool:
health_points -= damage health_points -= damage
return health_points <= 0 return health_points <= 0
```
You can also use your own nodes as return types: You can also use your own nodes as return types:
:: ```
# Inventory.gd # Inventory.gd
# Adds an item to the inventory and returns it. # Adds an item to the inventory and returns it.
@ -249,6 +248,7 @@ You can also use your own nodes as return types:
item.amount += amount item.amount += amount
return item return item
```
Typed or dynamic: stick to one style Typed or dynamic: stick to one style
------------------------------------ ------------------------------------
@ -263,8 +263,7 @@ Typed code takes a little more writing, but you get the benefits we
discussed above. Here's an example of the same, empty script, in a discussed above. Here's an example of the same, empty script, in a
dynamic style: dynamic style:
:: ```
extends Node extends Node
@ -274,11 +273,11 @@ dynamic style:
func _process(delta): func _process(delta):
pass pass
```
And with static typing: And with static typing:
:: ```
extends Node extends Node
@ -288,33 +287,34 @@ And with static typing:
func _process(delta: float) -> void: func _process(delta: float) -> void:
pass pass
```
As you can see, you can also use types with the engine's virtual As you can see, you can also use types with the engine's virtual
methods. Signal callbacks, like any methods, can also use types. Here's methods. Signal callbacks, like any methods, can also use types. Here's
a `body_entered` signal in a dynamic style: a `body_entered` signal in a dynamic style:
:: ```
func _on_Area2D_body_entered(body): func _on_Area2D_body_entered(body):
pass pass
```
And the same callback, with type hints: And the same callback, with type hints:
:: ```
func _on_area_entered(area: CollisionObject2D) -> void: func _on_area_entered(area: CollisionObject2D) -> void:
pass pass
```
You're free to replace, e.g. the `CollisionObject2D`, with your own type, You're free to replace, e.g. the `CollisionObject2D`, with your own type,
to cast parameters automatically: to cast parameters automatically:
:: ```
func _on_area_entered(bullet: Bullet) -> void: func _on_area_entered(bullet: Bullet) -> void:
if not bullet: if not bullet:
return return
take_damage(bullet.damage) take_damage(bullet.damage)
```
The `bullet` variable could hold any `CollisionObject2D` here, but The `bullet` variable could hold any `CollisionObject2D` here, but
we make sure it is our `Bullet`, a node we created for our project. If we make sure it is our `Bullet`, a node we created for our project. If
@ -338,32 +338,31 @@ use type hints. All the examples below **will trigger errors**.
You can't use Enums as types: You can't use Enums as types:
:: ```
enum MoveDirection {UP, DOWN, LEFT, RIGHT} enum MoveDirection {UP, DOWN, LEFT, RIGHT}
var current_direction: MoveDirection var current_direction: MoveDirection
```
You can't specify the type of individual members in an array. This will You can't specify the type of individual members in an array. This will
give you an error: give you an error:
:: ```
var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy] var enemies: Array = [$Goblin: Enemy, $Zombie: Enemy]
```
You can't force the assignment of types in a `for` loop, as each You can't force the assignment of types in a `for` loop, as each
element the `for` keyword loops over already has a different type. So you element the `for` keyword loops over already has a different type. So you
**cannot** write: **cannot** write:
:: ```
var names = ["John", "Marta", "Samantha", "Jimmy"] var names = ["John", "Marta", "Samantha", "Jimmy"]
for name: String in names: for name: String in names:
pass pass
```
Two scripts can't depend on each other in a cyclic fashion: Two scripts can't depend on each other in a cyclic fashion:
:: ```
# Player.gd # Player.gd
extends Area2D extends Area2D
@ -371,9 +370,9 @@ Two scripts can't depend on each other in a cyclic fashion:
var rifle: Rifle var rifle: Rifle
```
:: ```
# Rifle.gd # Rifle.gd
extends Area2D extends Area2D
@ -381,6 +380,7 @@ Two scripts can't depend on each other in a cyclic fashion:
var player: Player var player: Player
```
Summary Summary
------- -------

View File

@ -79,17 +79,17 @@ You can use two shorthands to shorten your code in GDScript. Firstly, putting th
`onready` keyword before a member variable makes it initialize right before `onready` keyword before a member variable makes it initialize right before
the `ready()` callback. the `ready()` callback.
.. code-block:: gdscript ```
onready var sprite = get_node("Sprite") onready var sprite = get_node("Sprite")
```
There is also a short notation for `get_node()`: the dollar sign, "$". You There is also a short notation for `get_node()`: the dollar sign, "$". You
place it before the name or path of the node you want to get. place it before the name or path of the node you want to get.
.. code-block:: gdscript ```
onready var sprite = $Sprite onready var sprite = $Sprite
onready var tween = $ShieldBar/Tween onready var tween = $ShieldBar/Tween
```
Creating nodes Creating nodes
-------------- --------------

View File

@ -233,22 +233,6 @@ Note:
func _init(): func _init():
print(data) print(data)
.. code-tab:: csharp
using System;
using Godot;
public class BotStatsTable : Resource
{
private Godot.Dictionary<String, BotStats> _stats = new Godot.Dictionary<String, BotStats>();
public BotStatsTable()
{
_stats["GodotBot"] = new BotStats(10); // Creates instance with 10 health.
_stats["DifferentBot"] = new BotStats(20); // A different one with 20 health.
GD.Print(_stats);
}
}
``` ```
Instead of just inlining the Dictionary values, one could also, alternatively... Instead of just inlining the Dictionary values, one could also, alternatively...

View File

@ -49,13 +49,13 @@ so we need to nullify the effects of Godot's transformations. We do this by sett
`POSITION` built-in to our desired position. `POSITION` bypasses the built-in transformations `POSITION` built-in to our desired position. `POSITION` bypasses the built-in transformations
and sets the vertex position directly. and sets the vertex position directly.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
void vertex() { void vertex() {
POSITION = vec4(VERTEX, 1.0); POSITION = vec4(VERTEX, 1.0);
} }
```
Even with this vertex shader, the quad keeps disappearing. This is due to frustum Even with this vertex shader, the quad keeps disappearing. This is due to frustum
culling, which is done on the CPU. Frustum culling uses the camera matrix and the culling, which is done on the CPU. Frustum culling uses the camera matrix and the
@ -78,9 +78,9 @@ Depth texture
To read from the depth texture, perform a texture lookup using `texture()` and To read from the depth texture, perform a texture lookup using `texture()` and
the uniform variable `DEPTH_TEXTURE`. the uniform variable `DEPTH_TEXTURE`.
.. code-block:: glsl ```
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x; float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
```
Note: Note:
Similar to accessing the screen texture, accessing the depth texture is only Similar to accessing the screen texture, accessing the depth texture is only
@ -102,25 +102,25 @@ coordinates (NDC). NDC run from `-1` to `1`, similar to clip space coordinates.
Reconstruct the NDC using `SCREEN_UV` for the `x` and `y` axis, and Reconstruct the NDC using `SCREEN_UV` for the `x` and `y` axis, and
the depth value for `z`. the depth value for `z`.
.. code-block:: glsl ```
void fragment() { void fragment() {
float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x; float depth = texture(DEPTH_TEXTURE, SCREEN_UV).x;
vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0; vec3 ndc = vec3(SCREEN_UV, depth) * 2.0 - 1.0;
} }
```
Convert NDC to view space by multiplying the NDC by `INV_PROJECTION_MATRIX`. Convert NDC to view space by multiplying the NDC by `INV_PROJECTION_MATRIX`.
Recall that view space gives positions relative to the camera, so the `z` value will give us Recall that view space gives positions relative to the camera, so the `z` value will give us
the distance to the point. the distance to the point.
.. code-block:: glsl ```
void fragment() { void fragment() {
... ...
vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0); vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
view.xyz /= view.w; view.xyz /= view.w;
float linear_depth = -view.z; float linear_depth = -view.z;
} }
```
Because the camera is facing the negative `z` direction, the position will have a negative `z` value. Because the camera is facing the negative `z` direction, the position will have a negative `z` value.
In order to get a usable depth value, we have to negate `view.z`. In order to get a usable depth value, we have to negate `view.z`.
@ -129,8 +129,7 @@ The world position can be constructed from the depth buffer using the following
that the `CAMERA_MATRIX` is needed to transform the position from view space into world space, so that the `CAMERA_MATRIX` is needed to transform the position from view space into world space, so
it needs to be passed to the fragment shader with a varying. it needs to be passed to the fragment shader with a varying.
.. code-block:: glsl ```
varying mat4 CAMERA; varying mat4 CAMERA;
void vertex() { void vertex() {
@ -142,6 +141,7 @@ it needs to be passed to the fragment shader with a varying.
vec4 world = CAMERA * INV_PROJECTION_MATRIX * vec4(ndc, 1.0); vec4 world = CAMERA * INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
vec3 world_position = world.xyz / world.w; vec3 world_position = world.xyz / world.w;
} }
```
An optimization An optimization
--------------- ---------------
@ -157,8 +157,7 @@ vertices, normals, colors, etc.
Now, attach a script to the MeshInstance and use the following code: Now, attach a script to the MeshInstance and use the following code:
:: ```
extends MeshInstance extends MeshInstance
func _ready(): func _ready():
@ -176,6 +175,7 @@ Now, attach a script to the MeshInstance and use the following code:
# Create mesh from mesh_array: # Create mesh from mesh_array:
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, mesh_array) mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, mesh_array)
```
Note: Note:
The triangle is specified in normalized device coordinates. Recall, NDC run The triangle is specified in normalized device coordinates. Recall, NDC run

View File

@ -59,24 +59,23 @@ Note:
need to create your own uniform in the shader and pass the `Viewport` texture in need to create your own uniform in the shader and pass the `Viewport` texture in
manually, like so: manually, like so:
.. code-block:: glsl ```
// Inside the Shader. // Inside the Shader.
uniform sampler2D ViewportTexture; uniform sampler2D ViewportTexture;
```
And you can pass the texture into the shader from GDScript like so: And you can pass the texture into the shader from GDScript like so:
:: ```
# In GDScript. # In GDScript.
func _ready(): func _ready():
$Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture()) $Sprite.material.set_shader_param("ViewportTexture", $Viewport.get_texture())
```
Copy the following code to your shader. The above code is a single pass edge detection filter, a Copy the following code to your shader. The above code is a single pass edge detection filter, a
`Sobel filter ( https://en.wikipedia.org/wiki/Sobel_operator )`. `Sobel filter ( https://en.wikipedia.org/wiki/Sobel_operator )`.
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
void fragment() { void fragment() {
@ -91,6 +90,7 @@ Copy the following code to your shader. The above code is a single pass edge det
col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz; col += texture(TEXTURE, UV + vec2(SCREEN_PIXEL_SIZE.x, -SCREEN_PIXEL_SIZE.y)).xyz;
COLOR.xyz = col; COLOR.xyz = col;
} }
```
Note: Note:
@ -134,8 +134,7 @@ As an example, you could write a full screen Gaussian blur effect by attaching t
to each of the `ViewportContainers`. The order in which you apply the shaders to each of the `ViewportContainers`. The order in which you apply the shaders
does not matter: does not matter:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
// Blurs the screen in the X-direction. // Blurs the screen in the X-direction.
@ -151,9 +150,9 @@ does not matter:
col += texture(TEXTURE, UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05; col += texture(TEXTURE, UV + vec2(4.0 * -SCREEN_PIXEL_SIZE.x, 0.0)).xyz * 0.05;
COLOR.xyz = col; COLOR.xyz = col;
} }
```
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
// Blurs the screen in the Y-direction. // Blurs the screen in the Y-direction.
@ -169,6 +168,7 @@ does not matter:
col += texture(TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05; col += texture(TEXTURE, UV + vec2(0.0, 4.0 * -SCREEN_PIXEL_SIZE.y)).xyz * 0.05;
COLOR.xyz = col; COLOR.xyz = col;
} }
```
Using the above code, you should end up with a full screen blur effect like below. Using the above code, you should end up with a full screen blur effect like below.

View File

@ -23,20 +23,22 @@ working with shaders, you need to code and think differently from other
programming languages. programming languages.
Suppose you want to update all the pixels in a texture to a given color. In Suppose you want to update all the pixels in a texture to a given color. In
GDScript, your code would use `for` loops:: GDScript, your code would use `for` loops:
```
for x in range(width): for x in range(width):
for y in range(height): for y in range(height):
set_color(x, y, some_color) set_color(x, y, some_color)
```
Your code is already part of a loop in a shader, so the corresponding code would Your code is already part of a loop in a shader, so the corresponding code would
look like this. look like this.
.. code-block:: glsl ```
void fragment() { void fragment() {
COLOR = some_color; COLOR = some_color;
} }
```
Note: Note:
@ -80,9 +82,9 @@ support different render modes, built-in variables, and processing functions.
In Godot, all shaders need to specify their type in the first line, like so: In Godot, all shaders need to specify their type in the first line, like so:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
```
Here are the available types: Here are the available types:
@ -96,10 +98,10 @@ Render modes
Shaders have optional render modes you can specify on the second line, after the Shaders have optional render modes you can specify on the second line, after the
shader type, like so: shader type, like so:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
render_mode unshaded, cull_disabled; render_mode unshaded, cull_disabled;
```
Render modes alter the way Godot applies the shader. For example, the Render modes alter the way Godot applies the shader. For example, the
`unshaded` mode makes the engine skip the built-in light processor function. `unshaded` mode makes the engine skip the built-in light processor function.

View File

@ -36,23 +36,22 @@ Write a custom shader for the leaves
This is a simple example of a shader for leaves: This is a simple example of a shader for leaves:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
render_mode depth_draw_alpha_prepass, cull_disabled, world_vertex_coords; render_mode depth_draw_alpha_prepass, cull_disabled, world_vertex_coords;
```
This is a spatial shader. There is no front/back culling (so leaves can be seen from both sides), and alpha prepass is used, so there are less depth artifacts that result from using transparency (and leaves cast shadow). Finally, for the sway effect, world coordinates are recommended, so the tree can be duplicated, moved, etc. and it will still work together with other trees. This is a spatial shader. There is no front/back culling (so leaves can be seen from both sides), and alpha prepass is used, so there are less depth artifacts that result from using transparency (and leaves cast shadow). Finally, for the sway effect, world coordinates are recommended, so the tree can be duplicated, moved, etc. and it will still work together with other trees.
.. code-block:: glsl ```
uniform sampler2D texture_albedo : hint_albedo; uniform sampler2D texture_albedo : hint_albedo;
uniform vec4 transmission : hint_color; uniform vec4 transmission : hint_color;
```
Here, the texture is read, as well as a transmission color, which is used to add some back-lighting to the leaves, simulating subsurface scattering. Here, the texture is read, as well as a transmission color, which is used to add some back-lighting to the leaves, simulating subsurface scattering.
.. code-block:: glsl ```
uniform float sway_speed = 1.0; uniform float sway_speed = 1.0;
uniform float sway_strength = 0.05; uniform float sway_strength = 0.05;
uniform float sway_phase_len = 8.0; uniform float sway_phase_len = 8.0;
@ -63,14 +62,14 @@ Here, the texture is read, as well as a transmission color, which is used to add
VERTEX.y += sin(VERTEX.y * sway_phase_len + TIME * sway_speed * 1.12412) * strength; VERTEX.y += sin(VERTEX.y * sway_phase_len + TIME * sway_speed * 1.12412) * strength;
VERTEX.z += sin(VERTEX.z * sway_phase_len * 0.9123 + TIME * sway_speed * 1.3123) * strength; VERTEX.z += sin(VERTEX.z * sway_phase_len * 0.9123 + TIME * sway_speed * 1.3123) * strength;
} }
```
This is the code to create the sway of the leaves. It's basic (just uses a sinewave multiplying by the time and axis position, but works well). Notice that the strength is multiplied by the color. Every axis uses a different small near 1.0 multiplication factor so axes don't appear in sync. This is the code to create the sway of the leaves. It's basic (just uses a sinewave multiplying by the time and axis position, but works well). Notice that the strength is multiplied by the color. Every axis uses a different small near 1.0 multiplication factor so axes don't appear in sync.
Finally, all that's left is the fragment shader: Finally, all that's left is the fragment shader:
.. code-block:: glsl ```
void fragment() { void fragment() {
vec4 albedo_tex = texture(texture_albedo, UV); vec4 albedo_tex = texture(texture_albedo, UV);
ALBEDO = albedo_tex.rgb; ALBEDO = albedo_tex.rgb;
@ -79,6 +78,7 @@ Finally, all that's left is the fragment shader:
ROUGHNESS = 1.0; ROUGHNESS = 1.0;
TRANSMISSION = transmission.rgb; TRANSMISSION = transmission.rgb;
} }
```
And this is pretty much it. And this is pretty much it.

View File

@ -25,11 +25,11 @@ It takes as argument the UV of the screen and returns a vec3 RGB with the color.
special built-in varying: SCREEN_UV can be used to obtain the UV for special built-in varying: SCREEN_UV can be used to obtain the UV for
the current fragment. As a result, this simple canvas_item fragment shader: the current fragment. As a result, this simple canvas_item fragment shader:
.. code-block:: glsl ```
void fragment() { void fragment() {
COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); COLOR = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
} }
```
results in an invisible object, because it just shows what lies behind. results in an invisible object, because it just shows what lies behind.
@ -53,8 +53,7 @@ special demo for *Screen Space Shaders*, that you can download to see
and learn. One example is a simple shader to adjust brightness, contrast and learn. One example is a simple shader to adjust brightness, contrast
and saturation: and saturation:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
uniform float brightness = 1.0; uniform float brightness = 1.0;
@ -70,6 +69,7 @@ and saturation:
COLOR.rgb = c; COLOR.rgb = c;
} }
```
Behind the scenes Behind the scenes
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
@ -150,10 +150,10 @@ converted via the inverse projection matrix.
The following code retrieves the 3D position below the pixel being drawn: The following code retrieves the 3D position below the pixel being drawn:
.. code-block:: glsl ```
void fragment() { void fragment() {
float depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r; float depth = textureLod(DEPTH_TEXTURE, SCREEN_UV, 0.0).r;
vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0); vec4 upos = INV_PROJECTION_MATRIX * vec4(SCREEN_UV * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec3 pixel_position = upos.xyz / upos.w; vec3 pixel_position = upos.xyz / upos.w;
} }
```

View File

@ -68,8 +68,7 @@ If not written to, these values will not be modified and be passed through as th
The user can disable the built-in modelview transform (projection will still happen later) and do The user can disable the built-in modelview transform (projection will still happen later) and do
it manually with the following code: it manually with the following code:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
render_mode skip_vertex_transform; render_mode skip_vertex_transform;
@ -77,6 +76,7 @@ it manually with the following code:
VERTEX = (EXTRA_MATRIX * (WORLD_MATRIX * vec4(VERTEX, 0.0, 1.0))).xy; VERTEX = (EXTRA_MATRIX * (WORLD_MATRIX * vec4(VERTEX, 0.0, 1.0))).xy;
} }
```
Note: Note:
`WORLD_MATRIX` is actually a modelview matrix. It takes input in local space and transforms it `WORLD_MATRIX` is actually a modelview matrix. It takes input in local space and transforms it
@ -84,21 +84,21 @@ Note:
In order to get the world space coordinates of a vertex, you have to pass in a custom uniform like so: In order to get the world space coordinates of a vertex, you have to pass in a custom uniform like so:
:: ```
material.set_shader_param("global_transform", get_global_transform()) material.set_shader_param("global_transform", get_global_transform())
```
Then, in your vertex shader: Then, in your vertex shader:
.. code-block:: glsl ```
uniform mat4 global_transform; uniform mat4 global_transform;
varying vec2 world_position; varying vec2 world_position;
void vertex(){ void vertex(){
world_position = (global_transform * vec4(VERTEX, 0.0, 1.0)).xy; world_position = (global_transform * vec4(VERTEX, 0.0, 1.0)).xy;
} }
```
`world_position` can then be used in either the vertex or fragment functions. `world_position` can then be used in either the vertex or fragment functions.
@ -149,18 +149,18 @@ when a custom fragment function is attached to these nodes, the texture lookup n
manually. Godot does not provide the texture color in the `COLOR` built-in variable; to read manually. Godot does not provide the texture color in the `COLOR` built-in variable; to read
the texture color for such nodes, use: the texture color for such nodes, use:
.. code-block:: glsl ```
COLOR = texture(TEXTURE, UV); COLOR = texture(TEXTURE, UV);
```
This differs from the behavior of the built-in normal map. If a normal map is attached, Godot uses This differs from the behavior of the built-in normal map. If a normal map is attached, Godot uses
it by default and assigns its value to the built-in `NORMAL` variable. If you are using a normal it by default and assigns its value to the built-in `NORMAL` variable. If you are using a normal
map meant for use in 3D, it will appear inverted. In order to use it in your shader, you must assign map meant for use in 3D, it will appear inverted. In order to use it in your shader, you must assign
it to the `NORMALMAP` property. Godot will handle converting it for use in 2D and overwriting `NORMAL`. it to the `NORMALMAP` property. Godot will handle converting it for use in 2D and overwriting `NORMAL`.
.. code-block:: glsl ```
NORMALMAP = texture(NORMAL_TEXTURE, UV).rgb; NORMALMAP = texture(NORMAL_TEXTURE, UV).rgb;
```
+----------------------------------+----------------------------------------------------------------+ +----------------------------------+----------------------------------------------------------------+
| Built-in | Description | | Built-in | Description |

View File

@ -89,19 +89,19 @@ Casting of types of different size is also not allowed. Conversion must be done
Example: Example:
.. code-block:: glsl ```
float a = 2; // invalid float a = 2; // invalid
float a = 2.0; // valid float a = 2.0; // valid
float a = float(2); // valid float a = float(2); // valid
```
Default integer constants are signed, so casting is always needed to convert to unsigned: Default integer constants are signed, so casting is always needed to convert to unsigned:
.. code-block:: glsl ```
int a = 2; // valid int a = 2; // valid
uint a = 2; // invalid uint a = 2; // invalid
uint a = uint(2); // valid uint a = uint(2); // valid
```
Members Members
~~~~~~~ ~~~~~~~
@ -119,8 +119,7 @@ Constructing
Construction of vector types must always pass: Construction of vector types must always pass:
.. code-block:: glsl ```
// The required amount of scalars // The required amount of scalars
vec4 a = vec4(0.0, 1.0, 2.0, 3.0); vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
// Complementary vectors and/or scalars // Complementary vectors and/or scalars
@ -128,16 +127,17 @@ Construction of vector types must always pass:
vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0); vec4 a = vec4(vec3(0.0, 1.0, 2.0), 3.0);
// A single scalar for the whole vector // A single scalar for the whole vector
vec4 a = vec4(0.0); vec4 a = vec4(0.0);
```
Construction of matrix types requires vectors of the same dimension as the matrix. You can Construction of matrix types requires vectors of the same dimension as the matrix. You can
also build a diagonal matrix using `matx(float)` syntax. Accordingly, `mat4(1.0)` is also build a diagonal matrix using `matx(float)` syntax. Accordingly, `mat4(1.0)` is
an identity matrix. an identity matrix.
.. code-block:: glsl ```
mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0)); mat2 m2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));
mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0)); mat3 m3 = mat3(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0), vec3(0.0, 0.0, 1.0));
mat4 identity = mat4(1.0); mat4 identity = mat4(1.0);
```
Matrices can also be built from a matrix of another dimension. Matrices can also be built from a matrix of another dimension.
There are two rules : There are two rules :
@ -145,11 +145,11 @@ If a larger matrix is constructed from a smaller matrix, the additional rows and
set to the values they would have in an identity matrix. If a smaller matrix is constructed set to the values they would have in an identity matrix. If a smaller matrix is constructed
from a larger matrix, the top, left submatrix of the larger matrix is used. from a larger matrix, the top, left submatrix of the larger matrix is used.
.. code-block:: glsl ```
mat3 basis = mat3(WORLD_MATRIX); mat3 basis = mat3(WORLD_MATRIX);
mat4 m4 = mat4(basis); mat4 m4 = mat4(basis);
mat2 m2 = mat2(m4); mat2 m2 = mat2(m4);
```
Swizzling Swizzling
~~~~~~~~~ ~~~~~~~~~
@ -157,8 +157,7 @@ Swizzling
It is possible to obtain any combination of components in any order, as long as the result It is possible to obtain any combination of components in any order, as long as the result
is another vector type (or scalar). This is easier shown than explained: is another vector type (or scalar). This is easier shown than explained:
.. code-block:: glsl ```
vec4 a = vec4(0.0, 1.0, 2.0, 3.0); vec4 a = vec4(0.0, 1.0, 2.0, 3.0);
vec3 b = a.rgb; // Creates a vec3 with vec4 components. vec3 b = a.rgb; // Creates a vec3 with vec4 components.
vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component. vec3 b = a.ggg; // Also valid; creates a vec3 and fills it with a single vec4 component.
@ -169,17 +168,18 @@ is another vector type (or scalar). This is easier shown than explained:
vec3 c = b.xrt; // Invalid, mixing different styles is forbidden. vec3 c = b.xrt; // Invalid, mixing different styles is forbidden.
b.rrr = a.rgb; // Invalid, assignment with duplication. b.rrr = a.rgb; // Invalid, assignment with duplication.
b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa. b.bgr = a.rgb; // Valid assignment. "b"'s "blue" component will be "a"'s "red" and vice versa.
```
Precision Precision
~~~~~~~~~ ~~~~~~~~~
It is possible to add precision modifiers to datatypes; use them for uniforms, variables, arguments and varyings: It is possible to add precision modifiers to datatypes; use them for uniforms, variables, arguments and varyings:
.. code-block:: glsl ```
lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1 lowp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // low precision, usually 8 bits per component mapped to 0-1
mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float mediump vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // medium precision, usually 16 bits or half float
highp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (default) highp vec4 a = vec4(0.0, 1.0, 2.0, 3.0); // high precision, uses full float or integer range (default)
```
Using lower precision for some operations can speed up the math involved (at the cost of less precision). Using lower precision for some operations can speed up the math involved (at the cost of less precision).
@ -204,16 +204,15 @@ Local arrays
Local arrays are declared in functions. They can use all of the allowed datatypes, except samplers. Local arrays are declared in functions. They can use all of the allowed datatypes, except samplers.
The array declaration follows a C-style syntax: `[const] + [precision] + typename + identifier + [array size]`. The array declaration follows a C-style syntax: `[const] + [precision] + typename + identifier + [array size]`.
.. code-block:: glsl ```
void fragment() { void fragment() {
float arr[3]; float arr[3];
} }
```
They can be initialized at the beginning like: They can be initialized at the beginning like:
.. code-block:: glsl ```
float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor float float_arr[3] = float[3] (1.0, 0.5, 0.0); // first constructor
int int_arr[3] = int[] (2, 1, 0); // second constructor int int_arr[3] = int[] (2, 1, 0); // second constructor
@ -221,35 +220,36 @@ They can be initialized at the beginning like:
vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor vec2 vec2_arr[3] = { vec2(1.0, 1.0), vec2(0.5, 0.5), vec2(0.0, 0.0) }; // third constructor
bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count bool bool_arr[] = { true, true, false }; // fourth constructor - size is defined automatically from the element count
```
You can declare multiple arrays (even with different sizes) in one expression: You can declare multiple arrays (even with different sizes) in one expression:
.. code-block:: glsl ```
float a[3] = float[3] (1.0, 0.5, 0.0), float a[3] = float[3] (1.0, 0.5, 0.0),
b[2] = { 1.0, 0.5 }, b[2] = { 1.0, 0.5 },
c[] = { 0.7 }, c[] = { 0.7 },
d = 0.0, d = 0.0,
e[5]; e[5];
```
To access an array element, use the indexing syntax: To access an array element, use the indexing syntax:
.. code-block:: glsl ```
float arr[3]; float arr[3];
arr[0] = 1.0; // setter arr[0] = 1.0; // setter
COLOR.r = arr[0]; // getter COLOR.r = arr[0]; // getter
```
Arrays also have a built-in function `.length()` (not to be confused with the built-in `length()` function). It doesn't accept any parameters and will return the array's size. Arrays also have a built-in function `.length()` (not to be confused with the built-in `length()` function). It doesn't accept any parameters and will return the array's size.
.. code-block:: glsl ```
float arr[] = { 0.0, 1.0, 0.5, -1.0 }; float arr[] = { 0.0, 1.0, 0.5, -1.0 };
for (int i = 0; i < arr.length(); i++) { for (int i = 0; i < arr.length(); i++) {
// ... // ...
} }
```
Note: Note:
@ -261,38 +261,38 @@ Constants
Use the `const` keyword before the variable declaration to make that variable immutable, which means that it cannot be modified. All basic types, except samplers can be declared as constants. Accessing and using a constant value is slightly faster than using a uniform. Constants must be initialized at their declaration. Use the `const` keyword before the variable declaration to make that variable immutable, which means that it cannot be modified. All basic types, except samplers can be declared as constants. Accessing and using a constant value is slightly faster than using a uniform. Constants must be initialized at their declaration.
.. code-block:: glsl ```
const vec2 a = vec2(0.0, 1.0); const vec2 a = vec2(0.0, 1.0);
vec2 b; vec2 b;
a = b; // invalid a = b; // invalid
b = a; // valid b = a; // valid
```
Constants cannot be modified and additionally cannot have hints, but multiple of them (if they have the same type) can be declared in a single expression e.g Constants cannot be modified and additionally cannot have hints, but multiple of them (if they have the same type) can be declared in a single expression e.g
.. code-block:: glsl ```
const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2); const vec2 V1 = vec2(1, 1), V2 = vec2(2, 2);
```
Similar to variables, arrays can also be declared with `const`. Similar to variables, arrays can also be declared with `const`.
.. code-block:: glsl ```
const float arr[] = { 1.0, 0.5, 0.0 }; const float arr[] = { 1.0, 0.5, 0.0 };
arr[0] = 1.0; // invalid arr[0] = 1.0; // invalid
COLOR.r = arr[0]; // valid COLOR.r = arr[0]; // valid
```
Constants can be declared both globally (outside of any function) or locally (inside a function). Constants can be declared both globally (outside of any function) or locally (inside a function).
Global constants are useful when you want to have access to a value throughout your shader that does not need to be modified. Like uniforms, global constants are shared between all shader stages, but they are not accessible outside of the shader. Global constants are useful when you want to have access to a value throughout your shader that does not need to be modified. Like uniforms, global constants are shared between all shader stages, but they are not accessible outside of the shader.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
const float PI = 3.14159265358979323846; const float PI = 3.14159265358979323846;
```
Operators Operators
--------- ---------
@ -332,8 +332,7 @@ Flow control
Godot Shading language supports the most common types of flow control: Godot Shading language supports the most common types of flow control:
.. code-block:: glsl ```
// if and else // if and else
if (cond) { if (cond) {
@ -369,6 +368,7 @@ Godot Shading language supports the most common types of flow control:
do { do {
} while(true); } while(true);
```
Keep in mind that, in modern GPUs, an infinite loop can exist and can freeze your application (including editor). Keep in mind that, in modern GPUs, an infinite loop can exist and can freeze your application (including editor).
Godot can't protect you from this, so be careful not to make this mistake! Godot can't protect you from this, so be careful not to make this mistake!
@ -389,8 +389,7 @@ Functions
It is possible to define functions in a Godot shader. They use the following syntax: It is possible to define functions in a Godot shader. They use the following syntax:
.. code-block:: glsl ```
ret_type func_name(args) { ret_type func_name(args) {
return ret_type; // if returning a value return ret_type; // if returning a value
} }
@ -400,6 +399,7 @@ It is possible to define functions in a Godot shader. They use the following syn
int sum2(int a, int b) { int sum2(int a, int b) {
return a + b; return a + b;
} }
```
You can only use functions that have been defined above (higher in the editor) the function from which you are calling You can only use functions that have been defined above (higher in the editor) the function from which you are calling
@ -413,11 +413,11 @@ Function arguments can have special qualifiers:
Example below: Example below:
.. code-block:: glsl ```
void sum2(int a, int b, inout int result) { void sum2(int a, int b, inout int result) {
result = a + b; result = a + b;
} }
```
Varyings Varyings
~~~~~~~~ ~~~~~~~~
@ -426,8 +426,7 @@ To send data from the vertex to the fragment (or light) processor function, *var
used. They are set for every primitive vertex in the *vertex processor*, and the used. They are set for every primitive vertex in the *vertex processor*, and the
value is interpolated for every pixel in the *fragment processor*. value is interpolated for every pixel in the *fragment processor*.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
varying vec3 some_color; varying vec3 some_color;
@ -443,11 +442,11 @@ value is interpolated for every pixel in the *fragment processor*.
void light() { void light() {
DIFFUSE_LIGHT = some_color * 100; // optionally DIFFUSE_LIGHT = some_color * 100; // optionally
} }
```
Varying can also be an array: Varying can also be an array:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
varying float var_arr[3]; varying float var_arr[3];
@ -460,11 +459,11 @@ Varying can also be an array:
void fragment() { void fragment() {
ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color ALBEDO = vec3(var_arr[0], var_arr[1], var_arr[2]); // red color
} }
```
It's also possible to send data from *fragment* to *light* processors using *varying* keyword. To do so you can assign it in the *fragment* and later use it in the *light* function. It's also possible to send data from *fragment* to *light* processors using *varying* keyword. To do so you can assign it in the *fragment* and later use it in the *light* function.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
varying vec3 some_light; varying vec3 some_light;
@ -476,11 +475,11 @@ It's also possible to send data from *fragment* to *light* processors using *var
void light() { void light() {
DIFFUSE_LIGHT = some_light; DIFFUSE_LIGHT = some_light;
} }
```
Note that varying may not be assigned in custom functions or a *light processor* function like: Note that varying may not be assigned in custom functions or a *light processor* function like:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
varying float test; varying float test;
@ -496,6 +495,7 @@ Note that varying may not be assigned in custom functions or a *light processor*
void light() { void light() {
test = 0.0; // Error too. test = 0.0; // Error too.
} }
```
This limitation was introduced to prevent incorrect usage before initialization. This limitation was introduced to prevent incorrect usage before initialization.
@ -505,8 +505,7 @@ Interpolation qualifiers
Certain values are interpolated during the shading pipeline. You can modify how these interpolations Certain values are interpolated during the shading pipeline. You can modify how these interpolations
are done by using *interpolation qualifiers*. are done by using *interpolation qualifiers*.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
varying flat vec3 our_color; varying flat vec3 our_color;
@ -518,6 +517,7 @@ are done by using *interpolation qualifiers*.
void fragment() { void fragment() {
ALBEDO = our_color; ALBEDO = our_color;
} }
```
There are two possible interpolation qualifiers: There are two possible interpolation qualifiers:
@ -537,17 +537,17 @@ Passing values to shaders is possible. These are global to the whole shader and
When a shader is later assigned to a material, the uniforms will appear as editable parameters in it. When a shader is later assigned to a material, the uniforms will appear as editable parameters in it.
Uniforms can't be written from within the shader. Uniforms can't be written from within the shader.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
uniform float some_value; uniform float some_value;
```
You can set uniforms in the editor in the material. Or you can set them through GDScript: You can set uniforms in the editor in the material. Or you can set them through GDScript:
:: ```
material.set_shader_param("some_value", some_value) material.set_shader_param("some_value", some_value)
```
Note: Note:
The first argument to `set_shader_param` is the name of the uniform in the shader. It The first argument to `set_shader_param` is the name of the uniform in the shader. It
@ -557,13 +557,13 @@ Any GLSL type except for *void* can be a uniform. Additionally, Godot provides
optional shader hints to make the compiler understand for what the uniform is optional shader hints to make the compiler understand for what the uniform is
used, and how the editor should allow users to modify it. used, and how the editor should allow users to modify it.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
uniform vec4 color : hint_color; uniform vec4 color : hint_color;
uniform float amount : hint_range(0, 1); uniform float amount : hint_range(0, 1);
uniform vec4 other_color : hint_color = vec4(1.0); uniform vec4 other_color : hint_color = vec4(1.0);
```
It's important to understand that textures that are supplied as color require hints for proper sRGB->linear conversion (i.e. `hint_albedo`), as Godot's 3D engine renders in linear color space. It's important to understand that textures that are supplied as color require hints for proper sRGB->linear conversion (i.e. `hint_albedo`), as Godot's 3D engine renders in linear color space.
@ -618,12 +618,12 @@ Note:
Uniforms can also be assigned default values: Uniforms can also be assigned default values:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
uniform vec4 some_vector = vec4(0.0); uniform vec4 some_vector = vec4(0.0);
uniform vec4 some_color : hint_color = vec4(1.0); uniform vec4 some_color : hint_color = vec4(1.0);
```
Built-in variables Built-in variables
------------------ ------------------

View File

@ -108,8 +108,7 @@ They can optionally be presented in world space by using the *world_vertex_coord
Users can disable the built-in modelview transform (projection will still happen later) and do Users can disable the built-in modelview transform (projection will still happen later) and do
it manually with the following code: it manually with the following code:
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
render_mode skip_vertex_transform; render_mode skip_vertex_transform;
@ -118,6 +117,7 @@ it manually with the following code:
NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz); NORMAL = normalize((MODELVIEW_MATRIX * vec4(NORMAL, 0.0)).xyz);
// same as above for binormal and tangent, if normal mapping is used // same as above for binormal and tangent, if normal mapping is used
} }
```
Other built-ins, such as UV, UV2 and COLOR, are also passed through to the fragment function if not modified. Other built-ins, such as UV, UV2 and COLOR, are also passed through to the fragment function if not modified.
@ -319,11 +319,11 @@ each light type.
Below is an example of a custom light function using a Lambertian lighting model: Below is an example of a custom light function using a Lambertian lighting model:
.. code-block:: glsl ```
void light() { void light() {
DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * ALBEDO; DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * ALBEDO;
} }
```
If you want the lights to add together, add the light contribution to `DIFFUSE_LIGHT` using `+=`, rather than overwriting it. If you want the lights to add together, add the light contribution to `DIFFUSE_LIGHT` using `+=`, rather than overwriting it.

View File

@ -26,8 +26,7 @@ Note:
Here is a complete shader example based on these guidelines: Here is a complete shader example based on these guidelines:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
// Screen-space shader to adjust a 2D scene's brightness, contrast // Screen-space shader to adjust a 2D scene's brightness, contrast
// and saturation. Taken from // and saturation. Taken from
@ -46,6 +45,7 @@ Here is a complete shader example based on these guidelines:
COLOR.rgb = c; COLOR.rgb = c;
} }
```
Formatting Formatting
---------- ----------
@ -65,38 +65,38 @@ Each indent level should be one tab greater than the block containing it.
**Good**: **Good**:
.. code-block:: glsl ```
void fragment() { void fragment() {
COLOR = vec3(1.0, 1.0, 1.0); COLOR = vec3(1.0, 1.0, 1.0);
} }
```
**Bad**: **Bad**:
.. code-block:: glsl ```
void fragment() { void fragment() {
COLOR = vec3(1.0, 1.0, 1.0); COLOR = vec3(1.0, 1.0, 1.0);
} }
```
Use 2 indent levels to distinguish continuation lines from Use 2 indent levels to distinguish continuation lines from
regular code blocks. regular code blocks.
**Good**: **Good**:
.. code-block:: glsl ```
vec2 st = vec2( vec2 st = vec2(
atan(NORMAL.x, NORMAL.z), atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y)); acos(NORMAL.y));
```
**Bad**: **Bad**:
.. code-block:: glsl ```
vec2 st = vec2( vec2 st = vec2(
atan(NORMAL.x, NORMAL.z), atan(NORMAL.x, NORMAL.z),
acos(NORMAL.y)); acos(NORMAL.y));
```
Line breaks and blank lines Line breaks and blank lines
@ -111,31 +111,30 @@ an `if` statement or similar.
**Good**: **Good**:
.. code-block:: glsl ```
void fragment() { void fragment() {
if (true) { if (true) {
// ... // ...
} }
} }
```
**Bad**: **Bad**:
.. code-block:: glsl ```
void fragment() void fragment()
{ {
if (true) if (true)
// ... // ...
} }
```
Blank lines Blank lines
~~~~~~~~~~~ ~~~~~~~~~~~
Surround function definitions with one (and only one) blank line: Surround function definitions with one (and only one) blank line:
.. code-block:: glsl ```
void do_something() { void do_something() {
// ... // ...
} }
@ -143,6 +142,7 @@ Surround function definitions with one (and only one) blank line:
void fragment() { void fragment() {
// ... // ...
} }
```
Use one (and only one) blank line inside functions to separate logical sections. Use one (and only one) blank line inside functions to separate logical sections.
@ -162,29 +162,29 @@ Never combine multiple statements on a single line.
**Good**: **Good**:
.. code-block:: glsl ```
void fragment() { void fragment() {
ALBEDO = vec3(1.0); ALBEDO = vec3(1.0);
EMISSION = vec3(1.0); EMISSION = vec3(1.0);
} }
```
**Bad**: **Bad**:
.. code-block:: glsl ```
void fragment() { void fragment() {
ALBEDO = vec3(1.0); EMISSION = vec3(1.0); ALBEDO = vec3(1.0); EMISSION = vec3(1.0);
} }
```
The only exception to that rule is the ternary operator: The only exception to that rule is the ternary operator:
.. code-block:: glsl ```
void fragment() { void fragment() {
bool should_be_white = true; bool should_be_white = true;
ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0); ALBEDO = should_be_white ? vec3(1.0) : vec3(0.0);
} }
```
Comment spacing Comment spacing
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
@ -194,23 +194,23 @@ This helps differentiate text comments from disabled code.
**Good**: **Good**:
.. code-block:: glsl ```
// This is a comment. // This is a comment.
//return; //return;
```
**Bad**: **Bad**:
.. code-block:: glsl ```
//This is a comment. //This is a comment.
// return; // return;
```
Don't use multiline comment syntax if your comment can fit on a single line: Don't use multiline comment syntax if your comment can fit on a single line:
.. code-block:: glsl ```
/* This is another comment. */ /* This is another comment. */
```
Note: Note:
@ -227,26 +227,26 @@ in function calls.
**Good**: **Good**:
.. code-block:: glsl ```
COLOR.r = 5.0; COLOR.r = 5.0;
COLOR.r = COLOR.g + 0.1; COLOR.r = COLOR.g + 0.1;
COLOR.b = some_function(1.0, 2.0); COLOR.b = some_function(1.0, 2.0);
```
**Bad**: **Bad**:
.. code-block:: glsl ```
COLOR.r=5.0; COLOR.r=5.0;
COLOR.r = COLOR.g+0.1; COLOR.r = COLOR.g+0.1;
COLOR.b = some_function (1.0,2.0); COLOR.b = some_function (1.0,2.0);
```
Don't use spaces to align expressions vertically: Don't use spaces to align expressions vertically:
.. code-block:: glsl ```
ALBEDO.r = 1.0; ALBEDO.r = 1.0;
EMISSION.r = 1.0; EMISSION.r = 1.0;
```
Floating-point numbers Floating-point numbers
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
@ -257,19 +257,19 @@ distinguishing numbers greater than 1 from those lower than 1.
**Good**: **Good**:
.. code-block:: glsl ```
void fragment() { void fragment() {
ALBEDO.rgb = vec3(5.0, 0.1, 0.2); ALBEDO.rgb = vec3(5.0, 0.1, 0.2);
} }
```
**Bad**: **Bad**:
.. code-block:: glsl ```
void fragment() { void fragment() {
ALBEDO.rgb = vec3(5., .1, .2); ALBEDO.rgb = vec3(5., .1, .2);
} }
```
Accessing vector members Accessing vector members
------------------------ ------------------------
@ -281,15 +281,15 @@ understand what the underlying data represents.
**Good**: **Good**:
.. code-block:: glsl ```
COLOR.rgb = vec3(5.0, 0.1, 0.2); COLOR.rgb = vec3(5.0, 0.1, 0.2);
```
**Bad**: **Bad**:
.. code-block:: glsl ```
COLOR.xyz = vec3(5.0, 0.1, 0.2); COLOR.xyz = vec3(5.0, 0.1, 0.2);
```
Naming conventions Naming conventions
------------------ ------------------
@ -303,11 +303,11 @@ Functions and variables
Use snake\_case to name functions and variables: Use snake\_case to name functions and variables:
.. code-block:: glsl ```
void some_function() { void some_function() {
float some_variable = 0.5; float some_variable = 0.5;
} }
```
Constants Constants
~~~~~~~~~ ~~~~~~~~~
@ -315,17 +315,16 @@ Constants
Write constants with CONSTANT\_CASE, that is to say in all caps with an Write constants with CONSTANT\_CASE, that is to say in all caps with an
underscore (\_) to separate words: underscore (\_) to separate words:
.. code-block:: glsl ```
const float GOLDEN_RATIO = 1.618; const float GOLDEN_RATIO = 1.618;
```
Code order Code order
---------- ----------
We suggest to organize shader code this way: We suggest to organize shader code this way:
.. code-block:: glsl ```
01. shader type declaration 01. shader type declaration
02. render mode declaration 02. render mode declaration
03. // docstring 03. // docstring
@ -338,6 +337,7 @@ We suggest to organize shader code this way:
08. vertex() function 08. vertex() function
09. fragment() function 09. fragment() function
10. light() function 10. light() function
```
We optimized the order to make it easy to read the code from top to bottom, to We optimized the order to make it easy to read the code from top to bottom, to
help developers reading the code for the first time understand how it works, and help developers reading the code for the first time understand how it works, and

View File

@ -59,13 +59,13 @@ Note:
ColorRect > CanvasItem > Material > Material > click / Edit > ShaderMaterial > Shader > `New Shader` > click / Edit: ColorRect > CanvasItem > Material > Material > click / Edit > ShaderMaterial > Shader > `New Shader` > click / Edit:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
void fragment() { void fragment() {
COLOR = vec4(UV.x, UV.y, 0.5, 1.0); COLOR = vec4(UV.x, UV.y, 0.5, 1.0);
} }
```
The above code renders a gradient like the one below. The above code renders a gradient like the one below.
@ -113,9 +113,9 @@ seam created by our texture coordinates. So how do we get a range of coordinates
the sphere in a nice way? One solution is to use a function that repeats on the domain of our texture. the sphere in a nice way? One solution is to use a function that repeats on the domain of our texture.
`sin` and `cos` are two such functions. Let's apply them to the texture and see what happens. `sin` and `cos` are two such functions. Let's apply them to the texture and see what happens.
.. code-block:: glsl ```
COLOR.xyz = vec3(sin(UV.x * 3.14159 * 4.0) * cos(UV.y * 3.14159 * 4.0) * 0.5 + 0.5); COLOR.xyz = vec3(sin(UV.x * 3.14159 * 4.0) * cos(UV.y * 3.14159 * 4.0) * 0.5 + 0.5);
```
![](img/planet_sincos.png) ![](img/planet_sincos.png)
@ -138,8 +138,7 @@ surface of the sphere, you never hit an edge, and hence you never create a seam
a pinch point on the pole. The following code converts the `UVs` into Cartesian a pinch point on the pole. The following code converts the `UVs` into Cartesian
coordinates. coordinates.
.. code-block:: glsl ```
float theta = UV.y * 3.14159; float theta = UV.y * 3.14159;
float phi = UV.x * 3.14159 * 2.0; float phi = UV.x * 3.14159 * 2.0;
vec3 unit = vec3(0.0, 0.0, 0.0); vec3 unit = vec3(0.0, 0.0, 0.0);
@ -148,6 +147,7 @@ coordinates.
unit.y = cos(theta) * -1.0; unit.y = cos(theta) * -1.0;
unit.z = cos(phi) * sin(theta); unit.z = cos(phi) * sin(theta);
unit = normalize(unit); unit = normalize(unit);
```
And if we use `unit` as an output `COLOR` value, we get: And if we use `unit` as an output `COLOR` value, we get:
@ -156,8 +156,7 @@ And if we use `unit` as an output `COLOR` value, we get:
Now that we can calculate the 3D position of the surface of the sphere, we can use 3D noise Now that we can calculate the 3D position of the surface of the sphere, we can use 3D noise
to make the planet. We will be using this noise function directly from a `Shadertoy ( https://www.shadertoy.com/view/Xsl3Dl )`: to make the planet. We will be using this noise function directly from a `Shadertoy ( https://www.shadertoy.com/view/Xsl3Dl )`:
.. code-block:: glsl ```
vec3 hash(vec3 p) { vec3 hash(vec3 p) {
p = vec3(dot(p, vec3(127.1, 311.7, 74.7)), p = vec3(dot(p, vec3(127.1, 311.7, 74.7)),
dot(p, vec3(269.5, 183.3, 246.1)), dot(p, vec3(269.5, 183.3, 246.1)),
@ -180,16 +179,17 @@ to make the planet. We will be using this noise function directly from a `Shader
mix(dot(hash(i + vec3(0.0, 1.0, 1.0)), f - vec3(0.0, 1.0, 1.0)), mix(dot(hash(i + vec3(0.0, 1.0, 1.0)), f - vec3(0.0, 1.0, 1.0)),
dot(hash(i + vec3(1.0, 1.0, 1.0)), f - vec3(1.0, 1.0, 1.0)), u.x), u.y), u.z ); dot(hash(i + vec3(1.0, 1.0, 1.0)), f - vec3(1.0, 1.0, 1.0)), u.x), u.y), u.z );
} }
```
Note: Note:
All credit goes to the author, Inigo Quilez. It is published under the `MIT` licence. All credit goes to the author, Inigo Quilez. It is published under the `MIT` licence.
Now to use `noise`, add the following to the `fragment` function: Now to use `noise`, add the following to the `fragment` function:
.. code-block:: glsl ```
float n = noise(unit * 5.0); float n = noise(unit * 5.0);
COLOR.xyz = vec3(n * 0.5 + 0.5); COLOR.xyz = vec3(n * 0.5 + 0.5);
```
![](img/planet_noise.png) ![](img/planet_noise.png)
@ -211,9 +211,9 @@ it *mixes* the two values together. In other APIs, this function is often called
However, `lerp` is typically reserved for mixing two floats together; `mix` can take any However, `lerp` is typically reserved for mixing two floats together; `mix` can take any
values whether it be floats or vector types. values whether it be floats or vector types.
.. code-block:: glsl ```
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), n * 0.5 + 0.5); COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), n * 0.5 + 0.5);
```
The first color is blue for the ocean. The second color is a kind of reddish color (because The first color is blue for the ocean. The second color is a kind of reddish color (because
all alien planets need red terrain). And finally, they are mixed together by `n * 0.5 + 0.5`. all alien planets need red terrain). And finally, they are mixed together by `n * 0.5 + 0.5`.
@ -226,9 +226,9 @@ That is a little more blurry than we want. Planets typically have a relatively c
land and sea. In order to do that, we will change the last term to `smoothstep(-0.1, 0.0, n)`. land and sea. In order to do that, we will change the last term to `smoothstep(-0.1, 0.0, n)`.
And thus the whole line becomes: And thus the whole line becomes:
.. code-block:: glsl ```
COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), smoothstep(-0.1, 0.0, n)); COLOR.xyz = mix(vec3(0.05, 0.3, 0.5), vec3(0.9, 0.4, 0.1), smoothstep(-0.1, 0.0, n));
```
What `smoothstep` does is return `0` if the third argument is below the first and `1` if the What `smoothstep` does is return `0` if the third argument is below the first and `1` if the
third argument is larger than the second and smoothly blends between `0` and `1` if the third number third argument is larger than the second and smoothly blends between `0` and `1` if the third number
@ -244,12 +244,12 @@ overall blobby structure of the continents. Then another layer breaks up the edg
another, and so on. What we will do is calculate `n` with four lines of shader code another, and so on. What we will do is calculate `n` with four lines of shader code
instead of just one. `n` becomes: instead of just one. `n` becomes:
.. code-block:: glsl ```
float n = noise(unit * 5.0) * 0.5; float n = noise(unit * 5.0) * 0.5;
n += noise(unit * 10.0) * 0.25; n += noise(unit * 10.0) * 0.25;
n += noise(unit * 20.0) * 0.125; n += noise(unit * 20.0) * 0.125;
n += noise(unit * 40.0) * 0.0625; n += noise(unit * 40.0) * 0.0625;
```
And now the planet looks like: And now the planet looks like:
@ -266,9 +266,9 @@ One final thing to make this look more like a planet. The ocean and the land ref
So we want the ocean to shine a little more than the land. We can do this by passing a fourth value So we want the ocean to shine a little more than the land. We can do this by passing a fourth value
into the `alpha` channel of our output `COLOR` and using it as a Roughness map. into the `alpha` channel of our output `COLOR` and using it as a Roughness map.
.. code-block:: glsl ```
COLOR.a = 0.3 + 0.7 * smoothstep(-0.1, 0.0, n); COLOR.a = 0.3 + 0.7 * smoothstep(-0.1, 0.0, n);
```
This line returns `0.3` for water and `1.0` for land. This means that the land is going to be quite This line returns `0.3` for water and `1.0` for land. This means that the land is going to be quite
rough, while the water will be quite smooth. rough, while the water will be quite smooth.
@ -293,9 +293,9 @@ drawn with slightly fainter colors and a `Roughness` value of `1` everywhere. To
go into the `Viewport` and enable the "Transparent Bg" property. Since we are now go into the `Viewport` and enable the "Transparent Bg" property. Since we are now
rendering one transparent object on top of another, we want to enable `blend_premul_alpha`: rendering one transparent object on top of another, we want to enable `blend_premul_alpha`:
.. code-block:: glsl ```
render_mode blend_premul_alpha; render_mode blend_premul_alpha;
```
This pre-multiplies the colors by the `alpha` value and then blends them correctly together. Typically, This pre-multiplies the colors by the `alpha` value and then blends them correctly together. Typically,
when blending one transparent color on top of another, even if the background has an `alpha` of `0` (as it when blending one transparent color on top of another, even if the background has an `alpha` of `0` (as it

View File

@ -63,9 +63,9 @@ Your first CanvasItem shader
In Godot, all shaders start with a line specifying what type of shader they are. In Godot, all shaders start with a line specifying what type of shader they are.
It uses the following format: It uses the following format:
.. code-block:: glsl ```
shader_type canvas_item; shader_type canvas_item;
```
Because we are writing a CanvasItem shader, we specify `canvas_item` in the Because we are writing a CanvasItem shader, we specify `canvas_item` in the
first line. All our code will go beneath this declaration. first line. All our code will go beneath this declaration.
@ -96,11 +96,11 @@ shorthand for constructing a vector with 4 numbers. For more information about
vectors see the `Vector math tutorial ( doc_vector_math )` `COLOR` is both vectors see the `Vector math tutorial ( doc_vector_math )` `COLOR` is both
an input variable to the fragment function and the final output from it. an input variable to the fragment function and the final output from it.
.. code-block:: glsl ```
void fragment(){ void fragment(){
COLOR = vec4(0.4, 0.6, 0.9, 1.0); COLOR = vec4(0.4, 0.6, 0.9, 1.0);
} }
```
![](img/blue-box.png) ![](img/blue-box.png)
@ -121,11 +121,11 @@ other functions or to assign values to `COLOR` directly.
![](img/iconuv.png) ![](img/iconuv.png)
.. code-block:: glsl ```
void fragment() { void fragment() {
COLOR = vec4(UV, 0.5, 1.0); COLOR = vec4(UV, 0.5, 1.0);
} }
```
![](img/UV.png) ![](img/UV.png)
@ -135,12 +135,12 @@ Using `TEXTURE` built-in
When you want to adjust a color in a Sprite you cannot just adjust the color When you want to adjust a color in a Sprite you cannot just adjust the color
from the texture manually like in the code below. from the texture manually like in the code below.
.. code-block:: glsl ```
void fragment(){ void fragment(){
//this shader will result in an all white rectangle //this shader will result in an all white rectangle
COLOR.b = 1.0; COLOR.b = 1.0;
} }
```
The default fragment function reads from a texture and displays it. When you The default fragment function reads from a texture and displays it. When you
overwrite the default fragment function, you lose that functionality, so you overwrite the default fragment function, you lose that functionality, so you
@ -149,12 +149,12 @@ function. Certain nodes, like Sprites, have a dedicated texture variable that
can be accessed in the shader using `TEXTURE`. Use it together with `UV` and can be accessed in the shader using `TEXTURE`. Use it together with `UV` and
`texture` to draw the Sprite. `texture` to draw the Sprite.
.. code-block:: glsl ```
void fragment(){ void fragment(){
COLOR = texture(TEXTURE, UV); //read from texture COLOR = texture(TEXTURE, UV); //read from texture
COLOR.b = 1.0; //set blue channel to 1.0 COLOR.b = 1.0; //set blue channel to 1.0
} }
```
![](img/blue-tex.png) ![](img/blue-tex.png)
@ -166,23 +166,23 @@ the entire shader.
You can use uniforms by defining them at the top of your shader like so: You can use uniforms by defining them at the top of your shader like so:
.. code-block:: glsl ```
uniform float size; uniform float size;
```
For more information about usage see the `Shading Language doc For more information about usage see the `Shading Language doc
( doc_shading_language )`. ( doc_shading_language )`.
Add a uniform to change the amount of blue in our Sprite. Add a uniform to change the amount of blue in our Sprite.
.. code-block:: glsl ```
uniform float blue = 1.0; // you can assign a default value to uniforms uniform float blue = 1.0; // you can assign a default value to uniforms
void fragment(){ void fragment(){
COLOR = texture(TEXTURE, UV); //read from texture COLOR = texture(TEXTURE, UV); //read from texture
COLOR.b = blue; COLOR.b = blue;
} }
```
Now you can change the amount of blue in the Sprite from the editor. Look back Now you can change the amount of blue in the Sprite from the editor. Look back
at the Inspector under where you created your shader. You should see a section at the Inspector under where you created your shader. You should see a section
@ -197,10 +197,10 @@ You can change uniforms from code using the function `set_shader_param()`
which is called on the node's material resource. With a Sprite node, the which is called on the node's material resource. With a Sprite node, the
following code can be used to set the `blue` uniform. following code can be used to set the `blue` uniform.
:: ```
var blue_value = 1.0 var blue_value = 1.0
material.set_shader_param("blue", blue_value) material.set_shader_param("blue", blue_value)
```
Note that the name of the uniform is a string. The string must match exactly Note that the name of the uniform is a string. The string must match exactly
with how it is written in the shader, including spelling and case. with how it is written in the shader, including spelling and case.
@ -221,21 +221,21 @@ viewport, or parent nodes).
You can offset the vertices by directly adding to `VERTEX`. You can offset the vertices by directly adding to `VERTEX`.
.. code-block:: glsl ```
void vertex() { void vertex() {
VERTEX += vec2(10.0, 0.0); VERTEX += vec2(10.0, 0.0);
} }
```
Combined with the `TIME` built-in variable, this can be used for simple Combined with the `TIME` built-in variable, this can be used for simple
animation. animation.
.. code-block:: glsl ```
void vertex() { void vertex() {
// Animate Sprite moving in big circle around its location // Animate Sprite moving in big circle around its location
VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0); VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
} }
```
Conclusion Conclusion
---------- ----------

View File

@ -107,9 +107,9 @@ shaders on the fly. The first thing Godot shaders need is a declaration of what
type of shader they are. We set the variable `shader_type` to `spatial` type of shader they are. We set the variable `shader_type` to `spatial`
because this is a spatial shader. because this is a spatial shader.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
```
Next we will define the `vertex()` function. The `vertex()` function Next we will define the `vertex()` function. The `vertex()` function
determines where the vertices of your `Mesh( MeshInstance )` appear in determines where the vertices of your `Mesh( MeshInstance )` appear in
@ -118,20 +118,20 @@ make our flat plane appear like a little terrain.
We define the vertex shader like so: We define the vertex shader like so:
.. code-block:: glsl ```
void vertex() { void vertex() {
} }
```
With nothing in the `vertex()` function, Godot will use its default vertex With nothing in the `vertex()` function, Godot will use its default vertex
shader. We can easily start to make changes by adding a single line: shader. We can easily start to make changes by adding a single line:
.. code-block:: glsl ```
void vertex() { void vertex() {
VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z); VERTEX.y += cos(VERTEX.x) * sin(VERTEX.z);
} }
```
Adding this line, you should get an image like the one below. Adding this line, you should get an image like the one below.
@ -146,11 +146,11 @@ What we want to achieve is the look of little hills; after all. `cos` and
`sin` already look kind of like hills. We do so by scaling the inputs to the `sin` already look kind of like hills. We do so by scaling the inputs to the
`cos` and `sin` functions. `cos` and `sin` functions.
.. code-block:: glsl ```
void vertex() { void vertex() {
VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0); VERTEX.y += cos(VERTEX.x * 4.0) * sin(VERTEX.z * 4.0);
} }
```
![](img/cos4.png) ![](img/cos4.png)
@ -170,9 +170,9 @@ generating a noise texture that can be accessed from a shader.
To access a texture in a shader add the following code near the top of your To access a texture in a shader add the following code near the top of your
shader, outside the `vertex()` function. shader, outside the `vertex()` function.
.. code-block:: glsl ```
uniform sampler2D noise; uniform sampler2D noise;
```
This will allow you to send a noise texture to the shader. Now look in the This will allow you to send a noise texture to the shader. Now look in the
inspector under your material. You should see a section called "Shader Params". inspector under your material. You should see a section called "Shader Params".
@ -200,10 +200,10 @@ a` channels at the position. Since the noise texture is grayscale, all of the
values are the same, so we can use any one of the channels as the height. In values are the same, so we can use any one of the channels as the height. In
this case we'll use the `r`, or `x` channel. this case we'll use the `r`, or `x` channel.
.. code-block:: glsl ```
float height = texture(noise, VERTEX.xz / 2.0 + 0.5).x; float height = texture(noise, VERTEX.xz / 2.0 + 0.5).x;
VERTEX.y += height; VERTEX.y += height;
```
Note: `xyzw` is the same as `rgba` in GLSL, so instead of `texture().x` Note: `xyzw` is the same as `rgba` in GLSL, so instead of `texture().x`
above, we could use `texture().r`. See the `OpenGL documentation above, we could use `texture().r`. See the `OpenGL documentation
@ -228,9 +228,9 @@ that can be used in the shader. To use a uniform, you declare it in your
Let's make a uniform that changes the height of the terrain. Let's make a uniform that changes the height of the terrain.
.. code-block:: glsl ```
uniform float height_scale = 0.5; uniform float height_scale = 0.5;
```
Godot lets you initialize a uniform with a value; here, `height_scale` is set Godot lets you initialize a uniform with a value; here, `height_scale` is set
@ -239,10 +239,10 @@ to `0.5`. You can set uniforms from GDScript by calling the function
passed from GDScript takes precedence over the value used to initialize it in passed from GDScript takes precedence over the value used to initialize it in
the shader. the shader.
:: ```
# called from the MeshInstance # called from the MeshInstance
mesh.material.set_shader_param("height_scale", 0.5) mesh.material.set_shader_param("height_scale", 0.5)
```
Note: Note:
Changing uniforms in Spatial-based nodes is different from Changing uniforms in Spatial-based nodes is different from
@ -257,9 +257,9 @@ of the uniform variable in the `Shader( Shader )`. You can use the
uniform variable anywhere inside your `Shader( Shader )`. Here, we will uniform variable anywhere inside your `Shader( Shader )`. Here, we will
use it to set the height value instead of arbitrarily multiplying by `0.5`. use it to set the height value instead of arbitrarily multiplying by `0.5`.
.. code-block:: glsl ```
VERTEX.y += height * height_scale; VERTEX.y += height * height_scale;
```
Now it looks much better. Now it looks much better.
@ -302,9 +302,9 @@ tutorial, for now we will read normals from a texture.
Instead we will rely on the NoiseTexture again to calculate normals for us. We Instead we will rely on the NoiseTexture again to calculate normals for us. We
do that by passing in a second noise texture. do that by passing in a second noise texture.
.. code-block:: glsl ```
uniform sampler2D normalmap; uniform sampler2D normalmap;
```
Set this second uniform texture to another NoiseTexture with another Set this second uniform texture to another NoiseTexture with another
OpenSimplexNoise. But this time, check off "As Normalmap". OpenSimplexNoise. But this time, check off "As Normalmap".
@ -315,10 +315,10 @@ Now, because this is a normalmap and not a per-vertex normal, we are going to
assign it in the `fragment()` function. The `fragment()` function will be assign it in the `fragment()` function. The `fragment()` function will be
explained in more detail in the next part of this tutorial. explained in more detail in the next part of this tutorial.
.. code-block:: glsl ```
void fragment() { void fragment() {
} }
```
When we have normals that correspond to a specific vertex we set `NORMAL`, but When we have normals that correspond to a specific vertex we set `NORMAL`, but
if you have a normalmap that comes from a texture, set the normal using if you have a normalmap that comes from a texture, set the normal using
@ -333,8 +333,7 @@ that with varyings.
Above the `vertex()` define a `vec2` called `tex_position`. And inside the Above the `vertex()` define a `vec2` called `tex_position`. And inside the
`vertex()` function assign `VERTEX.xz` to `tex_position`. `vertex()` function assign `VERTEX.xz` to `tex_position`.
.. code-block:: glsl ```
varying vec2 tex_position; varying vec2 tex_position;
void vertex() { void vertex() {
@ -343,14 +342,15 @@ Above the `vertex()` define a `vec2` called `tex_position`. And inside the
float height = texture(noise, tex_position).x; float height = texture(noise, tex_position).x;
... ...
} }
```
And now we can access `tex_position` from the `fragment()` function. And now we can access `tex_position` from the `fragment()` function.
.. code-block:: glsl ```
void fragment() { void fragment() {
NORMALMAP = texture(normalmap, tex_position).xyz; NORMALMAP = texture(normalmap, tex_position).xyz;
} }
```
With the normals in place the light now reacts to the height of the mesh With the normals in place the light now reacts to the height of the mesh
dynamically. dynamically.
@ -364,8 +364,7 @@ We can even drag the light around and the lighting will update automatically.
Here is the full code for this tutorial. You can see it is not very long as Here is the full code for this tutorial. You can see it is not very long as
Godot handles most of the difficult stuff for you. Godot handles most of the difficult stuff for you.
.. code-block:: glsl ```
shader_type spatial; shader_type spatial;
uniform float height_scale = 0.5; uniform float height_scale = 0.5;
@ -383,6 +382,7 @@ Godot handles most of the difficult stuff for you.
void fragment() { void fragment() {
NORMALMAP = texture(normalmap, tex_position).xyz; NORMALMAP = texture(normalmap, tex_position).xyz;
} }
```
That is everything for this part. Hopefully, you now understand the basics of That is everything for this part. Hopefully, you now understand the basics of
vertex shaders in Godot. In the next part of this tutorial we will write a vertex shaders in Godot. In the next part of this tutorial we will write a