Change character during the game

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By MightyRat

Hi! I’m very noob so I would love a “slow” explanation. I want to make some power ups
like super mario (fire, ice etc…), how can I change player’s script when he touchs the
power ups? I would like to destroy the old player and add a new one. I found this script on this forum: extends Area2D

“action_when_body_is_touched” & “SimpleG” are the groups where the players belongs.

onready var player = get_tree().get_nodes_in_group(“action_when_body_is_touched”)[0]
onready var player2 = get_tree().get_nodes_in_group(“SimpleG”)[0]
var pos = Vector2()

func _ready():
pass

func _on_Food_body_entered(body):
if body.get_name() == “Player”: # Player is the name of the kinematicBody2D
# code for kill the pizza
queue_free()
emit_signal(“hit”)
$shape.disabled = true # shape should be the name of your collisionShape2d, by default: CollisionShape2D

	# code for kill the player and set the new one.
	player.queue_free()
	pos = player.get_global_position()
	player2.position = pos
	player2.show()

But I can’t understand some things: When I set player2 on my scene I become player2 so how can I add them to groups?
What is that index number in groups?
What is pizza
So if someone can explain me this code it would be great.
Thanks!

:bust_in_silhouette: Reply From: njamster

how can I add them to groups?

Explained here.

What is that index number in groups?

get_nodes_in_group returns an Array (as documented here). The index number is used to access a certain element in that list, in your case: the first. Programming languages often start counting elements from 0 - that’s called 0-indexing.

What is pizza

Italian food. Very taste, you should try it… :wink:

if someone can explain me this code

Jokes aside: the script is attached to a node of type Area2D, apparently representing a food-item (namely: a pizza). If a body (i.e. a KinematicBody2D, a RigidBody2D or a StaticBody2D) named “Player” (exactly like this, names are case-sensitive, a node called “player” or “PLAYER” will not match!) enters that area, the pizza is deleted as soon as that is possible without breaking something else (that’s what’s queue_free() is doing, on the other hand free would delete the node immediately but make break things in your game, if other nodes still rely on this node being there) and emits a signal called “hit” that other nodes can connect to, if they want to react to this event. For example your player could play an animation when that signal is emitted or a counter could increase it’s value by 1 each time. Last but not least the collision shape of the pizza is disabled. So even if it isn’t deleted immediately, it cannot trigger the _on_Food_body_entered-method again. Now the pizza is gone for good! :smiley:

The second part is then ordering the node saved in the player-variable to delete itself as soon as possible, sets the position of the node saved in the player2-variable to the position of the node saved in the player-variable and then makes that node visible. That’s the part where the player characters are swapped out.

In order for this code to work you…

  1. need to make sure the Area2D’s body_entered-signal is connected to the method _on_Food_body_entered
  2. the root-node of your default player-scene is in the group action_when_body_is_touched (again: exactly like this, case-sensitive)
  3. the root-node of your alternative player-scene is in the group SimpleG

That being said I don’t think it’s a very good example to learn from. Here’s what I would do: Create a new scene for the item, make it an Area2D. Add a Sprite and a CollisionShape2D as children. Your tree should look like this now:

- Area2D
  - Sprite
  - CollisionShape2D

Next, add the following script to the Area2D-node:

extends Area2D

func _ready():
    connect("body_entered", self, "_on_body_entered")

func _on_body_entered(body):
    if body.name = "Player":
        $CollisionShape2D.disabled = true
        self.hide()

        if body.has_method("transform"):
            body.transform()

        queue_free()

Now create a default player-scene, let’s say a KinematicBody2D, again with a Sprite and a CollisionShape2D, and attach the following script to it:

extends KinematicBody2D

var transformed = false

# ... other stuff like the movement code

func transform():
    if not transformed:
        transformed = true
        # change what you want to change here, e.g.:
        $Sprite.texture = load("res://icon.png")

I would like to destroy the old player and add a new one.

Switching out the entire player-character is a radical measure that is (imho) rarely justified. And judging from your description, it isn’t in your case as well! Chances are you want to change only certain aspects and keep everything else intact.

However, if you really want to do this, there’s is no need for groups:

extends Area2D
    
var transformed_player = preload("<PathToAlternativePlayerScene>")

func _ready():
    connect("body_entered", self, "_on_body_entered")
			
func _on_body_entered(body):
	if body.name == "Player":
        $CollisionShape2D.disabled = true
        self.hide()

        body.hide()
        body.get_node("CollisionShape2D").disabled = true
        var new_player = transformed_player.instance()
        new_player.global_position = body.global_position
        get_parent().add_child(new_player)
        body.queue_free()

        queue_free()

I would love a “slow” explanation

I hope this qualifies as one. It certainly is a long one… ;D

Wow! Thanks man!

MightyRat | 2020-05-16 00:28

Maybe a bit late, but what would happen to all the functions where I wrote if body name == (“player”) ? Will the new trasformed one cause problems?

MightyRat | 2020-05-22 16:58

Not sure if I can follow… What do you mean by “all the functions” and “the new transformed one”? If you wrote if body.name == "Player", that won’t cause problems. Unless of course you rename your player node.

njamster | 2020-05-23 10:48

I made a portal with if body.name == “player” …
And when the player get trasformed the portal doesn ‘t work anymore

MightyRat | 2020-05-24 00:01

Then your transformed player scene isn’t named “player”!

Note that the names of nodes on the same layer have to be unique, so if you already have an instance called “player” and add another instance called “player”, the second one will be renamed by Godot in order to fulfill this constraint.

That’s why in many case it’s a bad idea to check a node’s name. Add both player nodes (the original and the transformed one) to a group “Player Character” and then check if a body is in that group with body.is_in_group("Player Character").

njamster | 2020-05-24 11:47

An other problem is how to access to the node of the new player, maybe it’s too much
complexity for my game…

MightyRat | 2020-05-25 09:50

When you instance the transformed player scene, the instance is returned and stored in new_player. As long as you keep that variable around, you can use it to access the node. You could for example store it in a singleton. And as long as you now the name of the instance (you can print it after instancing using print(new_player.name)), you can simply search for that node by its name (starting from the root of the tree) as well: get_tree().get_root().find_node("<NodeName>"). However, that shouldn’t really be necessary: you know where you added the node and how it is named.

njamster | 2020-05-25 12:16