General question about the logic structure (player hits and enemies)

Hi,

I’m still learning, but I already have a small game up and running. I’m currently cleaning up the code and trying to optimize it.

I have a general question for the more experienced among you regarding the overall structure:

  1. I have a pickup (root Area2D, outsourced in a scene) that, when touched by the player, emits a signal (for scoring points in the GameManager) and then becomes invisible. This means that the pickup basically manages itself. :check_box_with_check:

  2. I have an enemy (root CharacterBody2D, outsourced in a scene) that also disappears when its Area2D collision is touched by the player. Here, too, the element manages itself. :check_box_with_check:

The player (root CharacterBody2D, outsourced in a scene) is now giving me a headache. It has a health variable. This should be counted down when the player touches an enemy (or an enemy runs into the player). If the counter is <= 0, the player should fall off the platform with a given animation from the animationplayer.

Question: Where do I put all the logic for the player?

  • Do I store the player’s health variable in a global GameManager script,
    or directly in the player script?

  • How should the ‘kill player’ action be handled?
    Is it executed from a global GameManager, or is it managed in the player script? Or from the enemy script? Or by a signal? I don’t know!

  • What is the best strategy for detecting hits on the player?
    Does this happen inside the enemy or in the player script?

  • Where does it make sense to give signals (GameManager, enemy, player)?

  • And from where do I check the health value and make the player fall off the platform and start again? There are so many possibilities, and I don’t know which one is the best and most sensible.

Do you have any general structural tips for me?

Thank you very much!

Jeff

I like to keep health on the player script, and give a function for taking damage. This way the enemy can detect player collisions and deal appropriate damage.

For example the enemy may have the following function connected to their hitbox’s body_entered signal.

func _on_hitbox_body_entered(body: Node2D) -> void:
    if body is Player:
        body.take_damge(1)
1 Like

Get rid of the GameManager.

2 Likes

Thank you for your thoughts. Both are valid.
I took the following approach:

  • My level scene now contains:

    • all the platforms (individual scenes)
    • pickups (individual scenes)
    • enemies (individual scene)
    • the player (individual scene)
    • the canvas (individual scene)
    • and the score.
  • If the enemy is hit, it destroys itself inside its own script.

  • If a pickup is picked up, it destroys itself
    and calls a function in my level scene, which adds points and updates the canvas.

  • If the player is hit, its health decreases inside its own script,
    and calls a function in the canvas directly for an update.

I have eliminated all manual signals and the Global Manager. The code now appears less complex and cleaner.

Thank you.

When creating the second level, I realized that I have to do it a little differently.
In the meantime, I had to introduce signals so that each element can manage itself and change its values. I put the signals for this in an “autoload” script so that I can fire (emit) them from everywhere. The score is also global.