wd asd
wd asd

Reputation: 1

Error in spawning explosion after a projectile collides with a body in Godot 4. Intent is to create a rocket jump feature for retro FPS

This is My Character controller

extends CharacterBody3D

#crosshair
@onready var crosshair = $Head/Camera3D/Crosshair

#rocketman

#gun
var damage = 10
var current_weapon = 1

#gun
@onready var gun_anim = $Head/Camera3D/Hand/Gun1/AnimationPlayer
@onready var gun_anim2 = $Head/Camera3D/Hand/Gun2/AnimationPlayer
@onready var gun_anim3 = $Head/Camera3D/Hand/Gun3/AnimationPlayer

@onready var camera = $Head/Camera3D
@onready var raycast = $Head/Camera3D/RayCast3D

@onready var muzzle = $Head/Camera3D/Hand/lefthand/Hand_Cartoon_Hand/muzzle
@onready var head = $Head
@onready var gun1 = $Head/Camera3D/Hand/Gun1
@onready var gun2 = $Head/Camera3D/Hand/Gun2
@onready var gun3 = $Head/Camera3D/Hand/Gun3
@onready var bullet = preload("res://bullet.tscn")


#jumping
var jump_num = 0
var max_jump_num = 2

# Movement
const MAX_VELOCITY_AIR = 1.0
const MAX_VELOCITY_GROUND = 10.0
const MAX_ACCELERATION = 10 * MAX_VELOCITY_GROUND
const GRAVITY = 15.34
const STOP_SPEED = 1.5
const JUMP_IMPULSE = sqrt(2 * GRAVITY * 0.85)
const PLAYER_WALKING_MULTIPLIER = 0.666

var direction = Vector3()
var friction = 4
var wish_jump
var walking = false

# Camera
var sensitivity = 0.05

func _ready():
    Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)

    $bullet.connect("bullet_collision",self,"_on_bullet_collision")

func _on_bullet_collision(body):
        var explosion_instance = load("res://explosion.tscn").instantiate()
        explosion_instance.global_transform.origin = body.global_transform.origin
        get_tree().current_scene.add_child(explosion_instance)


func _input(event):
    # Camera rotation
    if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
        _handle_camera_rotation(event)
        
    if event is InputEventMouseButton:
        if event.is_pressed():
            if event.button_index == MOUSE_BUTTON_WHEEL_UP:
                if current_weapon < 3:
                    current_weapon += 1
                else:
                    current_weapon = 1
            if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
                if current_weapon > 1:
                    current_weapon -= 1
                else:
                    current_weapon = 3


func _handle_camera_rotation(event: InputEvent):
    # Rotate the camera based on the mouse movement
    rotate_y(deg_to_rad(-event.relative.x * sensitivity))
    $Head.rotate_x(deg_to_rad(-event.relative.y * sensitivity))
    
    # Stop the head from rotating to far up or down
    $Head.rotation.x = clamp($Head.rotation.x, deg_to_rad(-60), deg_to_rad(90))


func weapon_select():
    
    if Input.is_action_just_pressed("weapon1"):
        current_weapon = 1
    elif Input.is_action_just_pressed("weapon2"):
        current_weapon = 2
    elif Input.is_action_just_pressed("weapon3"):
        current_weapon = 3
        
    if current_weapon == 1:
        gun1.visible = true
    else:
        gun1.visible = false

    if current_weapon == 2:
        gun2.visible = true
        damage = 100
    else:
        gun2.visible = false

    if current_weapon == 3:
        gun3.visible = true
        damage = 200
    else:
        gun3.visible = false

#crosshair
    randomize()
    crosshair.position.x = get_viewport().size.x / 2 - 32
    crosshair.position.y = get_viewport().size.y / 2 - 32

#shootin time
func fire():
    if Input.is_action_just_pressed("fire"):
            if current_weapon == 1 and !gun_anim.is_playing():
                gun_anim.play("PistolFire")
            elif current_weapon == 2 and !gun_anim2.is_playing():
                gun_anim2.play("PistolFire_2")
            elif current_weapon == 3 and !gun_anim3.is_playing():
                gun_anim3.play("PistolFire_3")

            if raycast.is_colliding():
                var target = raycast.get_collider()
                if target.is_in_group("Enemy"):
                    target.health -= damage


func _physics_process(delta):
    process_input()
    process_movement(delta)
    weapon_select()
    fire()
    


func process_input():
    direction = Vector3()
    
    # Movement directions
    if Input.is_action_pressed("move_forward"):
        direction -= transform.basis.z
    elif Input.is_action_pressed("move_backward"):
        direction += transform.basis.z
    if Input.is_action_pressed("move_left"):
        direction -= transform.basis.x
    elif Input.is_action_pressed("move_right"):
        direction += transform.basis.x
        
    # Jumping
    wish_jump = Input.is_action_just_pressed("jump")
    
    # Walking
    #AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    if Input.is_action_just_pressed("sec_fire"):
        if raycast.is_colliding():
            var b = bullet.instantiate()
            muzzle.add_child(b)
            b.look_at(raycast.get_collision_point(), Vector3.UP)
            b.shoot = true

    
func process_movement(delta):
    # Get the normalized input direction so that we don't move faster on diagonals
    var wish_dir = direction.normalized()

    if is_on_floor():
        # If wish_jump is true then we won't apply any friction and allow the 
        # player to jump instantly, this gives us a single frame where we can 
        # perfectly bunny hop
        if wish_jump:
            velocity.y = JUMP_IMPULSE
            # Update velocity as if we are in the air
            velocity = update_velocity_air(wish_dir, delta)
            wish_jump = false
        else:
            if walking:
                velocity.x *= PLAYER_WALKING_MULTIPLIER
                velocity.z *= PLAYER_WALKING_MULTIPLIER
            
            velocity = update_velocity_ground(wish_dir, delta)
    else:
        # Only apply gravity while in the air
        velocity.y -= GRAVITY * delta
        velocity = update_velocity_air(wish_dir, delta)

    # Move the player once velocity has been calculated
    move_and_slide()
    
func accelerate(wish_dir: Vector3, max_speed: float, delta):
    # Get our current speed as a projection of velocity onto the wish_dir
    var current_speed = velocity.dot(wish_dir)
    # How much we accelerate is the difference between the max speed and the current speed
    # clamped to be between 0 and MAX_ACCELERATION which is intended to stop you from going too fast
    var add_speed = clamp(max_speed - current_speed, 0, MAX_ACCELERATION * delta)
    
    return velocity + add_speed * wish_dir
    
func update_velocity_ground(wish_dir: Vector3, delta):
    # Apply friction when on the ground and then accelerate
    var speed = velocity.length()
    
    if speed != 0:
        var control = max(STOP_SPEED, speed)
        var drop = control * friction * delta
        
        # Scale the velocity based on friction
        velocity *= max(speed - drop, 0) / speed
    
    return accelerate(wish_dir, MAX_VELOCITY_GROUND, delta)
    
func update_velocity_air(wish_dir: Vector3, delta):
    # Do not apply any friction
    return accelerate(wish_dir, MAX_VELOCITY_AIR, delta)

# Apply impulse to the character
func apply_impulse(_position: Vector3, impulse: Vector3):
# Apply the impulse to the character's velocity
    velocity += impulse


This is my projectile

extends RigidBody3D

signal bullet_collision

var shoot = false

const DAMAGE = 50
const SPEED = 10
var explosion = preload("res://explosion.tscn")

func _ready():
    set_as_top_level(true)

func _physics_process(_delta):
    if shoot:
        apply_impulse(-transform.basis.z, transform.basis.z)    

func _on_body_entered(body):
        emit_signal("bullet_collision", body)
        queue_free

func _on_area_3d_body_entered(body):
    if body.is_in_group("Enemy"):
        #smth smth spawn explosion
        body.health -= DAMAGE
        queue_free()
    else:
        queue_free()
    if body.is_in_group("Player"):
        #smth smth spawn explosion
        pass

This is my explosion script

extends Area3D

@export var speed = 10.0

func _on_body_entered(body):
    var vector = body.global_transform.origin - global_transform.origin
    var direction = vector.normalized()
    var distance_squared = vector.length_squared()
    var velocity = direction * speed/distance_squared
    
    if body.is_in_group("Player"): 
        body.velocity += velocity

this is my scene for reference enter image description here

Further note: the explosion script comes from a stackoverflow (how do you make rocket jumping in godot) response about rocket jumping.

The difficulties arise when trying to make this part "When the projectile collides with the scenario or another character, you will spawn the explosion scene, and queue_free the projectile"

I've tried with chatgpt, a lot. Most of the time it works and I just need to change around a few things to work. However this time it won't work. this is the response I get:

"The error message indicates that the second argument in the connect() function should be a callable, but you've passed a file path (res://Player-copy.gd). This typically happens when you have an erroneous or incomplete path for the function to connect to.

Ensure that the path is correct, and also make sure the function _on_bullet_collision is properly defined in your script. If it's defined but the error persists, double-check the path you're providing in the connect() function.

If the issue persists, please provide the relevant section of your code where the connect() function is called so I can assist you further."

For reference

And the code block I added from chat gpt is this for the script: Player-copy.gd

    $bullet.connect("bullet_collision",self,"_on_bullet_collision")

func _on_bullet_collision(body):
        var explosion_instance = load("res://explosion.tscn").instantiate()
        explosion_instance.global_transform.origin = body.global_transform.origin
        get_tree().current_scene.add_child(explosion_instance)

Upvotes: 0

Views: 63

Answers (1)

Bugfish
Bugfish

Reputation: 1729

The problem should be this line:

 $bullet.connect("bullet_collision",self,"_on_bullet_collision")

First Problem: The $ does not make sense, because its just a shortcut for get_node. And you do not have a node bullet in your scene as far as I see. So this line should actually be called with a $ nor should it be called on your resource variable "bullet". Instead it should be connected in your process_input function when you create an instance of the bullet.

Second Problem: Looking at your tags i assume your using Godot 4. The syntax for connecting signals have changed from Godot 3 to 4.

To connect correctly to the signal your process_input code should look like this:

#character controller
func process_input():
    #other code
    ...
    if Input.is_action_just_pressed("sec_fire"):
            if raycast.is_colliding():
                var b = bullet.instantiate()
                b.bullet_collision.connect(_on_bullet_collision) # connect the signal here
                muzzle.add_child(b)
                b.look_at(raycast.get_collision_point(), Vector3.UP)
                b.shoot = true

if you are not on godot 4 replace the newly added line with:

b.connect("bullet_collision",self,"_on_bullet_collision")

Upvotes: 1

Related Questions