├── 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 | [![NuGet version (Godot.Composition)](https://img.shields.io/badge/nuget-v1.3.1-blue?style=flat-square)](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 | ![component-tree](https://github.com/MysteriousMilk/Godot.Composition/assets/6441268/1c0f10e0-9fe3-4439-b385-412ed979a45f) 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 | --------------------------------------------------------------------------------