Reputation: 11
I have been trying to create an asteroid spawner for my game in Godot 4.2.
I've already managed to make my code work on the main scene when running the game, however I'd like to have the same script be able to run as a @tool
on my spawner scene, so I can test everything without having to run the game.
In my asteroid scene, the root node contains a script where asteroids shapes are defined inside _ready()
and require the parameters size
, noise_scale
and noise_adjustment
. Here is the relevant piece code:
extends RigidBody3D
var fnl = FastNoiseLite.new()
var mdt = MeshDataTool.new()
var surface_tool := SurfaceTool.new()
@onready var collision_shape_3d = $CollisionShape3D
@onready var mesh_instance_3d = $CollisionShape3D/MeshInstance3D
var size: float = 5
var noise_scale: float = 1
var noise_adjustment: float = 0
func _ready():
surface_tool.create_from(mesh_instance_3d.mesh,0)
mesh_instance_3d.mesh = surface_tool.commit()
...
...
I created a spawner scene, with a single node containing a script where my asteroid scene is instantiated multiple times, and size
, noise_scale
and noise_adjustment
are all set based on parameters given to the spawner.
@tool
extends Node3D
@export var asteroid_scene: PackedScene
@export var num_asteroids: int = 10
@export_range(0, 1500) var spawn_range: float = 500
@export_range(0, 500) var velocity_range: float = 200
@export_range(1, 500) var min_size: float = 5
@export_range(1, 500) var max_size: float = 200
@export_range(0, 10) var noise_scale: float = 1
@export_range(0, 10) var noise_adjustment: float = 0
# Called when the node enters the scene tree for the first time.
func _ready():
if max_size < min_size:
max_size = min_size
if min_size > max_size:
min_size = max_size
for i in range(num_asteroids):
var asteroid = asteroid_scene.instantiate()
var size = randf_range(min_size, max_size)
asteroid.size = size
asteroid.noise_scale = noise_scale
asteroid.noise_adjustment = noise_adjustment
...
...
add_child(asteroid)
When reloading the spawner scene I get the error message res://Scripts/asteroid_spawner.gd:27 - Invalid set index 'size' (on base: 'RigidBody3D (asteroid_shape_generator.gd)') with value of type 'float'.
I've already tested simply running asteroid.size
without assigning a value, but then I get another error: Invalid get index 'size' (on base: 'RigidBody3D (asteroid_shape_generator.gd)')
I've also tried moving the contents of the _ready()
function into another function and then calling this function inside _process
, which also didn't solve my issue.
I expected to simply have the asteroids spawn inside my spawner scene, just like they do in game (DEBUG)
Upvotes: 1
Views: 46
Reputation: 11
I've found out that since my asteroid spawner script is a @tool
, then my instantiated asteroids script must also be a @tool
. Simply adding this annotation to the header of my script solved the problem
Upvotes: 0
Reputation: 945
This is happening because it can not find a property called size
that belongs to the asteroid
object you have instantiated.
When you instantiate your asteroid_scene with var asteroid = asteroid_scene.instantiate()
, it is being instantiated as a Rigidbody3D
. If you check the documentation, you will see that a Rigidbody3D does not have a size
property:[https://docs.godotengine.org/en/stable/classes/class_rigidbody3d.html] (https://docs.godotengine.org/en/stable/classes/class_rigidbody3d.html)
If you want to assign to the size property that you declared in the script attached to your asteroid scene you need to let the spawner script know that you are instantiating an asteroid specifically. You can do this by giving your asteroid script a class_name
and then spawning the asteroid as an instance of that class.
E.g.
In the asteroid script:
class_name Asteroid
extends RigidBody3D
...
var size: float = 5
...
In the spawner script:
func _ready():
...
for i in range(num_asteroids):
var asteroid = asteroid_scene.instantiate() as Asteroid
asteroid.size = size
Upvotes: 1