#Learning Top Down Mechanics, Attempting to Add Bow and Arrow:

95 messages · Page 1 of 1 (latest)

dusty carbon
#

And yeah it's not working.

I'm wanting to be able to shoot via mouse, so I tried to set it up based on that.
The player is 8 directional with animations, and uses a prefix/suffix setup to determine animations
I also elected to separate movement and attack into separate functions that are called on in the physics_process.
Movement works just fine, but when I try to click for attack, it will crash the game and give me this error:
"Invalid call: Nonexistent function 'instance' in base 'PackedScene'.

I'll drop the code in the comments, since it's too long to put here. if you need any more info please let me know.

#


var speed = 100
var last_nonzero_velocity = Vector2.ZERO
var bow_damage = 10
var arrow_speed = 200

func _physics_process(delta):
    handle_movement()
    if Input.is_action_just_pressed("attack"):
        handle_attack()

func handle_movement():
    # Get input
    velocity = Vector2.ZERO
    
    if Input.is_action_pressed("right"):
        velocity.x += 1
    if Input.is_action_pressed("left"):
        velocity.x -= 1
    if Input.is_action_pressed("down"):
        velocity.y += 1
    if Input.is_action_pressed("up"):
        velocity.y -= 1
    
    # Move the player
    velocity = velocity.normalized()
    velocity = velocity * speed
    move_and_slide()
    
    # store the last nonzero velocity
    if velocity != Vector2.ZERO:
        last_nonzero_velocity = velocity
    
    # Set the animation based on the velocity
    if velocity.x != 0 || velocity.y != 0:
        $AnimatedSprite2D.play("walk_" + get_animation_suffix(velocity))  # "walk_" is the animation prefix
    else:
        $AnimatedSprite2D.play("idle_" + get_animation_suffix(last_nonzero_velocity))  # "idle_" is the animation prefix

# Function to get the animation suffix based on the velocity
func get_animation_suffix(velocity):
    velocity = velocity.normalized()
    print(velocity.x)
    print(velocity.y)
    
    if velocity.x == 1:
        return "right"
    elif velocity.y == 1:
        return "down"
    elif velocity.x == -1:
        return "left"
    elif velocity.y == -1:
        return "up"
    
    # Diagonal velocity
    if velocity.x > 0:
        if velocity.y > 0:
            return "down_right"
        else:
            return "up_right"
    else:
        if velocity.y > 0:
            return "down_left"
        else:
            return "up_left"
#
    # Calculate the direction based on mouse position
    var mouse_position = get_global_mouse_position()
    var player_direction = (mouse_position - global_position).normalized()
    
    # Load the arrow scene
    var arrow_scene = preload("res://Instanced_Objects/arrow.tscn")
    assert(arrow_scene, "Error loading arrow scene")

    # Instance the arrow scene
    var arrow_instance = arrow_scene.instance() as Node  # Use the appropriate type

    # Set the arrow's position
    arrow_instance.global_position = global_position
    
    # Set arrow properties (direction, speed, damage, etc.)
    arrow_instance.direction = player_direction
    arrow_instance.damage = bow_damage
    arrow_instance.speed = arrow_speed
    
    # Add the arrow to the scene
    add_child(arrow_instance)

    # Determine the attack animation based on the mouse position
    var attack_animation_suffix = get_animation_suffix(player_direction)

    # Play the appropriate attack animation
    $AnimatedSprite2D.play("attack_" + attack_animation_suffix)
rigid cairn
#

If Godot 4, you're looking for instantiate() rather than instance()

#

@dusty carbon

dusty carbon
#

Alright so, that stopped it from crashing at least. I also added a script to the arrow which I will share. But I recorded a short video to show what it's doing now. Here's the arrow's script:



# Properties
var speed: float = 200
var direction: Vector2 = Vector2.ZERO
var damage: int = 10

func _process(delta):
    # Move the arrow
    move_and_slide()

func _on_area_2d_body_entered(body):
    # Check for collisions with other entities
    if body.has_method("take_damage"):
        body.take_damage(damage)
    queue_free()
#

I can sometimes see the arrow just exist for a split second then disappear

rigid cairn
#

Does player have a take_damage() method? If so, the arrow could be colliding with the player and triggering this code

#

In that case you can change layers/masks around so that the Arrow body's mask does not match the player body's layer.

#

Actually it seems that queue_free() is called on any collision with a body, since it's outside the if statement

#

But yeah I recommend the mask/layer thing to fix it either way

dusty carbon
#

okay oooone sec

#

Okay no dice. the video shows that the arrow appears towards the bottom right of the screen when I click (I circled my mouse in the area) not only that, it doesn't play the attack animation fully. just flashes it for a second.

#

Another issue I just ran into is when I put the queue_free() inside the if statement, well...heres the clip

rigid cairn
#

Mind showing how you have the Arrow set up in its scene? (It might have been dragged away from the origin point of the scene)

dusty carbon
rigid cairn
#

You will also want to add the arrow as child of the main scene node.

#

Seems like right now it's being added as child of your CharacterBody2D

#

(Which would probably give it another "push" since it begins to inherit your player's position)

dusty carbon
#

Okay...so am I to take the handle_attack() function and move it over to a script on the world?

rigid cairn
#

You could do get_parent().add_child(arrow_instance)
or get_tree().current_scene.add_child(arrow_instance)

rigid cairn
#

Let me know if it keeps disappearing. Seems like a separate issue.

dusty carbon
#

Would I put that lil snippet of code you suggested here?

rigid cairn
#

Bit lower

dusty carbon
#

PFFFFT

#

WELL I GOT A RESULT XD

#

hold on one sec lemme get a clip

rigid cairn
#

oh lord

dusty carbon
rigid cairn
#

woah lmao

dusty carbon
#

yup XD

rigid cairn
#

Well seems to be spawning at the player now at least

#

and not disappearing

dusty carbon
#

yeah it'll only disappear instantly if it's queue_free is not in the if statement

rigid cairn
#

Anyway you could try:

#

Well

#

Do you want character to be able to push arrows around / be pushed by arrows?

dusty carbon
#

Not particularly. I was wanting the collider to only determine whether the arrow hits something. not make it so it can be moved.

#

I also need to figure out how to make it rotate towards the mouse on shot and figure out why the player animation is only lasting half a second

rigid cairn
rigid cairn
#

Player animation... make sure it's set to loop.

dusty carbon
#

these are where the collision mask is right? cause the arrow is set to 2, player set to 1

rigid cairn
#

Mm those are the light and visiblity masks, not collision - look a bit further up

#

Make sure the body is selected

dusty carbon
#

ah silly me

#

also, setting the attack animations to loop did just about nothing

#

it still just plays for one frame

rigid cairn
#

But yeah layer is where the object exists
Mask is which layers it detects

rigid cairn
#

Is constantly running

#

And overwrites this

dusty carbon
#

So two things then:
how do I stop idle from playing constantly only until the attack animation is done
and the arrow, while facing the mouse and not sending me flying anymore, is staying nice and still

rigid cairn
#

Could try

elif !$AnimatedSprite2D.is_playing():
#

just before you set it to idle

#

Or maybe wrap the whole thing in that

if !$AnimatedSprite2D.is_playing():
  if velocity.x != 0 || velocity.y != 0:
    ...
  else:
    ...
rigid cairn
dusty carbon
#

Alrighty that makes the animation play through! Now it just stops on the last frame of attack until I move

dusty carbon
rigid cairn
rigid cairn
dusty carbon
#
    if !$AnimatedSprite2D.is_playing():
        if velocity.x != 0 || velocity.y != 0:
            $AnimatedSprite2D.play("walk_" + get_animation_suffix(velocity))  # "walk_" is the animation prefix
        else:
            $AnimatedSprite2D.play("idle_" + get_animation_suffix(last_nonzero_velocity))  # "idle_" is the animation prefix```
rigid cairn
#

Odd, it should be setting to idle as soon as you're sitting still and no animations are currently playing.

dusty carbon
#

weird aint it?

#

On a side note, i got the arrow moving

#

just replaced move and slide with that

rigid cairn
#

If you want to preserve collisions (make the arrow bounce off walls maybe) you'll need to keep using move_and_slide() - just set the built-in velocity to the direction in _ready()

#

Otherwise you can use position its no big deal

dusty carbon
#

oh I don't want it bouncing. just disappearing on collision

#

oop two new problems XD

rigid cairn
dusty carbon
#

ikr

#

righty-o so, i shoot, and the arrow, despite me increasing the speed, moves at a snails pace, and also now the animation, once finished, stays there. and only changes when I shoot in a different direction

#

this is just...vexing

rigid cairn
#

You are multiplying by delta which is a value that is very small. (0.01-0.10) You'll need to make speed much larger

#

100-1000

#

But yeah I am very confused as to why it's not changing to idle animation when you're 1. sitting still and 2. no animations are playing

dusty carbon
#

no dice. I also just purged the delta multiplication and arrow goes at mach fuck, still cant change the speed tho XD

rigid cairn
#

Last ditch effort before I go AFK

if !$AnimatedSprite2D.is_playing():
        if velocity.is_equal_approx(Vector2.ZERO):
            $AnimatedSprite2D.play("walk_" + get_animation_suffix(velocity))  # "walk_" is the animation prefix
        else:
            $AnimatedSprite2D.play("idle_" + get_animation_suffix(last_nonzero_velocity))  # "idle_" is the animation prefix
#

(the is_equal_approx() is the change I did)

#

Just in case your velocity is hanging out at just above or below zero, causing the else to never be ran

dusty carbon
#

yup, nope. now even moving it stays on idle_down unless I shoot, in which case same as before it stays at the end of attack animation

rigid cairn
#

foiled again

dusty carbon
#

XD F homie, F

rigid cairn
#

I'll be back soonish, if you still need help I can keep at it

#

best of luck

dusty carbon
#

Aight go do you man

dusty carbon
#

Here's what I tried before waiting for a savior:
I tried adding a ready function such as:

    $AnimatedSprite2D._on_animated_sprite_2d_animation_finished.connect("animation_finished")```

Then I changed up the handle_attack function as such:

```func handle_attack():
    # Calculate the direction based on mouse position
    var mouse_position = get_global_mouse_position()
    var player_direction = (mouse_position - global_position).normalized()
    
    # Load the arrow scene
    var arrow_scene = preload("res://Instanced_Objects/arrow.tscn")
    assert(arrow_scene, "Error loading arrow scene")

    # Instance the arrow scene
    var arrow_instance = arrow_scene.instantiate() as Node  # Use the appropriate type

    # Set the arrow's position
    arrow_instance.global_position = global_position
    
    # Set arrow properties (direction, speed, damage, etc.)
    arrow_instance.direction = player_direction
    arrow_instance.damage = bow_damage
    arrow_instance.speed = arrow_speed
    
    # Add the arrow to the scene
    get_tree().current_scene.add_child(arrow_instance)
    arrow_instance.look_at(get_global_mouse_position())

    # Determine the attack animation based on the mouse position
    var attack_animation_suffix = get_animation_suffix(player_direction)

    # Play the appropriate attack animation
    $AnimatedSprite2D.play("attack_" + attack_animation_suffix)
    
    if $AnimatedSprite2D.animation_finished("attack_"):
        attacking = false


func _on_animated_sprite_2d_animation_finished():
    pass # Replace with function body.

Tried that, now it crashes immediately saying:
"Invalid getindex '_on_animated_sprite_2d_animation_finished' (on base: 'AnimatedSprite2D')"

#

oh I also added a variable "attacking" and set it to false, changing to true when the attack button is pressed

dusty carbon
#

FIGURED THE ARROW ISSUE OUT I'M DUMB

dusty carbon
#

HAHAHA

#

Got a bud who knows python really well, he downloaded godot and figured it out for me

#

So this is what he did: