Port code examples to C# (D)

Includes:
 * Decal
 * Dictionary
 * Directory
 * DisplayServer
 * DTLSServer
 * DynamicFont
 * EditorImportPlugin
 * EditorPlugin
 * EditorScenePostImport
 * EditorScript
 * EditorSettings
 * EditorTranslationParserPlugin
 * Engine
 * Expression

Co-authored-by: Aaron Franke <arnfranke@yahoo.com>
This commit is contained in:
HaSa1002 2020-09-13 14:45:36 +02:00
parent 88a3db5bff
commit 8fb113bb4c
14 changed files with 645 additions and 114 deletions

View file

@ -6,8 +6,9 @@
<description>
This class is used to store the state of a DTLS server. Upon [method setup] it converts connected [PacketPeerUDP] to [PacketPeerDTLS] accepting them via [method take_connection] as DTLS clients. Under the hood, this class is used to store the DTLS state and cookies of the server. The reason of why the state and cookies are needed is outside of the scope of this documentation.
Below a small example of how to use it:
[codeblock]
# server.gd
[codeblocks]
[gdscript]
# ServerNode.gd
extends Node
var dtls := DTLSServer.new()
@ -28,15 +29,64 @@
continue # It is normal that 50% of the connections fails due to cookie exchange.
print("Peer connected!")
peers.append(dtls_peer)
for p in peers:
p.poll() # Must poll to update the state.
if p.get_status() == PacketPeerDTLS.STATUS_CONNECTED:
while p.get_available_packet_count() &gt; 0:
print("Received message from client: %s" % p.get_packet().get_string_from_utf8())
p.put_packet("Hello DTLS client".to_utf8())
[/codeblock]
[codeblock]
# client.gd
[/gdscript]
[csharp]
using Godot;
using System;
// ServerNode.cs
public class ServerNode : Node
{
public DTLSServer Dtls = new DTLSServer();
public UDPServer Server = new UDPServer();
public Godot.Collections.Array&lt;PacketPeerDTLS&gt; Peers = new Godot.Collections.Array&lt;PacketPeerDTLS&gt;();
public override void _Ready()
{
Server.Listen(4242);
var key = GD.Load&lt;CryptoKey&gt;("key.key"); // Your private key.
var cert = GD.Load&lt;X509Certificate&gt;("cert.crt"); // Your X509 certificate.
Dtls.Setup(key, cert);
}
public override void _Process(float delta)
{
while (Server.IsConnectionAvailable())
{
PacketPeerUDP peer = Server.TakeConnection();
PacketPeerDTLS dtlsPeer = Dtls.TakeConnection(peer);
if (dtlsPeer.GetStatus() != PacketPeerDTLS.Status.Handshaking)
{
continue; // It is normal that 50% of the connections fails due to cookie exchange.
}
GD.Print("Peer connected!");
Peers.Add(dtlsPeer);
}
foreach (var p in Peers)
{
p.Poll(); // Must poll to update the state.
if (p.GetStatus() == PacketPeerDTLS.Status.Connected)
{
while (p.GetAvailablePacketCount() &gt; 0)
{
GD.Print("Received Message From Client: " + p.GetPacket().GetStringFromUTF8());
p.PutPacket("Hello Dtls Client".ToUTF8());
}
}
}
}
}
[/csharp]
[/codeblocks]
[codeblocks]
[gdscript]
# ClientNode.gd
extends Node
var dtls := PacketPeerDTLS.new()
@ -56,7 +106,42 @@
while dtls.get_available_packet_count() &gt; 0:
print("Connected: %s" % dtls.get_packet().get_string_from_utf8())
connected = true
[/codeblock]
[/gdscript]
[csharp]
using Godot;
using System.Text;
// ClientNode.cs
public class ClientNode : Node
{
public PacketPeerDTLS Dtls = new PacketPeerDTLS();
public PacketPeerUDP Udp = new PacketPeerUDP();
public bool Connected = false;
public override void _Ready()
{
Udp.ConnectToHost("127.0.0.1", 4242);
Dtls.ConnectToPeer(Udp, false); // Use true in production for certificate validation!
}
public override void _Process(float delta)
{
Dtls.Poll();
if (Dtls.GetStatus() == PacketPeerDTLS.Status.Connected)
{
if (!Connected)
{
// Try to contact server
Dtls.PutPacket("The Answer Is..42!".ToUTF8());
}
while (Dtls.GetAvailablePacketCount() > 0)
{
GD.Print("Connected: " + Dtls.GetPacket().GetStringFromUTF8());
Connected = true;
}
}
}
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
</tutorials>

View file

@ -20,10 +20,18 @@
Returns the [Texture2D] associated with the specified [enum DecalTexture]. This is a convenience method, in most cases you should access the texture directly.
For example, instead of [code]albedo_tex = $Decal.get_texture(Decal.TEXTURE_ALBEDO)[/code], use [code]albedo_tex = $Decal.texture_albedo[/code].
One case where this is better than accessing the texture directly is when you want to copy one Decal's textures to another. For example:
[codeblock]
[codeblocks]
[gdscript]
for i in Decal.TEXTURE_MAX:
$NewDecal.set_texture(i, $OldDecal.get_texture(i))
[/codeblock]
[/gdscript]
[csharp]
for (int i = 0; i &lt; (int)Decal.DecalTexture.Max; i++)
{
GetNode&lt;Decal&gt;("NewDecal").SetTexture(i, GetNode&lt;Decal&gt;("OldDecal").GetTexture(i));
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="set_texture">
@ -37,10 +45,18 @@
Sets the [Texture2D] associated with the specified [enum DecalTexture]. This is a convenience method, in most cases you should access the texture directly.
For example, instead of [code]$Decal.set_texture(Decal.TEXTURE_ALBEDO, albedo_tex)[/code], use [code]$Decal.texture_albedo = albedo_tex[/code].
One case where this is better than accessing the texture directly is when you want to copy one Decal's textures to another. For example:
[codeblock]
[codeblocks]
[gdscript]
for i in Decal.TEXTURE_MAX:
$NewDecal.set_texture(i, $OldDecal.get_texture(i))
[/codeblock]
[/gdscript]
[csharp]
for (int i = 0; i &lt; (int)Decal.DecalTexture.Max; i++)
{
GetNode&lt;Decal&gt;("NewDecal").SetTexture(i, GetNode&lt;Decal&gt;("OldDecal").GetTexture(i));
}
[/csharp]
[/codeblocks]
</description>
</method>
</methods>

View file

@ -9,7 +9,8 @@
Erasing elements while iterating over them [b]is not supported[/b] and will result in undefined behavior.
[b]Note:[/b] Dictionaries are always passed by reference. To get a copy of a dictionary which can be modified independently of the original dictionary, use [method duplicate].
Creating a dictionary:
[codeblock]
[codeblocks]
[gdscript]
var my_dir = {} # Creates an empty dictionary.
var points_dir = {"White": 50, "Yellow": 75, "Orange": 100}
var another_dir = {
@ -17,28 +18,74 @@
key2: value2,
key3: value3,
}
[/codeblock]
[/gdscript]
[csharp]
var myDir = new Godot.Collections.Dictionary(); // Creates an empty dictionary.
var pointsDir = new Godot.Collections.Dictionary
{
{"White", 50},
{"Yellow", 75},
{"Orange", 100}
};
[/csharp]
[/codeblocks]
You can access a dictionary's values by referencing the appropriate key. In the above example, [code]points_dir["White"][/code] will return [code]50[/code]. You can also write [code]points_dir.White[/code], which is equivalent. However, you'll have to use the bracket syntax if the key you're accessing the dictionary with isn't a fixed string (such as a number or variable).
[codeblock]
export(String, "White", "Yellow", "Orange") var my_color
[codeblocks]
[gdscript]
export(string, "White", "Yellow", "Orange") var my_color
var points_dir = {"White": 50, "Yellow": 75, "Orange": 100}
func _ready():
# We can't use dot syntax here as `my_color` is a variable.
var points = points_dir[my_color]
[/codeblock]
[/gdscript]
[csharp]
[Export(PropertyHint.Enum, "White,Yellow,Orange")]
public string MyColor { get; set; }
public Godot.Collections.Dictionary pointsDir = new Godot.Collections.Dictionary
{
{"White", 50},
{"Yellow", 75},
{"Orange", 100}
};
public override void _Ready()
{
int points = (int)pointsDir[MyColor];
}
[/csharp]
[/codeblocks]
In the above code, [code]points[/code] will be assigned the value that is paired with the appropriate color selected in [code]my_color[/code].
Dictionaries can contain more complex data:
[codeblock]
[codeblocks]
[gdscript]
my_dir = {"First Array": [1, 2, 3, 4]} # Assigns an Array to a String key.
[/codeblock]
[/gdscript]
[csharp]
var myDir = new Godot.Collections.Dictionary
{
{"First Array", new Godot.Collections.Array{1, 2, 3, 4}}
};
[/csharp]
[/codeblocks]
To add a key to an existing dictionary, access it like an existing key and assign to it:
[codeblock]
[codeblocks]
[gdscript]
var points_dir = {"White": 50, "Yellow": 75, "Orange": 100}
points_dir["Blue"] = 150 # Add "Blue" as a key and assign 150 as its value.
[/codeblock]
[/gdscript]
[csharp]
var pointsDir = new Godot.Collections.Dictionary
{
{"White", 50},
{"Yellow", 75},
{"Orange", 100}
};
pointsDir["blue"] = 150; // Add "Blue" as a key and assign 150 as its value.
[/csharp]
[/codeblocks]
Finally, dictionaries can contain different types of keys and values in the same dictionary:
[codeblock]
[codeblocks]
[gdscript]
# This is a valid dictionary.
# To access the string "Nested value" below, use `my_dir.sub_dir.sub_key` or `my_dir["sub_dir"]["sub_key"]`.
# Indexing styles can be mixed and matched depending on your needs.
@ -48,29 +95,75 @@
7: "Hello",
"sub_dir": {"sub_key": "Nested value"},
}
[/codeblock]
[/gdscript]
[csharp]
// This is a valid dictionary.
// To access the string "Nested value" below, use `my_dir.sub_dir.sub_key` or `my_dir["sub_dir"]["sub_key"]`.
// Indexing styles can be mixed and matched depending on your needs.
var myDir = new Godot.Collections.Dictionary {
{"String Key", 5},
{4, new Godot.Collections.Array{1,2,3}},
{7, "Hello"},
{"sub_dir", new Godot.Collections.Dictionary{{"sub_key", "Nested value"}}}
};
[/csharp]
[/codeblocks]
[b]Note:[/b] Unlike [Array]s, you can't compare dictionaries directly:
[codeblock]
array1 = [1, 2, 3]
array2 = [1, 2, 3]
[codeblocks]
[gdscript]
var array1 = [1, 2, 3]
var array2 = [1, 2, 3]
func compare_arrays():
print(array1 == array2) # Will print true.
dir1 = {"a": 1, "b": 2, "c": 3}
dir2 = {"a": 1, "b": 2, "c": 3}
var dir1 = {"a": 1, "b": 2, "c": 3}
var dir2 = {"a": 1, "b": 2, "c": 3}
func compare_dictionaries():
print(dir1 == dir2) # Will NOT print true.
[/codeblock]
[/gdscript]
[csharp]
// You have to use GD.Hash().
public Godot.Collections.Array array1 = new Godot.Collections.Array{1, 2, 3};
public Godot.Collections.Array array2 = new Godot.Collections.Array{1, 2, 3};
public void CompareArrays()
{
GD.Print(array1 == array2); // Will print FALSE!!
GD.Print(GD.Hash(array1) == GD.Hash(array2)); // Will print true.
}
public Godot.Collections.Dictionary dir1 = new Godot.Collections.Dictionary{{"a", 1}, {"b", 2}, {"c", 3}};
public Godot.Collections.Dictionary dir2 = new Godot.Collections.Dictionary{{"a", 1}, {"b", 2}, {"c", 3}};
public void CompareDictionaries()
{
GD.Print(dir1 == dir2); // Will NOT print true.
}
[/csharp]
[/codeblocks]
You need to first calculate the dictionary's hash with [method hash] before you can compare them:
[codeblock]
dir1 = {"a": 1, "b": 2, "c": 3}
dir2 = {"a": 1, "b": 2, "c": 3}
[codeblocks]
[gdscript]
var dir1 = {"a": 1, "b": 2, "c": 3}
var dir2 = {"a": 1, "b": 2, "c": 3}
func compare_dictionaries():
print(dir1.hash() == dir2.hash()) # Will print true.
[/codeblock]
[/gdscript]
[csharp]
// You have to use GD.Hash().
public Godot.Collections.Dictionary dir1 = new Godot.Collections.Dictionary{{"a", 1}, {"b", 2}, {"c", 3}};
public Godot.Collections.Dictionary dir2 = new Godot.Collections.Dictionary{{"a", 1}, {"b", 2}, {"c", 3}};
public void CompareDictionaries()
{
GD.Print(GD.Hash(dir1) == GD.Hash(dir2)); // Will print true.
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
<link title="GDScript basics: Dictionary">https://docs.godotengine.org/en/latest/getting_started/scripting/gdscript/gdscript_basics.html#dictionary</link>
@ -129,11 +222,20 @@
<description>
Returns [code]true[/code] if the dictionary has a given key.
[b]Note:[/b] This is equivalent to using the [code]in[/code] operator as follows:
[codeblock]
[codeblocks]
[gdscript]
# Will evaluate to `true`.
if "godot" in {"godot": "engine"}:
pass
[/codeblock]
[/gdscript]
[csharp]
// You have to use Contains() here as an alternative to GDScript's `in` operator.
if (new Godot.Collections.Dictionary{{"godot", "engine"}}.Contains("godot"))
{
// I am executed.
}
[/csharp]
[/codeblocks]
This method (like the [code]in[/code] operator) will evaluate to [code]true[/code] as long as the key exists, even if the associated value is [code]null[/code].
</description>
</method>
@ -151,12 +253,21 @@
</return>
<description>
Returns a hashed integer value representing the dictionary contents. This can be used to compare dictionaries by value:
[codeblock]
[codeblocks]
[gdscript]
var dict1 = {0: 10}
var dict2 = {0: 10}
# The line below prints `true`, whereas it would have printed `false` if both variables were compared directly.
print(dict1.hash() == dict2.hash())
[/codeblock]
[/gdscript]
[csharp]
var dict1 = new Godot.Collections.Dictionary{{0, 10}};
var dict2 = new Godot.Collections.Dictionary{{0, 10}};
// The line below prints `true`, whereas it would have printed `false` if both variables were compared directly.
// Dictionary has no Hash() method. Use GD.Hash() instead.
GD.Print(GD.Hash(dict1) == GD.Hash(dict2));
[/csharp]
[/codeblocks]
[b]Note:[/b] Dictionaries with the same keys/values but in a different order will have a different hash.
</description>
</method>

View file

@ -7,7 +7,8 @@
Directory type. It is used to manage directories and their content (not restricted to the project folder).
When creating a new [Directory], it must be explicitly opened using [method open] before most methods can be used. However, [method file_exists] and [method dir_exists] can be used without opening a directory. If so, they use a path relative to [code]res://[/code].
Here is an example on how to iterate through the files of a directory:
[codeblock]
[codeblocks]
[gdscript]
func dir_contents(path):
var dir = Directory.new()
if dir.open(path) == OK:
@ -21,7 +22,35 @@
file_name = dir.get_next()
else:
print("An error occurred when trying to access the path.")
[/codeblock]
[/gdscript]
[csharp]
public void DirContents(string path)
{
var dir = new Directory();
if (dir.Open(path) == Error.Ok)
{
dir.ListDirBegin();
string fileName = dir.GetNext();
while (fileName != "")
{
if (dir.CurrentIsDir())
{
GD.Print("Found directory: " + fileName);
}
else
{
GD.Print("Found file: " + fileName);
}
fileName = dir.GetNext();
}
}
else
{
GD.Print("An error occurred when trying to access the path.");
}
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
<link title="File system">https://docs.godotengine.org/en/latest/getting_started/step_by_step/filesystem.html</link>

View file

@ -923,7 +923,8 @@
<description>
Sets a polygonal region of the window which accepts mouse events. Mouse events outside the region will be passed through.
Passing an empty array will disable passthrough support (all mouse events will be intercepted by the window, which is the default behavior).
[codeblock]
[codeblocks]
[gdscript]
# Set region, using Path2D node.
DisplayServer.window_set_mouse_passthrough($Path2D.curve.get_baked_points())
@ -932,7 +933,18 @@
# Reset region to default.
DisplayServer.window_set_mouse_passthrough([])
[/codeblock]
[/gdscript]
[csharp]
// Set region, using Path2D node.
DisplayServer.WindowSetMousePassthrough(GetNode&lt;Path2D&gt;("Path2D").Curve.GetBakedPoints());
// Set region, using Polygon2D node.
DisplayServer.WindowSetMousePassthrough(GetNode&lt;Polygon2D&gt;("Polygon2D").Polygon);
// Reset region to default.
DisplayServer.WindowSetMousePassthrough(new Vector2[] {});
[/csharp]
[/codeblocks]
[b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux and macOS it is.
[b]Note:[/b] This method is implemented on Linux, macOS and Windows.
</description>

View file

@ -6,12 +6,20 @@
<description>
DynamicFont renders vector font files (such as TTF or OTF) dynamically at runtime instead of using a prerendered texture atlas like [BitmapFont]. This trades the faster loading time of [BitmapFont]s for the ability to change font parameters like size and spacing during runtime. [DynamicFontData] is used for referencing the font file paths. DynamicFont also supports defining one or more fallback fonts, which will be used when displaying a character not supported by the main font.
DynamicFont uses the [url=https://www.freetype.org/]FreeType[/url] library for rasterization.
[codeblock]
[codeblocks]
[gdscript]
var dynamic_font = DynamicFont.new()
dynamic_font.font_data = load("res://BarlowCondensed-Bold.ttf")
dynamic_font.size = 64
$"Label".set("custom_fonts/font", dynamic_font)
[/codeblock]
[/gdscript]
[csharp]
var dynamicFont = new DynamicFont();
dynamicFont.FontData = ResourceLoader.Load&lt;DynamicFontData&gt;("res://BarlowCondensed-Bold.ttf");
dynamicFont.Size = 64;
GetNode("Label").Set("custom_fonts/font", dynamicFont);
[/csharp]
[/codeblocks]
[b]Note:[/b] DynamicFont doesn't support features such as kerning, right-to-left typesetting, ligatures, text shaping, variable fonts and optional font features yet. If you wish to "bake" an optional font feature into a TTF font file, you can use [url=https://fontforge.org/]FontForge[/url] to do so. In FontForge, use [b]File &gt; Generate Fonts[/b], click [b]Options[/b], choose the desired features then generate the font.
</description>
<tutorials>

View file

@ -7,7 +7,8 @@
EditorImportPlugins provide a way to extend the editor's resource import functionality. Use them to import resources from custom files or to provide alternatives to the editor's existing importers. Register your [EditorPlugin] with [method EditorPlugin.add_import_plugin].
EditorImportPlugins work by associating with specific file extensions and a resource type. See [method get_recognized_extensions] and [method get_resource_type]. They may optionally specify some import presets that affect the import process. EditorImportPlugins are responsible for creating the resources and saving them in the [code].godot/imported[/code] directory.
Below is an example EditorImportPlugin that imports a [Mesh] from a file with the extension ".special" or ".spec":
[codeblock]
[codeblocks]
[gdscript]
tool
extends EditorImportPlugin
@ -39,14 +40,76 @@
var file = File.new()
if file.open(source_file, File.READ) != OK:
return FAILED
var mesh = Mesh.new()
# Fill the Mesh with data read in "file", left as an exercise to the reader
var mesh = ArrayMesh.new()
# Fill the Mesh with data read in "file", left as an exercise to the reader.
var filename = save_path + "." + get_save_extension()
ResourceSaver.save(filename, mesh)
return OK
[/codeblock]
[/gdscript]
[csharp]
using Godot;
using System;
public class MySpecialPlugin : EditorImportPlugin
{
public override String GetImporterName()
{
return "my.special.plugin";
}
public override String GetVisibleName()
{
return "Special Mesh Importer";
}
public override Godot.Collections.Array GetRecognizedExtensions()
{
return new Godot.Collections.Array{"special", "spec"};
}
public override String GetSaveExtension()
{
return "mesh";
}
public override String GetResourceType()
{
return "Mesh";
}
public override int GetPresetCount()
{
return 1;
}
public override String GetPresetName(int i)
{
return "Default";
}
public override Godot.Collections.Array GetImportOptions(int i)
{
return new Godot.Collections.Array{new Godot.Collections.Dictionary{{"name", "myOption"}, {"defaultValue", false}}};
}
public override int Import(String sourceFile, String savePath, Godot.Collections.Dictionary options, Godot.Collections.Array platformVariants, Godot.Collections.Array genFiles)
{
var file = new File();
if (file.Open(sourceFile, File.ModeFlags.Read) != Error.Ok)
{
return (int)Error.Failed;
}
var mesh = new ArrayMesh();
// Fill the Mesh with data read in "file", left as an exercise to the reader.
String filename = savePath + "." + GetSaveExtension();
ResourceSaver.Save(filename, mesh);
return (int)Error.Ok;
}
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
<link title="Import plugins">https://docs.godotengine.org/en/latest/tutorials/plugins/editor/import_plugins.html</link>
@ -84,14 +147,28 @@
</argument>
<description>
This method can be overridden to hide specific import options if conditions are met. This is mainly useful for hiding options that depend on others if one of them is disabled. For example:
[codeblock]
[codeblocks]
[gdscript]
func get_option_visibility(option, options):
# Only show the lossy quality setting if the compression mode is set to "Lossy".
if option == "compress/lossy_quality" and options.has("compress/mode"):
return int(options["compress/mode"]) == COMPRESS_LOSSY
return int(options["compress/mode"]) == COMPRESS_LOSSY # This is a constant that you set
return true
[/codeblock]
[/gdscript]
[csharp]
public void GetOptionVisibility(string option, Godot.Collections.Dictionary options)
{
// Only show the lossy quality setting if the compression mode is set to "Lossy".
if (option == "compress/lossyQuality" &amp;&amp; options.Contains("compress/mode"))
{
return (int)options["compress/mode"] == COMPRESS_LOSSY; // This is a constant you set
}
return true;
}
[/csharp]
[/codeblocks]
Return [code]true[/code] to make all options always visible.
</description>
</method>

View file

@ -227,21 +227,35 @@
</argument>
<description>
Called when there is a root node in the current edited scene, [method handles] is implemented and an [InputEvent] happens in the 2D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [code]event[/code], otherwise forwards [code]event[/code] to other Editor classes. Example:
[codeblock]
[codeblocks]
[gdscript]
# Prevents the InputEvent to reach other Editor classes
func forward_canvas_gui_input(event):
var forward = true
return forward
[/codeblock]
return true
[/gdscript]
[csharp]
// Prevents the InputEvent to reach other Editor classes
public override bool ForwardCanvasGuiInput(InputEvent @event)
{
return true;
}
[/csharp]
[/codeblocks]
Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. Example:
[codeblock]
# Consumes InputEventMouseMotion and forwards other InputEvent types
[codeblocks]
[gdscript]
# Consumes InputEventMouseMotion and forwards other InputEvent types.
func forward_canvas_gui_input(event):
var forward = false
if event is InputEventMouseMotion:
forward = true
return forward
[/codeblock]
return event is InputEventMouseMotion
[/gdscript]
[csharp]
// Consumes InputEventMouseMotion and forwards other InputEvent types.
public override bool ForwardCanvasGuiInput(InputEvent @event)
{
return @event is InputEventMouseMotion;
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="forward_spatial_gui_input" qualifiers="virtual">
@ -253,21 +267,35 @@
</argument>
<description>
Called when there is a root node in the current edited scene, [method handles] is implemented and an [InputEvent] happens in the 3D viewport. Intercepts the [InputEvent], if [code]return true[/code] [EditorPlugin] consumes the [code]event[/code], otherwise forwards [code]event[/code] to other Editor classes. Example:
[codeblock]
# Prevents the InputEvent to reach other Editor classes
[codeblocks]
[gdscript]
# Prevents the InputEvent to reach other Editor classes.
func forward_spatial_gui_input(camera, event):
var forward = true
return forward
[/codeblock]
return true
[/gdscript]
[csharp]
// Prevents the InputEvent to reach other Editor classes.
public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event)
{
return true;
}
[/csharp]
[/codeblocks]
Must [code]return false[/code] in order to forward the [InputEvent] to other Editor classes. Example:
[codeblock]
# Consumes InputEventMouseMotion and forwards other InputEvent types
[codeblocks]
[gdscript]
# Consumes InputEventMouseMotion and forwards other InputEvent types.
func forward_spatial_gui_input(camera, event):
var forward = false
if event is InputEventMouseMotion:
forward = true
return forward
[/codeblock]
return event is InputEventMouseMotion
[/gdscript]
[csharp]
// Consumes InputEventMouseMotion and forwards other InputEvent types.
public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event)
{
return @event is InputEventMouseMotion;
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="get_breakpoints" qualifiers="virtual">
@ -291,13 +319,24 @@
Override this method in your plugin to return a [Texture2D] in order to give it an icon.
For main screen plugins, this appears at the top of the screen, to the right of the "2D", "3D", "Script", and "AssetLib" buttons.
Ideally, the plugin icon should be white with a transparent background and 16x16 pixels in size.
[codeblock]
[codeblocks]
[gdscript]
func get_plugin_icon():
# You can use a custom icon:
return preload("res://addons/my_plugin/my_plugin_icon.svg")
# Or use a built-in icon:
return get_editor_interface().get_base_control().get_icon("Node", "EditorIcons")
[/codeblock]
[/gdscript]
[csharp]
public override Texture2D GetPluginIcon()
{
// You can use a custom icon:
return ResourceLoader.Load&lt;Texture2D&gt;("res://addons/my_plugin/my_plugin_icon.svg");
// Or use a built-in icon:
return GetEditorInterface().GetBaseControl().GetIcon("Node", "EditorIcons");
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="get_plugin_name" qualifiers="virtual">

View file

@ -6,24 +6,50 @@
<description>
Imported scenes can be automatically modified right after import by setting their [b]Custom Script[/b] Import property to a [code]tool[/code] script that inherits from this class.
The [method post_import] callback receives the imported scene's root node and returns the modified version of the scene. Usage example:
[codeblock]
tool # Needed so it runs in editor
[codeblocks]
[gdscript]
tool # Needed so it runs in editor.
extends EditorScenePostImport
# This sample changes all node names
# Called right after the scene is imported and gets the root node
# This sample changes all node names.
# Called right after the scene is imported and gets the root node.
func post_import(scene):
# Change all node names to "modified_[oldnodename]"
iterate(scene)
return scene # Remember to return the imported scene
func iterate(node):
if node != null:
node.name = "modified_" + node.name
for child in node.get_children():
iterate(child)
[/codeblock]
[/gdscript]
[csharp]
using Godot;
// This sample changes all node names.
// Called right after the scene is imported and gets the root node.
[Tool]
public class NodeRenamer : EditorScenePostImport
{
public override Object PostImport(Object scene)
{
// Change all node names to "modified_[oldnodename]"
Iterate(scene as Node);
return scene; // Remember to return the imported scene
}
public void Iterate(Node node)
{
if (node != null)
{
node.Name = "modified_" + node.Name;
foreach (Node child in node.GetChildren())
{
Iterate(child);
}
}
}
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
<link title="Importing 3D scenes: Custom script">https://docs.godotengine.org/en/latest/getting_started/workflow/assets/importing_scenes.html#custom-script</link>

View file

@ -7,13 +7,28 @@
Scripts extending this class and implementing its [method _run] method can be executed from the Script Editor's [b]File &gt; Run[/b] menu option (or by pressing [kbd]Ctrl + Shift + X[/kbd]) while the editor is running. This is useful for adding custom in-editor functionality to Godot. For more complex additions, consider using [EditorPlugin]s instead.
[b]Note:[/b] Extending scripts need to have [code]tool[/code] mode enabled.
[b]Example script:[/b]
[codeblock]
[codeblocks]
[gdscript]
tool
extends EditorScript
func _run():
print("Hello from the Godot Editor!")
[/codeblock]
[/gdscript]
[csharp]
using Godot;
using System;
[Tool]
public class HelloEditor : EditorScript
{
public override void _Run()
{
GD.Print("Hello from the Godot Editor!");
}
}
[/csharp]
[/codeblocks]
[b]Note:[/b] The script is run in the Editor context, which means the output is visible in the console window started with the Editor (stdout) instead of the usual Godot [b]Output[/b] dock.
</description>
<tutorials>

View file

@ -7,15 +7,24 @@
Object that holds the project-independent editor settings. These settings are generally visible in the [b]Editor &gt; Editor Settings[/b] menu.
Property names use slash delimiters to distinguish sections. Setting values can be of any [Variant] type. It's recommended to use [code]snake_case[/code] for editor settings to be consistent with the Godot editor itself.
Accessing the settings can be done using the following methods, such as:
[codeblock]
# `settings.set("some/property", value)` also works as this class overrides `_set()` internally.
settings.set_setting("some/property",value)
# `settings.get("some/property", value)` also works as this class overrides `_get()` internally.
[codeblocks]
[gdscript]
var settings = EditorInterface.get_editor_settings()
# `settings.set("some/property", 10)` also works as this class overrides `_set()` internally.
settings.set_setting("some/property", 10)
# `settings.get("some/property")` also works as this class overrides `_get()` internally.
settings.get_setting("some/property")
var list_of_settings = settings.get_property_list()
[/codeblock]
[/gdscript]
[csharp]
EditorSettings settings = GetEditorInterface().GetEditorSettings();
// `settings.set("some/property", value)` also works as this class overrides `_set()` internally.
settings.SetSetting("some/property", Value);
// `settings.get("some/property", value)` also works as this class overrides `_get()` internally.
settings.GetSetting("some/property");
Godot.Collections.Array listOfSettings = settings.GetPropertyList();
[/csharp]
[/codeblocks]
[b]Note:[/b] This class shouldn't be instantiated directly. Instead, access the singleton using [method EditorInterface.get_editor_settings].
</description>
<tutorials>
@ -32,8 +41,10 @@
- [code]type[/code]: [int] (see [enum Variant.Type])
- optionally [code]hint[/code]: [int] (see [enum PropertyHint]) and [code]hint_string[/code]: [String]
[b]Example:[/b]
[codeblock]
editor_settings.set("category/property_name", 0)
[codeblocks]
[gdscript]
var settings = EditorInterface.get_editor_settings()
settings.set("category/property_name", 0)
var property_info = {
"name": "category/property_name",
@ -42,8 +53,23 @@
"hint_string": "one,two,three"
}
editor_settings.add_property_info(property_info)
[/codeblock]
settings.add_property_info(property_info)
[/gdscript]
[csharp]
var settings = GetEditorInterface().GetEditorSettings();
settings.Set("category/property_name", 0);
var propertyInfo = new Godot.Collections.Dictionary
{
{"name", "category/propertyName"},
{"type", Variant.Type.Int},
{"hint", PropertyHint.Enum},
{"hint_string", "one,two,three"}
};
settings.AddPropertyInfo(propertyInfo);
[/csharp]
[/codeblocks]
</description>
</method>
<method name="erase">

View file

@ -9,45 +9,95 @@
When adding to [code]msgids_context_plural[/code], you must add the data using the format [code]["A", "B", "C"][/code], where [code]A[/code] represents the extracted string, [code]B[/code] represents the context, and [code]C[/code] represents the plural version of the extracted string. If you want to add only context but not plural, put [code]""[/code] for the plural slot. The idea is the same if you only want to add plural but not context. See the code below for concrete examples.
The extracted strings will be written into a POT file selected by user under "POT Generation" in "Localization" tab in "Project Settings" menu.
Below shows an example of a custom parser that extracts strings from a CSV file to write into a POT.
[codeblock]
[codeblocks]
[gdscript]
tool
extends EditorTranslationParserPlugin
func parse_file(path, msgids, msgids_context_plural):
var file = File.new()
file.open(path, File.READ)
var text = file.get_as_text()
var split_strs = text.split(",", false, 0)
var split_strs = text.split(",", false)
for s in split_strs:
msgids.append(s)
#print("Extracted string: " + s)
func get_recognized_extensions():
return ["csv"]
[/codeblock]
[/gdscript]
[csharp]
using Godot;
using System;
[Tool]
public class CustomParser : EditorTranslationParserPlugin
{
public override void ParseFile(string path, Godot.Collections.Array msgids, Godot.Collections.Array msgidsContextPlural)
{
var file = new File();
file.Open(path, File.ModeFlags.Read);
string text = file.GetAsText();
string[] splitStrs = text.Split(",", false);
foreach (var s in splitStrs)
{
msgids.Add(s);
//GD.Print("Extracted string: " + s)
}
}
public override Godot.Collections.Array GetRecognizedExtensions()
{
return new Godot.Collections.Array{"csv"};
}
}
[/csharp]
[/codeblocks]
To add a translatable string associated with context or plural, add it to [code]msgids_context_plural[/code]:
[codeblock]
[codeblocks]
[gdscript]
# This will add a message with msgid "Test 1", msgctxt "context", and msgid_plural "test 1 plurals".
msgids_context_plural.append(["Test 1", "context", "test 1 plurals"])
# This will add a message with msgid "A test without context" and msgid_plural "plurals".
msgids_context_plural.append(["A test without context", "", "plurals"])
# This will add a message with msgid "Only with context" and msgctxt "a friendly context".
msgids_context_plural.append(["Only with context", "a friendly context", ""])
[/codeblock]
[/gdscript]
[csharp]
// This will add a message with msgid "Test 1", msgctxt "context", and msgid_plural "test 1 plurals".
msgidsContextPlural.Add(new Godot.Collections.Array{"Test 1", "context", "test 1 Plurals"});
// This will add a message with msgid "A test without context" and msgid_plural "plurals".
msgidsContextPlural.Add(new Godot.Collections.Array{"A test without context", "", "plurals"});
// This will add a message with msgid "Only with context" and msgctxt "a friendly context".
msgidsContextPlural.Add(new Godot.Collections.Array{"Only with context", "a friendly context", ""});
[/csharp]
[/codeblocks]
[b]Note:[/b] If you override parsing logic for standard script types (GDScript, C#, etc.), it would be better to load the [code]path[/code] argument using [method ResourceLoader.load]. This is because built-in scripts are loaded as [Resource] type, not [File] type.
For example:
[codeblock]
[codeblocks]
[gdscript]
func parse_file(path, msgids, msgids_context_plural):
var res = ResourceLoader.load(path, "Script")
var text = res.get_source_code()
var text = res.source_code
# Parsing logic.
func get_recognized_extensions():
return ["gd"]
[/codeblock]
[/gdscript]
[csharp]
public override void ParseFile(string path, Godot.Collections.Array msgids, Godot.Collections.Array msgidsContextPlural)
{
var res = ResourceLoader.Load&lt;Script&gt;(path, "Script");
string text = res.SourceCode;
// Parsing logic.
}
public override Godot.Collections.Array GetRecognizedExtensions()
{
return new Godot.Collections.Array{"gd"};
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
</tutorials>

View file

@ -117,12 +117,24 @@
[code]year[/code] - Holds the year the version was released in as an int
[code]string[/code] - [code]major[/code] + [code]minor[/code] + [code]patch[/code] + [code]status[/code] + [code]build[/code] in a single String
The [code]hex[/code] value is encoded as follows, from left to right: one byte for the major, one byte for the minor, one byte for the patch version. For example, "3.1.12" would be [code]0x03010C[/code]. [b]Note:[/b] It's still an int internally, and printing it will give you its decimal representation, which is not particularly meaningful. Use hexadecimal literals for easy version comparisons from code:
[codeblock]
[codeblocks]
[gdscript]
if Engine.get_version_info().hex &gt;= 0x030200:
# Do things specific to version 3.2 or later
else:
# Do things specific to versions before 3.2
[/codeblock]
[/gdscript]
[csharp]
if ((int)Engine.GetVersionInfo()["hex"] &gt;= 0x030200)
{
// Do things specific to version 3.2 or later
}
else
{
// Do things specific to versions before 3.2
}
[/csharp]
[/codeblocks]
</description>
</method>
<method name="has_singleton" qualifiers="const">

View file

@ -7,21 +7,46 @@
An expression can be made of any arithmetic operation, built-in math function call, method call of a passed instance, or built-in type construction call.
An example expression text using the built-in math functions could be [code]sqrt(pow(3, 2) + pow(4, 2))[/code].
In the following example we use a [LineEdit] node to write our expression and show the result.
[codeblock]
onready var expression = Expression.new()
[codeblocks]
[gdscript]
var expression = Expression.new()
func _ready():
$LineEdit.connect("text_entered", self, "_on_text_entered")
func _on_text_entered(command):
var error = expression.parse(command, [])
var error = expression.parse(command)
if error != OK:
print(expression.get_error_text())
return
var result = expression.execute([], null, true)
var result = expression.execute()
if not expression.has_execute_failed():
$LineEdit.text = str(result)
[/codeblock]
[/gdscript]
[csharp]
public Expression expression = new Expression();
public override void _Ready()
{
GetNode("LineEdit").Connect("text_entered", this, nameof(OnTextEntered));
}
private void OnTextEntered(string command)
{
Error error = expression.Parse(command);
if (error != Error.Ok)
{
GD.Print(expression.GetErrorText());
return;
}
object result = expression.Execute();
if (!expression.HasExecuteFailed())
{
GetNode&lt;LineEdit&gt;("LineEdit").Text = result.ToString();
}
}
[/csharp]
[/codeblocks]
</description>
<tutorials>
</tutorials>