This site is currently in read-only mode during migration to a new platform.
You cannot post questions, answers or comments, as they would be lost during the migration otherwise.
0 votes

Problem

I am trying to instantiate a scene which i give to my scriped via an export variable of type PackedScene. I set up the variable and loaded the scene in the inspector as explained in the "first 3D game" tutorial.

But it is null and i can not find out why it wont load the scene through the Inspector.
If i try loading it directly through ia resource path it works. I printed the var in the _ready() and the _process() functions.

Any idea what causes this and how it can be fixed or avoided?

The code can be seen in the image as well as the loaded scene.
image Link-to-img.

Here the same part as code cell:

@export var weapon0 = PackedScene.new()
@onready var weapon1: PackedScene = preload("res://scenes/Weapons/base_weapon.tscn")

## initialize stast for player ship
func _init():
    var ship = ShipVariables.new(8, 5, 100, 0, 50, 0.2)
    super._init(ship)

func _ready():
    #ingametarget = get_tree().get_root().get_node("main/test")
    # Create Weapon
    print("ready:") 
    print(weapon0)
    print(weapon1)
    print("----------------------------")

Console Output:

ready:
<null>
<PackedScene#-9223372009608575851>
----------------------------
process:
<null>
-------

Both weapon0 and weapon1load the same scene as resource. (I also tested using load instead of preload and without using @onready annotation both works as well only the @export var won't work.)


Additonal Code in case it is relevant:

Full PlayerShip class:

extends "res://scenes/BaseShip.gd"

var ingametarget

#@export var weapon: PackedScene
@export var ammo = PackedScene.new()
@export var weapon0 = PackedScene.new()
@onready var weapon1: PackedScene = preload("res://scenes/Weapons/base_weapon.tscn")

## initialize stast for player ship
func _init():
    var ship = ShipVariables.new(8, 5, 100, 0, 50, 0.2)
    super._init(ship)

func _ready():
    ingametarget = get_tree().get_root().get_node("main/test")
    # Create Weapon
    print("ready:") 
    print(weapon0)
    print(weapon1)
    var testnode = get_node("weaponslot1")
    print(testnode)
    print("----------------------------")
    #instantiate_weapon(weapon0)
    #set_projectile_for_weapon("weapon0", ammo)

# Get the gravity from project settings 
var gravity = ProjectSettings.get_setting("physics/3d/default_gravity")

## Get the input from the player based on the four axis (left right top bottom)
## returns it as 2D Vector (note y component is the z component if used in 3D)
## y itself (up) is empty in this vector
func get_player_input() -> Vector2:
    var input = Input.get_vector("move_left", "move_right", "move_top", "move_bottom")
    return input

func _physics_process(delta):
    velocity = calc_velocity(get_player_input())
    move_and_slide()

func _process(delta):
    ## debugging stuff with "b"
    if Input.is_action_just_pressed("debug_btn"):
        print("process:") 
        print(weapon0)
        print("-------")
        #print("shooting")
        #shoot_weapon(ingametarget.position)
        #print(ingametarget.position)
        #transform = transform.looking_at(ingametarget.position)

The BaseShip class:

extends CharacterBody3D

#Stats
var ship_vars : ShipVariables

## init function that can be called by inheriting scenes to set vars
func _init(ship_vars_class):
    self.ship_vars = ship_vars_class

## Load the weapon scene
## instanciate the weapon in a weapon slot
func instantiate_weapon(w0: PackedScene) -> void:
    # set in variable class
    self.ship_vars.load_weapon0_scenes(w0)
    # instantiate
    var weapon0 = self.ship_vars.weapon_0.instantiate()
    weapon0.name = "weapon0"
    ###instance.transform.looking_at(target)  # or look_at ?
    $weaponslot1.add_child(weapon0)
    ## Assign projectile/ammo to weapon

## Load the weapon scene
## instanciate the weapon in a weapon slot
func set_projectile_for_weapon(weaponname: String, projectile: PackedScene) -> void:
    get_node(weaponname).set_projectile(projectile)


## Caluclate vector to be applied with move_and_slide
## Using normalized target_position or inputs and speed
func calc_velocity(input_dir) -> Vector3:
    # transform.basis: local coordinate system traveling with the object
    # Multiplying with transform.basis, means that the local system is used
    # therefor pressing forward moves in the look direction instead of global z
    var direction = (self.transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
    # apply velocity based in input dir
    # for other direction apply slow
    if direction.x:
        velocity.x += direction.x * ship_vars.move_speed * ship_vars.acceleration
        velocity.x = clamp(velocity.x, -ship_vars.move_speed, ship_vars.move_speed)
    else:
        velocity.x = move_toward(velocity.x, 0, ship_vars.break_speed)
    if direction.z:
        velocity.z += direction.z * ship_vars.move_speed * ship_vars.acceleration
        velocity.z = clamp(velocity.z, -ship_vars.move_speed, ship_vars.move_speed)
    else:
        velocity.z = move_toward(velocity.z, 0, ship_vars.break_speed)
    return velocity

## rotate ship to face the target
func rotate_to(target: Vector3) -> void:
    return

## shoot the loaded weapon with given projectile
func shoot_weapon(target: Vector3) -> void:
    # shoot without target (add dummy target)
    self.ship_vars.weapon_0.shoot(target)

## apply taken damage
func take_damage(damage: float) -> void:
    ship_vars.health -= damage

## delete character upon death and do effects
func die() -> void:
    return

## handle things like collision with other ships 
## calculate dmg based on armor and mass from both ships
## emitted from this ships CollisionShape if its a ship
func on_collision_enter():
    return

## handle hits from projectiles 
## signal emitted from this ships CollisonShape if its a projectile
func on_projectile_recieved():
    return

The BaseWeapon class:

extends StaticBody3D

#Stats
var weapon_vars : WeaponVariables

## init function that can be called by inheriting scenes to set vars
func _init():  #weapon_vars_class
    self.weapon_vars = WeaponVariables.new(1, 50, 1, 0, 0, false)

func set_projectile(_projectile):
    self.weapon_vars.load_projectile_scenes(_projectile)

## shoot the assigned projectile with the set variables
func shoot(target: Vector3):
    if not self.weapon_vars.auto_aim:
        # forward is (0,0,-1)
        target = ($BulletSpawn.transform.basis * Vector3(0,0,-1)).normalized()

    var bullet = self.weapon_vars.projectile  # preload("res://MyScene.tscn")
    var instance = bullet.instantiate()
    instance.transform.looking_at(target)  # or look_at ?
    get_tree().get_root().add_child(instance)

    """
    self.atk_speed = _atk_speed
    self.range = _range
    self.projectile_count = _projectile_count
    self.projectile_spread = _projectile_spread
    self.projectile_delay = _projectile_delay
    self.auto_aim = _auto_aim
    """

The ShipVariables class:

class_name ShipVariables #, "res://assets/image.png"

#class ShipVariables:
var move_speed : float
var turn_speed : float
var health : float
var armor : float
var mass : float
var break_speed : float
## in percentage of speed
var acceleration : float

# scenes to load #@export 
var weapon_0 : PackedScene

## Init a new ship variabel class containing:
## move_speed
## turn_speed
## health
## armor
## mass
## acceleration
func _init(_move_speed: float, _turn_speed: float,_health: float, _armor: float, _mass: float,
 _acceleration: float):
    self.move_speed = _move_speed
    self.turn_speed = _turn_speed
    self.health = _health
    self.armor = _armor
    self.mass = _mass
    self.break_speed = _move_speed * 0.05
    self.acceleration = _acceleration

func load_weapon0_scenes(weapon: PackedScene):
    self.weapon_0 = weapon

The WeaponVariables class:

class_name WeaponVariables #, "res://assets/image.png"

#class ShipVariables:
var atk_speed : float
var range : float # m
var projectile_count: int
var projectile_spread : float # random spray in percentage 0% = laser
var projectile_delay : float # How fast consecutive projectiles shoot  in s
var auto_aim : bool # aim the weapon
#var homing_projectile  # doesnt make sense here add to projectile

# scenes to load #@export 
var projectile : PackedScene

## Init a new weapon variabel class containing:
func _init(_atk_speed: float, _range: float, _projectile_count: int, _projectile_spread: float, 
_projectile_delay: float, _auto_aim: bool):
    self.atk_speed = _atk_speed
    self.range = _range
    self.projectile_count = _projectile_count
    self.projectile_spread = _projectile_spread
    self.projectile_delay = _projectile_delay
    self.auto_aim = _auto_aim

func load_projectile_scenes(_projectile: PackedScene):
    self.projectile = _projectile

(there are similar questions:
here and here here
but none gives a solution)

Godot version 4.0.1
in Engine by (18 points)

and how your ship look in inspector in main scene?

I suspect the interplay between inherited scenes/scripts here as at play, simply because a simple scene with just the weapon0 assignment works fine. There's also a weapon0 local in the BaseShip.gd script that, while local, I don't know the flow of how your game executes here so wonder if something is overwriting something else.

There are errors in the debugger, worth checking those too.

Oh wow you are right it was not loaded in the instance in the main scene. Somehow i though it would update automatically.

So i guess i should just always create the player by code to avoid such careless mistakes.

Is there a special interplay between inheriting scenes and scripts? I always assume they are the same and inherete the scene and then the script from that scene as a new script.

And the flow should simply be creating a player_ship then saving the scene for the weapon in an intern variable (in the base class) to later instance the weapon.

It really depends on the project; a simple scene tree is usually quite predictable but anything complex may have timing or ordering issues, perhaps complicated by a class hierarchy in scripts. It’s not unique to godot either, sometimes it’s just the way it is!

Glad you found the issue!

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read Frequently asked questions and How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to [email protected] with your username.