1

i suck at coding. I am currently trying to make a 2D box that can be destroyed when the player attacks, however (like i said before) i suck at coding. I managed to get it working somewhat (and by somewhat i mean not at all) the box has an animation that plays when the player attacks when in range, but the animation almost never plays (sometimes it does but idk why)

code for box

extends Area2D

var inside = false
var attacking = false
    
func _physics_process(delta):
    pass
func _on_Area2D_body_entered(body):
    if Input.is_action_just_pressed("Attack"):
        $AnimationPlayer.play("box_hit")
        $boxdeathtimer.set_wait_time(0.5)
        $boxdeathtimer.start()

func _on_boxdeathtimer_timeout():
    queue_free()

code for weapon (if needed)

extends RigidBody2D

var picked = false

func _ready(): 
    Global.weapon = self
func _exit_tree():
    Global.weapon = null

var attacking = false

func _physics_process(delta):
    if picked == true:
        self.position = get_node("../player/Position2D").global_position

func _input(event):
    if Input.is_action_just_pressed("e"): #picks up weapon when in range
        var bodies = $detector.get_overlapping_bodies()
        for b in bodies:
            if (b.get_name() == "player"):
                picked = true
                sleeping = true
                rotation_degrees = 90
    if Input.is_action_just_pressed("Attack"):
        
        if picked == true && Global.player.facing_right == true:
            $AnimationPlayer.play("attack")     
            attacking = true
        if picked == true && Global.player.facing_right == false:
            $AnimationPlayer.play("attack2")
            attacking = true
        
Ajajajaj
  • 49
  • 9
  • Presuming `Global.player` is `get_node("../player/")`, you can check `if (b == Global.player)` in the code for picking up the weapon. In fact, you could just check `$detector.overlaps_body(Global.player)`. Furthermore, to have the weapon move with the player, you can do this: `get_parent().remove_child(self); Global.player.get_node("Position2D").add_child(self)`, and you would not have to teleport the weapon every physics frame. Edit: removing it self like that would trigger `_exit_tree`. Edit 2: see also `is_queued_for_deletion` and `is_instance_valid`. – Theraot Nov 30 '21 at 05:38

1 Answers1

1

The body_entered signal notifies you when a physics body enters the area. The method is_action_just_pressed tells you if the (key associated with the) action were pressed the same (graphics, by default) frame.

So, in your code, everything inside here (Presuming the signal is properly connected):

func _on_Area2D_body_entered(body):
    if Input.is_action_just_pressed("Attack"):
        # CODE HERE

Can only run if the player pressed (the keys associated with the) action the same (graphics, by default) frame that a body entered the area, which is very hard to manage.


My suggestion is to give a "range of attack" area to the weapon. Then when the player attacks, you can use the signals of that area as it moves.


By the way, Avoid using is_action_just_pressed in _input. It is not intended for that. See Godot - Input.is_action_just_pressed() runs twice. In fact, I would argue to just use Input in _physics_process, unless you really need _input. See the link for what to replace is_action_just_pressed with, if you are working in _input.


So, it can look like this:

On the player side:

func _physics_process(delta):
    # … some other code …
    if Input.is_action_just_pressed("Attack"):
        if picked == true && Global.player.facing_right == true:
            $AnimationPlayer.play("attack")     
            attacking = true
        if picked == true && Global.player.facing_right == false:
            $AnimationPlayer.play("attack2")
            attacking = true
        if attacking:
            $RangeOfAttack.monitoring = true
            yield($AnimationPlayer, "animation_finished")
            $RangeOfAttack.monitoring = false
            # attacking = false # ?

func _on_RangeOfAttack_area_entered(area):
    if area.has_method("attacked"):
        area.attacked()

Where:

  • yield($AnimationPlayer, "animation_finished") is telling Godot to continue the execution after the $AnimationPlayer emits the "animation_finished"signal.
  • $RangeOfAttack refers to te range of attack area. Child node of the weapon.
  • _on_RangeOfAttack_area_entered is connected to the "area_entered" signal of the range of attack area. The reason I'm using "area_entered" instead of "body_entered" is that you made the box an area. If box weren't an area but some physics body, you could use the "body_entered" signal of the weapon (which is a RigidBody2D), and you would have no need for $RangeOfAttack.

On the target side:

func attacked():
   $AnimationPlayer.play("box_hit")
   yield($AnimationPlayer, "animation_finished")
   queue_free()
Theraot
  • 31,890
  • 5
  • 57
  • 86
  • im sorry, im really dumb what is the target side. – Ajajajaj Dec 01 '21 at 22:29
  • edit: im assuming the target side was the box that i wanted to get destroyed on attack. It works but the animation box_hit just loops – Ajajajaj Dec 01 '21 at 22:53
  • edit: nevermind im just dumb i got it to work thank you! – Ajajajaj Dec 01 '21 at 22:54
  • edit: nevermind i couldnt get it to work imma just give up for now – Ajajajaj Dec 01 '21 at 23:03
  • @JacobWhisenand yes, the target side would be the box, or whatever it is you want to take attacks. About the animation, I guess it was set to loop in the animation player. Then you say it didn't work, I have no idea what you tried or what other problem did you run into. – Theraot Dec 02 '21 at 05:17
  • what would the code look like if i changed the box to a kinematic body? – Ajajajaj Dec 02 '21 at 23:46
  • @JacobWhisenand The same approach would work, except you would not be detecting a area but a body, thus you don't need `area_entered` but `body_entered`. Also, the weapon is a rigid body, and rigid bodies have a `body_entered` signal you can use, thus you probably don't need to add an area for the detection. If you are going to use the rigid body signal, the fact that you are doing a teleport (`self.position = get_node("../player/Position2D").global_position`) could be a problem. In that case, you could have weapon become a child of the character, as I said in the comment under the question. – Theraot Dec 03 '21 at 05:02
  • @JacobWhisenand Something else about using the rigid body signal: you would need to set `contact_monitor` and `contacts_reported` in the rigid body. But that is only if you go that route. The area has no problem detecting bodies. Also, by the way, you can always add an area as child of the kinematic body, and if you do that, you won't need to change the code on the weapon side. – Theraot Dec 03 '21 at 05:54