Child Components in Code after Instantiate allways null

Godot Version

4.4

Question

I work with .net and have the following problem.
When I instantiate my player, all attached components remain null.

I instantiate via PlayerScene.Instantiate<Player.Player>(); a Player(RigidBody3D).tscn.

Player.tscn has Player.cs
All components are Node3D’s with their own script.

public override void _Ready() {
StatsComponent = GetNode<StatsComponent>(StatsComponentPath);
if (StatsComponent == null)
{
 GD.PushError("Player: StatsComponent not found!");
}
}


In my opinion, the components are set correctly in the Insprector via NodePath.
Values seem to be available in the engine via remote, but not in the .net code.

Am I missing something or why are the child components in Player only initialized with null?

Hi,

That seems weird. Have you tried adding a breakpoint on the player instantiation code to debug in details the instantiated node?

Could you send your full class code? However it looks like Stats Component Path might just be the node that you are trying to get the path too

The code did not work because of another error, but what I noticed is that the components were never null, they are filled with data, it is just displayed incorrectly in Visual Studio.

Idk why this is, but my problem is fixed.

Here a part of the Player and one of the component classes, just to understand:


public partial class Player : CharacterBody3D
{
    [ExportGroup("Components")]
    [Export] public NodePath StatsComponentPath { get; set; }
    [Export] public Array<NodePath> HurtboxComponentPaths { get; set; }
    [Export] public NodePath EquipmentComponentPath { get; set; }
    [Export] public NodePath InventoryComponentPath { get; set; }

    [ExportGroup("Animation")]
    [Export] public NodePath AnimationPlayerPath { get; set; }
    [Export] public string IdleAnimationName { get; set; } = "Idle";
    [Export] public string RunAnimationName { get; set; } = "Run";

    [ExportGroup("Movement Control")]
    [Export] public float MoveSpeed { get; set; } = 10.0f;
    [Export] public float StopForceMultiplier { get; set; } = 5.0f;

    [ExportGroup("Attack Control")]
    [Export] public NodePath DefaultMeleeAttackAreaPath { get; set; }

    [ExportGroup("Interaction")]
    [Export] public float MaxPickupRange { get; set; } = 7.0f;
    private const string INTERACT_ACTION = "interact";

    // General values
    public PlayerSpawnPointId LastPlayerSpawnPointId { get; set; }
    public long Exp { get; set; }

    private Camera3D _camera;
    private readonly float _gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();
    private Vector3 _targetWorldDirection = Vector3.Zero;
    private Vector3? _lookAtTarget;

    private AnimationPlayer _animationPlayer;
    public StatsComponent StatsComponent { get; private set; }
    public EquipmentComponent EquipmentComponent { get; private set; }
    public InventoryComponent InventoryComponent { get; private set; }
    private VisualController _visualController;
    private AttackComponent _activeMainHandAttackArea;
    private AttackComponent _activeOffHandAttackArea;

    private ItemInstance _currentMainHandWeapon;
    private ItemInstance _currentOffHandItem;

    private DroppedItem _currentlyHoveredItem;

    private enum PlayerAnimState { Idle, Run, Attack }
    private PlayerAnimState _currentAnimState = PlayerAnimState.Idle;

    private const string DEFAULT_ATTACK_ANIM = "default_attack";

    public override void _Ready()
    {
        StatsComponent = GetNodeOrNull<StatsComponent>(StatsComponentPath);
        if (StatsComponent == null)
        {
            GD.PushError("Player: StatsComponent not found!");
        }

        EquipmentComponent = GetNodeOrNull<EquipmentComponent>(EquipmentComponentPath);
        if (EquipmentComponent == null)
        {
            GD.PushError("Player: EquipmentComponent not found!");
        }

        InventoryComponent = GetNodeOrNull<InventoryComponent>(InventoryComponentPath);
        if (InventoryComponent == null)
        {
            GD.PushError("Player: InventoryComponent not found!");
        }

        _animationPlayer = GetNodeOrNull<AnimationPlayer>(AnimationPlayerPath);
        if (_animationPlayer == null)
        {
            GD.PushError("Player: AnimationPlayer not found at path: " + AnimationPlayerPath);
        }
        else
        {
            _animationPlayer.AnimationFinished += OnAttackAnimationFinished;
            PlayAnimation(IdleAnimationName);
            _currentAnimState = PlayerAnimState.Idle;
        }

        foreach (NodePath hurtboxComponentPath in HurtboxComponentPaths)
        {
            HurtboxComponent hurtboxComponent = GetNodeOrNull<HurtboxComponent>(hurtboxComponentPath);
            hurtboxComponent.HitDetected += OnHitReceived;
        }
    }

    public override void _Process(double delta)
    {
        PlayerInput();

        _camera ??= GetViewport().GetCamera3D();
        if (_camera != null)
        {
            CalculateAndEmitLookAtTarget();
        }
    }

    public override void _PhysicsProcess(double delta)
    {
        Movement(delta);
        UpdateAnimationState();
    }

    public override void _Input(InputEvent @event)
    {
        if (@event.IsActionPressed("attack"))
        {
            PerformMeleeAttack();
        }

        InteractAction();
    }

    #region Init

    private void FinalizeSetup()
    {
        Global.Instance.SetPlayer(this);
    }

    public void Initialize(PlayerDto dto)
    {
        if (dto == null)
        {
            GD.PushError($"{Name}: Initialize called with null dto!");
            return;
        }

        Name = dto.Name;

        if (StatsComponent == null)
        {
            GD.PushError($"{Name}: StatsComponent is null!");
            return;
        }

        Exp = dto.Exp;
        LastPlayerSpawnPointId = dto.LastPlayerSpawnPointId;

        StatsComponent.Armor = dto.Armor;
        StatsComponent.Health = dto.Health;
        StatsComponent.Heal(dto.Health.Max);
        StatsComponent.Mana = dto.Mana;
        StatsComponent.Stamina = dto.Stamina;

        CallDeferred(nameof(FinalizeSetup));
    }
}
[GlobalClass]
public partial class InventoryComponent : Node3D
{
    [Signal]
    public delegate void InventoryChangedEventHandler();

    [Export] public int MaxSlots { get; set; } = 20;
    [Export] public Array<InternalItemSlot> Items { get; private set; } = new();

    private Player.Player _playerOwner;

    public override void _Ready()
    {
        _playerOwner = GetOwner<Player.Player>();
        if (_playerOwner == null)
        {
            GD.PushWarning($"{Name}: Could not get Player owner.");
        }
    }

    #region Add item

    public bool AddItem(ItemInstance instanceToAdd) {}
}
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.