Godot Version
4.2.1
Question
Hey everyone! This is my first post because I have an issue that’s driving me crazy. I spent hours looking into this, tried a lot of stuff but didn’t work.
I want to create a grappling hook for my player, that gets launch when the rmb is pressed and then retracted when it’s released.
However, in the code it’s not working at all, even though Node A and B is properly assigned (by print debugging), and the position it also correct (in debug mode the position of the pinjoint is where it should be, it is raycasted). The player’s root node is a CharacterBody2D, which is a PhysicsBody2D, and a StaticBody2D gets created in the code where the raycast hits, which is also a PhysicsBody2D.
So I tried it in a scene by adding it manually:
With this I can move freely, as if the PinJoint2D does not exist at all. The PinJoint2D’s position can be seen as a brown cross:
I don’t understand why it isn’t even working when I do it manually in the scene, but here’s the code for the player movement and grappling:
extends CharacterBody2D
# Variables for movement
@export var speed: float = 200.0
@export var jump_force: float = 250.0
@export var gravity: float = 800.0
@export var grapple_speed: float = 500.0 # Speed of the grappling hook
@export var rope_length: float = 100.0 # Max length of the rope
@export var hook_range: float = 300.0 # Max range of the grapple
@onready var line: Line2D = $DirectionVector
@onready var rope: Line2D = $Rope
var direction
var grapple_position: Vector2 = Vector2.ZERO
var is_grappling: bool = false
var grapple_joint: PinJoint2D = null
var rope_attachment: StaticBody2D = null
func _ready():
rope.visible = false
rope_attachment = StaticBody2D.new()
func _physics_process(delta: float) -> void:
# Apply gravity
if not is_on_floor():
velocity.y += gravity * delta
else:
# Stop vertical movement when on the floor
velocity.y = 0
# Handle horizontal movement
var input_direction = Input.get_axis("Left", "Right")
velocity.x = input_direction * speed
# Handle jumping
if is_on_floor() and Input.is_action_just_pressed("Up"):
velocity.y = -jump_force
# Handle dash
if Input.is_action_just_pressed("LeftClick"):
dash(direction, 200)
if Input.is_action_just_pressed("RightClick"):
if !is_grappling:
shoot_grapple()
if Input.is_action_just_released("RightClick"):
if is_grappling:
release_grapple()
# Move the character
move_and_slide()
func _process(delta):
var mouse_position = get_global_mouse_position()
var player_position = global_position
direction = (mouse_position - player_position).normalized()
var direction_vector = mouse_position - player_position
line.global_position = player_position
line.points = [Vector2.ZERO, direction_vector]
if is_grappling:
rope.set_point_position(1, to_local(grapple_position))
#print("Rope visible: ", rope.visible)
#print("Rope points: ", rope.points)
#func _input(event: InputEvent) -> void:
#if event.is_action_pressed("RightClick"):
#if !is_grappling:
#shoot_grapple()
#
#if event.is_action_released("RightClick"):
#if is_grappling:
#release_grapple()
func dash(direction: Vector2, distance: float) -> void:
var target_position = global_position + direction * distance
# Check if landing zone is clear
var space_state = get_world_2d().direct_space_state
var params = PhysicsPointQueryParameters2D.new()
params.set_position(target_position)
var collision = space_state.intersect_point(params, 1)
print(collision)
if !collision:
global_position = target_position
print("Dashed to: ", target_position)
else:
print("Collision detected! Cannot dash")
# grapple_position: Position in the world where the hook attaches after a successful hit.
# is_grappling: Boolean to track whether the player is currently grappling.
# grapple_joint: A PinJoint2D used to connect the player to the grapple point physically.
# rope_attachment: A Node2D used as the fixed point for the rope.
func shoot_grapple():
if is_grappling:
return
var target_pos = global_position + direction * hook_range
var space_state = get_world_2d().direct_space_state
var params = PhysicsRayQueryParameters2D.create(global_position, target_pos)
var hit = space_state.intersect_ray(params)
print("Hit position: ", hit)
if hit:
grapple_position = hit.position
is_grappling = true
rope.visible = true
#if not rope_attachment.is_inside_tree():
#add_child(rope_attachment)
#print("Added rope_attachment to the tree")
rope_attachment.position = grapple_position
print("Rope_attachment position: ", rope_attachment.position)
if not rope_attachment.is_inside_tree():
get_tree().current_scene.add_child(rope_attachment)
grapple_joint = PinJoint2D.new()
grapple_joint.node_a = get_path()
grapple_joint.node_b = rope_attachment.get_path()
grapple_joint.position = grapple_position
print("Node A: ", grapple_joint.node_a)
print("Node B: ", grapple_joint.node_b)
print("Grapple joint position: ", grapple_joint.position)
get_tree().current_scene.add_child(grapple_joint)
else:
print("Grapple missed")
func release_grapple():
if is_grappling:
is_grappling = false
rope.visible = false
if grapple_joint != null:
grapple_joint.queue_free()
grapple_joint = null
Thanks for the help in advance!