#Need help with bullethell shooting mechanic

22 messages · Page 1 of 1 (latest)

copper pebble
#

Hi im trying to do some kind of vampire survivors clone for learning purposes and i have a little problem with my shooting mechanic. So basically right now my weapon uses timer to shoot arrows towards enemies but i want it to shoot only if there is enemy in range and i cant figure out how to make it work. My code so far looks like this:

extends Area2D

        
func _physics_process(delta: float) -> void:
    var enemies_in_range = get_overlapping_bodies()
    if enemies_in_range.size() > 0:
        var target_enemy = enemies_in_range[0]
        look_at(target_enemy.global_position)

func shoot():
    const ARROW = preload("res://arrow.tscn")
    var new_arrow = ARROW.instantiate()
    new_arrow.global_position = %ShootingPoint.global_position
    new_arrow.global_rotation = %ShootingPoint.global_rotation
    %ShootingPoint.add_child(new_arrow)

func _on_timer_timeout() -> void:
    shoot()

I would be glad if someone could explain to me what I need to do in steps that a total beginner could understand(or look it up on youtube/google). Thanks!

kind timber
#

well, as the code is written right now, there is nothing telling the arrow to shoot if there are enemies in range, it's just telling it to shoot when the timer is 0

#

what you could do is something like

if enemies_in_range.length > 0:
  timer.start()
else
  timer.stop()
copper pebble
#

Yes i know that. I've tried writing some code for it but it would just spit out errors so i deleted it and went back to the "original"

#

Ill try it out

kind timber
#

what kind of error?

copper pebble
#

alright i've added the code you wrote but right now i get an error saying that identifier "timer" is not declared. So it means that i have to add the timer by code, not by adding a child node of timer to my weapon node?

kind timber
#

well the "timer" that I put there is supposed to be the reference to the clock you are using to trigger the shot

copper pebble
#

Im using the "Timer" node to set the attack speed of my weapon and _on_timer_timeout function to trigger the shot

uncut estuary
#

When you figure out the timer thing via create_timer, you should

  1. Move target_enemy initialization out to the class level, not the function level (usually right below the extends line)
  2. Make target_enemy set to null if the size of enemies_in_range is zero or less.
  3. Have a can_shoot variable at class level that is set to true initially.
  4. Shoot immediately when an enemy is in range in _physics_process (if statement under if enemies_in_range.size() ), then set the can_shoot to false, then activate the timer.
  5. When timer is timed out, set variable can_shoot to true again.
#

That makes your weapon shoot first whenever an enemy appears in range, then delay, and if an enemy is in range, shoot again.

Otherwise you'll have to time your player to be in range during the timer activation periods, which isn't what the player would expect

copper pebble
#

Okay, it makes sense logic-wise for me, ill try to figure out the code today. Thank you! c:

kind timber
#

if you take the timer node and drag it to the top of your code while holding CTRL you will create something like:

@onready var timer: Timer = $Path_to_timer

then you can use the timer variable to start or stop the timer

kind timber
zealous zodiac
#

gotcha

copper pebble
#

@zealous zodiac that's where this code is coming from, but i didnt really saw him add the mechanic that i wanted to add so i can try to do something by myself and not just copy someone's tutorial, but thanks!

zealous zodiac
#

make sure the timer is a one shot

#

or add the check for enemies in range in the _on_timer_timeout function

uncut estuary
#

You can also do a tween with a tween_callback and have it set to loop. Save yourself a node and a time_out signal, etc

zealous zodiac
#

I did this for my tower defence:

func _physics_process(_delta: float) -> void:
    if _targets.is_empty():
        return
    if attack_timer.is_stopped():
        shoot(_targets)
        attack_timer.start()


func _on_attack_range_area_entered(area: Area2D) -> void:
    if area is Enemy:
        _targets.append(area)


func _on_attack_range_area_exited(area: Area2D) -> void:
    _targets.erase(area)

Well, a bit more stuff but this should give the gist of it.

#

the timer is a oneshot, non autostart