feat: automatic injection of elements in scene.
This commit is contained in:
@@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
|
|
||||||
namespace GodotHostTest.Game;
|
namespace GodotHostTest.Game;
|
||||||
|
|
||||||
public partial class GameRootServiceSource : RootServiceSource
|
public partial class LevelServiceSource : RootServiceSource
|
||||||
{
|
{
|
||||||
[Export] private TargetSpawner _targetSpawner = null!;
|
[Export] private TargetSpawner _targetSpawner = null!;
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
namespace GodotHostTest.Interfaces;
|
||||||
|
|
||||||
|
public interface ILevel
|
||||||
|
{
|
||||||
|
}
|
||||||
+21
-2
@@ -1,11 +1,13 @@
|
|||||||
|
#if TOOLS
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Runtime.Loader;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace GodotHostTest.addons.godot_di;
|
namespace GodotHostTest.GodotDI.Editor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Taken from https://github.com/godotengine/godot-proposals/issues/12294
|
/// Taken from https://github.com/godotengine/godot-proposals/issues/12294
|
||||||
@@ -23,7 +25,7 @@ public static class GodotScriptPathCache
|
|||||||
{
|
{
|
||||||
if (_assemblyLoadEventHandler != null) return;
|
if (_assemblyLoadEventHandler != null) return;
|
||||||
Initialize();
|
Initialize();
|
||||||
System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())!
|
AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly())!
|
||||||
.Unloading += _ => { Deinitialize(); };
|
.Unloading += _ => { Deinitialize(); };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,4 +89,21 @@ public static class GodotScriptPathCache
|
|||||||
InitializeIfNeeded();
|
InitializeIfNeeded();
|
||||||
return PathToType.TryGetValue(path, out type);
|
return PathToType.TryGetValue(path, out type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Type? GetCSharpScriptType(this GodotObject node)
|
||||||
|
{
|
||||||
|
if (node.GetScript().AsGodotObject() is CSharpScript script &&
|
||||||
|
TryGetTypeFromPath(script.ResourcePath, out var type))
|
||||||
|
return type;
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsServiceSource(this GodotObject node)
|
||||||
|
{
|
||||||
|
return node.GetScript().AsGodotObject() is CSharpScript script &&
|
||||||
|
TryGetTypeFromPath(script.ResourcePath, out var type) &&
|
||||||
|
type.IsAssignableTo(typeof(ServiceSource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
|
#if TOOLS
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Godot;
|
using Godot;
|
||||||
|
|
||||||
namespace GodotHostTest.GodotDI;
|
namespace GodotHostTest.GodotDI.Editor;
|
||||||
|
|
||||||
public partial class ScopePickerEditor : EditorProperty
|
public partial class ScopePickerEditor : EditorProperty
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The "combo box" that picks a choice from the scopes.
|
|
||||||
/// </summary>
|
|
||||||
private OptionButton _optionButton = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The currently chosen type.
|
/// The currently chosen type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -21,6 +17,11 @@ public partial class ScopePickerEditor : EditorProperty
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private string[] _fullyQualifiedNames;
|
private string[] _fullyQualifiedNames;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The "combo box" that picks a choice from the scopes.
|
||||||
|
/// </summary>
|
||||||
|
private OptionButton _optionButton = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A guard against internal changes while the property is being updated.
|
/// A guard against internal changes while the property is being updated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -93,3 +94,4 @@ public partial class ScopePickerEditor : EditorProperty
|
|||||||
_optionButton.Selected = Array.IndexOf(_fullyQualifiedNames, _currentType);
|
_optionButton.Selected = Array.IndexOf(_fullyQualifiedNames, _currentType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GodotHostTest.GodotDI;
|
||||||
|
|
||||||
|
public static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
public enum SingleFailureReason
|
||||||
|
{
|
||||||
|
LessThanOne,
|
||||||
|
MoreThanOne
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T? Single<T>(this IEnumerable<T> enumerable, out SingleFailureReason? failureReason)
|
||||||
|
where T : class
|
||||||
|
{
|
||||||
|
using var enumerator = enumerable.GetEnumerator();
|
||||||
|
if (!enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
failureReason = SingleFailureReason.LessThanOne;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = enumerator.Current;
|
||||||
|
if (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
failureReason = SingleFailureReason.MoreThanOne;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
failureReason = null;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
namespace GodotHostTest.addons.godot_di;
|
|
||||||
|
|
||||||
// RandomIntEditor.cs
|
|
||||||
#if TOOLS
|
|
||||||
using Godot;
|
|
||||||
|
|
||||||
public partial class RandomIntEditor : EditorProperty
|
|
||||||
{
|
|
||||||
// The main control for editing the property.
|
|
||||||
private Button _propertyControl = new Button();
|
|
||||||
|
|
||||||
// An internal value of the property.
|
|
||||||
private int _currentValue = 0;
|
|
||||||
|
|
||||||
// A guard against internal changes when the property is updated.
|
|
||||||
private bool _updating = false;
|
|
||||||
|
|
||||||
public RandomIntEditor()
|
|
||||||
{
|
|
||||||
// Add the control as a direct child of EditorProperty node.
|
|
||||||
AddChild(_propertyControl);
|
|
||||||
// Make sure the control is able to retain the focus.
|
|
||||||
AddFocusable(_propertyControl);
|
|
||||||
// Setup the initial state and connect to the signal to track changes.
|
|
||||||
RefreshControlText();
|
|
||||||
_propertyControl.Pressed += OnButtonPressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnButtonPressed()
|
|
||||||
{
|
|
||||||
// Ignore the signal if the property is currently being updated.
|
|
||||||
if (_updating)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate a new random integer between 0 and 99.
|
|
||||||
_currentValue = (int)GD.Randi() % 100;
|
|
||||||
RefreshControlText();
|
|
||||||
EmitChanged(GetEditedProperty(), _currentValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void _UpdateProperty()
|
|
||||||
{
|
|
||||||
// Read the current value from the property.
|
|
||||||
var newValue = (int)GetEditedObject().Get(GetEditedProperty());
|
|
||||||
if (newValue == _currentValue)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the control with the new value.
|
|
||||||
_updating = true;
|
|
||||||
_currentValue = newValue;
|
|
||||||
RefreshControlText();
|
|
||||||
_updating = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshControlText()
|
|
||||||
{
|
|
||||||
_propertyControl.Text = $"Value: {_currentValue}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
uid://db526cjmnd4q7
|
|
||||||
@@ -9,17 +9,22 @@ namespace GodotHostTest.GodotDI;
|
|||||||
[Icon("res://addons/GodotDI/ServiceSource.svg")]
|
[Icon("res://addons/GodotDI/ServiceSource.svg")]
|
||||||
public partial class ServiceSource : Node
|
public partial class ServiceSource : Node
|
||||||
{
|
{
|
||||||
[Export] private string _serviceScope = typeof(RootScope).AssemblyQualifiedName!;
|
private static readonly string RootScopeQualifiedName = typeof(RootScope).AssemblyQualifiedName!;
|
||||||
|
[Export] private string _serviceScope = RootScopeQualifiedName;
|
||||||
[Export] protected Node?[] InjectedNodes = [];
|
[Export] protected Node?[] InjectedNodes = [];
|
||||||
|
|
||||||
internal void ResolveInjectedNodes(IServiceProvider serviceProvider)
|
internal void ResolveInjectedNodes(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
// if this service source must also act as a scope, create the scope and use its service provider
|
// if this service source must also act as a scope, create the scope and use its service provider
|
||||||
if (_serviceScope != "")
|
if (_serviceScope != RootScopeQualifiedName)
|
||||||
{
|
{
|
||||||
serviceProvider = serviceProvider
|
var serviceProviderCreator = serviceProvider.GetRequiredService<Func<IProvider, IServiceProvider>>();
|
||||||
.CreateScope()
|
var scopedProvider = serviceProvider.GetRequiredService<IProvider>()
|
||||||
.ServiceProvider;
|
.GetScopedProvider(ScopeTypesAndNames.GetType(_serviceScope));
|
||||||
|
serviceProvider = serviceProviderCreator(scopedProvider);
|
||||||
|
// serviceProvider = serviceProvider
|
||||||
|
// .CreateScope()
|
||||||
|
// .ServiceProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve the injected nodes against the chosen service provider
|
// resolve the injected nodes against the chosen service provider
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ name="GodotDI"
|
|||||||
description=""
|
description=""
|
||||||
author="owof games"
|
author="owof games"
|
||||||
version="0.1"
|
version="0.1"
|
||||||
script="GodotDI.cs"
|
script="Editor/Plugin.cs"
|
||||||
|
|||||||
+5
-137
@@ -1,142 +1,10 @@
|
|||||||
[gd_scene format=3 uid="uid://vsburk37p4u"]
|
[gd_scene format=3 uid="uid://vsburk37p4u"]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" uid="uid://3v8kq1rfwhmr" path="res://Game/target.tscn" id="2_c77vg"]
|
[ext_resource type="Script" uid="uid://bvjvojer872c8" path="res://Game/Root.cs" id="1_iqet6"]
|
||||||
[ext_resource type="Script" uid="uid://cxfrtglku2gfa" path="res://Game/TargetSpawner.cs" id="3_iqet6"]
|
[ext_resource type="PackedScene" uid="uid://xfd48ep5qi1k" path="res://Game/level.tscn" id="3_bbjqm"]
|
||||||
[ext_resource type="Script" uid="uid://dk578xnlxjwrr" path="res://Game/GameRootScope.cs" id="3_t2jh2"]
|
|
||||||
[ext_resource type="Script" uid="uid://bgrjeadxhgmvi" path="res://Game/Score.cs" id="4_t2jh2"]
|
|
||||||
[ext_resource type="Texture2D" uid="uid://y1ros3y7hag2" path="res://icon.svg" id="5_4ty2a"]
|
|
||||||
[ext_resource type="Script" uid="uid://ce7gnp1lcrrdv" path="res://Game/Turret.cs" id="6_rckqj"]
|
|
||||||
[ext_resource type="PackedScene" uid="uid://brbufibn7hi3o" path="res://Game/projectile.tscn" id="7_rckqj"]
|
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_bbjqm"]
|
|
||||||
length = 0.001
|
|
||||||
tracks/0/type = "value"
|
|
||||||
tracks/0/imported = false
|
|
||||||
tracks/0/enabled = true
|
|
||||||
tracks/0/path = NodePath("Score:offset_transform_position")
|
|
||||||
tracks/0/interp = 1
|
|
||||||
tracks/0/loop_wrap = true
|
|
||||||
tracks/0/keys = {
|
|
||||||
"times": PackedFloat32Array(0),
|
|
||||||
"transitions": PackedFloat32Array(1),
|
|
||||||
"update": 0,
|
|
||||||
"values": [Vector2(0, 0)]
|
|
||||||
}
|
|
||||||
tracks/1/type = "value"
|
|
||||||
tracks/1/imported = false
|
|
||||||
tracks/1/enabled = true
|
|
||||||
tracks/1/path = NodePath("Score:offset_transform_rotation")
|
|
||||||
tracks/1/interp = 1
|
|
||||||
tracks/1/loop_wrap = true
|
|
||||||
tracks/1/keys = {
|
|
||||||
"times": PackedFloat32Array(0),
|
|
||||||
"transitions": PackedFloat32Array(1),
|
|
||||||
"update": 0,
|
|
||||||
"values": [0.0]
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_4ty2a"]
|
|
||||||
resource_name = "wiggle"
|
|
||||||
length = 0.3
|
|
||||||
tracks/0/type = "value"
|
|
||||||
tracks/0/imported = false
|
|
||||||
tracks/0/enabled = true
|
|
||||||
tracks/0/path = NodePath("Score:offset_transform_position")
|
|
||||||
tracks/0/interp = 1
|
|
||||||
tracks/0/loop_wrap = true
|
|
||||||
tracks/0/keys = {
|
|
||||||
"times": PackedFloat32Array(0, 0.049999997, 0.099999994, 0.15, 0.2, 0.24999999),
|
|
||||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1),
|
|
||||||
"update": 0,
|
|
||||||
"values": [Vector2(0, 0), Vector2(10, -10), Vector2(-10, -10), Vector2(10, 10), Vector2(-10, 10), Vector2(0, 0)]
|
|
||||||
}
|
|
||||||
tracks/1/type = "value"
|
|
||||||
tracks/1/imported = false
|
|
||||||
tracks/1/enabled = true
|
|
||||||
tracks/1/path = NodePath("Score:offset_transform_rotation")
|
|
||||||
tracks/1/interp = 1
|
|
||||||
tracks/1/loop_wrap = true
|
|
||||||
tracks/1/keys = {
|
|
||||||
"times": PackedFloat32Array(0, 0.049999997, 0.099999994, 0.15, 0.2, 0.24999999),
|
|
||||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1),
|
|
||||||
"update": 0,
|
|
||||||
"values": [0.0, 0.08726646, -0.08726646, 0.08726646, -0.08726646, 0.0]
|
|
||||||
}
|
|
||||||
|
|
||||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_rckqj"]
|
|
||||||
_data = {
|
|
||||||
&"RESET": SubResource("Animation_bbjqm"),
|
|
||||||
&"wiggle": SubResource("Animation_4ty2a")
|
|
||||||
}
|
|
||||||
|
|
||||||
[node name="Node2D" type="Node2D" unique_id=920849082]
|
[node name="Node2D" type="Node2D" unique_id=920849082]
|
||||||
|
script = ExtResource("1_iqet6")
|
||||||
|
_levelScene = ExtResource("3_bbjqm")
|
||||||
|
|
||||||
[node name="RootScope" type="Node" parent="." unique_id=1453141084 node_paths=PackedStringArray("_targetSpawner", "InjectedNodes")]
|
[node name="Level" parent="." unique_id=1331632726 instance=ExtResource("3_bbjqm")]
|
||||||
script = ExtResource("3_t2jh2")
|
|
||||||
_targetSpawner = NodePath("../Spawner")
|
|
||||||
InjectedNodes = [NodePath("../Turret"), NodePath("../Target"), NodePath("../Target2"), NodePath("../Target3"), NodePath("../Target4"), NodePath("../Target5"), NodePath("../Target6"), NodePath("../Spawner"), NodePath("../Score")]
|
|
||||||
|
|
||||||
[node name="Target" parent="." unique_id=2101558052 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(198, 143)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Target2" parent="." unique_id=1480287846 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(669, 64)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Target3" parent="." unique_id=242951997 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(1060, 263)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Target4" parent="." unique_id=1190782819 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(961, 620)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Target5" parent="." unique_id=1183803617 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(469, 591)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Target6" parent="." unique_id=2017833482 instance=ExtResource("2_c77vg")]
|
|
||||||
position = Vector2(53, 417)
|
|
||||||
TimeOutDurationInSeconds = 2.0
|
|
||||||
|
|
||||||
[node name="Spawner" type="Node2D" parent="." unique_id=1331368036 node_paths=PackedStringArray("Timer")]
|
|
||||||
script = ExtResource("3_iqet6")
|
|
||||||
TimeBetweenSpawns = 1.5
|
|
||||||
Timer = NodePath("Timer")
|
|
||||||
|
|
||||||
[node name="Timer" type="Timer" parent="Spawner" unique_id=1142098929]
|
|
||||||
|
|
||||||
[node name="Score" type="ColorRect" parent="." unique_id=1990204205 node_paths=PackedStringArray("_animationPlayer", "_label")]
|
|
||||||
offset_right = 200.0
|
|
||||||
offset_bottom = 40.0
|
|
||||||
offset_transform_enabled = true
|
|
||||||
color = Color(1, 1, 1, 0.2784314)
|
|
||||||
script = ExtResource("4_t2jh2")
|
|
||||||
_animationPlayer = NodePath("../AnimationPlayer")
|
|
||||||
_label = NodePath("Label")
|
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="Score" unique_id=1929317028]
|
|
||||||
layout_mode = 1
|
|
||||||
anchors_preset = -1
|
|
||||||
anchor_right = 1.0
|
|
||||||
anchor_bottom = 1.0
|
|
||||||
offset_left = 10.0
|
|
||||||
offset_right = -10.0
|
|
||||||
grow_horizontal = 2
|
|
||||||
grow_vertical = 2
|
|
||||||
theme_override_colors/font_color = Color(0, 0, 0, 1)
|
|
||||||
text = "Score: 0"
|
|
||||||
vertical_alignment = 1
|
|
||||||
|
|
||||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="." unique_id=1749711689]
|
|
||||||
libraries/ = SubResource("AnimationLibrary_rckqj")
|
|
||||||
|
|
||||||
[node name="Turret" type="Sprite2D" parent="." unique_id=1932581411]
|
|
||||||
modulate = Color(1.92523e-07, 0.88004553, 0.8802661, 1)
|
|
||||||
position = Vector2(539, 307)
|
|
||||||
texture = ExtResource("5_4ty2a")
|
|
||||||
script = ExtResource("6_rckqj")
|
|
||||||
_projectilePackedScene = ExtResource("7_rckqj")
|
|
||||||
|
|
||||||
[connection signal="timeout" from="Spawner/Timer" to="Spawner" method="OnTimer"]
|
|
||||||
|
|||||||
Reference in New Issue
Block a user