I’m trying to change an object’s collision shape and sprite size via script and the object is an instantiated object. The object’s collision shape and sprite size is supposed to change when the player hits a power up but godot isn’t able to find the node that I’m referencing to. It hits me with this error: Invalid assignment of property or key ‘scale’ with value of type ‘Vector2’ on a base object of type ‘null instance’. This is the script for both power up and player:
player.gd:
extends CharacterBody2D
class_name Player
signal player_destroyed
@onready var ray_cast_2d = $RayCast2D
@onready var ray_pos = ray_cast_2d.position
@export var move_speed = 200
@onready var ShootingPoint = $PlayerSprite/ShootingPoint
@onready var Power = preload("res://power_up.tscn")
var bullet_speed = 2000
var bullet_scene = preload("res://bullet.tscn")
var coin = preload("res://coin.tscn")
var bullet_amount = 1
var dead = false
var isShooting = false
var targetPosition: Vector2
var shootDirection: Vector2
var BulletPosition: Vector2
var BULLET = bullet_scene.instantiate()
func get_input():
look_at(get_global_mouse_position())
var dir = Vector2.ZERO
velocity = transform.x * dir * bullet_speed
if Input.is_action_just_pressed("shoot"):
shoot()
func _process(delta):
global_rotation = global_position.direction_to(get_global_mouse_position()).angle() + PI/2.0
setBulletPosition(BULLET.global_position)
func _physics_process(delta):
get_input()
var direction = Vector2.ZERO
var input = Input.get_axis("left", "right")
if dead:
return
if Input.is_action_just_pressed("shoot"):
if bullet_amount == 1:
shoot()
if Input.is_action_just_pressed("throw"):
throw_coin()
if input > 0:
direction = Vector2.RIGHT
elif input < 0:
direction = Vector2.LEFT
else:
direction = Vector2.ZERO
var delta_movement = move_speed * delta * direction.x
position.x += delta_movement
move_and_slide()
func kill():
move_speed = 0
if dead:
return
dead = true
$PlayerSprite.hide()
#$GameOverScreen/GameOver.show()
z_index = -1
queue_free()
func restart():
get_tree().reload_current_scene()
func setBulletPosition(pos: Vector2):
BulletPosition = pos
func BulletSpread():
var spawnPosition = BulletPosition
for n in 4:
var BULLET2 = bullet_scene.instantiate()
match n:
1:
targetPosition = Vector2.UP
2:
targetPosition = Vector2.DOWN
3:
targetPosition = Vector2.LEFT
4:
targetPosition = Vector2.RIGHT
shootDirection = (targetPosition - global_position).normalized()
BULLET.start(spawnPosition.global_position, rotation)
get_tree().root.add_child(BULLET)
BULLET.transform = spawnPosition.global_transform
func shoot():
$Muzzle.show()
$Muzzle/Timer.start()
$ShootSound.play()
if isShooting:
return
isShooting = true
targetPosition = get_global_mouse_position()
shootDirection = (targetPosition - global_position).normalized()
BULLET.start(ShootingPoint.global_position, rotation)
get_tree().root.add_child(BULLET)
BULLET.transform = ShootingPoint.global_transform
await get_tree().create_timer(0.2).timeout
isShooting = false
bullet_amount -= 1
func SetType(num):
match num:
1:
BULLET.get_node("../BulletArea/CollisionShape2D/").scale = Vector2(100, 100)
BULLET.get_node("Sprite2D").scale = Vector2(100, 100)
2:
BULLET.MOVE_SPEED += 5000
func throw_coin():
targetPosition = get_global_mouse_position()
shootDirection = (targetPosition - global_position).normalized()
var COIN = coin.instantiate()
COIN.set_coin(global_position, targetPosition)
get_tree().get_root().call_deferred("add_child", COIN)
await get_tree().create_timer(0.2).timeout
func _on_player_area_area_entered(area: Area2D) -> void:
kill()
player_destroyed.emit()
power_up.gd:
extends Area2D
class_name PowerUp
@onready var players = Player.new()
func _on_body_entered(body: Node2D) -> void:
print("body entered")
func _on_area_entered(area: Area2D) -> void:
if "PlayerArea" in area.name:
players.SetType(1)
print("got powerup")
The “…” gets you to /root. This is above /Bullet.
Try BULLET.get_node("BulletArea/CollisionShape2D/").scale = Vector2(100, 100)
(Or some version thereof. You can experiment string paths using print statements)
Personally, I can’t stand using string paths and would much rather code named nodes.
Give Bullet a script with an entry like: @onready var bullet_area_cshape:CollisionShape2D = $BulletArea/CollisionShape2D
And then you can simply say: BULLET.bullet_area_cshape.scale = ...
PS: You are going against convention to uppercase a variable. Usually people uppercase constants. I am not saying its wrong, just unconventional.
Here is the GDScript style guide.
PS I suppose scripts are classes. And classes can run code outside of functions, so I suppose this makes sense. It must surely be bad practice to do this though and to me at least seems to serve virtually no purpose. I am honestly still amazed at this though and thank you again @sancho2 for the correction. Much appreciated.
# In your bullet script:
func _ready() -> void:
add_to_group("bullet")
# In your player script:
func function_you_need_to_get_the_bullet -> void:
var bullets_array = get_tree().get_nodes_in_group("bullet")
for bullet in bullets_array:
# Do your stuff with bullet
Btw some things i saw in your code:
In game you can only have a unique bullet at time inside screen? Otherwise your approarch to manage the bullets storing their referencies in a unique variable will fail.
In your BulletSpread function you instantiate new bullets but never do nothing with them.
NEVER scale objects that rely on physics, that can break their physics detection and lead to undefinied behavior, always change their size/extents.
to address to child node properties regardless of what an when initialized, you can use parent’s node signal callback self.child_entered_tree.connect(func(child): if &'scale' in child: child.scale=Vector(2, 2)
From the error you put when you wrote this (the “Invalid assignment of property or key ‘scale’ with value of type ‘Vector2’ on a base object of type ‘null instance”) that means you’re getting a null value when you try to access the nodes, using groups would make sure u’ll get the node or do nothing if no valid node is there, also, you can put a script in the bullet to make the changes and use groups to call the function needed.
I didn’t said that, you only store one bullet reference, if you have two bullet at the same time, you’ll be able to manipulate only the last bullet instantiated, the other one you’ll lost the reference for her when instantiate the new bullet because you’ll replace the reference for the first bullet for the second one, as i said, if you only have one bullet in the scene that will work, but if you can put more than that at the same time u’ll be unable to manipate the other bullets (unless you store them on an array or use groups).