Exported variables get set to null, game won't launch, Your first 3D game tutorial

Godot Version

4.2

Question

Hello! I’m trying to do the 3d tutorial (have already done the 2d one) and got to the point that we spawn mobs. But since when they run into each other they just stop, and never try to start going again, I thought I’d try to add that functionality myself.

But after trying that, for some reason, the min_speed and max_speed exported attributes get set to null on launch. the ui shows them as zero but I get “invalid type in utility function “randi_range()”, cannot convert argument 2 from nil to int”

Mob.gd script

extends CharacterBody3D

@export var min_speed = 10
@export var max_speed = 18


func initialize(start_position, player_position):
    look_at_from_position(start_position, player_position, Vector3.UP)
    rotate_y(randf_range(-PI / 4, PI / 4))

    var random_speed = randi_range(min_speed, max_speed)
    velocity = Vector3.FORWARD * random_speed

    velocity = velocity.rotated(Vector3.UP, rotation.y)


func _physics_process(_delta):
    # I can confirm it's this conditional branch that causes problems
    if velocity.length() == 0:
        velocity = Vector3.FORWARD
        velocity = velocity.rotated(Vector3.UP, rotation.y)
        
    # This branch also has some weirdness but becomes stable. Described below
    if velocity.length() < min_speed:
        velocity = velocity.normalized() * (velocity.length() + 1)
    move_and_slide()


func _on_visibility_notifier_screen_exited():
    queue_free()
    pass # Replace with function body.

By commenting out the different conditionals I’ve isolated the fully broken version to being the conditional for if the mob.velocity is 0. However adding the second conditional makes it break once, but then run reliably after resetting the variables through the ui after the initial run. (The difference being the first branch always sets them to nil).

I don’t see how min_speed or max_speed ever get set to null here, especially through checking if velocity is 0.

Happy to provide any additional information. Thank you!

i see what’s wrong here

how do you spawn this mob? show the code?

1 Like

Try setting the min/max speed variables in the inspector, sometimes the data gets nullified; not sure exactly why but it is rare, only seems to be when the scene is new or moved.

1 Like

I don’t have access to the code right now, but it’s unchanged from the tutorial. There’s a timer, and main.gd listens to the timer timing out. it has the mob.tscn as a packed scene.

It instantiates a mob, and then calls initialize on it immediately. there’s no intervening lines of code (I’ll respond with the code when I can).

Yeah, I have to reset them through the editor. With the second conditional block it only needs to be done once, and then it starts running ok. But if I have the first conditional that checks for if velocity is 0, it does it on every game launch so it never launches. Even if i explicitly set the variables in the editor immediately prior to launch .

you need to add_child(new_mob) after .instantiate()
then call the initialize()

1 Like

Oh cool!

The docs do not have that order properly then. Once I’m back on my computer I’ll test that out, and then submit a PR to change the order in the example/instructions.

Thank you so much!

Edit to add: I’ll mark your answer as the solution too as soon as I verify there’s nothing weird going on with my system.

1 Like

That worked to let my game run, thank you! It does now make every mob except for the first walk on it’s head (rotated around the x axis so its legs/tentacles are in the air) and also deletes my player almost immediately.
player.gd

extends CharacterBody3D

@export var speed = 14
@export var fall_acceleration = 75

var target_velocity = Vector3.ZERO


func _physics_process(delta):
	var direction = Vector3.ZERO

	if Input.is_action_pressed("move_right"):
		direction.x += 1
	if Input.is_action_pressed("move_left"):
		direction.x -= 1
	if Input.is_action_pressed("move_back"):
		direction.z += 1
	if Input.is_action_pressed("move_forward"):
		direction.z -= 1


	if direction != Vector3.ZERO:
		direction = direction.normalized()

	$Pivot.basis = Basis.looking_at(direction)

	target_velocity.x = direction.x * speed
	target_velocity.z = direction.z * speed

	if not is_on_floor(): # If in the air, fall towards the floor. Literally gravity
		target_velocity.y = target_velocity.y - (fall_acceleration * delta)
	velocity = target_velocity

	move_and_slide()

and main.gd

extends Node

@export var mob_scene: PackedScene



# Called when the node enters the scene tree for the first time.
func _ready():
	pass # Replace with function body.


# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
	pass


func _on_mob_timer_timeout():
	var mob = mob_scene.instantiate()
	add_child(mob)

	var mob_spawn_location = get_node("SpawnPath/SpawnLocation")
	mob_spawn_location.progress_ratio = randf()

	var player_position = $Player.position
	mob.initialize(mob_spawn_location.position, player_position)

I double checked that the player wasn’t falling through the floor by raising htem above the floor to see what happened, and even after putting them back the mobs are no longer walking on their head, but the player still disappears. in case that helps indicate what’s wrong.

from this file:


it said add_child(mob) after you initialize it.

but you shouldnt copy and paste this, because it looks like a Godot 3 project, with that .instance() syntax, which is typical old syntax for Godot 3. now it’s instantiate

and weird thing is that, if you changed to version 1.1.0:

it said you should add_child(mob) before the initialize. idk what’s wrong with this tutorial

Hmm the in progress file in the tutorial, rather than the example, has

var mob = mob_scene.instantiate()

instead of .instance(). so I did have that call correctly.

But yeah… seems like it might just have a lot of weirdness from not being fully updated? Which there is a flag about on the website, but I thought it’d still be functional lol. Maybe I’ll go look for a different tutorial to follow. Thank you for looking at this and pointing that out! If you have any suggestions for where to go instead, I’d love to hear them! No pressure though

nice to hear you are still enthusiastic of learning the godot 3d, unfortunately i’m not into godot 3d learning yet. my main is making 2d game. so you will probably wait for Brackeys do 3d tutorial for Godot, if you really want to learn “better” 3d tutorial
tho like i ever heard kidscancode also done many 3d projects tutorials, but iirc they were all in godot 3, might there be an update there.

1 Like