How can I make the player pick up coins not initially there at runtime?

Godot Version

4.3.stable

Question

So I have a very basic player script at the moment and coins I can spawn where I click. I’d like the player to be able to pick up these coins and increment a coin counter by a value depending on which coin they pick up.

From what I can guess, I need to use the on_body_entered signal somewhere and make something execute whenever the player passes through the coin body, but what I don’t understand is how to do this when the coin does not exist initially. Whenever I try to drop it into the coin script, the option to choose the player is not there, because it doesn’t know its going to be a sibling yet.

I’m sorry I’m very new to Godot, but here is my script for spawning the coins. The player script is just the default Characterbody2D script for now but I do plan to change that later.

extends Node2D

var copper_coin = preload(“res://copper_coin.tscn”)
var silver_coin = preload(“res://silver_coin.tscn”)
var gold_coin = preload(“res://gold_coin.tscn”)
@onready var character_body_2d = $CharacterBody2D

func _physics_process(delta):
if Input.is_action_just_pressed(“left_click”):
var value = randi_range(0,2)

  if value == 0:
  	inst_copper(get_global_mouse_position())
  if value == 1:
  	inst_silver(get_global_mouse_position())
  if value == 2:
  	inst_gold(get_global_mouse_position())

func inst_copper(pos):
var copper_coin_instance = copper_coin.instantiate()
copper_coin_instance.position = pos
copper_coin_instance.linear_velocity = Vector2(randf_range(-100,100),randf_range(-100,-200))
add_child(copper_coin_instance)

func inst_silver(pos):
var silver_coin_instance = silver_coin.instantiate()
silver_coin_instance.position = pos
silver_coin_instance.linear_velocity = Vector2(randf_range(-100,100),randf_range(-100,-200))
add_child(silver_coin_instance)

func inst_gold(pos):
var gold_coin_instance = gold_coin.instantiate()
gold_coin_instance.position = pos
gold_coin_instance.linear_velocity = Vector2(randf_range(-100,100),randf_range(-100,-200))
add_child(gold_coin_instance)

Do the coins have a Area2D? Connect the body_entered signal to the coin script itself, then use and if statement to detect only when it’s the player within the area, by name is a good option or by type

func _on_body_entered(body):
    if body.name == "Player":
        pass # collect coin
        self.queue_free()

I tried adding this to my coin script, but the player doesn’t seem to want to interact with the coin at all. I even included a line “print(body)” as a test and nothing shows up.

The coins are a RigidBody2D and the player is a CharacterBody2D. Maybe it has to do with having them on different collision layers? I had to put them on different layers because I wanted the coins to collide physically with my tilemaplayer but not the player. Perhaps there’s a better method to handling the coins with area2D?

Try an extra Area2D as part of the coin for just detecting the player. They look really good as RigidBodies

This seems to work thanks! I thought I might need some more optimized solution but even spawning hundreds of coins doesn’t seem to cause any lag so I won’t worry about it right now I guess. Thank you so much for your help!

Good collision masks will help reduce lag, you might just have a faster solution than RigidBodies alone!

The rigidbody is still there on top of the Area2D which is why I thought maybe it wasn’t the most optimal option. Would it be more optimal to maybe disable the rigidbody once the coin lands on the ground or is there some way I can just make the coin an Area2D instead of a rigidbody and prevent it from falling through the ground?

RigidBody is good, Godot automatically puts them to “Sleep” when a physics object hasn’t moved for a little over a second. As long as you haven’t disabled sleeping, the coins should be super performant when sleeping.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.