├── GeneratorTester
├── Program.cs
├── ComponentOne.cs
├── ComponentTwo.cs
├── BaseEntity.cs
├── ComponentThree.cs
├── TestComponent.cs
├── TestEntity.cs
└── GeneratorTester.csproj
├── Godot.Composition
├── Attributes
│ ├── EntityAttribute.cs
│ ├── ComponentAttribute.cs
│ └── ComponentDependencyAttribute.cs
├── IComponent.cs
├── ComponentHelper.cs
├── Godot.Composition.csproj
├── IEntity.cs
├── ExtensionMethods.cs
├── ComponentContainer.cs
└── INode.cs
├── Godot.Composition.SourceGenerator
├── Properties
│ └── launchSettings.json
├── Godot.Composition.SourceGenerator.csproj
└── GodotCompositionGenerator.cs
├── .github
└── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
├── LICENSE
├── Godot.Composition.sln
├── .gitignore
└── README.md
/GeneratorTester/Program.cs:
--------------------------------------------------------------------------------
1 | // See https://aka.ms/new-console-template for more information
2 | Console.WriteLine("Hello, World!");
3 |
--------------------------------------------------------------------------------
/Godot.Composition/Attributes/EntityAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Godot.Composition;
4 |
5 | public class EntityAttribute : Attribute
6 | {
7 | }
8 |
--------------------------------------------------------------------------------
/Godot.Composition.SourceGenerator/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "DebugGeneratorTester": {
4 | "commandName": "DebugRoslynComponent",
5 | "targetProject": "..\\GeneratorTester\\GeneratorTester.csproj"
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/Godot.Composition/IComponent.cs:
--------------------------------------------------------------------------------
1 | namespace Godot.Composition;
2 |
3 | ///
4 | /// Interface for a component node.
5 | ///
6 | public interface IComponent : INode
7 | {
8 | ///
9 | /// Resolves component reference dependencies specified for this component.
10 | ///
11 | void ResolveDependencies();
12 | }
13 |
--------------------------------------------------------------------------------
/Godot.Composition/Attributes/ComponentAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Godot.Composition;
4 |
5 | public class ComponentAttribute : Attribute
6 | {
7 | public Type ParentType
8 | {
9 | get;
10 | set;
11 | }
12 |
13 | public ComponentAttribute(Type parentType)
14 | {
15 | ParentType = parentType;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/GeneratorTester/ComponentOne.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using Godot.Composition;
3 | using System.ComponentModel;
4 |
5 | namespace GeneratorTester
6 | {
7 | [Component(typeof(CharacterBody2D))]
8 | public partial class ComponentOne : Node
9 | {
10 | public override void _Ready()
11 | {
12 | base._Ready();
13 | InitializeComponent();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/GeneratorTester/ComponentTwo.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using Godot.Composition;
3 | using System.ComponentModel;
4 |
5 | namespace GeneratorTester
6 | {
7 | [Component(typeof(CharacterBody2D))]
8 | public partial class ComponentTwo : Node
9 | {
10 | public override void _Ready()
11 | {
12 | base._Ready();
13 | InitializeComponent();
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/GeneratorTester/BaseEntity.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using Godot.Composition;
3 |
4 | namespace GeneratorTester
5 | {
6 | public interface ITestInterface
7 | {
8 |
9 | }
10 |
11 | [Entity]
12 | public partial class BaseEntity : CharacterBody2D, ITestInterface
13 | {
14 | public override void _Ready()
15 | {
16 | base._Ready();
17 | InitializeEntity();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/GeneratorTester/ComponentThree.cs:
--------------------------------------------------------------------------------
1 | using Godot.Composition;
2 | using Godot;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace DifferentNamespace;
10 |
11 | [Component(typeof(CharacterBody2D))]
12 | public partial class ComponentThree : Node
13 | {
14 | public override void _Ready()
15 | {
16 | base._Ready();
17 | InitializeComponent();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Godot.Composition/Attributes/ComponentDependencyAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Godot.Composition;
4 |
5 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
6 | public class ComponentDependencyAttribute : Attribute
7 | {
8 | public Type ComponentType
9 | {
10 | get;
11 | set;
12 | }
13 |
14 | public ComponentDependencyAttribute(Type componentType)
15 | {
16 | ComponentType = componentType;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/GeneratorTester/TestComponent.cs:
--------------------------------------------------------------------------------
1 | using DifferentNamespace;
2 | using Godot;
3 | using Godot.Composition;
4 | using System.ComponentModel;
5 |
6 | namespace GeneratorTester
7 | {
8 | [Component(typeof(CharacterBody2D))]
9 | [ComponentDependency(typeof(ComponentOne))]
10 | [ComponentDependency(typeof(ComponentTwo))]
11 | [ComponentDependency(typeof(ComponentThree))]
12 | public partial class TestComponent : Node
13 | {
14 | public override void _Ready()
15 | {
16 | base._Ready();
17 | InitializeComponent();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/GeneratorTester/TestEntity.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using Godot.Composition;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace GeneratorTester
10 | {
11 | [Entity]
12 | public partial class TestEntity : BaseEntity
13 | {
14 | public override void _Ready()
15 | {
16 | base._Ready();
17 | InitializeEntity();
18 | }
19 |
20 | public void TestFunction()
21 | {
22 | var component = GetComponent();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/GeneratorTester/GeneratorTester.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | disable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Godot.Composition/ComponentHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Godot.Composition;
2 |
3 | ///
4 | /// Helper class that contains methods for working with s.
5 | ///
6 | public static class ComponentHelper
7 | {
8 | ///
9 | /// Helper method used to find s associated with a node/entity.
10 | ///
11 | public static T FindComponent(Node parentNode) where T : IComponent
12 | {
13 | T component = default;
14 |
15 | foreach (var child in parentNode.GetChildren())
16 | {
17 | if (child is IComponent c && c.GetType().InheritsOrImplements(typeof(T)))
18 | {
19 | component = (T)c;
20 | break;
21 | }
22 | }
23 |
24 | return component;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Godot.Composition.SourceGenerator/Godot.Composition.SourceGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | false
6 | true
7 |
8 | *$(MSBuildProjectFile)*
9 |
10 |
11 |
12 | 1.3.1.0
13 | 1.3.1.0
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 MilkWyzard
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Godot.Composition/Godot.Composition.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0;net7.0;net8.0
4 | true
5 | Godot.Composition
6 | 1.3.1.0
7 | 1.3.1.0
8 | Godot.Composition
9 | $(AssemblyVersion)
10 | This library provides an approach for doing composition in Godot.
11 | README.md
12 | git
13 | https://github.com/MysteriousMilk/Godot.Composition
14 | Godot;Godot4;gamedev;GameDevelopment;C#
15 | LICENSE
16 | MilkMediaProductions
17 | True
18 |
19 |
20 |
21 | True
22 | \
23 |
24 |
25 | True
26 | \
27 |
28 |
29 |
30 |
31 | true
32 | analyzers/dotnet/cs
33 | true
34 | true
35 | Content
36 | true
37 | Always
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Godot.Composition/IEntity.cs:
--------------------------------------------------------------------------------
1 | using Godot;
2 | using System.Collections.Generic;
3 |
4 | namespace Godot.Composition;
5 |
6 | ///
7 | /// Interface for an Entity node.
8 | ///
9 | public interface IEntity : INode
10 | {
11 | ///
12 | /// Checks to see if the has been initialized.
13 | ///
14 | /// True if the InitializeEntity function has ran, False if not.
15 | bool IsEntityInitialized();
16 |
17 | ///
18 | /// Initializes the by finding it's components and resolving
19 | /// component references.
20 | ///
21 | void InitializeEntity();
22 |
23 | ///
24 | /// Resolves component reference dependencies specified for all components on this .
25 | ///
26 | void ResolveDependencies();
27 |
28 | ///
29 | /// Checks to see if the contains a component specified by type T.
30 | ///
31 | /// The type of to check for.
32 | /// True if the has the , False otherwise.
33 | bool HasComponent() where T : IComponent;
34 |
35 | ///
36 | /// Gets the component specified by type T.
37 | ///
38 | /// The type of to check for.
39 | ///
40 | /// Null if the does not have the ,
41 | /// otherwise, the .
42 | ///
43 | T GetComponent() where T : IComponent;
44 |
45 | ///
46 | /// Gets the component specified by type T.
47 | ///
48 | /// The type of to check for.
49 | /// The name of the component/node in the scene tree.
50 | ///
51 | /// Null if the does not have the ,
52 | /// otherwise, the .
53 | ///
54 | T GetComponentByName(string name) where T : IComponent;
55 |
56 | ///
57 | /// Enumerates all components attached to the .
58 | ///
59 | /// An enumerable collection of s.
60 | IEnumerable Components();
61 |
62 | ///
63 | /// Sets the value of a component once the component has been initialized.
64 | /// This method is useful for "pre-setting" values after spawning an entity, since
65 | /// the entity's _Ready method has not yet been called.
66 | ///
67 | /// The type of to set the value on.
68 | /// Property within the component to set the value.
69 | /// Value to set.
70 | void OnReadySet(Godot.StringName propertyName, Godot.Variant val) where T : IComponent;
71 | }
72 |
--------------------------------------------------------------------------------
/Godot.Composition.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33213.308
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Godot.Composition.SourceGenerator", "Godot.Composition.SourceGenerator\Godot.Composition.SourceGenerator.csproj", "{D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Godot.Composition", "Godot.Composition\Godot.Composition.csproj", "{4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}"
9 | ProjectSection(ProjectDependencies) = postProject
10 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D} = {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}
11 | EndProjectSection
12 | EndProject
13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GeneratorTester", "GeneratorTester\GeneratorTester.csproj", "{D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}"
14 | EndProject
15 | Global
16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
17 | Debug|Any CPU = Debug|Any CPU
18 | ExportDebug|Any CPU = ExportDebug|Any CPU
19 | ExportRelease|Any CPU = ExportRelease|Any CPU
20 | Release|Any CPU = Release|Any CPU
21 | EndGlobalSection
22 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
23 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
24 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.Debug|Any CPU.Build.0 = Debug|Any CPU
25 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU
26 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU
27 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.ExportRelease|Any CPU.ActiveCfg = Release|Any CPU
28 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.ExportRelease|Any CPU.Build.0 = Release|Any CPU
29 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.Release|Any CPU.ActiveCfg = Release|Any CPU
30 | {D29A4CB8-8F36-47F3-80E9-CB9B6D3F675D}.Release|Any CPU.Build.0 = Release|Any CPU
31 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
32 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
33 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
34 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
35 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
36 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
37 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.Release|Any CPU.ActiveCfg = ExportRelease|Any CPU
38 | {4EB1A0A8-31DA-4578-9382-7D1E5F1D0EA1}.Release|Any CPU.Build.0 = ExportRelease|Any CPU
39 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
40 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
41 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
42 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
43 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
44 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
45 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.Release|Any CPU.ActiveCfg = ExportRelease|Any CPU
46 | {D05BC36D-6EE9-4E2D-8889-F1A1E1EE0AFE}.Release|Any CPU.Build.0 = ExportRelease|Any CPU
47 | EndGlobalSection
48 | GlobalSection(SolutionProperties) = preSolution
49 | HideSolutionNode = FALSE
50 | EndGlobalSection
51 | GlobalSection(ExtensibilityGlobals) = postSolution
52 | SolutionGuid = {A6C29A93-D76C-4754-B429-118F145C29A2}
53 | EndGlobalSection
54 | EndGlobal
55 |
--------------------------------------------------------------------------------
/Godot.Composition/ExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Godot.Composition;
6 |
7 | internal static class ExtensionMethods
8 | {
9 | ///
10 | /// Finds all descendant s (of the given s) that meet a specific condition.
11 | ///
12 | /// The parent s to conduct the search from.
13 | /// Delegate that evaluates each s to see if it belongs in the return list.
14 | /// List of all s that meet the given condition.
15 | internal static IEnumerable FindDescendantNodesIf(this Node node, Predicate predicate)
16 | {
17 | List nodesFound = new List();
18 | FindDescendantNodesIf(node, predicate, ref nodesFound);
19 | return nodesFound;
20 | }
21 |
22 | private static void FindDescendantNodesIf(Node node, Predicate predicate, ref List nodesFound)
23 | {
24 | foreach (var child in node.GetChildren())
25 | {
26 | var childNode = child as Node;
27 |
28 | if (childNode != null)
29 | {
30 | if (predicate.Invoke(childNode))
31 | nodesFound.Add(childNode);
32 |
33 | FindDescendantNodesIf(childNode, predicate, ref nodesFound);
34 | }
35 | }
36 | }
37 |
38 | ///
39 | /// Determines if the calling inherits from or implements the passed .
40 | ///
41 | /// The calling (possible child).
42 | /// The passed in (possible parent of the child).
43 | /// True if the child inherits or implements the parent .
44 | internal static bool InheritsOrImplements(this Type child, Type parent)
45 | {
46 | var currentChild = parent.IsGenericTypeDefinition && child.IsGenericType ? child.GetGenericTypeDefinition() : child;
47 |
48 | while (currentChild != typeof(object))
49 | {
50 | if (parent == currentChild || currentChild.HasAnyInterfaces(parent))
51 | return true;
52 |
53 | currentChild = currentChild.BaseType != null && parent.IsGenericTypeDefinition && currentChild.BaseType.IsGenericType
54 | ? currentChild.BaseType.GetGenericTypeDefinition()
55 | : currentChild.BaseType;
56 |
57 | if (currentChild == null)
58 | return false;
59 | }
60 |
61 | return false;
62 | }
63 |
64 | ///
65 | /// Checks to see if the calling has any interfaces of the interface .
66 | ///
67 | /// The calling .
68 | /// The interface .
69 | /// True if it has the interface, false if not.
70 | internal static bool HasAnyInterfaces(this Type type, Type interfaceType)
71 | {
72 | return type.GetInterfaces().Any(childInterface =>
73 | {
74 | var currentInterface = interfaceType.IsGenericTypeDefinition && childInterface.IsGenericType
75 | ? childInterface.GetGenericTypeDefinition()
76 | : childInterface;
77 | return currentInterface == interfaceType;
78 | });
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Aa][Rr][Mm]/
27 | [Aa][Rr][Mm]64/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Ll]og/
32 | [Ll]ogs/
33 |
34 | # Visual Studio 2015/2017 cache/options directory
35 | .vs/
36 | # Uncomment if you have tasks that create the project's static files in wwwroot
37 | #wwwroot/
38 |
39 | # Visual Studio 2017 auto generated files
40 | Generated\ Files/
41 |
42 | # MSTest test Results
43 | [Tt]est[Rr]esult*/
44 | [Bb]uild[Ll]og.*
45 |
46 | # NUnit
47 | *.VisualState.xml
48 | TestResult.xml
49 | nunit-*.xml
50 |
51 | # Build Results of an ATL Project
52 | [Dd]ebugPS/
53 | [Rr]eleasePS/
54 | dlldata.c
55 |
56 | # Benchmark Results
57 | BenchmarkDotNet.Artifacts/
58 |
59 | # .NET Core
60 | project.lock.json
61 | project.fragment.lock.json
62 | artifacts/
63 |
64 | # StyleCop
65 | StyleCopReport.xml
66 |
67 | # Files built by Visual Studio
68 | *_i.c
69 | *_p.c
70 | *_h.h
71 | *.ilk
72 | *.meta
73 | *.obj
74 | *.iobj
75 | *.pch
76 | *.pdb
77 | *.ipdb
78 | *.pgc
79 | *.pgd
80 | *.rsp
81 | *.sbr
82 | *.tlb
83 | *.tli
84 | *.tlh
85 | *.tmp
86 | *.tmp_proj
87 | *_wpftmp.csproj
88 | *.log
89 | *.vspscc
90 | *.vssscc
91 | .builds
92 | *.pidb
93 | *.svclog
94 | *.scc
95 |
96 | # Chutzpah Test files
97 | _Chutzpah*
98 |
99 | # Visual C++ cache files
100 | ipch/
101 | *.aps
102 | *.ncb
103 | *.opendb
104 | *.opensdf
105 | *.sdf
106 | *.cachefile
107 | *.VC.db
108 | *.VC.VC.opendb
109 |
110 | # Visual Studio profiler
111 | *.psess
112 | *.vsp
113 | *.vspx
114 | *.sap
115 |
116 | # Visual Studio Trace Files
117 | *.e2e
118 |
119 | # TFS 2012 Local Workspace
120 | $tf/
121 |
122 | # Guidance Automation Toolkit
123 | *.gpState
124 |
125 | # ReSharper is a .NET coding add-in
126 | _ReSharper*/
127 | *.[Rr]e[Ss]harper
128 | *.DotSettings.user
129 |
130 | # TeamCity is a build add-in
131 | _TeamCity*
132 |
133 | # DotCover is a Code Coverage Tool
134 | *.dotCover
135 |
136 | # AxoCover is a Code Coverage Tool
137 | .axoCover/*
138 | !.axoCover/settings.json
139 |
140 | # Visual Studio code coverage results
141 | *.coverage
142 | *.coveragexml
143 |
144 | # NCrunch
145 | _NCrunch_*
146 | .*crunch*.local.xml
147 | nCrunchTemp_*
148 |
149 | # MightyMoose
150 | *.mm.*
151 | AutoTest.Net/
152 |
153 | # Web workbench (sass)
154 | .sass-cache/
155 |
156 | # Installshield output folder
157 | [Ee]xpress/
158 |
159 | # DocProject is a documentation generator add-in
160 | DocProject/buildhelp/
161 | DocProject/Help/*.HxT
162 | DocProject/Help/*.HxC
163 | DocProject/Help/*.hhc
164 | DocProject/Help/*.hhk
165 | DocProject/Help/*.hhp
166 | DocProject/Help/Html2
167 | DocProject/Help/html
168 |
169 | # Click-Once directory
170 | publish/
171 |
172 | # Publish Web Output
173 | *.[Pp]ublish.xml
174 | *.azurePubxml
175 | # Note: Comment the next line if you want to checkin your web deploy settings,
176 | # but database connection strings (with potential passwords) will be unencrypted
177 | *.pubxml
178 | *.publishproj
179 |
180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
181 | # checkin your Azure Web App publish settings, but sensitive information contained
182 | # in these scripts will be unencrypted
183 | PublishScripts/
184 |
185 | # NuGet Packages
186 | *.nupkg
187 | # NuGet Symbol Packages
188 | *.snupkg
189 | # The packages folder can be ignored because of Package Restore
190 | **/[Pp]ackages/*
191 | # except build/, which is used as an MSBuild target.
192 | !**/[Pp]ackages/build/
193 | # Uncomment if necessary however generally it will be regenerated when needed
194 | #!**/[Pp]ackages/repositories.config
195 | # NuGet v3's project.json files produces more ignorable files
196 | *.nuget.props
197 | *.nuget.targets
198 |
199 | # Microsoft Azure Build Output
200 | csx/
201 | *.build.csdef
202 |
203 | # Microsoft Azure Emulator
204 | ecf/
205 | rcf/
206 |
207 | # Windows Store app package directories and files
208 | AppPackages/
209 | BundleArtifacts/
210 | Package.StoreAssociation.xml
211 | _pkginfo.txt
212 | *.appx
213 | *.appxbundle
214 | *.appxupload
215 |
216 | # Visual Studio cache files
217 | # files ending in .cache can be ignored
218 | *.[Cc]ache
219 | # but keep track of directories ending in .cache
220 | !?*.[Cc]ache/
221 |
222 | # Others
223 | ClientBin/
224 | ~$*
225 | *~
226 | *.dbmdl
227 | *.dbproj.schemaview
228 | *.jfm
229 | *.pfx
230 | *.publishsettings
231 | orleans.codegen.cs
232 |
233 | # Including strong name files can present a security risk
234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
235 | #*.snk
236 |
237 | # Since there are multiple workflows, uncomment next line to ignore bower_components
238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
239 | #bower_components/
240 |
241 | # RIA/Silverlight projects
242 | Generated_Code/
243 |
244 | # Backup & report files from converting an old project file
245 | # to a newer Visual Studio version. Backup files are not needed,
246 | # because we have git ;-)
247 | _UpgradeReport_Files/
248 | Backup*/
249 | UpgradeLog*.XML
250 | UpgradeLog*.htm
251 | ServiceFabricBackup/
252 | *.rptproj.bak
253 |
254 | # SQL Server files
255 | *.mdf
256 | *.ldf
257 | *.ndf
258 |
259 | # Business Intelligence projects
260 | *.rdl.data
261 | *.bim.layout
262 | *.bim_*.settings
263 | *.rptproj.rsuser
264 | *- [Bb]ackup.rdl
265 | *- [Bb]ackup ([0-9]).rdl
266 | *- [Bb]ackup ([0-9][0-9]).rdl
267 |
268 | # Microsoft Fakes
269 | FakesAssemblies/
270 |
271 | # GhostDoc plugin setting file
272 | *.GhostDoc.xml
273 |
274 | # Node.js Tools for Visual Studio
275 | .ntvs_analysis.dat
276 | node_modules/
277 |
278 | # Visual Studio 6 build log
279 | *.plg
280 |
281 | # Visual Studio 6 workspace options file
282 | *.opt
283 |
284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
285 | *.vbw
286 |
287 | # Visual Studio LightSwitch build output
288 | **/*.HTMLClient/GeneratedArtifacts
289 | **/*.DesktopClient/GeneratedArtifacts
290 | **/*.DesktopClient/ModelManifest.xml
291 | **/*.Server/GeneratedArtifacts
292 | **/*.Server/ModelManifest.xml
293 | _Pvt_Extensions
294 |
295 | # Paket dependency manager
296 | .paket/paket.exe
297 | paket-files/
298 |
299 | # FAKE - F# Make
300 | .fake/
301 |
302 | # CodeRush personal settings
303 | .cr/personal
304 |
305 | # Python Tools for Visual Studio (PTVS)
306 | __pycache__/
307 | *.pyc
308 |
309 | # Cake - Uncomment if you are using it
310 | # tools/**
311 | # !tools/packages.config
312 |
313 | # Tabs Studio
314 | *.tss
315 |
316 | # Telerik's JustMock configuration file
317 | *.jmconfig
318 |
319 | # BizTalk build output
320 | *.btp.cs
321 | *.btm.cs
322 | *.odx.cs
323 | *.xsd.cs
324 |
325 | # OpenCover UI analysis results
326 | OpenCover/
327 |
328 | # Azure Stream Analytics local run output
329 | ASALocalRun/
330 |
331 | # MSBuild Binary and Structured Log
332 | *.binlog
333 |
334 | # NVidia Nsight GPU debugger configuration file
335 | *.nvuser
336 |
337 | # MFractors (Xamarin productivity tool) working folder
338 | .mfractor/
339 |
340 | # Local History for Visual Studio
341 | .localhistory/
342 |
343 | # BeatPulse healthcheck temp database
344 | healthchecksdb
345 |
346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
347 | MigrationBackup/
348 |
349 | # Ionide (cross platform F# VS Code tools) working folder
350 | .ionide/
351 |
352 | /package/*.*
353 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Godot.Composition
2 | [](https://www.nuget.org/packages/Godot.Composition/1.3.1)
3 |
4 | This library provides a solution for building game entities in Godot through composition over inheritance. Godot's node-based architecture requires some level of inheritance. However, minimizing inheritance and refactoring much of your game's logic into components may go a long way towards cleaner, more reusable code.
5 |
6 | There are several major approaches to managing entities and data in game development such as inheritance, component-based entity (CBE) model, and entity component systems (ECS). Each of theses models have their pros and cons. While an ECS model is essentially impossible to do in (vanilla) Godot, we can achieve pseudo-CBE/composition approach. So, if you are trying to do composition in Godot, this library will provide some tools and boiler-plate code to help.
7 |
8 | ## Entities in Godot.Composition
9 | Godot.Composition requires indicating which objects are entities. In Godot.Composition, entities are simply Godot nodes that can have attached components. Entities are indicated through the use of a class attribute, and must be initialized in the **_Ready** method. The class attribute is paired with a source-code generator which generates a lot of the *boiler-plate* code. Because of this, the class must be marked *partial*, which will likely be the case anyway since Godot C# uses source-code generators as well.
10 |
11 | ```C#
12 | using Godot;
13 | using Godot.Composition;
14 |
15 | [Entity]
16 | public partial class Player : CharacterBody2D
17 | {
18 | public override void _Ready()
19 | {
20 | InitializeEntity();
21 | }
22 | }
23 | ```
24 | Depending on implementation and composition level desired, you may end up with several entity classes or just one. For example, you may want all kinematic entities your game to by one entity class where the all the logic is separated into components. Or, you may desired to use a little inheritance and make multiple entity classes, such as, *Player* and *NPC*, etc.
25 |
26 | ## Components in Godot.Composition
27 | The ultimate goal of composition is building reusable components. So, if all your entities need to move around the world, it makes sense to abstract this into a *VelocityComponent*. Likewise, if all your entities have health and can be damaged, that code might be abstracted into a *HealthComponent* and a *HitboxComponent*.
28 |
29 | Lets take a look at how to specify a component in Godot.Composition. It works similar to entities. Since this is Godot, the components must be nodes as well (this can help with things like node communication via signal, etc.) Mark components with a class attribute and specify the type of entity that the component can be associated with. The component must be initialized in the **_Ready** method.
30 |
31 | ```C#
32 | using Godot;
33 | using Godot.Composition;
34 |
35 | [Component(typeof(CharacterBody2D))]
36 | public partial class VelocityComponent : Node
37 | {
38 | public override void _Ready()
39 | {
40 | InitializeComponent();
41 | }
42 | }
43 | ```
44 |
45 | Components do not have to just be the base Node type. They can be whichever node type makes the most sense for the component. Take a *HitboxComponent* for example. This would be best implemented as an *Area* or *Area2D*.
46 |
47 | ```C#
48 | using Godot;
49 | using Godot.Composition;
50 |
51 | [Component(typeof(CharacterBody2D))]
52 | public partial class HitboxComponent : Area2D
53 | {
54 | public override void _Ready()
55 | {
56 | InitializeComponent();
57 | }
58 | }
59 | ```
60 |
61 | All components will have a protected reference to the entity that they belong to. This can be useful for encapsulating the logic with a component. For example, below is a partial implementation of a *VelocityComponent*.
62 |
63 | ```C#
64 | using Godot;
65 | using Godot.Composition;
66 |
67 | [Component(typeof(CharacterBody2D))]
68 | public partial class VelocityComponent : Node
69 | {
70 | [Export]
71 | public float Acceleration { get; set; }
72 |
73 | public Vector2 Velocity { get; private set; }
74 |
75 | public void AccelerateToVelocity(Vector2 vel)
76 | {
77 | Velocity = vel + Acceleration;
78 | }
79 |
80 | public override void _Ready()
81 | {
82 | InitializeComponent();
83 | }
84 |
85 | public void Move()
86 | {
87 | // The parent reference here will be a reference to the CharacterBody2D entity.
88 | parent.Velocity = Velocity;
89 | parent.MoveAndSlide();
90 | Velocity = parent.Velocity;
91 | }
92 | }
93 | ```
94 | All component nodes for an entity should be placed as a direct child of the entity node with the scene in Godot. An example of a Godot scene tree with components can be scene below.
95 |
96 | 
97 |
98 | ## Accessing Components from an Entity
99 | Even with most logic abstracted into components, there will likely be some glue code required to bring everything together. If access to a specific component is needed in the Entity script, components can be retrieved as seen below.
100 |
101 | ```C#
102 | using Godot;
103 | using Godot.Composition;
104 |
105 | [Entity]
106 | public partial class Player : CharacterBody2D
107 | {
108 | ...
109 |
110 | public override void _PhysicsProcess(double delta)
111 | {
112 | var velocityComponent = GetComponent();
113 | velocityComponent.AccelerateToVelocity(direction * maxSpeed);
114 | velocityComponent.Move();
115 | }
116 | }
117 | ```
118 |
119 | Components can also be retrieved using their interface type.
120 |
121 | ```C#
122 | using Godot;
123 | using Godot.Composition;
124 |
125 | [Component(typeof(CharacterBody2D))]
126 | public partial class NetworkClientComponent : Node, INetworkComponent
127 | {
128 | public long NetworkId { get; set; }
129 |
130 | public override void _Ready()
131 | {
132 | InitializeComponent();
133 | }
134 | }
135 |
136 | [Entity]
137 | public partial class Player : CharacterBody2D
138 | {
139 | public override void _Ready()
140 | {
141 | InitializeEntity();
142 |
143 | var networkComponent = GetComponent();
144 | networkComponent.NetworkId = Multiplayer.GetUniqueId();
145 | }
146 | }
147 | ```
148 |
149 | ## Adding Component Dependencies
150 | Adding references to other components is easy with the *ComponentDependency* attribute. Adding a *ComponentDependency* attribute to a component will automatically insert a protected reference to the dependent component. See the example below.
151 |
152 | ```C#
153 | using Godot;
154 | using Godot.Composition;
155 |
156 | [Component(typeof(CharacterBody2D))]
157 | [ComponentDependency(typeof(HealthComponent))]
158 | public partial class HitboxComponent : Area2D
159 | {
160 | ...
161 |
162 | public void OnAreaEntered(Area2D area)
163 | {
164 | // Area entered, calculated damage
165 | double damage;
166 |
167 | healthComponent.ApplyDamage(damage);
168 | }
169 | }
170 | ```
171 |
172 | ## Entity Ready Callback
173 | Sometimes, its necessary to run code on a component immediately after an Entity node is ready. Godot.Composition allows the developer to setup a special function that will be called immediately after the parent Entity's *_Ready* function is called.
174 |
175 | Simply define a function called *void OnEntityReady()* on your component and it will be called automatically.
176 |
177 | ```C#
178 | using Godot;
179 | using Godot.Composition;
180 |
181 | [Component(typeof(CharacterBody2D))]
182 | public partial class HealthComponent : Node
183 | {
184 | ...
185 |
186 | /// Called directly after the parent entity's
187 | /// _Ready function.
188 | public void OnEntityReady()
189 | {
190 | ...
191 | }
192 | }
193 | ```
194 |
195 | ## Setting Component Values Programmatically
196 | Occationally, Entity nodes will be created or spawned using a C#. When instantiating Entities in this manner, the Entity's Components do not become available until the Entity's *_Ready* function is called when added to the Scene Tree. Instead of deferring a frame (or two) to set values on a component, you can queue up values to set during the *_Ready* function. Consider the following scenario. We are spawning fast moving projectile. We desire to associate some data with a *DamageComponent* on the projectile, but we don't want to wait several frames to set the value since the projectile's lifetime will be so short. Here we can use the OnReadySet method. See below.
197 |
198 | ```C#
199 | using Godot;
200 | using Godot.Composition;
201 |
202 | [Entity]
203 | public partial class Gun : Node2D
204 | {
205 | ...
206 |
207 | public void Fire()
208 | {
209 | // instantiate projectile
210 | var projectileScn = ResourceLoader.Load("res://Projectile.tscn");
211 | var projectile = projectileScn.Instantiate();
212 |
213 | projectile.OnReadySet(DamageComponent.PropertyName.Damage, 20.0);
214 | }
215 | }
216 | ```
217 |
--------------------------------------------------------------------------------
/Godot.Composition/ComponentContainer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 |
8 | namespace Godot.Composition;
9 |
10 | ///
11 | /// Container collection that maps component object references to their type for easy lookup.
12 | ///
13 | public class ComponentContainer : IEnumerable, ICollection
14 | {
15 | private Dictionary>> components;
16 |
17 | ///
18 | /// Number of components in the container.
19 | ///
20 | public int Count => components.Count;
21 |
22 | ///
23 | /// Indicates if the collection is read only or not.
24 | ///
25 | public bool IsReadOnly => false;
26 |
27 | ///
28 | /// Default constructor.
29 | ///
30 | public ComponentContainer()
31 | {
32 | components = new Dictionary>>();
33 | }
34 |
35 | ///
36 | /// Constructor which builds the container based on the given node.
37 | /// It searches the given node for components to add.
38 | ///
39 | /// The entity node that contains component nodes.
40 | public ComponentContainer(Node entity) : this()
41 | {
42 | AddEntityComponents(entity);
43 | }
44 |
45 | ///
46 | /// Finds all child component nodes for the given node and adds them to the container.
47 | ///
48 | /// The entity node.
49 | public void AddEntityComponents(Node entity)
50 | {
51 | foreach (var c in entity.FindDescendantNodesIf(n => n is IComponent).Cast())
52 | Add(c);
53 | }
54 |
55 | #region Components
56 | ///
57 | /// Checks to see if the contains a component specified by type T.
58 | ///
59 | /// The type of to check for.
60 | /// True if the has the , False otherwise.
61 | public bool HasComponent() where T : IComponent
62 | {
63 | var searchType = typeof(T);
64 | return components.ContainsKey(searchType);
65 | }
66 |
67 | ///
68 | /// Gets the component specified by type T.
69 | ///
70 | /// The type of to check for.
71 | ///
72 | /// Null if the does not have the ,
73 | /// otherwise, the .
74 | ///
75 | public T GetComponent() where T : IComponent
76 | {
77 | var searchType = typeof(T);
78 | T component = default(T);
79 |
80 | if (components.ContainsKey(searchType) && components[searchType].Count > 0)
81 | {
82 | if (components[searchType].First().TryGetTarget(out IComponent c))
83 | component = (T)c;
84 | else
85 | components.Remove(searchType);
86 | }
87 |
88 | return component;
89 | }
90 |
91 | ///
92 | /// Gets the component specified by type T.
93 | ///
94 | /// The type of to check for.
95 | ///
96 | /// Null if the does not have the ,
97 | /// otherwise, the .
98 | ///
99 | public T GetComponentByName(string name) where T : IComponent
100 | {
101 | var searchType = typeof(T);
102 | T component = default(T);
103 |
104 | if (components.ContainsKey(searchType) && components[searchType].Count > 0)
105 | {
106 | var match = components[searchType].FirstOrDefault(x =>
107 | {
108 | x.TryGetTarget(out IComponent c);
109 | return c.Name == name;
110 | });
111 |
112 | if (match != null && match.TryGetTarget(out IComponent c))
113 | component = (T)c;
114 | }
115 |
116 | return component;
117 | }
118 |
119 | ///
120 | /// Gets the component by a specified type.
121 | ///
122 | /// The type of component to get.
123 | /// The first component of the given type, or null if no component in the conatiner matches the type.
124 | public IComponent GetComponentByType(Type type)
125 | {
126 | if (components.ContainsKey(type) &&
127 | components[type].Count > 0 &&
128 | components[type].First().TryGetTarget(out IComponent c))
129 | {
130 | return c;
131 | }
132 | return null;
133 | }
134 | #endregion
135 |
136 | #region IEnumerableT
137 | ///
138 | /// Returns an enumerator that iterates through a collection.
139 | ///
140 | /// An enumerator that can be used to iterate through the collection.
141 | public IEnumerator GetEnumerator()
142 | {
143 | foreach (var componentList in components.Values)
144 | {
145 | foreach (var weakComponent in componentList)
146 | {
147 | if (weakComponent.TryGetTarget(out IComponent c))
148 | yield return c;
149 | }
150 | }
151 | }
152 |
153 | ///
154 | /// Returns an enumerator that iterates through a collection.
155 | ///
156 | /// An object that can be used to iterate through the collection.
157 | IEnumerator IEnumerable.GetEnumerator()
158 | {
159 | foreach (var componentList in components.Values)
160 | {
161 | foreach (var weakComponent in componentList)
162 | {
163 | if (weakComponent.TryGetTarget(out IComponent c))
164 | yield return c;
165 | }
166 | }
167 | }
168 | #endregion
169 |
170 | #region ICollectionT
171 | ///
172 | /// Adds an to the collection.
173 | ///
174 | /// The to add to the collection.
175 | /// Throws an if the item is not a .
176 | public void Add(IComponent item)
177 | {
178 | if (item is not Node)
179 | throw new ArgumentException("Component must be a node.", nameof(item));
180 |
181 | var key = item.GetType();
182 |
183 | if (!components.ContainsKey(key))
184 | components.Add(key, new List>());
185 |
186 | var weakRef = new WeakReference(item);
187 | components[key].Add(weakRef);
188 |
189 | // add lookup for interface types as well
190 | foreach (var interfaceType in key.GetInterfaces())
191 | {
192 | // don't add keyed entry for IComponent
193 | if (interfaceType == typeof(IComponent))
194 | continue;
195 |
196 | // don't add key entries for interfaces that don't inherit IComponent
197 | if (!interfaceType.InheritsOrImplements(typeof(IComponent)))
198 | continue;
199 |
200 | if (!components.ContainsKey(interfaceType))
201 | components.Add(interfaceType, new List>());
202 |
203 | weakRef = new WeakReference(item);
204 | components[interfaceType].Add(weakRef);
205 | }
206 | }
207 |
208 | ///
209 | /// Clears the collection.
210 | ///
211 | public void Clear()
212 | {
213 | components.Clear();
214 | }
215 |
216 | ///
217 | /// Checks to see if a is contained with the collection.
218 | ///
219 | /// The to check for.
220 | /// True if the is in the collection, False if not.
221 | public bool Contains(IComponent item)
222 | {
223 | bool contains = false;
224 | foreach (var componentList in components.Values)
225 | {
226 | contains = componentList.Any(c =>
227 | {
228 | bool found = false;
229 | if (c.TryGetTarget(out IComponent target))
230 | found = ReferenceEquals(target, item);
231 | return found;
232 | });
233 |
234 | if (contains)
235 | break;
236 | }
237 |
238 | return contains;
239 | }
240 |
241 | ///
242 | /// Copys the elements of the collection to an , starting a a particular index.
243 | ///
244 | ///
245 | /// he one-dimensional that is the destination of the elements copied from .
246 | /// The Array must have zero-based indexing.
247 | ///
248 | /// The zero-based index in array at which copying begins.
249 | /// This exception is thrown if the array parameter is null.
250 | public void CopyTo(IComponent[] array, int arrayIndex)
251 | {
252 | if (array == null)
253 | throw new ArgumentNullException("array");
254 |
255 | var list = this.ToList();
256 | list.CopyTo(array, arrayIndex);
257 | }
258 |
259 | ///
260 | /// Removes an from the collection.
261 | ///
262 | /// The to remove.
263 | /// True if the item was removed, False if not.
264 | public bool Remove(IComponent item)
265 | {
266 | return components.Remove(item.GetType());
267 | }
268 | #endregion
269 | }
270 |
--------------------------------------------------------------------------------
/Godot.Composition.SourceGenerator/GodotCompositionGenerator.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.CodeAnalysis;
2 | using Microsoft.CodeAnalysis.CSharp;
3 | using Microsoft.CodeAnalysis.CSharp.Syntax;
4 | using Microsoft.CodeAnalysis.Text;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 |
10 | namespace Godot.Composition.SourceGenerator
11 | {
12 | public class CompositionComponentRef
13 | {
14 | public string TypeName { get; set; }
15 | public string TypeNamespace { get; set; }
16 | public string VariableName { get; set; }
17 |
18 | public CompositionComponentRef(string type, string ns, string varName)
19 | {
20 | TypeName = type;
21 | TypeNamespace = ns;
22 | VariableName = varName;
23 | }
24 | }
25 |
26 | public class EntityClassData
27 | {
28 | public ClassDeclarationSyntax ClassSyntax { get; set; }
29 | public string ClassName { get; set; }
30 | public string Namespace { get; set; }
31 | public bool HasEntityBaseClass { get; set; }
32 | public List BaseClassTypes { get; set; }
33 |
34 | public EntityClassData(ClassDeclarationSyntax syntax, string className, string ns)
35 | {
36 | ClassSyntax = syntax;
37 | ClassName = className;
38 | Namespace = ns;
39 | HasEntityBaseClass = false;
40 | BaseClassTypes = new List();
41 | }
42 | }
43 |
44 | public class ComponentClassData
45 | {
46 | public string ClassName { get; set; }
47 | public string Namespace { get; set; }
48 | public string ParentTypeName { get; set; }
49 | public string ParentTypeNamespace { get; set; }
50 | public List ComponentRefs { get; set; }
51 |
52 | public ComponentClassData(INamedTypeSymbol classSymbol, TypeInfo parentType)
53 | {
54 | ClassName = classSymbol.Name;
55 | ParentTypeName = parentType.Type.Name;
56 |
57 | if (classSymbol.ContainingNamespace != null && !classSymbol.ContainingNamespace.IsGlobalNamespace)
58 | Namespace = classSymbol.ContainingNamespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted));
59 |
60 | if (parentType.Type.ContainingNamespace != null && !parentType.Type.ContainingNamespace.IsGlobalNamespace)
61 | ParentTypeNamespace = parentType.Type.ContainingNamespace.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted));
62 |
63 | ComponentRefs = new List();
64 | }
65 | }
66 |
67 | [Generator]
68 | public class GodotCompositionGenerator : ISourceGenerator
69 | {
70 | public void Initialize(GeneratorInitializationContext context)
71 | {
72 | }
73 |
74 | public void Execute(GeneratorExecutionContext context)
75 | {
76 | var classWithAttributes = context.Compilation.SyntaxTrees.Where(st => st.GetRoot().DescendantNodes().OfType()
77 | .Any(p => p.DescendantNodes().OfType().Any()));
78 |
79 | List entityClassList = new List();
80 | List componentClassList = new List();
81 |
82 | foreach (SyntaxTree tree in classWithAttributes)
83 | {
84 | var semanticModel = context.Compilation.GetSemanticModel(tree);
85 |
86 | foreach (var declaredClass in tree
87 | .GetRoot()
88 | .DescendantNodes()
89 | .OfType()
90 | .Where(cd => cd.DescendantNodes().OfType().Any()))
91 | {
92 | if (declaredClass == null)
93 | continue;
94 |
95 | var componentClassNodes = declaredClass
96 | .DescendantNodes()
97 | .OfType()
98 | .FirstOrDefault(a => a.DescendantTokens().Any(dt => dt.IsKind(SyntaxKind.IdentifierToken) && dt.Parent != null && semanticModel.GetTypeInfo(dt.Parent).Type?.Name == "ComponentAttribute"))
99 | ?.DescendantTokens()
100 | ?.Where(dt => dt.IsKind(SyntaxKind.IdentifierToken))
101 | ?.ToList();
102 |
103 | var componentRefAttributeNodes = declaredClass
104 | .DescendantNodes()
105 | .OfType()
106 | .Where(a => a.DescendantTokens().Any(dt => dt.IsKind(SyntaxKind.IdentifierToken) && dt.Parent != null && semanticModel.GetTypeInfo(dt.Parent).Type?.Name == "ComponentDependencyAttribute")).ToList();
107 |
108 | var entityClassNodes = declaredClass
109 | .DescendantNodes()
110 | .OfType()
111 | .FirstOrDefault(a => a.DescendantTokens().Any(dt => dt.IsKind(SyntaxKind.IdentifierToken) && dt.Parent != null && semanticModel.GetTypeInfo(dt.Parent).Type?.Name == "EntityAttribute"))
112 | ?.DescendantTokens()
113 | ?.Where(dt => dt.IsKind(SyntaxKind.IdentifierToken))
114 | ?.ToList();
115 |
116 | if (componentClassNodes != null)
117 | {
118 | var attributeParentClass = semanticModel.GetTypeInfo(componentClassNodes.Last().Parent);
119 | var classTypeSymbol = semanticModel.GetDeclaredSymbol(declaredClass);
120 |
121 | if (classTypeSymbol == null)
122 | {
123 | context.ReportDiagnostic(Diagnostic.Create(
124 | new DiagnosticDescriptor(id: "GDCOMP0001",
125 | title: "blah blah",
126 | messageFormat: "blah blah",
127 | category: "Usage",
128 | DiagnosticSeverity.Error,
129 | isEnabledByDefault: true,
130 | "blah blah"),
131 | declaredClass.GetLocation(),
132 | declaredClass.SyntaxTree.FilePath));
133 | }
134 |
135 | var componentClassData = new ComponentClassData(classTypeSymbol, attributeParentClass);
136 |
137 | foreach (var attribute in componentRefAttributeNodes)
138 | {
139 | var componentRefClassNodes = attribute
140 | .DescendantTokens()
141 | ?.Where(dt => dt.IsKind(SyntaxKind.IdentifierToken))
142 | ?.ToList();
143 | if (componentRefClassNodes != null && componentRefClassNodes.Count > 0)
144 | {
145 | var typeInfo = semanticModel.GetTypeInfo(componentRefClassNodes.Last().Parent);
146 | string typeName = typeInfo.Type.Name;
147 | string typeNamespace = typeInfo.Type.ContainingNamespace?.Name ?? null;
148 | string varName = typeName.Substring(0, 1).ToLower() + typeName.Substring(1);
149 |
150 | componentClassData.ComponentRefs.Add(new CompositionComponentRef(typeName, typeNamespace, varName));
151 | }
152 | }
153 | componentClassList.Add(componentClassData);
154 |
155 | }
156 | else if (entityClassNodes != null)
157 | {
158 | var entityClassData = new EntityClassData(declaredClass, declaredClass.Identifier.ToString(), GetNamespace(declaredClass, semanticModel));
159 |
160 | foreach (var type in declaredClass.BaseList.Types)
161 | {
162 | var typeInfo = semanticModel.GetTypeInfo(type.Type);
163 | entityClassData.BaseClassTypes.Add(typeInfo.Type?.Name ?? string.Empty);
164 | }
165 |
166 | entityClassList.Add(entityClassData);
167 | }
168 | }
169 | }
170 |
171 | foreach (var classData in entityClassList)
172 | {
173 | // Determine if the base class for this Entity class is another Entity
174 | foreach (string baseType in classData.BaseClassTypes)
175 | {
176 | if (entityClassList.Any(e => e.ClassName == baseType))
177 | {
178 | classData.HasEntityBaseClass = true;
179 | break;
180 | }
181 | }
182 |
183 | if (!classData.HasEntityBaseClass)
184 | {
185 | var srcBuilder = new StringBuilder();
186 | WriteEntityClass(ref srcBuilder, classData);
187 | context.AddSource($"{classData.ClassName}_IEntity.g", SourceText.From(srcBuilder.ToString(), Encoding.UTF8));
188 | }
189 | }
190 |
191 | foreach (var classData in componentClassList)
192 | {
193 | var srcBuilder = new StringBuilder();
194 | WriteComponentClass(ref srcBuilder, classData);
195 | context.AddSource($"{classData.ClassName}_IComponent.g", SourceText.From(srcBuilder.ToString(), Encoding.UTF8));
196 | }
197 | }
198 |
199 | private string GetNamespace(ClassDeclarationSyntax classSyntax, SemanticModel semanticModel)
200 | {
201 | var classTypeSymbol = semanticModel.GetDeclaredSymbol(classSyntax);
202 | if (classTypeSymbol == null)
203 | return string.Empty;
204 |
205 | string ns = string.Empty;
206 |
207 | var namespaceSymbol = classTypeSymbol.ContainingNamespace;
208 | if (namespaceSymbol != null && !namespaceSymbol.IsGlobalNamespace)
209 | {
210 | ns = namespaceSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.WithGlobalNamespaceStyle(SymbolDisplayGlobalNamespaceStyle.Omitted));
211 | if (ns == null)
212 | ns = string.Empty;
213 | }
214 |
215 | return ns;
216 | }
217 |
218 | private void WriteComponentClass(ref StringBuilder srcBuilder, ComponentClassData classData)
219 | {
220 | string classNs = classData.Namespace;
221 | string attributeTypeNs = classData.ParentTypeNamespace;
222 |
223 | srcBuilder.AppendLine("using Godot;");
224 | srcBuilder.AppendLine("using Godot.Composition;");
225 |
226 | if (!string.IsNullOrEmpty(attributeTypeNs) && attributeTypeNs != "Godot")
227 | srcBuilder.AppendLine("using " + attributeTypeNs + ";");
228 |
229 | var componentNamespaces = classData.ComponentRefs.Select(x => x.TypeNamespace).Distinct();
230 |
231 | // ensure namespaces for component dependencies get added to the generated source
232 | foreach (var ns in componentNamespaces)
233 | {
234 | if (!string.IsNullOrEmpty(ns) && ns != classNs)
235 | srcBuilder.AppendLine("using " + ns + ";");
236 | }
237 |
238 | srcBuilder.AppendLine();
239 |
240 | if (!string.IsNullOrEmpty(classNs))
241 | {
242 | srcBuilder.AppendLine("namespace " + classNs + ";");
243 | srcBuilder.AppendLine();
244 | }
245 |
246 | srcBuilder.AppendLine($"public partial class {classData.ClassName} : Godot.Composition.IComponent");
247 | srcBuilder.AppendLine("{");
248 | srcBuilder.AppendLine($" protected {classData.ParentTypeName} parent;");
249 |
250 | foreach (var compRefName in classData.ComponentRefs)
251 | srcBuilder.AppendLine(" protected " + compRefName.TypeName + " " + compRefName.VariableName + ";");
252 |
253 | srcBuilder.AppendLine();
254 |
255 | WriteInitializeComponentMethod(ref srcBuilder, classData.ParentTypeName, " ");
256 | srcBuilder.AppendLine();
257 | WriteComponentResolveDependenciesMethod(ref srcBuilder, classData.ComponentRefs, " ");
258 |
259 | srcBuilder.AppendLine("}");
260 | }
261 |
262 | private void WriteInitializeComponentMethod(ref StringBuilder srcBuilder, string parentTypeName, string indent)
263 | {
264 | srcBuilder.AppendLine(indent + "protected void InitializeComponent()");
265 | srcBuilder.AppendLine(indent + "{");
266 | srcBuilder.AppendLine(indent + " parent = GetParent<" + parentTypeName + ">();");
267 | srcBuilder.AppendLine(indent + " ");
268 | srcBuilder.AppendLine(indent + " if (parent is IEntity e)");
269 | srcBuilder.AppendLine(indent + " {");
270 | srcBuilder.AppendLine(indent + " if (e.IsEntityInitialized())");
271 | srcBuilder.AppendLine(indent + " e.ResolveDependencies();");
272 | srcBuilder.AppendLine(indent + " }");
273 | srcBuilder.AppendLine(indent + "}");
274 | }
275 |
276 | private void WriteComponentResolveDependenciesMethod(ref StringBuilder srcBuilder, List componentRefNames, string indent)
277 | {
278 | srcBuilder.AppendLine(indent + "public void ResolveDependencies()");
279 | srcBuilder.AppendLine(indent + "{");
280 | srcBuilder.AppendLine(indent + " if (parent != null && parent is IEntity ent)");
281 | srcBuilder.AppendLine(indent + " {");
282 |
283 | foreach (var compRefName in componentRefNames)
284 | {
285 | srcBuilder.AppendLine(indent + " " + compRefName.VariableName + " = ent.GetComponent<" + compRefName.TypeName + ">();");
286 | }
287 |
288 | srcBuilder.AppendLine(indent + " }");
289 | srcBuilder.AppendLine(indent + "}");
290 | }
291 |
292 | private void WriteEntityClass(ref StringBuilder srcBuilder, EntityClassData entityClassData)
293 | {
294 | srcBuilder.AppendLine("using Godot;");
295 | srcBuilder.AppendLine("using Godot.Composition;");
296 | srcBuilder.AppendLine("using System;");
297 | srcBuilder.AppendLine("using System.Collections.Generic;");
298 | srcBuilder.AppendLine("using System.Linq;");
299 | srcBuilder.AppendLine();
300 |
301 | if (!string.IsNullOrEmpty(entityClassData.Namespace))
302 | {
303 | srcBuilder.AppendLine($"namespace {entityClassData.Namespace};");
304 | srcBuilder.AppendLine();
305 | }
306 |
307 | srcBuilder.AppendLine($"public partial class {entityClassData.ClassName} : Godot.Composition.IEntity");
308 | srcBuilder.AppendLine("{");
309 |
310 | srcBuilder.AppendLine(" protected System.Collections.Generic.List> onReadySetList = new System.Collections.Generic.List>();");
311 | srcBuilder.AppendLine(" protected Godot.Composition.ComponentContainer container = new Godot.Composition.ComponentContainer();");
312 | srcBuilder.AppendLine(" protected bool isEntityInitialized = false;");
313 | srcBuilder.AppendLine();
314 |
315 | WriteIsEntityInitializedMethod(ref srcBuilder, " ");
316 | srcBuilder.AppendLine();
317 |
318 | WriteInitializeEntityMethod(ref srcBuilder, " ");
319 | srcBuilder.AppendLine();
320 |
321 | WriteEntityResolveDependenciesMethod(ref srcBuilder, " ");
322 | srcBuilder.AppendLine();
323 |
324 | WriteHasComponentMethod(ref srcBuilder, " ");
325 | srcBuilder.AppendLine();
326 |
327 | WriteGetComponentMethod(ref srcBuilder, " ");
328 | srcBuilder.AppendLine();
329 |
330 | WriteGetComponentByNameMethod(ref srcBuilder, " ");
331 | srcBuilder.AppendLine();
332 |
333 | WriteComponentsMethod(ref srcBuilder, " ");
334 | srcBuilder.AppendLine();
335 |
336 | WriteOnReadySetMethod(ref srcBuilder, " ");
337 | srcBuilder.AppendLine();
338 |
339 | srcBuilder.AppendLine("}");
340 | }
341 |
342 | private void WriteIsEntityInitializedMethod(ref StringBuilder srcBuilder, string indent)
343 | {
344 | srcBuilder.AppendLine(indent + "public bool IsEntityInitialized()");
345 | srcBuilder.AppendLine(indent + "{");
346 | srcBuilder.AppendLine(indent + " return isEntityInitialized;");
347 | srcBuilder.AppendLine(indent + "}");
348 | }
349 |
350 | private void WriteInitializeEntityMethod(ref StringBuilder srcBuilder, string indent)
351 | {
352 | srcBuilder.AppendLine(indent + "public void InitializeEntity()");
353 | srcBuilder.AppendLine(indent + "{");
354 | srcBuilder.AppendLine(indent + " if (isEntityInitialized)");
355 | srcBuilder.AppendLine(indent + " return;");
356 | srcBuilder.AppendLine(indent + " ");
357 | srcBuilder.AppendLine(indent + " container.AddEntityComponents(this);");
358 | srcBuilder.AppendLine(indent + " ResolveDependencies();");
359 | srcBuilder.AppendLine(indent + " ");
360 | srcBuilder.AppendLine(indent + " foreach (var item in onReadySetList)");
361 | srcBuilder.AppendLine(indent + " {");
362 | srcBuilder.AppendLine(indent + " var component = container.GetComponentByType(item.Item1);");
363 | srcBuilder.AppendLine(indent + " if (component != null)");
364 | srcBuilder.AppendLine(indent + " component.Set(item.Item2, item.Item3);");
365 | srcBuilder.AppendLine(indent + " }");
366 | srcBuilder.AppendLine(indent + " ");
367 | srcBuilder.AppendLine(indent + " onReadySetList.Clear();");
368 | srcBuilder.AppendLine(indent + " isEntityInitialized = true;");
369 | srcBuilder.AppendLine(indent + " ");
370 | srcBuilder.AppendLine(indent + " foreach (var component in container)");
371 | srcBuilder.AppendLine(indent + " {");
372 | srcBuilder.AppendLine(indent + " if (component is Godot.GodotObject gdObj && gdObj.HasMethod(\"OnEntityReady\"))");
373 | srcBuilder.AppendLine(indent + " Connect(SignalName.Ready, new Callable(gdObj, \"OnEntityReady\"));");
374 | srcBuilder.AppendLine(indent + " }");
375 | srcBuilder.AppendLine(indent + "}");
376 | }
377 |
378 | private void WriteEntityResolveDependenciesMethod(ref StringBuilder srcBuilder, string indent)
379 | {
380 | srcBuilder.AppendLine(indent + "public void ResolveDependencies()");
381 | srcBuilder.AppendLine(indent + "{");
382 | srcBuilder.AppendLine(indent + " foreach (var component in container)");
383 | srcBuilder.AppendLine(indent + " component.ResolveDependencies();");
384 | srcBuilder.AppendLine(indent + "}");
385 | }
386 |
387 | private void WriteHasComponentMethod(ref StringBuilder srcBuilder, string indent)
388 | {
389 | srcBuilder.AppendLine(indent + "public bool HasComponent() where T : IComponent");
390 | srcBuilder.AppendLine(indent + "{");
391 | srcBuilder.AppendLine(indent + " return container.HasComponent();");
392 | srcBuilder.AppendLine(indent + "}");
393 | }
394 |
395 | private void WriteGetComponentMethod(ref StringBuilder srcBuilder, string indent)
396 | {
397 | srcBuilder.AppendLine(indent + "public T GetComponent() where T : IComponent");
398 | srcBuilder.AppendLine(indent + "{");
399 | srcBuilder.AppendLine(indent + " return container.GetComponent();");
400 | srcBuilder.AppendLine(indent + "}");
401 | }
402 |
403 | private void WriteGetComponentByNameMethod(ref StringBuilder srcBuilder, string indent)
404 | {
405 | srcBuilder.AppendLine(indent + "public T GetComponentByName(string name) where T : IComponent");
406 | srcBuilder.AppendLine(indent + "{");
407 | srcBuilder.AppendLine(indent + " return container.GetComponentByName(name);");
408 | srcBuilder.AppendLine(indent + "}");
409 | }
410 |
411 | private void WriteComponentsMethod(ref StringBuilder srcBuilder, string indent)
412 | {
413 | srcBuilder.AppendLine(indent + "public System.Collections.Generic.IEnumerable Components()");
414 | srcBuilder.AppendLine(indent + "{");
415 | srcBuilder.AppendLine(indent + " return container.AsEnumerable();");
416 | srcBuilder.AppendLine(indent + "}");
417 | }
418 |
419 | private void WriteOnReadySetMethod(ref StringBuilder srcBuilder, string indent)
420 | {
421 | srcBuilder.AppendLine(indent + "public void OnReadySet(Godot.StringName propertyName, Godot.Variant val) where T : Godot.Composition.IComponent");
422 | srcBuilder.AppendLine(indent + "{");
423 | srcBuilder.AppendLine(indent + " var type = typeof(T);");
424 | srcBuilder.AppendLine(indent + " onReadySetList.Add(new System.Tuple(type, propertyName, val));");
425 | srcBuilder.AppendLine(indent + "}");
426 | }
427 | }
428 | }
429 |
--------------------------------------------------------------------------------
/Godot.Composition/INode.cs:
--------------------------------------------------------------------------------
1 | using Godot.Collections;
2 |
3 | namespace Godot.Composition;
4 |
5 | public interface INode
6 | {
7 | //
8 | // Summary:
9 | // The name of the node. This name is unique among the siblings (other child nodes
10 | // from the same parent). When set to an existing name, the node will be automatically
11 | // renamed.
12 | // Note: Auto-generated names might include the @ character, which is reserved for
13 | // unique names when using Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode).
14 | // When setting the name manually, any @ will be removed.
15 | StringName Name { get; set; }
16 |
17 | //
18 | // Summary:
19 | // The node owner. A node can have any other node as owner (as long as it is a valid
20 | // parent, grandparent, etc. ascending in the tree). When saving a node (using Godot.PackedScene),
21 | // all the nodes it owns will be saved with it. This allows for the creation of
22 | // complex Godot.SceneTrees, with instancing and subinstancing.
23 | // Note: If you want a child to be persisted to a Godot.PackedScene, you must set
24 | // Godot.Node.Owner in addition to calling Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode).
25 | // This is typically relevant for tool scripts and editor plugins. If Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)
26 | // is called without setting Godot.Node.Owner, the newly added Godot.Node will not
27 | // be visible in the scene tree, though it will be visible in the 2D/3D view.
28 | Node Owner { get; set; }
29 |
30 | //
31 | // Summary:
32 | // Returns the object's built-in class name, as a System.String. See also Godot.Object.IsClass(System.String).
33 | // Note: This method ignores class_name declarations. If this object's script has
34 | // defined a class_name, the base, built-in class name is returned instead.
35 | string GetClass();
36 |
37 | //
38 | // Summary:
39 | // Returns true if the object inherits from the given [param class]. See also Godot.Object.GetClass.
40 | // [codeblocks]
41 | // [gdscript]
42 | // var sprite2d = Sprite2D.new()
43 | // sprite2d.is_class("Sprite2D") # Returns true
44 | // sprite2d.is_class("Node") # Returns true
45 | // sprite2d.is_class("Node3D") # Returns false
46 | // [/gdscript]
47 | // [csharp]
48 | // var sprite2d = new Sprite2D();
49 | // sprite2d.IsClass("Sprite2D"); // Returns true
50 | // sprite2d.IsClass("Node"); // Returns true
51 | // sprite2d.IsClass("Node3D"); // Returns false
52 | // [/csharp]
53 | // [/codeblocks]
54 | // Note: This method ignores class_name declarations in the object's script.
55 | bool IsClass(string @class);
56 |
57 | //
58 | // Summary:
59 | // Assigns [param value] to the given [param property]. If the property does not
60 | // exist or the given [param value]'s type doesn't match, nothing happens.
61 | // [codeblocks]
62 | // [gdscript]
63 | // var node = Node2D.new()
64 | // node.set("global_scale", Vector2(8, 2.5))
65 | // print(node.global_scale) # Prints (8, 2.5)
66 | // [/gdscript]
67 | // [csharp]
68 | // var node = new Node2D();
69 | // node.Set("global_scale", new Vector2(8, 2.5));
70 | // GD.Print(node.GlobalScale); // Prints Vector2(8, 2.5)
71 | // [/csharp]
72 | // [/codeblocks]
73 | // Note: In C#, [param property] must be in snake_case when referring to built-in
74 | // Godot properties. Prefer using the names exposed in the PropertyName class to
75 | // avoid allocating a new Godot.StringName on each call.
76 | void Set(StringName property, Variant value);
77 |
78 | //
79 | // Summary:
80 | // Returns the Variant value of the given [param property]. If the [param property]
81 | // does not exist, this method returns null.
82 | // [codeblocks]
83 | // [gdscript]
84 | // var node = Node2D.new()
85 | // node.rotation = 1.5
86 | // var a = node.get("rotation") # a is 1.5
87 | // [/gdscript]
88 | // [csharp]
89 | // var node = new Node2D();
90 | // node.Rotation = 1.5f;
91 | // var a = node.Get("rotation"); // a is 1.5
92 | // [/csharp]
93 | // [/codeblocks]
94 | // Note: In C#, [param property] must be in snake_case when referring to built-in
95 | // Godot properties. Prefer using the names exposed in the PropertyName class to
96 | // avoid allocating a new Godot.StringName on each call.
97 | Variant Get(StringName property);
98 |
99 | //
100 | // Summary:
101 | // Emits the given [param signal] by name. The signal must exist, so it should be
102 | // a built-in signal of this class or one of its inherited classes, or a user-defined
103 | // signal (see Godot.Object.AddUserSignal(System.String,Godot.Collections.Array)).
104 | // This method supports a variable number of arguments, so parameters can be passed
105 | // as a comma separated list.
106 | // Returns Godot.Error.Unavailable if [param signal] does not exist or the parameters
107 | // are invalid.
108 | // [codeblocks]
109 | // [gdscript]
110 | // emit_signal("hit", "sword", 100)
111 | // emit_signal("game_over")
112 | // [/gdscript]
113 | // [csharp]
114 | // EmitSignal("Hit", "sword", 100);
115 | // EmitSignal("GameOver");
116 | // [/csharp]
117 | // [/codeblocks]
118 | // Note: In C#, [param signal] must be in snake_case when referring to built-in
119 | // Godot signals. Prefer using the names exposed in the SignalName class to avoid
120 | // allocating a new Godot.StringName on each call.
121 | Error EmitSignal(StringName signal, params Variant[] args);
122 |
123 | //
124 | // Summary:
125 | // Calls the [param method] on the object and returns the result. This method supports
126 | // a variable number of arguments, so parameters can be passed as a comma separated
127 | // list.
128 | // [codeblocks]
129 | // [gdscript]
130 | // var node = Node3D.new()
131 | // node.call("rotate", Vector3(1.0, 0.0, 0.0), 1.571)
132 | // [/gdscript]
133 | // [csharp]
134 | // var node = new Node3D();
135 | // node.Call("rotate", new Vector3(1f, 0f, 0f), 1.571f);
136 | // [/csharp]
137 | // [/codeblocks]
138 | // Note: In C#, [param method] must be in snake_case when referring to built-in
139 | // Godot methods. Prefer using the names exposed in the MethodName class to avoid
140 | // allocating a new Godot.StringName on each call.
141 | Variant Call(StringName method, params Variant[] args);
142 |
143 | //
144 | // Summary:
145 | // Calls the [param method] on the object during idle time. This method supports
146 | // a variable number of arguments, so parameters can be passed as a comma separated
147 | // list.
148 | // [codeblocks]
149 | // [gdscript]
150 | // var node = Node3D.new()
151 | // node.call_deferred("rotate", Vector3(1.0, 0.0, 0.0), 1.571)
152 | // [/gdscript]
153 | // [csharp]
154 | // var node = new Node3D();
155 | // node.CallDeferred("rotate", new Vector3(1f, 0f, 0f), 1.571f);
156 | // [/csharp]
157 | // [/codeblocks]
158 | // Note: In C#, [param method] must be in snake_case when referring to built-in
159 | // Godot methods. Prefer using the names exposed in the MethodName class to avoid
160 | // allocating a new Godot.StringName on each call.
161 | Variant CallDeferred(StringName method, params Variant[] args);
162 |
163 | //
164 | // Summary:
165 | // Assigns [param value] to the given [param property], after the current frame's
166 | // physics step. This is equivalent to calling Godot.Object.Set(Godot.StringName,Godot.Variant)
167 | // through Godot.Object.CallDeferred(Godot.StringName,Godot.Variant[]).
168 | // [codeblocks]
169 | // [gdscript]
170 | // var node = Node2D.new()
171 | // add_child(node)
172 | // node.rotation = 45.0
173 | // node.set_deferred("rotation", 90.0)
174 | // print(node.rotation) # Prints 45.0
175 | // await get_tree().process_frame
176 | // print(node.rotation) # Prints 90.0
177 | // [/gdscript]
178 | // [csharp]
179 | // var node = new Node2D();
180 | // node.Rotation = 45f;
181 | // node.SetDeferred("rotation", 90f);
182 | // GD.Print(node.Rotation); // Prints 45.0
183 | // await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame);
184 | // GD.Print(node.Rotation); // Prints 90.0
185 | // [/csharp]
186 | // [/codeblocks]
187 | // Note: In C#, [param property] must be in snake_case when referring to built-in
188 | // Godot properties. Prefer using the names exposed in the PropertyName class to
189 | // avoid allocating a new Godot.StringName on each call.
190 | void SetDeferred(StringName property, Variant value);
191 |
192 | //
193 | // Summary:
194 | // Calls the [param method] on the object and returns the result. Unlike Godot.Object.Call(Godot.StringName,Godot.Variant[]),
195 | // this method expects all parameters to be contained inside [param arg_array].
196 | // [codeblocks]
197 | // [gdscript]
198 | // var node = Node3D.new()
199 | // node.callv("rotate", [Vector3(1.0, 0.0, 0.0), 1.571])
200 | // [/gdscript]
201 | // [csharp]
202 | // var node = new Node3D();
203 | // node.Callv("rotate", new Godot.Collections.Array { new Vector3(1f, 0f, 0f), 1.571f
204 | // });
205 | // [/csharp]
206 | // [/codeblocks]
207 | // Note: In C#, [param method] must be in snake_case when referring to built-in
208 | // Godot methods. Prefer using the names exposed in the MethodName class to avoid
209 | // allocating a new Godot.StringName on each call
210 | Variant Callv(StringName method, Godot.Collections.Array argArray);
211 |
212 | //
213 | // Summary:
214 | // Connects a [param signal] by name to a [param callable]. Optional [param flags]
215 | // can be also added to configure the connection's behavior (see Godot.Object.ConnectFlags
216 | // constants).
217 | // A signal can only be connected once to the same Godot.Callable. If the signal
218 | // is already connected, this method returns Godot.Error.InvalidParameter and pushes
219 | // an error message, unless the signal is connected with Godot.Object.ConnectFlags.ReferenceCounted.
220 | // To prevent this, use Godot.Object.IsConnected(Godot.StringName,Godot.Callable)
221 | // first to check for existing connections.
222 | // If the [param callable]'s object is freed, the connection will be lost.
223 | // Examples with recommended syntax:
224 | // Connecting signals is one of the most common operations in Godot and the API
225 | // gives many options to do so, which are described further down. The code block
226 | // below shows the recommended approach.
227 | // [codeblocks]
228 | // [gdscript]
229 | // func _ready():
230 | // var button = Button.new()
231 | // # `button_down` here is a Signal variant type, and we thus call the Signal.connect()
232 | // method, not Object.connect().
233 | // # See discussion below for a more in-depth overview of the API.
234 | // button.button_down.connect(_on_button_down)
235 | // # This assumes that a `Player` class exists, which defines a `hit` signal.
236 | // var player = Player.new()
237 | // # We use Signal.connect() again, and we also use the Callable.bind() method,
238 | // # which returns a new Callable with the parameter binds.
239 | // player.hit.connect(_on_player_hit.bind("sword", 100))
240 | // func _on_button_down():
241 | // print("Button down!")
242 | // func _on_player_hit(weapon_type, damage):
243 | // print("Hit with weapon %s for %d damage." % [weapon_type, damage])
244 | // [/gdscript]
245 | // [csharp]
246 | // public override void _Ready()
247 | // {
248 | // var button = new Button();
249 | // // C# supports passing signals as events, so we can use this idiomatic construct:
250 | // button.ButtonDown += OnButtonDown;
251 | // // This assumes that a `Player` class exists, which defines a `Hit` signal.
252 | // var player = new Player();
253 | // // Signals as events (`player.Hit += OnPlayerHit;`) do not support argument binding.
254 | // You have to use:
255 | // player.Hit.Connect(OnPlayerHit, new Godot.Collections.Array {"sword", 100 });
256 | // }
257 | // private void OnButtonDown()
258 | // {
259 | // GD.Print("Button down!");
260 | // }
261 | // private void OnPlayerHit(string weaponType, int damage)
262 | // {
263 | // GD.Print(String.Format("Hit with weapon {0} for {1} damage.", weaponType, damage));
264 | // }
265 | // [/csharp]
266 | // [/codeblocks]
267 | // Object.connect() or Signal.connect()?
268 | // As seen above, the recommended method to connect signals is not Godot.Object.Connect(Godot.StringName,Godot.Callable,System.UInt32).
269 | // The code block below shows the four options for connecting signals, using either
270 | // this legacy method or the recommended Signal.connect, and using either an implicit
271 | // Godot.Callable or a manually defined one.
272 | // [codeblocks]
273 | // [gdscript]
274 | // func _ready():
275 | // var button = Button.new()
276 | // # Option 1: Object.connect() with an implicit Callable for the defined function.
277 | // button.connect("button_down", _on_button_down)
278 | // # Option 2: Object.connect() with a constructed Callable using a target object
279 | // and method name.
280 | // button.connect("button_down", Callable(self, "_on_button_down"))
281 | // # Option 3: Signal.connect() with an implicit Callable for the defined function.
282 | // button.button_down.connect(_on_button_down)
283 | // # Option 4: Signal.connect() with a constructed Callable using a target object
284 | // and method name.
285 | // button.button_down.connect(Callable(self, "_on_button_down"))
286 | // func _on_button_down():
287 | // print("Button down!")
288 | // [/gdscript]
289 | // [csharp]
290 | // public override void _Ready()
291 | // {
292 | // var button = new Button();
293 | // // Option 1: Object.Connect() with an implicit Callable for the defined function.
294 | // button.Connect("button_down", OnButtonDown);
295 | // // Option 2: Object.connect() with a constructed Callable using a target object
296 | // and method name.
297 | // button.Connect("button_down", new Callable(self, nameof(OnButtonDown)));
298 | // // Option 3: Signal.connect() with an implicit Callable for the defined function.
299 | // button.ButtonDown.Connect(OnButtonDown);
300 | // // Option 3b: In C#, we can use signals as events and connect with this more
301 | // idiomatic syntax:
302 | // button.ButtonDown += OnButtonDown;
303 | // // Option 4: Signal.connect() with a constructed Callable using a target object
304 | // and method name.
305 | // button.ButtonDown.Connect(new Callable(self, nameof(OnButtonDown)));
306 | // }
307 | // private void OnButtonDown()
308 | // {
309 | // GD.Print("Button down!");
310 | // }
311 | // [/csharp]
312 | // [/codeblocks]
313 | // While all options have the same outcome (button's Godot.BaseButton.ButtonDown
314 | // signal will be connected to _on_button_down), option 3 offers the best validation:
315 | // it will print a compile-time error if either the button_down Godot.SignalInfo
316 | // or the _on_button_down Godot.Callable are not defined. On the other hand, option
317 | // 2 only relies on string names and will only be able to validate either names
318 | // at runtime: it will print a runtime error if "button_down" doesn't correspond
319 | // to a signal, or if "_on_button_down" is not a registered method in the object
320 | // self. The main reason for using options 1, 2, or 4 would be if you actually need
321 | // to use strings (e.g. to connect signals programmatically based on strings read
322 | // from a configuration file). Otherwise, option 3 is the recommended (and fastest)
323 | // method.
324 | // Binding and passing parameters:
325 | // The syntax to bind parameters is through Callable.bind, which returns a copy
326 | // of the Godot.Callable with its parameters bound.
327 | // When calling Godot.Object.EmitSignal(Godot.StringName,Godot.Variant[]), the signal
328 | // parameters can be also passed. The examples below show the relationship between
329 | // these signal parameters and bound parameters.
330 | // [codeblocks]
331 | // [gdscript]
332 | // func _ready():
333 | // # This assumes that a `Player` class exists, which defines a `hit` signal.
334 | // var player = Player.new()
335 | // player.hit.connect(_on_player_hit.bind("sword", 100))
336 | // # Parameters added when emitting the signal are passed first.
337 | // player.emit_signal("hit", "Dark lord", 5)
338 | // # We pass two arguments when emitting (`hit_by`, `level`),
339 | // # and bind two more arguments when connecting (`weapon_type`, `damage`).
340 | // func _on_player_hit(hit_by, level, weapon_type, damage):
341 | // print("Hit by %s (level %d) with weapon %s for %d damage." % [hit_by, level,
342 | // weapon_type, damage])
343 | // [/gdscript]
344 | // [csharp]
345 | // public override void _Ready()
346 | // {
347 | // // This assumes that a `Player` class exists, which defines a `Hit` signal.
348 | // var player = new Player();
349 | // // Option 1: Using Callable.Bind(). This way we can still use signals as events.
350 | // player.Hit += OnPlayerHit.Bind("sword", 100);
351 | // // Option 2: Using a `binds` Array in Signal.Connect().
352 | // player.Hit.Connect(OnPlayerHit, new Godot.Collections.Array{ "sword", 100 });
353 | // // Parameters added when emitting the signal are passed first.
354 | // player.EmitSignal("hit", "Dark lord", 5);
355 | // }
356 | // // We pass two arguments when emitting (`hit_by`, `level`),
357 | // // and bind two more arguments when connecting (`weapon_type`, `damage`).
358 | // private void OnPlayerHit(string hitBy, int level, string weaponType, int damage)
359 | // {
360 | // GD.Print(String.Format("Hit by {0} (level {1}) with weapon {2} for {3} damage.",
361 | // hitBy, level, weaponType, damage));
362 | // }
363 | // [/csharp]
364 | // [/codeblocks]
365 | Error Connect(StringName signal, Callable callable, uint flags = 0u);
366 |
367 | //
368 | // Summary:
369 | // Disconnects a [param signal] by name from a given [param callable]. If the connection
370 | // does not exist, generates an error. Use Godot.Object.IsConnected(Godot.StringName,Godot.Callable)
371 | // to make sure that the connection exists.
372 | void Disconnect(StringName signal, Callable callable);
373 |
374 | //
375 | // Summary:
376 | // Returns true if a connection exists between the given [param signal] name and
377 | // [param callable].
378 | // Note: In C#, [param signal] must be in snake_case when referring to built-in
379 | // Godot methods. Prefer using the names exposed in the SignalName class to avoid
380 | // allocating a new Godot.StringName on each call.
381 | bool IsConnected(StringName signal, Callable callable);
382 |
383 | //
384 | // Summary:
385 | // Returns true if the Godot.Node.QueueFree method was called for the object.
386 | bool IsQueuedForDeletion();
387 |
388 | //
389 | // Summary:
390 | // Fetches a node. The Godot.NodePath can be either a relative path (from the current
391 | // node) or an absolute path (in the scene tree) to a node. If the path does not
392 | // exist, a null instance is returned and an error is logged. Attempts to access
393 | // methods on the return value will result in an "Attempt to call on a
394 | // null instance." error. Note: Fetching absolute paths only works when the node
395 | // is inside the scene tree (see Godot.Node.IsInsideTree).
396 | //
397 | // Parameters:
398 | // path:
399 | // The path to the node to fetch.
400 | //
401 | // Type parameters:
402 | // T:
403 | // The type to cast to. Should be a descendant of Godot.Node.
404 | //
405 | // Returns:
406 | // The Godot.Node at the given path.
407 | //
408 | // Exceptions:
409 | // T:System.InvalidCastException:
410 | // The fetched node can't be casted to the given type T.
411 | T GetNode(NodePath path) where T : class;
412 |
413 | //
414 | // Summary:
415 | // Fetches a node. The Godot.NodePath can be either a relative path (from the current
416 | // node) or an absolute path (in the scene tree) to a node. If the path does not
417 | // exist, a null instance is returned and an error is logged. Attempts to access
418 | // methods on the return value will result in an "Attempt to call on a
419 | // null instance." error. Note: Fetching absolute paths only works when the node
420 | // is inside the scene tree (see Godot.Node.IsInsideTree).
421 | //
422 | // Parameters:
423 | // path:
424 | // The path to the node to fetch.
425 | //
426 | // Type parameters:
427 | // T:
428 | // The type to cast to. Should be a descendant of Godot.Node.
429 | //
430 | // Returns:
431 | // The Godot.Node at the given path, or null if not found.
432 | T GetNodeOrNull(NodePath path) where T : class;
433 |
434 | //
435 | // Summary:
436 | // Returns a child node by its index (see Godot.Node.GetChildCount(System.Boolean)).
437 | // This method is often used for iterating all children of a node. Negative indices
438 | // access the children from the last one. To access a child node via its name, use
439 | // Godot.Node.GetNode(Godot.NodePath).
440 | //
441 | // Parameters:
442 | // idx:
443 | // Child index.
444 | //
445 | // includeInternal:
446 | // If false, internal children are skipped (see internal parameter in Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)).
447 | //
448 | // Type parameters:
449 | // T:
450 | // The type to cast to. Should be a descendant of Godot.Node.
451 | //
452 | // Returns:
453 | // The child Godot.Node at the given index idx.
454 | //
455 | // Exceptions:
456 | // T:System.InvalidCastException:
457 | // The fetched node can't be casted to the given type T.
458 | T GetChild(int idx, bool includeInternal = false) where T : class;
459 |
460 | //
461 | // Summary:
462 | // Returns a child node by its index (see Godot.Node.GetChildCount(System.Boolean)).
463 | // This method is often used for iterating all children of a node. Negative indices
464 | // access the children from the last one. To access a child node via its name, use
465 | // Godot.Node.GetNode(Godot.NodePath).
466 | //
467 | // Parameters:
468 | // idx:
469 | // Child index.
470 | //
471 | // includeInternal:
472 | // If false, internal children are skipped (see internal parameter in Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)).
473 | //
474 | // Type parameters:
475 | // T:
476 | // The type to cast to. Should be a descendant of Godot.Node.
477 | //
478 | // Returns:
479 | // The child Godot.Node at the given index idx, or null if not found.
480 | T GetChildOrNull(int idx, bool includeInternal = false) where T : class;
481 |
482 | //
483 | // Summary:
484 | // The node owner. A node can have any other node as owner (as long as it is a valid
485 | // parent, grandparent, etc. ascending in the tree). When saving a node (using Godot.PackedScene),
486 | // all the nodes it owns will be saved with it. This allows for the creation of
487 | // complex Godot.SceneTrees, with instancing and subinstancing.
488 | //
489 | // Type parameters:
490 | // T:
491 | // The type to cast to. Should be a descendant of Godot.Node.
492 | //
493 | // Returns:
494 | // The owner Godot.Node.
495 | //
496 | // Exceptions:
497 | // T:System.InvalidCastException:
498 | // The fetched node can't be casted to the given type T.
499 | T GetOwner() where T : class;
500 |
501 | //
502 | // Summary:
503 | // The node owner. A node can have any other node as owner (as long as it is a valid
504 | // parent, grandparent, etc. ascending in the tree). When saving a node (using Godot.PackedScene),
505 | // all the nodes it owns will be saved with it. This allows for the creation of
506 | // complex Godot.SceneTrees, with instancing and subinstancing.
507 | //
508 | // Type parameters:
509 | // T:
510 | // The type to cast to. Should be a descendant of Godot.Node.
511 | //
512 | // Returns:
513 | // The owner Godot.Node, or null if there is no owner.
514 | T GetOwnerOrNull() where T : class;
515 |
516 | //
517 | // Summary:
518 | // Returns the parent node of the current node, or a null instance if the node lacks
519 | // a parent.
520 | //
521 | // Type parameters:
522 | // T:
523 | // The type to cast to. Should be a descendant of Godot.Node.
524 | //
525 | // Returns:
526 | // The parent Godot.Node.
527 | //
528 | // Exceptions:
529 | // T:System.InvalidCastException:
530 | // The fetched node can't be casted to the given type T.
531 | T GetParent() where T : class;
532 |
533 | //
534 | // Summary:
535 | // Returns the parent node of the current node, or a null instance if the node lacks
536 | // a parent.
537 | //
538 | // Type parameters:
539 | // T:
540 | // The type to cast to. Should be a descendant of Godot.Node.
541 | //
542 | // Returns:
543 | // The parent Godot.Node, or null if the node has no parent.
544 | T GetParentOrNull() where T : class;
545 |
546 | //
547 | // Summary:
548 | // Adds a child [param node]. Nodes can have any number of children, but every child
549 | // must have a unique name. Child nodes are automatically deleted when the parent
550 | // node is deleted, so an entire scene can be removed by deleting its topmost node.
551 | // If [param force_readable_name] is true, improves the readability of the added
552 | // [param node]. If not named, the [param node] is renamed to its type, and if it
553 | // shares Godot.Node.Name with a sibling, a number is suffixed more appropriately.
554 | // This operation is very slow. As such, it is recommended leaving this to false,
555 | // which assigns a dummy name featuring @ in both situations.
556 | // If [param internal] is different than Godot.Node.InternalMode.Disabled, the child
557 | // will be added as internal node. Such nodes are ignored by methods like Godot.Node.GetChildren(System.Boolean),
558 | // unless their parameter include_internal is true.The intended usage is to hide
559 | // the internal nodes from the user, so the user won't accidentally delete or modify
560 | // them. Used by some GUI nodes, e.g. Godot.ColorPicker. See Godot.Node.InternalMode
561 | // for available modes.
562 | // Note: If the child node already has a parent, the function will fail. Use Godot.Node.RemoveChild(Godot.Node)
563 | // first to remove the node from its current parent. For example:
564 | // [codeblocks]
565 | // [gdscript]
566 | // var child_node = get_child(0)
567 | // if child_node.get_parent():
568 | // child_node.get_parent().remove_child(child_node)
569 | // add_child(child_node)
570 | // [/gdscript]
571 | // [csharp]
572 | // Node childNode = GetChild(0);
573 | // if (childNode.GetParent() != null)
574 | // {
575 | // childNode.GetParent().RemoveChild(childNode);
576 | // }
577 | // AddChild(childNode);
578 | // [/csharp]
579 | // [/codeblocks]
580 | // If you need the child node to be added below a specific node in the list of children,
581 | // use Godot.Node.AddSibling(Godot.Node,System.Boolean) instead of this method.
582 | // Note: If you want a child to be persisted to a Godot.PackedScene, you must set
583 | // Godot.Node.Owner in addition to calling Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode).
584 | // This is typically relevant for tool scripts and editor plugins. If Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)
585 | // is called without setting Godot.Node.Owner, the newly added Godot.Node will not
586 | // be visible in the scene tree, though it will be visible in the 2D/3D view.
587 | public void AddChild(Node node, bool forceReadableName = false, Node.InternalMode @internal = Node.InternalMode.Disabled);
588 |
589 | ///
590 | /// Removes a child node. The node is NOT deleted and must be deleted manually.
591 | /// Note: This function may set the Godot.Node.Owner of the removed Node (or its
592 | /// descendants) to be null, if that Godot.Node.Owner is no longer a parent or ancestor.
593 | ///
594 | /// The to remove.
595 | public void RemoveChild(Node node);
596 |
597 | ///
598 | /// Returns the number of child nodes.
599 | ///
600 | /// If false, internal children aren't counted (see AddChild).
601 | /// The number of children for this .
602 | int GetChildCount(bool includeInternal = false);
603 |
604 | //
605 | // Summary:
606 | // Returns an array of references to node's children.
607 | // If [param include_internal] is false, the returned array won't include internal
608 | // children (see internal parameter in Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)).
609 | Array GetChildren(bool includeInternal = false);
610 |
611 | //
612 | // Summary:
613 | // Returns a child node by its index (see Godot.Node.GetChildCount(System.Boolean)).
614 | // This method is often used for iterating all children of a node.
615 | // Negative indices access the children from the last one.
616 | // If [param include_internal] is false, internal children are skipped (see internal
617 | // parameter in Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)).
618 | // To access a child node via its name, use Godot.Node.GetNode(Godot.NodePath).
619 | Node GetChild(int idx, bool includeInternal = false);
620 |
621 | //
622 | // Summary:
623 | // Returns true if the node that the Godot.NodePath points to exists.
624 | bool HasNode(NodePath path);
625 |
626 | //
627 | // Summary:
628 | // Fetches a node. The Godot.NodePath can be either a relative path (from the current
629 | // node) or an absolute path (in the scene tree) to a node. If the path does not
630 | // exist, null is returned and an error is logged. Attempts to access methods on
631 | // the return value will result in an "Attempt to call on a null instance."
632 | // error.
633 | // Note: Fetching absolute paths only works when the node is inside the scene tree
634 | // (see Godot.Node.IsInsideTree).
635 | // Example: Assume your current node is Character and the following tree:
636 | // /root
637 | // /root/Character
638 | // /root/Character/Sword
639 | // /root/Character/Backpack/Dagger
640 | // /root/MyGame
641 | // /root/Swamp/Alligator
642 | // /root/Swamp/Mosquito
643 | // /root/Swamp/Goblin
644 | // Possible paths are:
645 | // [codeblocks]
646 | // [gdscript]
647 | // get_node("Sword")
648 | // get_node("Backpack/Dagger")
649 | // get_node("../Swamp/Alligator")
650 | // get_node("/root/MyGame")
651 | // [/gdscript]
652 | // [csharp]
653 | // GetNode("Sword");
654 | // GetNode("Backpack/Dagger");
655 | // GetNode("../Swamp/Alligator");
656 | // GetNode("/root/MyGame");
657 | // [/csharp]
658 | // [/codeblocks]
659 | Node GetNode(NodePath path);
660 |
661 | //
662 | // Summary:
663 | // Similar to Godot.Node.GetNode(Godot.NodePath), but does not log an error if [param
664 | // path] does not point to a valid Godot.Node.
665 | Node GetNodeOrNull(NodePath path);
666 |
667 | //
668 | // Summary:
669 | // Returns the parent node of the current node, or null if the node lacks a parent.
670 | Node GetParent();
671 |
672 | //
673 | // Summary:
674 | // Finds the first descendant of this node whose name matches [param pattern] as
675 | // in String.match.
676 | // [param pattern] does not match against the full path, just against individual
677 | // node names. It is case-sensitive, with "*" matching zero or more characters and
678 | // "?" matching any single character except ".").
679 | // If [param recursive] is true, all child nodes are included, even if deeply nested.
680 | // Nodes are checked in tree order, so this node's first direct child is checked
681 | // first, then its own direct children, etc., before moving to the second direct
682 | // child, and so on. If [param recursive] is false, only this node's direct children
683 | // are matched.
684 | // If [param owned] is true, this method only finds nodes who have an assigned Godot.Node.Owner.
685 | // This is especially important for scenes instantiated through a script, because
686 | // those scenes don't have an owner.
687 | // Returns null if no matching Godot.Node is found.
688 | // Note: As this method walks through all the descendants of the node, it is the
689 | // slowest way to get a reference to another node. Whenever possible, consider using
690 | // Godot.Node.GetNode(Godot.NodePath) with unique names instead (see Godot.Node.UniqueNameInOwner),
691 | // or caching the node references into variable.
692 | // Note: To find all descendant nodes matching a pattern or a class type, see Godot.Node.FindChildren(System.String,System.String,System.Boolean,System.Boolean).
693 | Node FindChild(string pattern, bool recursive = true, bool owned = true);
694 |
695 | //
696 | // Summary:
697 | // Finds descendants of this node whose name matches [param pattern] as in String.match,
698 | // and/or type matches [param type] as in Godot.Object.IsClass(System.String).
699 | // [param pattern] does not match against the full path, just against individual
700 | // node names. It is case-sensitive, with "*" matching zero or more characters and
701 | // "?" matching any single character except ".").
702 | // [param type] will check equality or inheritance, and is case-sensitive. "Object"
703 | // will match a node whose type is "Node" but not the other way around.
704 | // If [param recursive] is true, all child nodes are included, even if deeply nested.
705 | // Nodes are checked in tree order, so this node's first direct child is checked
706 | // first, then its own direct children, etc., before moving to the second direct
707 | // child, and so on. If [param recursive] is false, only this node's direct children
708 | // are matched.
709 | // If [param owned] is true, this method only finds nodes who have an assigned Godot.Node.Owner.
710 | // This is especially important for scenes instantiated through a script, because
711 | // those scenes don't have an owner.
712 | // Returns an empty array if no matching nodes are found.
713 | // Note: As this method walks through all the descendants of the node, it is the
714 | // slowest way to get references to other nodes. Whenever possible, consider caching
715 | // the node references into variables.
716 | // Note: If you only want to find the first descendant node that matches a pattern,
717 | // see Godot.Node.FindChild(System.String,System.Boolean,System.Boolean).
718 | Array FindChildren(string pattern, string type = "", bool recursive = true, bool owned = true);
719 |
720 | //
721 | // Summary:
722 | // Finds the first parent of the current node whose name matches [param pattern]
723 | // as in String.match.
724 | // [param pattern] does not match against the full path, just against individual
725 | // node names. It is case-sensitive, with "*" matching zero or more characters and
726 | // "?" matching any single character except ".").
727 | // Note: As this method walks upwards in the scene tree, it can be slow in large,
728 | // deeply nested scene trees. Whenever possible, consider using Godot.Node.GetNode(Godot.NodePath)
729 | // with unique names instead (see Godot.Node.UniqueNameInOwner), or caching the
730 | // node references into variable.
731 | Node FindParent(string pattern);
732 |
733 | //
734 | // Summary:
735 | // Returns true if this node is currently inside a Godot.SceneTree.
736 | bool IsInsideTree();
737 |
738 | //
739 | // Summary:
740 | // Returns true if the given node is a direct or indirect child of the current node.
741 | bool IsAncestorOf(Node node);
742 |
743 | //
744 | // Summary:
745 | // Returns true if the given node occurs later in the scene hierarchy than the current
746 | // node.
747 | bool IsGreaterThan(Node node);
748 |
749 | //
750 | // Summary:
751 | // Returns the absolute path of the current node. This only works if the current
752 | // node is inside the scene tree (see Godot.Node.IsInsideTree).
753 | NodePath GetPath();
754 |
755 | //
756 | // Summary:
757 | // Returns the relative Godot.NodePath from this node to the specified [param node].
758 | // Both nodes must be in the same scene or the function will fail.
759 | // If [param use_unique_path] is true, returns the shortest path considering unique
760 | // node.
761 | // Note: If you get a relative path which starts from a unique node, the path may
762 | // be longer than a normal relative path due to the addition of the unique node's
763 | // name.
764 | NodePath GetPathTo(Node node, bool useUniquePath = false);
765 |
766 | //
767 | // Summary:
768 | // Adds the node to a group. Groups are helpers to name and organize a subset of
769 | // nodes, for example "enemies" or "collectables". A node can be in any number of
770 | // groups. Nodes can be assigned a group at any time, but will not be added until
771 | // they are inside the scene tree (see Godot.Node.IsInsideTree). See notes in the
772 | // description, and the group methods in Godot.SceneTree.
773 | // The [param persistent] option is used when packing node to Godot.PackedScene
774 | // and saving to file. Non-persistent groups aren't stored.
775 | // Note: For performance reasons, the order of node groups is not guaranteed. The
776 | // order of node groups should not be relied upon as it can vary across project
777 | // runs.
778 | void AddToGroup(StringName group, bool persistent = false);
779 |
780 | //
781 | // Summary:
782 | // Removes a node from the [param group]. Does nothing if the node is not in the
783 | // [param group]. See notes in the description, and the group methods in Godot.SceneTree.
784 | void RemoveFromGroup(StringName group);
785 |
786 | //
787 | // Summary:
788 | // Returns true if this node is in the specified group. See notes in the description,
789 | // and the group methods in Godot.SceneTree.
790 | bool IsInGroup(StringName group);
791 |
792 | //
793 | // Summary:
794 | // Moves a child node to a different index (order) among the other children. Since
795 | // calls, signals, etc. are performed by tree order, changing the order of children
796 | // nodes may be useful. If [param to_index] is negative, the index will be counted
797 | // from the end.
798 | // Note: Internal children can only be moved within their expected "internal range"
799 | // (see internal parameter in Godot.Node.AddChild(Godot.Node,System.Boolean,Godot.Node.InternalMode)).
800 | void MoveChild(Node childNode, int toIndex);
801 |
802 | //
803 | // Summary:
804 | // Returns an array listing the groups that the node is a member of.
805 | // Note: For performance reasons, the order of node groups is not guaranteed. The
806 | // order of node groups should not be relied upon as it can vary across project
807 | // runs.
808 | // Note: The engine uses some group names internally (all starting with an underscore).
809 | // To avoid conflicts with internal groups, do not add custom groups whose name
810 | // starts with an underscore. To exclude internal groups while looping over Godot.Node.GetGroups,
811 | // use the following snippet:
812 | // [codeblocks]
813 | // [gdscript]
814 | // # Stores the node's non-internal groups only (as an array of Strings).
815 | // var non_internal_groups = []
816 | // for group in get_groups():
817 | // if not group.begins_with("_"):
818 | // non_internal_groups.push_back(group)
819 | // [/gdscript]
820 | // [csharp]
821 | // // Stores the node's non-internal groups only (as a List of strings).
822 | // List nonInternalGroups = new List();
823 | // foreach (string group in GetGroups())
824 | // {
825 | // if (!group.BeginsWith("_"))
826 | // nonInternalGroups.Add(group);
827 | // }
828 | // [/csharp]
829 | // [/codeblocks]
830 | Array GetGroups();
831 |
832 | //
833 | // Summary:
834 | // Queues a node for deletion at the end of the current frame. When deleted, all
835 | // of its child nodes will be deleted as well. This method ensures it's safe to
836 | // delete the node, contrary to Godot.Object.Free. Use Godot.Object.IsQueuedForDeletion
837 | // to check whether a node will be deleted at the end of the frame.
838 | void QueueFree();
839 |
840 | //
841 | // Summary:
842 | // Deletes the object from memory. Pre-existing references to the object become
843 | // invalid, and any attempt to access them will result in a run-time error. Checking
844 | // the references with @GlobalScope.is_instance_valid will return false.
845 | void Free();
846 |
847 | //
848 | // Summary:
849 | // Sets the node's multiplayer authority to the peer with the given peer ID. The
850 | // multiplayer authority is the peer that has authority over the node on the network.
851 | // Useful in conjunction with Godot.Node.RpcConfig(Godot.StringName,Godot.Variant)
852 | // and the Godot.MultiplayerAPI. Inherited from the parent node by default, which
853 | // ultimately defaults to peer ID 1 (the server). If [param recursive], the given
854 | // peer is recursively set as the authority for all children of this node.
855 | void SetMultiplayerAuthority(int id, bool recursive = true);
856 |
857 | //
858 | // Summary:
859 | // Returns the peer ID of the multiplayer authority for this node. See Godot.Node.SetMultiplayerAuthority(System.Int32,System.Boolean).
860 | int GetMultiplayerAuthority();
861 |
862 | //
863 | // Summary:
864 | // Returns true if the local system is the multiplayer authority of this node.
865 | bool IsMultiplayerAuthority();
866 |
867 | //
868 | // Summary:
869 | // Converts this Godot.Object to a string.
870 | //
871 | // Returns:
872 | // A string representation of this object.
873 | string ToString();
874 | }
875 |
--------------------------------------------------------------------------------