0 votes

Thank you in advance.
I am trying to implement code into my game for a grappling hook. The code works on its own in the test game provided but I cant seem to get it to work in mine.

The problem is that I reference a variable of a child node in the parent's script but it is null.
Full error is: "Invalid set index 'global_position' (on base: 'null instance') with value of type 'Vector2'.

When it throws the error I notice that the game has NOT loaded the Player, GrappleHook, nor Tip assets into the game

I printed the child node ($Tip) and its null in mine but not in the test code.

I reference the child node like so onready var hookTip = $Tip
The code that throws the error is this func _physics_process(_delta: float) -> void: hookTip.global_position = tip
In case it matters, heres the node structure of the scene

  • Node2d: World
  • ...KinematicBody2d: Player
  • ......Node2d: GrappleHook
  • .........KinematicBody2d: Tip

I have tried looking it up and finding why its happening, but no dice. Im already referencing it with onready so shouldnt the child nodes be loading before it tries to call it in the physics_process?

Godot version 3.4.2
in Engine by (17 points)

Another clue I found is the error in the command window that launches with Godot:
'Error: (Node not found: "Tip" (relative to "/root/World/Player").)'

I believe that the path should go down one further into "/root/World/Player/GrappleHook". I thought that I can put scenes into scenes without pathing problems, that relative pathing would sort it out but did I do something wrong in this?

Could you show us the original code?

This is the original code from the tutorial that works, the only changes that I made to it when I put it into my code is an onready reference to Tip

extends Node2D

onready var links = $Links      # A slightly easier reference to the links
var direction := Vector2(0,0)   # The direction in which the chain was shot
var tip := Vector2(0,0)         # The global position the tip should be in
                                # We use an extra var for this, because the chain is 
                                # connected to the player and thus all .position
                                # properties would get messed with when the player
                                # moves.

const SPEED = 50    # The speed with which the chain moves

var flying = false  # Whether the chain is moving through the air
var hooked = false  # Whether the chain has connected to a wall

# shoot() shoots the chain in a given direction
func shoot(dir: Vector2) -> void:
    direction = dir.normalized()    # Normalize the direction and save it
    flying = true                   # Keep track of our current scan
    tip = self.global_position      # reset the tip position to the player's position

# release() the chain
func release() -> void:
    flying = false  # Not flying anymore    
    hooked = false  # Not attached anymore

# Every graphics frame we update the visuals
func _process(_delta: float) -> void:
    self.visible = flying or hooked # Only visible if flying or attached to something
    if not self.visible:
        return  # Not visible -> nothing to draw
    var tip_loc = to_local(tip) # Easier to work in local coordinates
    # We rotate the links (= chain) and the tip to fit on the line between self.position (= origin = player.position) and the tip
    links.rotation = self.position.angle_to_point(tip_loc) - deg2rad(90)
    $Tip.rotation = self.position.angle_to_point(tip_loc) - deg2rad(90)
    links.position = tip_loc                        # The links are moved to start at the tip
    links.region_rect.size.y = tip_loc.length()     # and get extended for the distance between (0,0) and the tip

# Every physics frame we update the tip position
func _physics_process(_delta: float) -> void:
    $Tip.global_position = tip  # The player might have moved and thus updated the position of the tip -> reset it
    if flying:
        # `if move_and_collide()` always moves, but returns true if we did collide
        if $Tip.move_and_collide(direction * SPEED):
            hooked = true   # Got something!
            flying = false  # Not flying anymore
    tip = $Tip.global_position  # set `tip` as starting position for next frame

If I undestand your diagrams and text correctly, Tip is a grand grand grand child of the World2D.
You never specified which Node uses the code above ? Is it the World or is it GrappleHook?

Sorry, GrappleHook is where this script is located and where the error is occurring. I made the grapplehook scene and everything and put it into my Player which is in turn inside the World node. So World > Player > GrappleHook (where the error happens) > Tip (which is null).

1 Answer

+1 vote
Best answer

It looks fine.
The problem must lie somewhere else. Are You sure You don't have another grappling hook somewhere in the scene ? Like from old test purposes ? And it is orphaned or somewhere else in scene hierarchy ? You can print self if unsure

Is there any condition queuing free your grappling hook or Tip ?

by (7,445 points)
selected by

Unfortunately no, theres no queue_free for grappling hook and its the only grappling hook in the scene. It just seems to be trying to get the GrappleHook before its made, since when I run the game I get the error before my player or grapplehook show; they're null.
Its got me puzzled so much because its such simple code but I cant find the error.

The player script for some reason was swapped over to the GrappleHook script.
I must have accidentally dragged the GrappleHook.gd over first instead of GrappleHook.tscn and it overwrote the Player script without realizing it. I wasted so much time over something so stupid.
Thank you for your help, I was losing my mind.

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.