Scene node3D Instantiate() dynamecly but not shown in the main scene

Godot Version

4.2

Question

Hey all ,
i have gun and i try to make bullet fire from the gun
i able to initiate the bullet object from code , but it does not shown in the main scene
no matter what i do .
here is the tree :
image
and the rifle scene :
image
This is the bullet:
image

And here is the code that supposed to load the bullet into the scene , i can’t see it .
seach for : if(!GunAnimationPlayer.IsPlaying())

using Godot;
using System;
using static Godot.GD;

public partial class player : CharacterBody3D
{
	public  float Speed = 5.0f;
	public const float JumpVelocity = 8.5f;

	// Get the gravity from the project settings to be synced with RigidBody nodes.
	public float gravity = 9.8f;

	private const float SENSITIVITY = 0.3f;
	private Node3D Head;
	private Camera3D Camera;
	private AnimationPlayer GunAnimationPlayer;
	private RayCast3D GunRayCast3D;
    private PackedScene Bullet;

    private  float tBot = 0.0f;
	private const float BOB_FREQ = 2.0f;
	private const float BOB_AMP = 0.08f;
	private const float WALK_SPEED = 5.0f;
	private const float SPRINT_SPEED = 8.0f;
	private const float BASE_FOV = 75.0f;
	private const float FOV_CHANGE = 1.5f;


    [Signal]
    public delegate void PlayerHitEventHandler();

    public override void _Ready()
	{
        //Print("Test Print");
        Bullet = Load<PackedScene>("res://bullet.tscn");
        GunRayCast3D = GetNode<RayCast3D>("Head/Camera3D/SteampunkRifle/RayCast3D");
        Head = GetNode<Node3D>("Head");
        Camera = GetNode<Camera3D>("Head/Camera3D");
        GunAnimationPlayer = GetNode<AnimationPlayer>("Head/Camera3D/SteampunkRifle/AnimationPlayer");
        Input.MouseMode = Input.MouseModeEnum.Captured;
	}

	public override void _Input(InputEvent @event)
	{
		if(@event.IsPressed())
		{
            if (@event.IsAction("exit"))
            {
                this.GetTree().Root.PropagateNotification((int)CharacterBody3D.NotificationWMCloseRequest);
                this.GetTree().Quit();
            }
        }
	}

	public override void _UnhandledInput(InputEvent @event)
	{
		if (@event is InputEventMouseMotion inputEventMouseMotion)
		{
            Head.RotateY(-Mathf.DegToRad(inputEventMouseMotion.Relative.X) * SENSITIVITY);
            Camera.RotateX(-Mathf.DegToRad(inputEventMouseMotion.Relative.Y) * SENSITIVITY);

            Vector3 rotationCamera = new Vector3(Mathf.Clamp(Camera.Rotation.X, Mathf.DegToRad(-40), Mathf.DegToRad(60)),
                Camera.Rotation.Y, 0);

            Camera.Rotation = rotationCamera;
        }
	}

	public override void _PhysicsProcess(double delta)
	{
		Vector3 velocity = Velocity;

		// Add the gravity.
		if (!IsOnFloor())
			velocity.Y -= gravity * (float)delta;

		// Handle Jump.
		if (Input.IsActionJustPressed("jump") && IsOnFloor())
		{
			velocity.Y = JumpVelocity;
			//Print(velocity.Y);
		}

		if(Input.IsActionPressed("shoot"))
		{
			if(!GunAnimationPlayer.IsPlaying())
			{
				GunAnimationPlayer.Play("shoot");
                bullet instance = (bullet)Bullet.Instantiate();
				string s =  instance.NameBullet;

				instance.Position = GunRayCast3D.GlobalPosition;
				Transform3D transform3D = new Transform3D();
				transform3D.Basis = GunRayCast3D.Transform.Basis;
				instance.Transform = transform3D;
                GetParent().AddChild(instance);


            }
		}
		
		if(Input.IsActionPressed("speed"))
		{
			//Print("speed pressed");
			Speed = SPRINT_SPEED;
		}
		else
		{
            //Print("speed not pressed");
            Speed = WALK_SPEED;
		}

		//Print(Speed);

		// Get the input direction and handle the movement/deceleration.
		// As good practice, you should replace UI actions with custom gameplay actions.
		Vector2 inputDir = Input.GetVector("left", "right", "up", "down");
		Vector3 direction = (Head.Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();

		if (IsOnFloor()) { 
			if (direction != Vector3.Zero)
			{
				velocity.X = direction.X * Speed;
				velocity.Z = direction.Z * Speed;
			}
			else
			{
                velocity.X = Mathf.Lerp(velocity.X, direction.X * Speed, (float)delta * 7.0f);
                velocity.Z = Mathf.Lerp(velocity.Z, direction.Z * Speed, (float)delta * 7.0f);
            }
        }
		else
		{

			velocity.X = Mathf.Lerp(velocity.X, direction.X * Speed, (float)delta * 3.0f);
            velocity.Z = Mathf.Lerp(velocity.Z, direction.Z * Speed, (float)delta * 3.0f);


            /*direction.Lerp(new Vector3(direction.X * Speed, direction.Y, direction.Z*Speed),(float)delta * 3.0f);
			velocity = direction;*/
        }


        float b2f = IsOnFloor() ? 1f : 0f;

        tBot += (float)delta * velocity.Length() * b2f;
		Transform3D CameraTransform = Camera.Transform;
		CameraTransform.Origin = headBob(tBot);
        Camera.Transform = CameraTransform;


        Velocity = velocity;


		float VelocityClamp = Mathf.Clamp(velocity.Length(), 0.5f, SPRINT_SPEED * 2);
		float TragetFov = BASE_FOV + FOV_CHANGE * VelocityClamp;
		Camera.Fov = Mathf.Lerp(Camera.Fov, TragetFov, (float)delta * 8.0f);
		
		MoveAndSlide();
	}

	private Vector3 headBob(float time)
	{
		Vector3 pos = Vector3.Zero;
		pos.Y = Mathf.Sin(time * BOB_FREQ) * BOB_AMP;
		pos.X = Mathf.Cos(time * BOB_FREQ/2) * BOB_AMP;
		return pos;
	}

	public void Hit(Vector3 dir)
	{
        EmitSignal(SignalName.PlayerHit);
		//Print(dir);
		Velocity += dir * 8.0f; 

    }

}

Here is the bullet code which when i set break point in to the
public override void _Process(double delta) it stopping there .

using Godot;
using System;
using static Godot.GD;

public partial class bullet : Node3D
{
	private MeshInstance3D mesh;
	private RayCast3D rayCast;
	private const float SPEED = 40.0f;
	private string nameBullet = "Bullt";

	public string NameBullet
	{
		get { return nameBullet; }
	}

	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		mesh = GetNode<MeshInstance3D>("MeshInstance3D");
        rayCast = GetNode<RayCast3D>("RayCast3D");

    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
	{
		Position += Transform.Basis * new Vector3(0, 0, -SPEED) * (float)delta;
	}
}

Thanks

You need to set Owner, see the documentation Node — Godot Engine (stable) documentation in English

i did set like that :

GunAnimationPlayer.Play("shoot");
                bullet instance = (bullet)Bullet.Instantiate();
				string s =  instance.NameBullet;

				instance.Position = GunRayCast3D.GlobalPosition;
				Transform3D transform3D = new Transform3D();
				transform3D.Basis = GunRayCast3D.Transform.Basis;
				instance.Transform = transform3D;
                GetParent().AddChild(instance);
				instance.Owner = GetParent();
				//GetParent().Owner = instance;

But it didnt help , did both ways .

what else can it be ?

instance.Owner = GetParent().owner;

But I don’t use C#, so take that into account :smiley:

ETA - I find, in gdscript, this works:
something.set_owner(self.owner)

still not working , i will do small demo scene to that i can upload it maybe

1 Like

looks like i can’t upload the project here so i will paste the simplified source code
bullet.cs

using Godot;
using System;
using static Godot.GD;

public partial class bullet : Node3D
{
    private MeshInstance3D mesh;
    private RayCast3D rayCast;
    private const float SPEED = 40.0f;
    private string nameBullet = "Bullt";

    public string NameBullet
    {
        get { return nameBullet; }
    }

    // Called when the node enters the scene tree for the first time.
    public override void _Ready()
    {
        mesh = GetNode<MeshInstance3D>("MeshInstance3D");
        rayCast = GetNode<RayCast3D>("RayCast3D");

    }

    // Called every frame. 'delta' is the elapsed time since the previous frame.
    public override void _Process(double delta)
    {
        Position += Transform.Basis * new Vector3(0, 0, -SPEED) * (float)delta;
    }
}

player.cs:

using Godot;
using static Godot.GD;
using System;

public partial class Player : CharacterBody3D
{
	public const float Speed = 5.0f;
	public const float JumpVelocity = 4.5f;
    private RayCast3D GunRayCast3D;
    private PackedScene Bullet;

    // Get the gravity from the project settings to be synced with RigidBody nodes.
    public float gravity = ProjectSettings.GetSetting("physics/3d/default_gravity").AsSingle();


    public override void _Input(InputEvent @event)
    {
        if (@event.IsPressed())
        {
            if (@event.IsAction("exit"))
            {
                this.GetTree().Root.PropagateNotification((int)CharacterBody3D.NotificationWMCloseRequest);
                this.GetTree().Quit();
            }
        }
    }

    public override void _Ready()
	{
        
        Bullet = Load<PackedScene>("res://bullet.tscn");
        GunRayCast3D = GetNode<RayCast3D>("MeshInstance3D/Node3D/Camera3D/SteampunkRifle/RayCast3D");
    }

	public override void _PhysicsProcess(double delta)
	{
		Vector3 velocity = Velocity;

		// Add the gravity.
		if (!IsOnFloor())
			velocity.Y -= gravity * (float)delta;

		// Handle Jump.
		if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
			velocity.Y = JumpVelocity;

		// Get the input direction and handle the movement/deceleration.
		// As good practice, you should replace UI actions with custom gameplay actions.
		Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
		Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
		if (direction != Vector3.Zero)
		{
			velocity.X = direction.X * Speed;
			velocity.Z = direction.Z * Speed;
		}
		else
		{
			velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
			velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed);
		}



        if (Input.IsActionPressed("shoot"))
        {
           
                bullet instance = (bullet)Bullet.Instantiate();
                string s = instance.NameBullet;

                instance.Position = GunRayCast3D.GlobalPosition;
                Transform3D transform3D = new Transform3D();
                transform3D.Basis = GunRayCast3D.Transform.Basis;
                instance.Transform = transform3D;
                GetParent().AddChild(instance);
                instance.Owner = GetParent().Owner;    
        }

        Velocity = velocity;
		MoveAndSlide();
	}
}

Main scene :


bullet:

SteampunkRifle

Maybe someone else with C# can better help you. However, this may help:

  1. In gdscript if I do instance.owner = self.owner it does not work
  2. If I use the set_owner() method, it does work.

I found what was wrong , as im following tutorials to learn i must say , be careful not all tutorials are Precise ,
so any way the solution to my problem is to add it as child to the Rifle it self not to the
Node3D main node

Rifle.AddChild(instance);

My question now is who responsible to destroy this object after it added and fired ?

If you don’t care, then the node tree will clean-up when it unloads.

If you don’t want bullets adding-up then make an array of them and re-use them. Hide the ones that are “done” and reset the next one to be used.

You can also loop that array, on some exit condition, and bullet.queue_free() on each element.

1 Like

the array is good idea , wander what is more officiant it terms of performance to use array or queue free () ?
when the case is constant shooting from machine gun

Well, the array keeps track of all the nodes instanced; hence it is able to free them too.